%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2001-2015. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%% A copy of small_SUITE_data/src/big_local_type.erl, where
%%% abstract_expr() is opaque. The transformation of forms to types is
%%% now much faster than it used to be, for this module.
-module(big_local_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_anno:line().
-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() :: af_module()
| af_export()
| af_import()
| af_compile()
| af_file()
| af_record_decl()
| af_wild_attribute()
| af_function_decl().
-type af_module() :: {attribute, line(), module, module()}.
-type af_export() :: {attribute, line(), export, af_fa_list()}.
-type af_import() :: {attribute, line(), import, 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, af_record_name(), [af_field_decl()]}.
-type af_field_decl() :: {record_field, line(), af_atom()}
| {record_field, line(), af_atom(), abstract_expr()}.
%% Types and specs, among other things...
-type af_wild_attribute() :: {attribute, line(), af_atom(), any()}.
-type af_function_decl() ::
{function, line(), function(), arity(), af_clause_seq()}.
-opaque abstract_expr() :: af_literal()
| af_match(abstract_expr())
| af_variable()
| af_tuple(abstract_expr())
| af_nil()
| af_cons(abstract_expr())
| af_bin(abstract_expr())
| af_binary_op(abstract_expr())
| af_unary_op(abstract_expr())
| af_record_access(abstract_expr())
| af_record_update(abstract_expr())
| af_record_index()
| af_record_field(abstract_expr())
| af_catch()
| af_local_call()
| af_remote_call()
| af_list_comprehension()
| af_binary_comprehension()
| af_block()
| af_if()
| af_case()
| af_try()
| af_receive()
| af_local_fun()
| af_remote_fun()
| af_fun()
| af_query()
| af_query_access().
-type af_record_update(T) :: {record,
line(),
abstract_expr(),
af_record_name(),
[af_record_field(T)]}.
-type af_catch() :: {'catch', line(), abstract_expr()}.
-type af_local_call() :: {call, line(), af_local_function(), af_args()}.
-type af_remote_call() :: {call, line(), af_remote_function(), af_args()}.
-type af_args() :: [abstract_expr()].
-type af_local_function() :: abstract_expr().
-type af_remote_function() ::
{remote, line(), abstract_expr(), abstract_expr()}.
-type af_list_comprehension() ::
{lc, line(), af_template(), af_qualifier_seq()}.
-type af_binary_comprehension() ::
{bc, line(), af_template(), af_qualifier_seq()}.
-type af_template() :: abstract_expr().
-type af_qualifier_seq() :: [af_qualifier()].
-type af_qualifier() :: af_generator() | af_filter().
-type af_generator() :: {generate, line(), af_pattern(), abstract_expr()}
| {b_generate, line(), af_pattern(), abstract_expr()}.
-type af_filter() :: abstract_expr().
-type af_block() :: {block, line(), af_body()}.
-type af_if() :: {'if', line(), af_clause_seq()}.
-type af_case() :: {'case', line(), abstract_expr(), af_clause_seq()}.
-type af_try() :: {'try',
line(),
af_body(),
af_clause_seq(),
af_catch_clause_seq(),
af_body()}.
-type af_clause_seq() :: [af_clause(), ...].
-type af_catch_clause_seq() :: [af_clause(), ...].
-type af_receive() ::
{'receive', line(), af_clause_seq()}
| {'receive', line(), af_clause_seq(), abstract_expr(), af_body()}.
-type af_local_fun() :: {'fun', line(), {function, function(), arity()}}.
-type af_remote_fun() ::
{'fun', line(), {function, module(), function(), arity()}}
| {'fun', line(), {function, af_atom(), af_atom(), af_integer()}}.
-type af_fun() :: {'fun', line(), {clauses, af_clause_seq()}}.
-type af_query() :: {'query', line(), af_list_comprehension()}.
-type af_query_access() ::
{record_field, line(), abstract_expr(), af_field_name()}.
-type abstract_clause() :: af_clause() | af_catch_clause().
-type af_clause() ::
{clause, line(), [af_pattern()], af_guard_seq(), af_body()}.
-type af_catch_clause() ::
{clause, line(), [af_catch_pattern()], af_guard_seq(), af_body()}.
-type af_catch_pattern() ::
{af_catch_class(), af_pattern(), af_anon_variable()}.
-type af_catch_class() ::
af_variable()
| af_lit_atom(throw) | af_lit_atom(error) | af_lit_atom(exit).
-type af_body() :: [abstract_expr(), ...].
-type af_guard_seq() :: [af_guard()].
-type af_guard() :: [af_guard_test(), ...].
-type af_guard_test() :: af_literal()
| af_variable()
| af_tuple(af_guard_test())
| af_nil()
| af_cons(af_guard_test())
| af_bin(af_guard_test())
| af_binary_op(af_guard_test())
| af_unary_op(af_guard_test())
| af_record_access(af_guard_test())
| af_record_index()
| af_record_field(af_guard_test())
| af_guard_call()
| af_remote_guard_call().
-type af_record_access(T) ::
{record, line(), af_record_name(), [af_record_field(T)]}.
-type af_guard_call() :: {call, line(), function(), [af_guard_test()]}.
-type af_remote_guard_call() ::
{call, line(), atom(), af_lit_atom(erlang), [af_guard_test()]}.
-type af_pattern() :: af_literal()
| af_match(af_pattern())
| af_variable()
| af_anon_variable()
| af_tuple(af_pattern())
| af_nil()
| af_cons(af_pattern())
| af_bin(af_pattern())
| af_binary_op(af_pattern())
| af_unary_op(af_pattern())
| af_record_index()
| af_record_field(af_pattern()).
-type af_literal() :: af_atom() | af_integer() | af_float() | af_string().
-type af_atom() :: 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(), [af_binelement(T)]}.
-type af_binelement(T) :: {bin_element,
line(),
T,
af_binelement_size(),
type_specifier_list()}.
-type af_binelement_size() :: default | abstract_expr().
-type af_binary_op(T) :: {op, line(), T, af_binop(), T}.
-type af_binop() :: '/' | '*' | 'div' | 'rem' | 'band' | 'and' | '+' | '-'
| 'bor' | 'bxor' | 'bsl' | 'bsr' | 'or' | 'xor' | '++'
| '--' | '==' | '/=' | '=<' | '<' | '>=' | '>' | '=:='
| '=/='.
-type af_unary_op(T) :: {op, line(), 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_anno:line(), module(), error_description()}.
-type token() :: {Tag :: atom(), Line :: erl_anno: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).