%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%% A stripped version of erl_parse.yrl.
%%%
%%% A type for the abstract format with *external* types has been added.
%%% The type of the abstract format is not up-to-date, but it does not
%%% matter since the purpose of the type is to stress the conversion
%%% of type forms to erl_type().
-module(big_external_type).
-export([parse_form/1,parse_exprs/1,parse_term/1]).
-export([normalise/1,tokens/1,tokens/2]).
-export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]).
-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0,
error_info/0]).
%% Start of Abstract Format
-type line() :: erl_scan:line().
-export_type([af_record_index/0, af_record_field/1, af_record_name/0,
af_field_name/0, af_function_decl/0]).
-export_type([af_module/0, af_export/0, af_import/0, af_fa_list/0,
af_compile/0, af_file/0, af_record_decl/0,
af_field_decl/0, af_wild_attribute/0,
af_record_update/1, af_catch/0, af_local_call/0,
af_remote_call/0, af_args/0, af_local_function/0,
af_remote_function/0, af_list_comprehension/0,
af_binary_comprehension/0, af_template/0,
af_qualifier_seq/0, af_qualifier/0, af_generator/0,
af_filter/0, af_block/0, af_if/0, af_case/0, af_try/0,
af_clause_seq/0, af_catch_clause_seq/0, af_receive/0,
af_local_fun/0, af_remote_fun/0, af_fun/0, af_query/0,
af_query_access/0, af_clause/0,
af_catch_clause/0, af_catch_pattern/0, af_catch_class/0,
af_body/0, af_guard_seq/0, af_guard/0, af_guard_test/0,
af_record_access/1, af_guard_call/0,
af_remote_guard_call/0, af_pattern/0, af_literal/0,
af_atom/0, af_lit_atom/1, af_integer/0, af_float/0,
af_string/0, af_match/1, af_variable/0,
af_anon_variable/0, af_tuple/1, af_nil/0, af_cons/1,
af_bin/1, af_binelement/1, af_binelement_size/0,
af_binary_op/1, af_binop/0, af_unary_op/1, af_unop/0]).
-type abstract_form() :: ?MODULE:af_module()
| ?MODULE:af_export()
| ?MODULE:af_import()
| ?MODULE:af_compile()
| ?MODULE:af_file()
| ?MODULE:af_record_decl()
| ?MODULE:af_wild_attribute()
| ?MODULE:af_function_decl().
-type af_module() :: {attribute, line(), module, module()}.
-type af_export() :: {attribute, line(), export, ?MODULE:af_fa_list()}.
-type af_import() :: {attribute, line(), import, ?MODULE:af_fa_list()}.
-type af_fa_list() :: [{function(), arity()}].
-type af_compile() :: {attribute, line(), compile, any()}.
-type af_file() :: {attribute, line(), file, {string(), line()}}.
-type af_record_decl() ::
{attribute, line(), record, ?MODULE:af_record_name(), [?MODULE:af_field_decl()]}.
-type af_field_decl() :: {record_field, line(), ?MODULE:af_atom()}
| {record_field, line(), ?MODULE:af_atom(), ?MODULE:abstract_expr()}.
%% Types and specs, among other things...
-type af_wild_attribute() :: {attribute, line(), ?MODULE:af_atom(), any()}.
-type af_function_decl() ::
{function, line(), function(), arity(), ?MODULE:af_clause_seq()}.
-type abstract_expr() :: ?MODULE:af_literal()
| ?MODULE:af_match(?MODULE:abstract_expr())
| ?MODULE:af_variable()
| ?MODULE:af_tuple(?MODULE:abstract_expr())
| ?MODULE:af_nil()
| ?MODULE:af_cons(?MODULE:abstract_expr())
| ?MODULE:af_bin(?MODULE:abstract_expr())
| ?MODULE:af_binary_op(?MODULE:abstract_expr())
| ?MODULE:af_unary_op(?MODULE:abstract_expr())
| ?MODULE:af_record_access(?MODULE:abstract_expr())
| ?MODULE:af_record_update(?MODULE:abstract_expr())
| ?MODULE:af_record_index()
| ?MODULE:af_record_field(?MODULE:abstract_expr())
| ?MODULE:af_catch()
| ?MODULE:af_local_call()
| ?MODULE:af_remote_call()
| ?MODULE:af_list_comprehension()
| ?MODULE:af_binary_comprehension()
| ?MODULE:af_block()
| ?MODULE:af_if()
| ?MODULE:af_case()
| ?MODULE:af_try()
| ?MODULE:af_receive()
| ?MODULE:af_local_fun()
| ?MODULE:af_remote_fun()
| ?MODULE:af_fun()
| ?MODULE:af_query()
| ?MODULE:af_query_access().
-type af_record_update(T) :: {record,
line(),
?MODULE:abstract_expr(),
?MODULE:af_record_name(),
[?MODULE:af_record_field(T)]}.
-type af_catch() :: {'catch', line(), ?MODULE:abstract_expr()}.
-type af_local_call() :: {call, line(), ?MODULE:af_local_function(), ?MODULE:af_args()}.
-type af_remote_call() :: {call, line(), ?MODULE:af_remote_function(), ?MODULE:af_args()}.
-type af_args() :: [?MODULE:abstract_expr()].
-type af_local_function() :: ?MODULE:abstract_expr().
-type af_remote_function() ::
{remote, line(), ?MODULE:abstract_expr(), ?MODULE:abstract_expr()}.
-type af_list_comprehension() ::
{lc, line(), ?MODULE:af_template(), ?MODULE:af_qualifier_seq()}.
-type af_binary_comprehension() ::
{bc, line(), ?MODULE:af_template(), ?MODULE:af_qualifier_seq()}.
-type af_template() :: ?MODULE:abstract_expr().
-type af_qualifier_seq() :: [?MODULE:af_qualifier()].
-type af_qualifier() :: ?MODULE:af_generator() | ?MODULE:af_filter().
-type af_generator() :: {generate, line(), ?MODULE:af_pattern(), ?MODULE:abstract_expr()}
| {b_generate, line(), ?MODULE:af_pattern(), ?MODULE:abstract_expr()}.
-type af_filter() :: ?MODULE:abstract_expr().
-type af_block() :: {block, line(), ?MODULE:af_body()}.
-type af_if() :: {'if', line(), ?MODULE:af_clause_seq()}.
-type af_case() :: {'case', line(), ?MODULE:abstract_expr(), ?MODULE:af_clause_seq()}.
-type af_try() :: {'try',
line(),
?MODULE:af_body(),
?MODULE:af_clause_seq(),
?MODULE:af_catch_clause_seq(),
?MODULE:af_body()}.
-type af_clause_seq() :: [?MODULE:af_clause(), ...].
-type af_catch_clause_seq() :: [?MODULE:af_clause(), ...].
-type af_receive() ::
{'receive', line(), ?MODULE:af_clause_seq()}
| {'receive', line(), ?MODULE:af_clause_seq(), ?MODULE:abstract_expr(), ?MODULE:af_body()}.
-type af_local_fun() :: {'fun', line(), {function, function(), arity()}}.
-type af_remote_fun() ::
{'fun', line(), {function, module(), function(), arity()}}
| {'fun', line(), {function, ?MODULE:af_atom(), ?MODULE:af_atom(), ?MODULE:af_integer()}}.
-type af_fun() :: {'fun', line(), {clauses, ?MODULE:af_clause_seq()}}.
-type af_query() :: {'query', line(), ?MODULE:af_list_comprehension()}.
-type af_query_access() ::
{record_field, line(), ?MODULE:abstract_expr(), ?MODULE:af_field_name()}.
-type abstract_clause() :: ?MODULE:af_clause() | ?MODULE:af_catch_clause().
-type af_clause() ::
{clause, line(), [?MODULE:af_pattern()], ?MODULE:af_guard_seq(), ?MODULE:af_body()}.
-type af_catch_clause() ::
{clause, line(), [?MODULE:af_catch_pattern()], ?MODULE:af_guard_seq(), ?MODULE:af_body()}.
-type af_catch_pattern() ::
{?MODULE:af_catch_class(), ?MODULE:af_pattern(), ?MODULE:af_anon_variable()}.
-type af_catch_class() ::
?MODULE:af_variable()
| ?MODULE:af_lit_atom(throw) | ?MODULE:af_lit_atom(error) | ?MODULE:af_lit_atom(exit).
-type af_body() :: [?MODULE:abstract_expr(), ...].
-type af_guard_seq() :: [?MODULE:af_guard()].
-type af_guard() :: [?MODULE:af_guard_test(), ...].
-type af_guard_test() :: ?MODULE:af_literal()
| ?MODULE:af_variable()
| ?MODULE:af_tuple(?MODULE:af_guard_test())
| ?MODULE:af_nil()
| ?MODULE:af_cons(?MODULE:af_guard_test())
| ?MODULE:af_bin(?MODULE:af_guard_test())
| ?MODULE:af_binary_op(?MODULE:af_guard_test())
| ?MODULE:af_unary_op(?MODULE:af_guard_test())
| ?MODULE:af_record_access(?MODULE:af_guard_test())
| ?MODULE:af_record_index()
| ?MODULE:af_record_field(?MODULE:af_guard_test())
| ?MODULE:af_guard_call()
| ?MODULE:af_remote_guard_call().
-type af_record_access(T) ::
{record, line(), ?MODULE:af_record_name(), [?MODULE:af_record_field(T)]}.
-type af_guard_call() :: {call, line(), function(), [?MODULE:af_guard_test()]}.
-type af_remote_guard_call() ::
{call, line(), atom(), ?MODULE:af_lit_atom(erlang), [?MODULE:af_guard_test()]}.
-type af_pattern() :: ?MODULE:af_literal()
| ?MODULE:af_match(?MODULE:af_pattern())
| ?MODULE:af_variable()
| ?MODULE:af_anon_variable()
| ?MODULE:af_tuple(?MODULE:af_pattern())
| ?MODULE:af_nil()
| ?MODULE:af_cons(?MODULE:af_pattern())
| ?MODULE:af_bin(?MODULE:af_pattern())
| ?MODULE:af_binary_op(?MODULE:af_pattern())
| ?MODULE:af_unary_op(?MODULE:af_pattern())
| ?MODULE:af_record_index()
| ?MODULE:af_record_field(?MODULE:af_pattern()).
-type af_literal() :: ?MODULE:af_atom() | ?MODULE:af_integer() | ?MODULE:af_float() | ?MODULE:af_string().
-type af_atom() :: ?MODULE:af_lit_atom(atom()).
-type af_lit_atom(A) :: {atom, line(), A}.
-type af_integer() :: {integer, line(), non_neg_integer()}.
-type af_float() :: {float, line(), float()}.
-type af_string() :: {string, line(), [byte()]}.
-type af_match(T) :: {match, line(), T, T}.
-type af_variable() :: {var, line(), atom()}.
-type af_anon_variable() :: {var, line(), '_'}.
-type af_tuple(T) :: {tuple, line(), [T]}.
-type af_nil() :: {nil, line()}.
-type af_cons(T) :: {cons, line, T, T}.
-type af_bin(T) :: {bin, line(), [?MODULE:af_binelement(T)]}.
-type af_binelement(T) :: {bin_element,
line(),
T,
?MODULE:af_binelement_size(),
type_specifier_list()}.
-type af_binelement_size() :: default | ?MODULE:abstract_expr().
-type af_binary_op(T) :: {op, line(), T, ?MODULE:af_binop(), T}.
-type af_binop() :: '/' | '*' | 'div' | 'rem' | 'band' | 'and' | '+' | '-'
| 'bor' | 'bxor' | 'bsl' | 'bsr' | 'or' | 'xor' | '++'
| '--' | '==' | '/=' | '=<' | '<' | '>=' | '>' | '=:='
| '=/='.
-type af_unary_op(T) :: {op, line(), ?MODULE:af_unop(), T}.
-type af_unop() :: '+' | '*' | 'bnot' | 'not'.
%% See also lib/stdlib/{src/erl_bits.erl,include/erl_bits.hrl}.
-type type_specifier_list() :: default | [type_specifier(), ...].
-type type_specifier() :: af_type()
| af_signedness()
| af_endianness()
| af_unit().
-type af_type() :: integer
| float
| binary
| bytes
| bitstring
| bits
| utf8
| utf16
| utf32.
-type af_signedness() :: signed | unsigned.
-type af_endianness() :: big | little | native.
-type af_unit() :: {unit, 1..256}.
-type af_record_index() ::
{record_index, line(), af_record_name(), af_field_name()}.
-type af_record_field(T) :: {record_field, line(), af_field_name(), T}.
-type af_record_name() :: atom().
-type af_field_name() :: atom().
%% End of Abstract Format
-type error_description() :: term().
-type error_info() :: {erl_scan:line(), module(), error_description()}.
-type token() :: {Tag :: atom(), Line :: erl_scan:line()}.
%% mkop(Op, Arg) -> {op,Line,Op,Arg}.
%% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}.
-define(mkop2(L, OpPos, R),
begin
{Op,Pos} = OpPos,
{op,Pos,Op,L,R}
end).
-define(mkop1(OpPos, A),
begin
{Op,Pos} = OpPos,
{op,Pos,Op,A}
end).
%% keep track of line info in tokens
-define(line(Tup), element(2, Tup)).
%% Entry points compatible to old erl_parse.
%% These really suck and are only here until Calle gets multiple
%% entry points working.
-spec parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} when
Tokens :: [token()],
AbsForm :: abstract_form(),
ErrorInfo :: error_info().
parse_form([{'-',L1},{atom,L2,spec}|Tokens]) ->
parse([{'-',L1},{'spec',L2}|Tokens]);
parse_form([{'-',L1},{atom,L2,callback}|Tokens]) ->
parse([{'-',L1},{'callback',L2}|Tokens]);
parse_form(Tokens) ->
parse(Tokens).
-spec parse_exprs(Tokens) -> {ok, ExprList} | {error, ErrorInfo} when
Tokens :: [token()],
ExprList :: [abstract_expr()],
ErrorInfo :: error_info().
parse_exprs(Tokens) ->
case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of
{ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} ->
{ok,Exprs};
{error,_} = Err -> Err
end.
-spec parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} when
Tokens :: [token()],
Term :: term(),
ErrorInfo :: error_info().
parse_term(Tokens) ->
case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of
{ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[Expr]}]}} ->
try normalise(Expr) of
Term -> {ok,Term}
catch
_:_R -> {error,{?line(Expr),?MODULE,"bad term"}}
end;
{ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[_E1,E2|_Es]}]}} ->
{error,{?line(E2),?MODULE,"bad term"}};
{error,_} = Err -> Err
end.
%% Convert between the abstract form of a term and a term.
-spec normalise(AbsTerm) -> Data when
AbsTerm :: abstract_expr(),
Data :: term().
normalise({char,_,C}) -> C;
normalise({integer,_,I}) -> I;
normalise({float,_,F}) -> F;
normalise({atom,_,A}) -> A;
normalise({string,_,S}) -> S;
normalise({nil,_}) -> [];
normalise({bin,_,Fs}) ->
{value, B, _} =
eval_bits:expr_grp(Fs, [],
fun(E, _) ->
{value, normalise(E), []}
end, [], true),
B;
normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
normalise({tuple,_,Args}) ->
list_to_tuple(normalise_list(Args));
%% Atom dot-notation, as in 'foo.bar.baz'
%% Special case for unary +/-.
normalise({op,_,'+',{char,_,I}}) -> I;
normalise({op,_,'+',{integer,_,I}}) -> I;
normalise({op,_,'+',{float,_,F}}) -> F;
normalise({op,_,'-',{char,_,I}}) -> -I; %Weird, but compatible!
normalise({op,_,'-',{integer,_,I}}) -> -I;
normalise({op,_,'-',{float,_,F}}) -> -F;
normalise(X) -> erlang:error({badarg, X}).
normalise_list([H|T]) ->
[normalise(H)|normalise_list(T)];
normalise_list([]) ->
[].
%% Generate a list of tokens representing the abstract term.
-spec tokens(AbsTerm) -> Tokens when
AbsTerm :: abstract_expr(),
Tokens :: [token()].
tokens(Abs) ->
tokens(Abs, []).
-spec tokens(AbsTerm, MoreTokens) -> Tokens when
AbsTerm :: abstract_expr(),
MoreTokens :: [token()],
Tokens :: [token()].
tokens({char,L,C}, More) -> [{char,L,C}|More];
tokens({integer,L,N}, More) -> [{integer,L,N}|More];
tokens({float,L,F}, More) -> [{float,L,F}|More];
tokens({atom,L,A}, More) -> [{atom,L,A}|More];
tokens({var,L,V}, More) -> [{var,L,V}|More];
tokens({string,L,S}, More) -> [{string,L,S}|More];
tokens({nil,L}, More) -> [{'[',L},{']',L}|More];
tokens({cons,L,Head,Tail}, More) ->
[{'[',L}|tokens(Head, tokens_tail(Tail, More))];
tokens({tuple,L,[]}, More) ->
[{'{',L},{'}',L}|More];
tokens({tuple,L,[E|Es]}, More) ->
[{'{',L}|tokens(E, tokens_tuple(Es, ?line(E), More))].
tokens_tail({cons,L,Head,Tail}, More) ->
[{',',L}|tokens(Head, tokens_tail(Tail, More))];
tokens_tail({nil,L}, More) ->
[{']',L}|More];
tokens_tail(Other, More) ->
L = ?line(Other),
[{'|',L}|tokens(Other, [{']',L}|More])].
tokens_tuple([E|Es], Line, More) ->
[{',',Line}|tokens(E, tokens_tuple(Es, ?line(E), More))];
tokens_tuple([], Line, More) ->
[{'}',Line}|More].
%% Give the relative precedences of operators.
inop_prec('=') -> {150,100,100};
inop_prec('!') -> {150,100,100};
inop_prec('orelse') -> {160,150,150};
inop_prec('andalso') -> {200,160,160};
inop_prec('==') -> {300,200,300};
inop_prec('/=') -> {300,200,300};
inop_prec('=<') -> {300,200,300};
inop_prec('<') -> {300,200,300};
inop_prec('>=') -> {300,200,300};
inop_prec('>') -> {300,200,300};
inop_prec('=:=') -> {300,200,300};
inop_prec('=/=') -> {300,200,300};
inop_prec('++') -> {400,300,300};
inop_prec('--') -> {400,300,300};
inop_prec('+') -> {400,400,500};
inop_prec('-') -> {400,400,500};
inop_prec('bor') -> {400,400,500};
inop_prec('bxor') -> {400,400,500};
inop_prec('bsl') -> {400,400,500};
inop_prec('bsr') -> {400,400,500};
inop_prec('or') -> {400,400,500};
inop_prec('xor') -> {400,400,500};
inop_prec('*') -> {500,500,600};
inop_prec('/') -> {500,500,600};
inop_prec('div') -> {500,500,600};
inop_prec('rem') -> {500,500,600};
inop_prec('band') -> {500,500,600};
inop_prec('and') -> {500,500,600};
inop_prec('#') -> {800,700,800};
inop_prec(':') -> {900,800,900};
inop_prec('.') -> {900,900,1000}.
-type pre_op() :: 'catch' | '+' | '-' | 'bnot' | 'not' | '#'.
-spec preop_prec(pre_op()) -> {0 | 600 | 700, 100 | 700 | 800}.
preop_prec('catch') -> {0,100};
preop_prec('+') -> {600,700};
preop_prec('-') -> {600,700};
preop_prec('bnot') -> {600,700};
preop_prec('not') -> {600,700};
preop_prec('#') -> {700,800}.
-spec func_prec() -> {800,700}.
func_prec() -> {800,700}.
-spec max_prec() -> 1000.
max_prec() -> 1000.
parse(T) ->
bar:foo(T).