%%
%% %CopyrightBegin%
%%
  Copyright Ericsson AB 2001 - 2022 . 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
%%
%%     -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),
    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).


 %CopyrightBegin%


 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     -2.0

 Unless required by applicable law or agreed to in writing, software
 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.
 Start of Abstract Format
 Types and specs, among other things...
 See also lib/stdlib/{src/erl_bits.erl,include/erl_bits.hrl}.
 End of Abstract Format
 keep track of line info in tokens
 Entry points compatible to old erl_parse.
 entry points working.
  Convert between the abstract form of a term and a term.
 Atom dot-notation, as in 'foo.bar.baz'
 Special case for unary +/-.
Weird, but compatible!
  Generate a list of tokens representing the abstract term.
 Give the relative precedences of operators.
  Copyright Ericsson AB 2001 - 2022 . All Rights Reserved .
  Licensed under the Apache License , Version 2.0 ( the " License " ) ;
  distributed under the License is distributed on an " AS IS " BASIS ,


-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]).


-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()}.

-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'.

-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().


-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).

-define(line(Tup), element(2, Tup)).

  These really suck and are only here until Calle gets multiple

-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.


-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),
    B;
normalise({cons,_,Head,Tail}) ->
    [normalise(Head)|normalise(Tail)];
normalise({tuple,_,Args}) ->
    list_to_tuple(normalise_list(Args));
normalise({op,_,'+',{char,_,I}}) -> I;
normalise({op,_,'+',{integer,_,I}}) -> I;
normalise({op,_,'+',{float,_,F}}) -> F;
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([]) ->
    [].


-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].


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).
