%% -*- erlang -*-
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-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%
%%
%% Definition of the Erlang grammar.
Nonterminals
form
attribute attr_val
function function_clauses function_clause
clause_args clause_guard clause_body
expr expr_100 expr_150 expr_160 expr_200 expr_300 expr_400 expr_500
expr_600 expr_700 expr_800
expr_max
list tail
list_comprehension lc_expr lc_exprs
binary_comprehension
tuple
%struct
record_expr record_tuple record_field record_fields
map_expr map_tuple map_field map_field_assoc map_field_exact map_fields map_key
if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr
fun_expr fun_clause fun_clauses atom_or_var integer_or_var
try_expr try_catch try_clause try_clauses
function_call argument_list
exprs guard
atomic strings
prefix_op mult_op add_op list_op comp_op
binary bin_elements bin_element bit_expr
opt_bit_size_expr bit_size_expr opt_bit_type_list bit_type_list bit_type
top_type top_type_100 top_types type typed_expr typed_attr_val
type_sig type_sigs type_guard type_guards fun_type fun_type_100 binary_type
type_spec spec_fun typed_exprs typed_record_fields field_types field_type
map_pair_types map_pair_type
bin_base_type bin_unit_type type_200 type_300 type_400 type_500.
Terminals
char integer float atom string var
'(' ')' ',' '->' '{' '}' '[' ']' '|' '||' '<-' ';' ':' '#' '.'
'after' 'begin' 'case' 'try' 'catch' 'end' 'fun' 'if' 'of' 'receive' 'when'
'andalso' 'orelse'
'bnot' 'not'
'*' '/' 'div' 'rem' 'band' 'and'
'+' '-' 'bor' 'bxor' 'bsl' 'bsr' 'or' 'xor'
'++' '--'
'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '=>' ':='
'<<' '>>'
'!' '=' '::' '..' '...'
'spec' 'callback' % helper
dot.
Expect 2.
Rootsymbol form.
form -> attribute dot : '$1'.
form -> function dot : '$1'.
attribute -> '-' atom attr_val : build_attribute('$2', '$3').
attribute -> '-' atom typed_attr_val : build_typed_attribute('$2','$3').
attribute -> '-' atom '(' typed_attr_val ')' : build_typed_attribute('$2','$4').
attribute -> '-' 'spec' type_spec : build_type_spec('$2', '$3').
attribute -> '-' 'callback' type_spec : build_type_spec('$2', '$3').
type_spec -> spec_fun type_sigs : {'$1', '$2'}.
type_spec -> '(' spec_fun type_sigs ')' : {'$2', '$3'}.
spec_fun -> atom : '$1'.
spec_fun -> atom ':' atom : {'$1', '$3'}.
%% The following two are retained only for backwards compatibility;
%% they are not part of the EEP syntax and should be removed.
spec_fun -> atom '/' integer '::' : {'$1', '$3'}.
spec_fun -> atom ':' atom '/' integer '::' : {'$1', '$3', '$5'}.
typed_attr_val -> expr ',' typed_record_fields : {typed_record, '$1', '$3'}.
typed_attr_val -> expr '::' top_type : {type_def, '$1', '$3'}.
typed_record_fields -> '{' typed_exprs '}' : {tuple, ?anno('$1'), '$2'}.
typed_exprs -> typed_expr : ['$1'].
typed_exprs -> typed_expr ',' typed_exprs : ['$1'|'$3'].
typed_exprs -> expr ',' typed_exprs : ['$1'|'$3'].
typed_exprs -> typed_expr ',' exprs : ['$1'|'$3'].
typed_expr -> expr '::' top_type : {typed,'$1','$3'}.
type_sigs -> type_sig : ['$1'].
type_sigs -> type_sig ';' type_sigs : ['$1'|'$3'].
type_sig -> fun_type : '$1'.
type_sig -> fun_type 'when' type_guards : {type, ?anno('$1'), bounded_fun,
['$1','$3']}.
type_guards -> type_guard : ['$1'].
type_guards -> type_guard ',' type_guards : ['$1'|'$3'].
type_guard -> atom '(' top_types ')' : {type, ?anno('$1'), constraint,
['$1', '$3']}.
type_guard -> var '::' top_type : build_def('$1', '$3').
top_types -> top_type : ['$1'].
top_types -> top_type ',' top_types : ['$1'|'$3'].
top_type -> var '::' top_type_100 : {ann_type, ?anno('$1'), ['$1','$3']}.
top_type -> top_type_100 : '$1'.
top_type_100 -> type_200 : '$1'.
top_type_100 -> type_200 '|' top_type_100 : lift_unions('$1','$3').
type_200 -> type_300 '..' type_300 : {type, ?anno('$1'), range,
['$1', '$3']}.
type_200 -> type_300 : '$1'.
type_300 -> type_300 add_op type_400 : ?mkop2('$1', '$2', '$3').
type_300 -> type_400 : '$1'.
type_400 -> type_400 mult_op type_500 : ?mkop2('$1', '$2', '$3').
type_400 -> type_500 : '$1'.
type_500 -> prefix_op type : ?mkop1('$1', '$2').
type_500 -> type : '$1'.
type -> '(' top_type ')' : '$2'.
type -> var : '$1'.
type -> atom : '$1'.
type -> atom '(' ')' : build_gen_type('$1').
type -> atom '(' top_types ')' : build_type('$1', '$3').
type -> atom ':' atom '(' ')' : {remote_type, ?anno('$1'),
['$1', '$3', []]}.
type -> atom ':' atom '(' top_types ')' : {remote_type, ?anno('$1'),
['$1', '$3', '$5']}.
type -> '[' ']' : {type, ?anno('$1'), nil, []}.
type -> '[' top_type ']' : {type, ?anno('$1'), list, ['$2']}.
type -> '[' top_type ',' '...' ']' : {type, ?anno('$1'),
nonempty_list, ['$2']}.
type -> '#' '{' '}' : {type, ?anno('$1'), map, []}.
type -> '#' '{' map_pair_types '}' : {type, ?anno('$1'), map, '$3'}.
type -> '{' '}' : {type, ?anno('$1'), tuple, []}.
type -> '{' top_types '}' : {type, ?anno('$1'), tuple, '$2'}.
type -> '#' atom '{' '}' : {type, ?anno('$1'), record, ['$2']}.
type -> '#' atom '{' field_types '}' : {type, ?anno('$1'),
record, ['$2'|'$4']}.
type -> binary_type : '$1'.
type -> integer : '$1'.
type -> 'fun' '(' ')' : {type, ?anno('$1'), 'fun', []}.
type -> 'fun' '(' fun_type_100 ')' : '$3'.
fun_type_100 -> '(' '...' ')' '->' top_type
: {type, ?anno('$1'), 'fun',
[{type, ?anno('$1'), any}, '$5']}.
fun_type_100 -> fun_type : '$1'.
fun_type -> '(' ')' '->' top_type : {type, ?anno('$1'), 'fun',
[{type, ?anno('$1'), product, []}, '$4']}.
fun_type -> '(' top_types ')' '->' top_type
: {type, ?anno('$1'), 'fun',
[{type, ?anno('$1'), product, '$2'},'$5']}.
map_pair_types -> map_pair_type : ['$1'].
map_pair_types -> map_pair_type ',' map_pair_types : ['$1'|'$3'].
map_pair_type -> top_type '=>' top_type : {type, ?anno('$2'), map_field_assoc,['$1','$3']}.
field_types -> field_type : ['$1'].
field_types -> field_type ',' field_types : ['$1'|'$3'].
field_type -> atom '::' top_type : {type, ?anno('$1'), field_type,
['$1', '$3']}.
binary_type -> '<<' '>>' : {type, ?anno('$1'),binary,
[abstract2(0, ?anno('$1')),
abstract2(0, ?anno('$1'))]}.
binary_type -> '<<' bin_base_type '>>' : {type, ?anno('$1'),binary,
['$2', abstract2(0, ?anno('$1'))]}.
binary_type -> '<<' bin_unit_type '>>' : {type, ?anno('$1'),binary,
[abstract2(0, ?anno('$1')), '$2']}.
binary_type -> '<<' bin_base_type ',' bin_unit_type '>>'
: {type, ?anno('$1'), binary, ['$2', '$4']}.
bin_base_type -> var ':' type : build_bin_type(['$1'], '$3').
bin_unit_type -> var ':' var '*' type : build_bin_type(['$1', '$3'], '$5').
attr_val -> expr : ['$1'].
attr_val -> expr ',' exprs : ['$1' | '$3'].
attr_val -> '(' expr ',' exprs ')' : ['$2' | '$4'].
function -> function_clauses : build_function('$1').
function_clauses -> function_clause : ['$1'].
function_clauses -> function_clause ';' function_clauses : ['$1'|'$3'].
function_clause -> atom clause_args clause_guard clause_body :
{clause,?anno('$1'),element(3, '$1'),'$2','$3','$4'}.
clause_args -> argument_list : element(1, '$1').
clause_guard -> 'when' guard : '$2'.
clause_guard -> '$empty' : [].
clause_body -> '->' exprs: '$2'.
expr -> 'catch' expr : {'catch',?anno('$1'),'$2'}.
expr -> expr_100 : '$1'.
expr_100 -> expr_150 '=' expr_100 : {match,?anno('$2'),'$1','$3'}.
expr_100 -> expr_150 '!' expr_100 : ?mkop2('$1', '$2', '$3').
expr_100 -> expr_150 : '$1'.
expr_150 -> expr_160 'orelse' expr_150 : ?mkop2('$1', '$2', '$3').
expr_150 -> expr_160 : '$1'.
expr_160 -> expr_200 'andalso' expr_160 : ?mkop2('$1', '$2', '$3').
expr_160 -> expr_200 : '$1'.
expr_200 -> expr_300 comp_op expr_300 :
?mkop2('$1', '$2', '$3').
expr_200 -> expr_300 : '$1'.
expr_300 -> expr_400 list_op expr_300 :
?mkop2('$1', '$2', '$3').
expr_300 -> expr_400 : '$1'.
expr_400 -> expr_400 add_op expr_500 :
?mkop2('$1', '$2', '$3').
expr_400 -> expr_500 : '$1'.
expr_500 -> expr_500 mult_op expr_600 :
?mkop2('$1', '$2', '$3').
expr_500 -> expr_600 : '$1'.
expr_600 -> prefix_op expr_700 :
?mkop1('$1', '$2').
expr_600 -> map_expr : '$1'.
expr_600 -> expr_700 : '$1'.
expr_700 -> function_call : '$1'.
expr_700 -> record_expr : '$1'.
expr_700 -> expr_800 : '$1'.
expr_800 -> expr_max ':' expr_max :
{remote,?anno('$2'),'$1','$3'}.
expr_800 -> expr_max : '$1'.
expr_max -> var : '$1'.
expr_max -> atomic : '$1'.
expr_max -> list : '$1'.
expr_max -> binary : '$1'.
expr_max -> list_comprehension : '$1'.
expr_max -> binary_comprehension : '$1'.
expr_max -> tuple : '$1'.
%%expr_max -> struct : '$1'.
expr_max -> '(' expr ')' : '$2'.
expr_max -> 'begin' exprs 'end' : {block,?anno('$1'),'$2'}.
expr_max -> if_expr : '$1'.
expr_max -> case_expr : '$1'.
expr_max -> receive_expr : '$1'.
expr_max -> fun_expr : '$1'.
expr_max -> try_expr : '$1'.
list -> '[' ']' : {nil,?anno('$1')}.
list -> '[' expr tail : {cons,?anno('$1'),'$2','$3'}.
tail -> ']' : {nil,?anno('$1')}.
tail -> '|' expr ']' : '$2'.
tail -> ',' expr tail : {cons,?anno('$2'),'$2','$3'}.
binary -> '<<' '>>' : {bin,?anno('$1'),[]}.
binary -> '<<' bin_elements '>>' : {bin,?anno('$1'),'$2'}.
bin_elements -> bin_element : ['$1'].
bin_elements -> bin_element ',' bin_elements : ['$1'|'$3'].
bin_element -> bit_expr opt_bit_size_expr opt_bit_type_list :
{bin_element,?anno('$1'),'$1','$2','$3'}.
bit_expr -> prefix_op expr_max : ?mkop1('$1', '$2').
bit_expr -> expr_max : '$1'.
opt_bit_size_expr -> ':' bit_size_expr : '$2'.
opt_bit_size_expr -> '$empty' : default.
opt_bit_type_list -> '/' bit_type_list : '$2'.
opt_bit_type_list -> '$empty' : default.
bit_type_list -> bit_type '-' bit_type_list : ['$1' | '$3'].
bit_type_list -> bit_type : ['$1'].
bit_type -> atom : element(3,'$1').
bit_type -> atom ':' integer : { element(3,'$1'), element(3,'$3') }.
bit_size_expr -> expr_max : '$1'.
list_comprehension -> '[' expr '||' lc_exprs ']' :
{lc,?anno('$1'),'$2','$4'}.
binary_comprehension -> '<<' binary '||' lc_exprs '>>' :
{bc,?anno('$1'),'$2','$4'}.
lc_exprs -> lc_expr : ['$1'].
lc_exprs -> lc_expr ',' lc_exprs : ['$1'|'$3'].
lc_expr -> expr : '$1'.
lc_expr -> expr '<-' expr : {generate,?anno('$2'),'$1','$3'}.
lc_expr -> binary '<=' expr : {b_generate,?anno('$2'),'$1','$3'}.
tuple -> '{' '}' : {tuple,?anno('$1'),[]}.
tuple -> '{' exprs '}' : {tuple,?anno('$1'),'$2'}.
%%struct -> atom tuple :
%% {struct,?anno('$1'),element(3, '$1'),element(3, '$2')}.
map_expr -> '#' map_tuple :
{map, ?anno('$1'),'$2'}.
map_expr -> expr_max '#' map_tuple :
{map, ?anno('$2'),'$1','$3'}.
map_expr -> map_expr '#' map_tuple :
{map, ?anno('$2'),'$1','$3'}.
map_tuple -> '{' '}' : [].
map_tuple -> '{' map_fields '}' : '$2'.
map_fields -> map_field : ['$1'].
map_fields -> map_field ',' map_fields : ['$1' | '$3'].
map_field -> map_field_assoc : '$1'.
map_field -> map_field_exact : '$1'.
map_field_assoc -> map_key '=>' expr :
{map_field_assoc,?anno('$1'),'$1','$3'}.
map_field_exact -> map_key ':=' expr :
{map_field_exact,?anno('$1'),'$1','$3'}.
map_key -> expr : '$1'.
%% N.B. This is called from expr_700.
%% N.B. Field names are returned as the complete object, even if they are
%% always atoms for the moment, this might change in the future.
record_expr -> '#' atom '.' atom :
{record_index,?anno('$1'),element(3, '$2'),'$4'}.
record_expr -> '#' atom record_tuple :
{record,?anno('$1'),element(3, '$2'),'$3'}.
record_expr -> expr_max '#' atom '.' atom :
{record_field,?anno('$2'),'$1',element(3, '$3'),'$5'}.
record_expr -> expr_max '#' atom record_tuple :
{record,?anno('$2'),'$1',element(3, '$3'),'$4'}.
record_expr -> record_expr '#' atom '.' atom :
{record_field,?anno('$2'),'$1',element(3, '$3'),'$5'}.
record_expr -> record_expr '#' atom record_tuple :
{record,?anno('$2'),'$1',element(3, '$3'),'$4'}.
record_tuple -> '{' '}' : [].
record_tuple -> '{' record_fields '}' : '$2'.
record_fields -> record_field : ['$1'].
record_fields -> record_field ',' record_fields : ['$1' | '$3'].
record_field -> var '=' expr : {record_field,?anno('$1'),'$1','$3'}.
record_field -> atom '=' expr : {record_field,?anno('$1'),'$1','$3'}.
%% N.B. This is called from expr_700.
function_call -> expr_800 argument_list :
{call,?anno('$1'),'$1',element(1, '$2')}.
if_expr -> 'if' if_clauses 'end' : {'if',?anno('$1'),'$2'}.
if_clauses -> if_clause : ['$1'].
if_clauses -> if_clause ';' if_clauses : ['$1' | '$3'].
if_clause -> guard clause_body :
{clause,?anno(hd(hd('$1'))),[],'$1','$2'}.
case_expr -> 'case' expr 'of' cr_clauses 'end' :
{'case',?anno('$1'),'$2','$4'}.
cr_clauses -> cr_clause : ['$1'].
cr_clauses -> cr_clause ';' cr_clauses : ['$1' | '$3'].
cr_clause -> expr clause_guard clause_body :
{clause,?anno('$1'),['$1'],'$2','$3'}.
receive_expr -> 'receive' cr_clauses 'end' :
{'receive',?anno('$1'),'$2'}.
receive_expr -> 'receive' 'after' expr clause_body 'end' :
{'receive',?anno('$1'),[],'$3','$4'}.
receive_expr -> 'receive' cr_clauses 'after' expr clause_body 'end' :
{'receive',?anno('$1'),'$2','$4','$5'}.
fun_expr -> 'fun' atom '/' integer :
{'fun',?anno('$1'),{function,element(3, '$2'),element(3, '$4')}}.
fun_expr -> 'fun' atom_or_var ':' atom_or_var '/' integer_or_var :
{'fun',?anno('$1'),{function,'$2','$4','$6'}}.
fun_expr -> 'fun' fun_clauses 'end' :
build_fun(?anno('$1'), '$2').
atom_or_var -> atom : '$1'.
atom_or_var -> var : '$1'.
integer_or_var -> integer : '$1'.
integer_or_var -> var : '$1'.
fun_clauses -> fun_clause : ['$1'].
fun_clauses -> fun_clause ';' fun_clauses : ['$1' | '$3'].
fun_clause -> argument_list clause_guard clause_body :
{Args,Anno} = '$1',
{clause,Anno,'fun',Args,'$2','$3'}.
fun_clause -> var argument_list clause_guard clause_body :
{clause,element(2, '$1'),element(3, '$1'),element(1, '$2'),'$3','$4'}.
try_expr -> 'try' exprs 'of' cr_clauses try_catch :
build_try(?anno('$1'),'$2','$4','$5').
try_expr -> 'try' exprs try_catch :
build_try(?anno('$1'),'$2',[],'$3').
try_catch -> 'catch' try_clauses 'end' :
{'$2',[]}.
try_catch -> 'catch' try_clauses 'after' exprs 'end' :
{'$2','$4'}.
try_catch -> 'after' exprs 'end' :
{[],'$2'}.
try_clauses -> try_clause : ['$1'].
try_clauses -> try_clause ';' try_clauses : ['$1' | '$3'].
try_clause -> expr clause_guard clause_body :
A = ?anno('$1'),
{clause,A,[{tuple,A,[{atom,A,throw},'$1',{var,A,'_'}]}],'$2','$3'}.
try_clause -> atom ':' expr clause_guard clause_body :
A = ?anno('$1'),
{clause,A,[{tuple,A,['$1','$3',{var,A,'_'}]}],'$4','$5'}.
try_clause -> var ':' expr clause_guard clause_body :
A = ?anno('$1'),
{clause,A,[{tuple,A,['$1','$3',{var,A,'_'}]}],'$4','$5'}.
argument_list -> '(' ')' : {[],?anno('$1')}.
argument_list -> '(' exprs ')' : {'$2',?anno('$1')}.
exprs -> expr : ['$1'].
exprs -> expr ',' exprs : ['$1' | '$3'].
guard -> exprs : ['$1'].
guard -> exprs ';' guard : ['$1'|'$3'].
atomic -> char : '$1'.
atomic -> integer : '$1'.
atomic -> float : '$1'.
atomic -> atom : '$1'.
atomic -> strings : '$1'.
strings -> string : '$1'.
strings -> string strings :
{string,?anno('$1'),element(3, '$1') ++ element(3, '$2')}.
prefix_op -> '+' : '$1'.
prefix_op -> '-' : '$1'.
prefix_op -> 'bnot' : '$1'.
prefix_op -> 'not' : '$1'.
mult_op -> '/' : '$1'.
mult_op -> '*' : '$1'.
mult_op -> 'div' : '$1'.
mult_op -> 'rem' : '$1'.
mult_op -> 'band' : '$1'.
mult_op -> 'and' : '$1'.
add_op -> '+' : '$1'.
add_op -> '-' : '$1'.
add_op -> 'bor' : '$1'.
add_op -> 'bxor' : '$1'.
add_op -> 'bsl' : '$1'.
add_op -> 'bsr' : '$1'.
add_op -> 'or' : '$1'.
add_op -> 'xor' : '$1'.
list_op -> '++' : '$1'.
list_op -> '--' : '$1'.
comp_op -> '==' : '$1'.
comp_op -> '/=' : '$1'.
comp_op -> '=<' : '$1'.
comp_op -> '<' : '$1'.
comp_op -> '>=' : '$1'.
comp_op -> '>' : '$1'.
comp_op -> '=:=' : '$1'.
comp_op -> '=/=' : '$1'.
Erlang code.
-export([parse_form/1,parse_exprs/1,parse_term/1]).
-export([normalise/1,abstract/1,tokens/1,tokens/2]).
-export([abstract/2]).
-export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]).
-export([type_inop_prec/1,type_preop_prec/1]).
-export([map_anno/2, fold_anno/3, mapfold_anno/3,
new_anno/1, anno_to_term/1, anno_from_term/1]).
%% The following directive is needed for (significantly) faster compilation
%% of the generated .erl file by the HiPE compiler. Please do not remove.
-compile([{hipe,[{regalloc,linear_scan}]}]).
-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0,
error_info/0]).
%% XXX. To be refined.
-type abstract_clause() :: term().
-type abstract_expr() :: term().
-type abstract_form() :: term().
-type error_description() :: term().
-type error_info() :: {erl_anno:line(), module(), error_description()}.
-type token() :: erl_scan:token().
%% mkop(Op, Arg) -> {op,Anno,Op,Arg}.
%% mkop(Left, Op, Right) -> {op,Anno,Op,Left,Right}.
-define(mkop2(L, OpAnno, R),
begin
{Op,Anno} = OpAnno,
{op,Anno,Op,L,R}
end).
-define(mkop1(OpAnno, A),
begin
{Op,Anno} = OpAnno,
{op,Anno,Op,A}
end).
%% keep track of annotation info in tokens
-define(anno(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([{'-',A1},{atom,A2,spec}|Tokens]) ->
parse([{'-',A1},{'spec',A2}|Tokens]);
parse_form([{'-',A1},{atom,A2,callback}|Tokens]) ->
parse([{'-',A1},{'callback',A2}|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) ->
A = erl_anno:new(0),
case parse([{atom,A,f},{'(',A},{')',A},{'->',A}|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) ->
A = erl_anno:new(0),
case parse([{atom,A,f},{'(',A},{')',A},{'->',A}|Tokens]) of
{ok,{function,_Af,f,0,[{clause,_Ac,[],[],[Expr]}]}} ->
try normalise(Expr) of
Term -> {ok,Term}
catch
_:_R -> {error,{location(?anno(Expr)),?MODULE,"bad term"}}
end;
{ok,{function,_Af,f,A,[{clause,_Ac,[],[],[_E1,E2|_Es]}]}} ->
{error,{location(?anno(E2)),?MODULE,"bad term"}};
{error,_} = Err -> Err
end.
-type attributes() :: 'export' | 'file' | 'import' | 'module'
| 'opaque' | 'record' | 'type'.
build_typed_attribute({atom,Aa,record},
{typed_record, {atom,_An,RecordName}, RecTuple}) ->
{attribute,Aa,record,{RecordName,record_tuple(RecTuple)}};
build_typed_attribute({atom,Aa,Attr},
{type_def, {call,_,{atom,_,TypeName},Args}, Type})
when Attr =:= 'type' ; Attr =:= 'opaque' ->
case lists:all(fun({var, _, _}) -> true;
(_) -> false
end, Args) of
true -> {attribute,Aa,Attr,{TypeName,Type,Args}};
false -> error_bad_decl(Aa, Attr)
end;
build_typed_attribute({atom,Aa,Attr},_) ->
case Attr of
record -> error_bad_decl(Aa, record);
type -> error_bad_decl(Aa, type);
opaque -> error_bad_decl(Aa, opaque);
_ -> ret_err(Aa, "bad attribute")
end.
build_type_spec({Kind,Aa}, {SpecFun, TypeSpecs})
when (Kind =:= spec) or (Kind =:= callback) ->
NewSpecFun =
case SpecFun of
{atom, _, Fun} ->
{Fun, find_arity_from_specs(TypeSpecs)};
{{atom,_, Mod}, {atom,_, Fun}} ->
{Mod,Fun,find_arity_from_specs(TypeSpecs)};
{{atom, _, Fun}, {integer, _, Arity}} ->
%% Old style spec. Allow this for now.
{Fun,Arity};
{{atom,_, Mod}, {atom, _, Fun}, {integer, _, Arity}} ->
%% Old style spec. Allow this for now.
{Mod,Fun,Arity}
end,
{attribute,Aa,Kind,{NewSpecFun, TypeSpecs}}.
find_arity_from_specs([Spec|_]) ->
%% Use the first spec to find the arity. If all are not the same,
%% erl_lint will find this.
Fun = case Spec of
{type, _, bounded_fun, [F, _]} -> F;
{type, _, 'fun', _} = F -> F
end,
{type, _, 'fun', [{type, _, product, Args},_]} = Fun,
length(Args).
build_def({var, A, '_'}, _Types) ->
ret_err(A, "bad type variable");
build_def(LHS, Types) ->
IsSubType = {atom, ?anno(LHS), is_subtype},
{type, ?anno(LHS), constraint, [IsSubType, [LHS, Types]]}.
lift_unions(T1, {type, _Aa, union, List}) ->
{type, ?anno(T1), union, [T1|List]};
lift_unions(T1, T2) ->
{type, ?anno(T1), union, [T1, T2]}.
build_gen_type({atom, Aa, tuple}) ->
{type, Aa, tuple, any};
build_gen_type({atom, Aa, map}) ->
{type, Aa, map, any};
build_gen_type({atom, Aa, Name}) ->
Tag = type_tag(Name, 0),
{Tag, Aa, Name, []}.
build_bin_type([{var, _, '_'}|Left], Int) ->
build_bin_type(Left, Int);
build_bin_type([], Int) ->
Int;
build_bin_type([{var, Aa, _}|_], _) ->
ret_err(Aa, "Bad binary type").
build_type({atom, A, Name}, Types) ->
Tag = type_tag(Name, length(Types)),
{Tag, A, Name, Types}.
type_tag(TypeName, NumberOfTypeVariables) ->
case erl_internal:is_type(TypeName, NumberOfTypeVariables) of
true -> type;
false -> user_type
end.
abstract2(Term, Anno) ->
Line = erl_anno:line(Anno),
abstract(Term, Line).
%% build_attribute(AttrName, AttrValue) ->
%% {attribute,Anno,module,Module}
%% {attribute,Anno,export,Exports}
%% {attribute,Anno,import,Imports}
%% {attribute,Anno,record,{Name,Inits}}
%% {attribute,Anno,file,{Name,Line}}
%% {attribute,Anno,Name,Val}
build_attribute({atom,Aa,module}, Val) ->
case Val of
[{atom,_Am,Module}] ->
{attribute,Aa,module,Module};
[{atom,_Am,Module},ExpList] ->
{attribute,Aa,module,{Module,var_list(ExpList)}};
_Other ->
error_bad_decl(Aa, module)
end;
build_attribute({atom,Aa,export}, Val) ->
case Val of
[ExpList] ->
{attribute,Aa,export,farity_list(ExpList)};
_Other -> error_bad_decl(Aa, export)
end;
build_attribute({atom,Aa,import}, Val) ->
case Val of
[{atom,_Am,Mod},ImpList] ->
{attribute,Aa,import,{Mod,farity_list(ImpList)}};
_Other -> error_bad_decl(Aa, import)
end;
build_attribute({atom,Aa,record}, Val) ->
case Val of
[{atom,_An,Record},RecTuple] ->
{attribute,Aa,record,{Record,record_tuple(RecTuple)}};
_Other -> error_bad_decl(Aa, record)
end;
build_attribute({atom,Aa,file}, Val) ->
case Val of
[{string,_An,Name},{integer,_Al,Line}] ->
{attribute,Aa,file,{Name,Line}};
_Other -> error_bad_decl(Aa, file)
end;
build_attribute({atom,Aa,Attr}, Val) ->
case Val of
[Expr0] ->
Expr = attribute_farity(Expr0),
{attribute,Aa,Attr,term(Expr)};
_Other -> ret_err(Aa, "bad attribute")
end.
var_list({cons,_Ac,{var,_,V},Tail}) ->
[V|var_list(Tail)];
var_list({nil,_An}) -> [];
var_list(Other) ->
ret_err(?anno(Other), "bad variable list").
attribute_farity({cons,A,H,T}) ->
{cons,A,attribute_farity(H),attribute_farity(T)};
attribute_farity({tuple,A,Args0}) ->
Args = attribute_farity_list(Args0),
{tuple,A,Args};
attribute_farity({map,A,Args0}) ->
Args = attribute_farity_map(Args0),
{map,A,Args};
attribute_farity({op,A,'/',{atom,_,_}=Name,{integer,_,_}=Arity}) ->
{tuple,A,[Name,Arity]};
attribute_farity(Other) -> Other.
attribute_farity_list(Args) ->
[attribute_farity(A) || A <- Args].
%% It is not meaningful to have farity keys.
attribute_farity_map(Args) ->
[{Op,A,K,attribute_farity(V)} || {Op,A,K,V} <- Args].
-spec error_bad_decl(erl_anno:anno(), attributes()) -> no_return().
error_bad_decl(Anno, S) ->
ret_err(Anno, io_lib:format("bad ~w declaration", [S])).
farity_list({cons,_Ac,{op,_Ao,'/',{atom,_Aa,A},{integer,_Ai,I}},Tail}) ->
[{A,I}|farity_list(Tail)];
farity_list({nil,_An}) -> [];
farity_list(Other) ->
ret_err(?anno(Other), "bad function arity").
record_tuple({tuple,_At,Fields}) ->
record_fields(Fields);
record_tuple(Other) ->
ret_err(?anno(Other), "bad record declaration").
record_fields([{atom,Aa,A}|Fields]) ->
[{record_field,Aa,{atom,Aa,A}}|record_fields(Fields)];
record_fields([{match,_Am,{atom,Aa,A},Expr}|Fields]) ->
[{record_field,Aa,{atom,Aa,A},Expr}|record_fields(Fields)];
record_fields([{typed,Expr,TypeInfo}|Fields]) ->
[Field] = record_fields([Expr]),
TypeInfo1 =
case Expr of
{match, _, _, _} -> TypeInfo; %% If we have an initializer.
{atom, Aa, _} ->
case has_undefined(TypeInfo) of
false ->
lift_unions(abstract2(undefined, Aa), TypeInfo);
true ->
TypeInfo
end
end,
[{typed_record_field,Field,TypeInfo1}|record_fields(Fields)];
record_fields([Other|_Fields]) ->
ret_err(?anno(Other), "bad record field");
record_fields([]) -> [].
has_undefined({atom,_,undefined}) ->
true;
has_undefined({ann_type,_,[_,T]}) ->
has_undefined(T);
has_undefined({type,_,union,Ts}) ->
lists:any(fun has_undefined/1, Ts);
has_undefined(_) ->
false.
term(Expr) ->
try normalise(Expr)
catch _:_R -> ret_err(?anno(Expr), "bad attribute")
end.
%% build_function([Clause]) -> {function,Anno,Name,Arity,[Clause]}
build_function(Cs) ->
Name = element(3, hd(Cs)),
Arity = length(element(4, hd(Cs))),
{function,?anno(hd(Cs)),Name,Arity,check_clauses(Cs, Name, Arity)}.
%% build_fun(Anno, [Clause]) -> {'fun',Anno,{clauses,[Clause]}}.
build_fun(Anno, Cs) ->
Name = element(3, hd(Cs)),
Arity = length(element(4, hd(Cs))),
CheckedCs = check_clauses(Cs, Name, Arity),
case Name of
'fun' ->
{'fun',Anno,{clauses,CheckedCs}};
Name ->
{named_fun,Anno,Name,CheckedCs}
end.
check_clauses(Cs, Name, Arity) ->
[case C of
{clause,A,N,As,G,B} when N =:= Name, length(As) =:= Arity ->
{clause,A,As,G,B};
{clause,A,_N,_As,_G,_B} ->
ret_err(A, "head mismatch")
end || C <- Cs].
build_try(A,Es,Scs,{Ccs,As}) ->
{'try',A,Es,Scs,Ccs,As}.
-spec ret_err(_, _) -> no_return().
ret_err(Anno, S) ->
return_error(location(Anno), S).
location(Anno) ->
erl_anno:location(Anno).
%% 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));
normalise({map,_,Pairs}=M) ->
maps:from_list(lists:map(fun
%% only allow '=>'
({map_field_assoc,_,K,V}) -> {normalise(K),normalise(V)};
(_) -> erlang:error({badarg,M})
end, Pairs));
%% 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([]) ->
[].
-spec abstract(Data) -> AbsTerm when
Data :: term(),
AbsTerm :: abstract_expr().
abstract(T) ->
Anno = erl_anno:new(0),
abstract(T, Anno, enc_func(epp:default_encoding())).
-type encoding_func() :: fun((non_neg_integer()) -> boolean()).
%%% abstract/2 takes line and encoding options
-spec abstract(Data, Options) -> AbsTerm when
Data :: term(),
Options :: Line | [Option],
Option :: {line, Line} | {encoding, Encoding},
Encoding :: 'latin1' | 'unicode' | 'utf8' | 'none' | encoding_func(),
Line :: erl_anno:line(),
AbsTerm :: abstract_expr().
abstract(T, Line) when is_integer(Line) ->
Anno = erl_anno:new(Line),
abstract(T, Anno, enc_func(epp:default_encoding()));
abstract(T, Options) when is_list(Options) ->
Line = proplists:get_value(line, Options, 0),
Encoding = proplists:get_value(encoding, Options,epp:default_encoding()),
EncFunc = enc_func(Encoding),
Anno = erl_anno:new(Line),
abstract(T, Anno, EncFunc).
-define(UNICODE(C),
(C < 16#D800 orelse
C > 16#DFFF andalso C < 16#FFFE orelse
C > 16#FFFF andalso C =< 16#10FFFF)).
enc_func(latin1) -> fun(C) -> C < 256 end;
enc_func(unicode) -> fun(C) -> ?UNICODE(C) end;
enc_func(utf8) -> fun(C) -> ?UNICODE(C) end;
enc_func(none) -> none;
enc_func(Fun) when is_function(Fun, 1) -> Fun;
enc_func(Term) -> erlang:error({badarg, Term}).
abstract(T, A, _E) when is_integer(T) -> {integer,A,T};
abstract(T, A, _E) when is_float(T) -> {float,A,T};
abstract(T, A, _E) when is_atom(T) -> {atom,A,T};
abstract([], A, _E) -> {nil,A};
abstract(B, A, _E) when is_bitstring(B) ->
{bin, A, [abstract_byte(Byte, A) || Byte <- bitstring_to_list(B)]};
abstract([H|T], A, none=E) ->
{cons,A,abstract(H, A, E),abstract(T, A, E)};
abstract(List, A, E) when is_list(List) ->
abstract_list(List, [], A, E);
abstract(Tuple, A, E) when is_tuple(Tuple) ->
{tuple,A,abstract_tuple_list(tuple_to_list(Tuple), A, E)};
abstract(Map, A, E) when is_map(Map) ->
{map,A,abstract_map_fields(maps:to_list(Map),A,E)}.
abstract_list([H|T], String, A, E) ->
case is_integer(H) andalso H >= 0 andalso E(H) of
true ->
abstract_list(T, [H|String], A, E);
false ->
AbstrList = {cons,A,abstract(H, A, E),abstract(T, A, E)},
not_string(String, AbstrList, A, E)
end;
abstract_list([], String, A, _E) ->
{string, A, lists:reverse(String)};
abstract_list(T, String, A, E) ->
not_string(String, abstract(T, A, E), A, E).
not_string([C|T], Result, A, E) ->
not_string(T, {cons, A, {integer, A, C}, Result}, A, E);
not_string([], Result, _A, _E) ->
Result.
abstract_tuple_list([H|T], A, E) ->
[abstract(H, A, E)|abstract_tuple_list(T, A, E)];
abstract_tuple_list([], _A, _E) ->
[].
abstract_map_fields(Fs,A,E) ->
[{map_field_assoc,A,abstract(K,A,E),abstract(V,A,E)}||{K,V}<-Fs].
abstract_byte(Byte, A) when is_integer(Byte) ->
{bin_element, A, {integer, A, Byte}, default, default};
abstract_byte(Bits, A) ->
Sz = bit_size(Bits),
<<Val:Sz>> = Bits,
{bin_element, A, {integer, A, Val}, {integer, A, Sz}, default}.
%% 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,A,C}, More) -> [{char,A,C}|More];
tokens({integer,A,N}, More) -> [{integer,A,N}|More];
tokens({float,A,F}, More) -> [{float,A,F}|More];
tokens({atom,Aa,A}, More) -> [{atom,Aa,A}|More];
tokens({var,A,V}, More) -> [{var,A,V}|More];
tokens({string,A,S}, More) -> [{string,A,S}|More];
tokens({nil,A}, More) -> [{'[',A},{']',A}|More];
tokens({cons,A,Head,Tail}, More) ->
[{'[',A}|tokens(Head, tokens_tail(Tail, More))];
tokens({tuple,A,[]}, More) ->
[{'{',A},{'}',A}|More];
tokens({tuple,A,[E|Es]}, More) ->
[{'{',A}|tokens(E, tokens_tuple(Es, ?anno(E), More))].
tokens_tail({cons,A,Head,Tail}, More) ->
[{',',A}|tokens(Head, tokens_tail(Tail, More))];
tokens_tail({nil,A}, More) ->
[{']',A}|More];
tokens_tail(Other, More) ->
A = ?anno(Other),
[{'|',A}|tokens(Other, [{']',A}|More])].
tokens_tuple([E|Es], Anno, More) ->
[{',',Anno}|tokens(E, tokens_tuple(Es, ?anno(E), More))];
tokens_tuple([], Anno, More) ->
[{'}',Anno}|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() -> 900.
max_prec() -> 900.
-type prec() :: non_neg_integer().
-type type_inop() :: '::' | '|' | '..' | '+' | '-' | 'bor' | 'bxor'
| 'bsl' | 'bsr' | '*' | '/' | 'div' | 'rem' | 'band'.
-type type_preop() :: '+' | '-' | 'bnot' | '#'.
-spec type_inop_prec(type_inop()) -> {prec(), prec(), prec()}.
type_inop_prec('=') -> {150,100,100};
type_inop_prec('::') -> {160,150,150};
type_inop_prec('|') -> {180,170,170};
type_inop_prec('..') -> {300,200,300};
type_inop_prec('+') -> {400,400,500};
type_inop_prec('-') -> {400,400,500};
type_inop_prec('bor') -> {400,400,500};
type_inop_prec('bxor') -> {400,400,500};
type_inop_prec('bsl') -> {400,400,500};
type_inop_prec('bsr') -> {400,400,500};
type_inop_prec('*') -> {500,500,600};
type_inop_prec('/') -> {500,500,600};
type_inop_prec('div') -> {500,500,600};
type_inop_prec('rem') -> {500,500,600};
type_inop_prec('band') -> {500,500,600};
type_inop_prec('#') -> {800,700,800}.
-spec type_preop_prec(type_preop()) -> {prec(), prec()}.
type_preop_prec('+') -> {600,700};
type_preop_prec('-') -> {600,700};
type_preop_prec('bnot') -> {600,700};
type_preop_prec('#') -> {700,800}.
-spec map_anno(Fun, Abstr) -> NewAbstr when
Fun :: fun((Anno) -> Anno),
Anno :: erl_anno:anno(),
Abstr :: abstract_form() | abstract_expr(),
NewAbstr :: abstract_form() | abstract_expr().
map_anno(F0, Abstr) ->
F = fun(A, Acc) -> {F0(A), Acc} end,
{NewAbstr, []} = modify_anno1(Abstr, [], F),
NewAbstr.
-spec fold_anno(Fun, Acc0, Abstr) -> NewAbstr when
Fun :: fun((Anno, AccIn) -> AccOut),
Anno :: erl_anno:anno(),
Acc0 :: term(),
AccIn :: term(),
AccOut :: term(),
Abstr :: abstract_form() | abstract_expr(),
NewAbstr :: abstract_form() | abstract_expr().
fold_anno(F0, Acc0, Abstr) ->
F = fun(A, Acc) -> {A, F0(A, Acc)} end,
{_, NewAcc} = modify_anno1(Abstr, Acc0, F),
NewAcc.
-spec mapfold_anno(Fun, Acc0, Abstr) -> {NewAbstr, Acc1} when
Fun :: fun((Anno, AccIn) -> {Anno, AccOut}),
Anno :: erl_anno:anno(),
Acc0 :: term(),
Acc1 :: term(),
AccIn :: term(),
AccOut :: term(),
Abstr :: abstract_form() | abstract_expr(),
NewAbstr :: abstract_form() | abstract_expr().
mapfold_anno(F, Acc0, Abstr) ->
modify_anno1(Abstr, Acc0, F).
-spec new_anno(Term) -> Abstr when
Term :: term(),
Abstr :: abstract_form() | abstract_expr().
new_anno(Term) ->
map_anno(fun erl_anno:new/1, Term).
-spec anno_to_term(Abstr) -> term() when
Abstr :: abstract_form() | abstract_expr().
anno_to_term(Abstract) ->
map_anno(fun erl_anno:to_term/1, Abstract).
-spec anno_from_term(Term) -> abstract_form() | abstract_expr() when
Term :: term().
anno_from_term(Term) ->
map_anno(fun erl_anno:from_term/1, Term).
%% Forms.
%% Recognize what sys_pre_expand does:
modify_anno1({'fun',A,F,{_,_,_}=Id}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{F1,Ac2} = modify_anno1(F, Ac1, Mf),
{{'fun',A1,F1,Id},Ac2};
modify_anno1({named_fun,A,N,F,{_,_,_}=Id}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{F1,Ac2} = modify_anno1(F, Ac1, Mf),
{{named_fun,A1,N,F1,Id},Ac2};
modify_anno1({attribute,A,N,[V]}, Ac, Mf) ->
{{attribute,A1,N1,V1},Ac1} = modify_anno1({attribute,A,N,V}, Ac, Mf),
{{attribute,A1,N1,[V1]},Ac1};
%% End of sys_pre_expand special forms.
modify_anno1({function,F,A}, Ac, _Mf) ->
{{function,F,A},Ac};
modify_anno1({function,M,F,A}, Ac, Mf) ->
{M1,Ac1} = modify_anno1(M, Ac, Mf),
{F1,Ac2} = modify_anno1(F, Ac1, Mf),
{A1,Ac3} = modify_anno1(A, Ac2, Mf),
{{function,M1,F1,A1},Ac3};
modify_anno1({attribute,A,record,{Name,Fields}}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{Fields1,Ac2} = modify_anno1(Fields, Ac1, Mf),
{{attribute,A1,record,{Name,Fields1}},Ac2};
modify_anno1({attribute,A,spec,{Fun,Types}}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{Types1,Ac2} = modify_anno1(Types, Ac1, Mf),
{{attribute,A1,spec,{Fun,Types1}},Ac2};
modify_anno1({attribute,A,callback,{Fun,Types}}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{Types1,Ac2} = modify_anno1(Types, Ac1, Mf),
{{attribute,A1,callback,{Fun,Types1}},Ac2};
modify_anno1({attribute,A,type,{TypeName,TypeDef,Args}}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{TypeDef1,Ac2} = modify_anno1(TypeDef, Ac1, Mf),
{Args1,Ac3} = modify_anno1(Args, Ac2, Mf),
{{attribute,A1,type,{TypeName,TypeDef1,Args1}},Ac3};
modify_anno1({attribute,A,opaque,{TypeName,TypeDef,Args}}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{TypeDef1,Ac2} = modify_anno1(TypeDef, Ac1, Mf),
{Args1,Ac3} = modify_anno1(Args, Ac2, Mf),
{{attribute,A1,opaque,{TypeName,TypeDef1,Args1}},Ac3};
modify_anno1({attribute,A,Attr,Val}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{{attribute,A1,Attr,Val},Ac1};
modify_anno1({warning,W}, Ac, _Mf) ->
{{warning,W},Ac};
modify_anno1({error,W}, Ac, _Mf) ->
{{error,W},Ac};
%% Expressions.
modify_anno1({clauses,Cs}, Ac, Mf) ->
{Cs1,Ac1} = modify_anno1(Cs, Ac, Mf),
{{clauses,Cs1},Ac1};
modify_anno1({typed_record_field,Field,Type}, Ac, Mf) ->
{Field1,Ac1} = modify_anno1(Field, Ac, Mf),
{Type1,Ac2} = modify_anno1(Type, Ac1, Mf),
{{typed_record_field,Field1,Type1},Ac2};
modify_anno1({Tag,A}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{{Tag,A1},Ac1};
modify_anno1({Tag,A,E1}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{E11,Ac2} = modify_anno1(E1, Ac1, Mf),
{{Tag,A1,E11},Ac2};
modify_anno1({Tag,A,E1,E2}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{E11,Ac2} = modify_anno1(E1, Ac1, Mf),
{E21,Ac3} = modify_anno1(E2, Ac2, Mf),
{{Tag,A1,E11,E21},Ac3};
modify_anno1({bin_element,A,E1,E2,TSL}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{E11,Ac2} = modify_anno1(E1, Ac1, Mf),
{E21,Ac3} = modify_anno1(E2, Ac2, Mf),
{{bin_element,A1,E11,E21, TSL},Ac3};
modify_anno1({Tag,A,E1,E2,E3}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{E11,Ac2} = modify_anno1(E1, Ac1, Mf),
{E21,Ac3} = modify_anno1(E2, Ac2, Mf),
{E31,Ac4} = modify_anno1(E3, Ac3, Mf),
{{Tag,A1,E11,E21,E31},Ac4};
modify_anno1({Tag,A,E1,E2,E3,E4}, Ac, Mf) ->
{A1,Ac1} = Mf(A, Ac),
{E11,Ac2} = modify_anno1(E1, Ac1, Mf),
{E21,Ac3} = modify_anno1(E2, Ac2, Mf),
{E31,Ac4} = modify_anno1(E3, Ac3, Mf),
{E41,Ac5} = modify_anno1(E4, Ac4, Mf),
{{Tag,A1,E11,E21,E31,E41},Ac5};
modify_anno1([H|T], Ac, Mf) ->
{H1,Ac1} = modify_anno1(H, Ac, Mf),
{T1,Ac2} = modify_anno1(T, Ac1, Mf),
{[H1|T1],Ac2};
modify_anno1([], Ac, _Mf) -> {[],Ac};
modify_anno1(E, Ac, _Mf) when not is_tuple(E), not is_list(E) -> {E,Ac}.
%% vim: ft=erlang