%% ``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 via the world wide web 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. %% %% The Initial Developer of the Original Code is Richard Carlsson. %% Copyright (C) 1999-2002 Richard Carlsson. %% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: cerl.erl,v 1.3 2010/03/04 13:54:20 maria Exp $ %% ===================================================================== %% @doc Core Erlang abstract syntax trees. %% %%
This module defines an abstract data type for representing Core %% Erlang source code as syntax trees.
%% %%A recommended starting point for the first-time user is the
%% documentation of the function type/1
.
This module deals with the composition and decomposition of %% syntactic entities (as opposed to semantic ones); its %% purpose is to hide all direct references to the data structures %% used to represent these entities. With few exceptions, the %% functions in this module perform no semantic interpretation of %% their inputs, and in general, the user is assumed to pass %% type-correct arguments - if this is not done, the effects are not %% defined.
%% %%The internal representations of abstract syntax trees are
%% subject to change without notice, and should not be documented
%% outside this module. Furthermore, we do not give any guarantees on
%% how an abstract syntax tree may or may not be represented, with
%% the following exceptions: no syntax tree is represented by a
%% single atom, such as none
, by a list constructor
%% [X | Y]
, or by the empty list []
. This
%% can be relied on when writing functions that operate on syntax
%% trees.
Every abstract syntax tree has a type, given by the
%% function type/1
. In addition,
%% each syntax tree has a list of user annotations (cf. get_ann/1
), which are included
%% in the Core Erlang syntax.
Node
. Current node types
%% are:
%%
%% alias | %%apply | %%binary | %%bitstr | %%call | %%case | %%catch | %%
clause | %%cons | %%fun | %%let | %%letrec | %%literal | %%module | %%
primop | %%receive | %%seq | %%try | %%tuple | %%values | %%var | %%
Note: The name of the primary constructor function for a node
%% type is always the name of the type itself, prefixed by
%% "c_
"; recognizer predicates are correspondingly
%% prefixed by "is_c_
". Furthermore, to simplify
%% preservation of annotations (cf. get_ann/1
), there are
%% analogous constructor functions prefixed by "ann_c_
"
%% and "update_c_
", for setting the annotation list of
%% the new node to either a specific value or to the annotations of an
%% existing node, respectively.
true
if Node
is a leaf node,
%% otherwise false
. The current leaf node types are
%% literal
and var
.
%%
%% Note: all literals (cf. is_literal/1
) are leaf
%% nodes, even if they represent structured (constant) values such as
%% {foo, [bar, baz]}
. Also note that variables are leaf
%% nodes but not literals.
Node
to
%% Annotations
.
%%
%% @see get_ann/1
%% @see add_ann/2
%% @see copy_ann/2
set_ann(Node, List) ->
setelement(2, Node, List).
%% @spec add_ann(Annotations::[term()], Node::cerl()) -> cerl()
%%
%% @doc Appends Annotations
to the list of user
%% annotations of Node
.
%%
%% Note: this is equivalent to set_ann(Node, Annotations ++
%% get_ann(Node))
, but potentially more efficient.
Source
%% to Target
.
%%
%% Note: this is equivalent to set_ann(Target,
%% get_ann(Source))
, but potentially more efficient.
Term
must be a literal term, i.e., one that can be
%% represented as a source code literal. Thus, it may not contain a
%% process identifier, port, reference, binary or function value as a
%% subterm.
%%
%% Note: This is a constant time operation.
%% %% @see ann_abstract/2 %% @see concrete/1 %% @see is_literal/1 %% @see is_literal_term/1 abstract(T) -> #literal{val = T}. %% @spec ann_abstract(Annotations::[term()], Term::term()) -> cerl() %% @see abstract/1 ann_abstract(As, T) -> #literal{val = T, ann = As}. %% @spec is_literal_term(Term::term()) -> boolean() %% %% @doc Returnstrue
if Term
can be
%% represented as a literal, otherwise false
. This
%% function takes time proportional to the size of Term
.
%%
%% @see abstract/1
is_literal_term(T) when integer(T) -> true;
is_literal_term(T) when float(T) -> true;
is_literal_term(T) when atom(T) -> true;
is_literal_term([]) -> true;
is_literal_term([H | T]) ->
case is_literal_term(H) of
true ->
is_literal_term(T);
false ->
false
end;
is_literal_term(T) when tuple(T) ->
is_literal_term_list(tuple_to_list(T));
is_literal_term(_) ->
false.
is_literal_term_list([T | Ts]) ->
case is_literal_term(T) of
true ->
is_literal_term_list(Ts);
false ->
false
end;
is_literal_term_list([]) ->
true.
%% @spec concrete(Node::cerl()) -> term()
%%
%% @doc Returns the Erlang term represented by a syntax tree. An
%% exception is thrown if Node
does not represent a
%% literal term.
%%
%% Note: This is a constant time operation.
%% %% @see abstract/1 %% @see is_literal/1 %% Because the normal tuple and list constructor operations always %% return a literal if the arguments are literals, 'concrete' and %% 'is_literal' never need to traverse the structure. concrete(#literal{val = V}) -> V. %% @spec is_literal(Node::cerl()) -> boolean() %% %% @doc Returnstrue
if Node
represents a
%% literal term, otherwise false
. This function returns
%% true
if and only if the value of
%% concrete(Node)
is defined.
%%
%% Note: This is a constant time operation.
%% %% @see abstract/1 %% @see concrete/1 %% @see fold_literal/1 is_literal(#literal{}) -> true; is_literal(_) -> false. %% @spec fold_literal(Node::cerl()) -> cerl() %% %% @doc Assures that literals have a compact representation. This is %% occasionally useful ifc_cons_skel/2
,
%% c_tuple_skel/1
or unfold_literal/1
were
%% used in the construction of Node
, and you want to revert
%% to the normal "folded" representation of literals. If
%% Node
represents a tuple or list constructor, its
%% elements are rewritten recursively, and the node is reconstructed
%% using c_cons/2
or c_tuple/1
, respectively;
%% otherwise, Node
is not changed.
%%
%% @see is_literal/1
%% @see c_cons_skel/2
%% @see c_tuple_skel/1
%% @see c_cons/2
%% @see c_tuple/1
%% @see unfold_literal/1
fold_literal(Node) ->
case type(Node) of
tuple ->
update_c_tuple(Node, fold_literal_list(tuple_es(Node)));
cons ->
update_c_cons(Node, fold_literal(cons_hd(Node)),
fold_literal(cons_tl(Node)));
_ ->
Node
end.
fold_literal_list([E | Es]) ->
[fold_literal(E) | fold_literal_list(Es)];
fold_literal_list([]) ->
[].
%% @spec unfold_literal(Node::cerl()) -> cerl()
%%
%% @doc Assures that literals have a fully expanded representation. If
%% Node
represents a literal tuple or list constructor, its
%% elements are rewritten recursively, and the node is reconstructed
%% using c_cons_skel/2
or c_tuple_skel/1
,
%% respectively; otherwise, Node
is not changed. The {@link
%% fold_literal/1} can be used to revert to the normal compact
%% representation.
%%
%% @see is_literal/1
%% @see c_cons_skel/2
%% @see c_tuple_skel/1
%% @see c_cons/2
%% @see c_tuple/1
%% @see fold_literal/1
unfold_literal(Node) ->
case type(Node) of
literal ->
copy_ann(Node, unfold_concrete(concrete(Node)));
_ ->
Node
end.
unfold_concrete(Val) ->
case Val of
_ when tuple(Val) ->
c_tuple_skel(unfold_concrete_list(tuple_to_list(Val)));
[H|T] ->
c_cons_skel(unfold_concrete(H), unfold_concrete(T));
_ ->
abstract(Val)
end.
unfold_concrete_list([E | Es]) ->
[unfold_concrete(E) | unfold_concrete_list(Es)];
unfold_concrete_list([]) ->
[].
%% ---------------------------------------------------------------------
-record(module, {ann = [], name, exports, attrs, defs}).
%% @spec c_module(Name::cerl(), Exports, Definitions) -> cerl()
%%
%% Exports = [cerl()]
%% Definitions = [{cerl(), cerl()}]
%%
%% @equiv c_module(Name, Exports, [], Definitions)
c_module(Name, Exports, Es) ->
#module{name = Name, exports = Exports, attrs = [], defs = Es}.
%% @spec c_module(Name::cerl(), Exports, Attributes, Definitions) ->
%% cerl()
%%
%% Exports = [cerl()]
%% Attributes = [{cerl(), cerl()}]
%% Definitions = [{cerl(), cerl()}]
%%
%% @doc Creates an abstract module definition. The result represents
%% %% module Name [E1, ..., Ek] %% attributes [K1 = T1, ..., %% Km = Tm] %% V1 = F1 %% ... %% Vn = Fn %% end%% %% if
Exports
= [E1, ..., Ek]
,
%% Attributes
= [{K1, T1}, ..., {Km, Tm}]
,
%% and Definitions
= [{V1, F1}, ..., {Vn,
%% Fn}]
.
%%
%% Name
and all the Ki
must be atom
%% literals, and all the Ti
must be constant literals. All
%% the Vi
and Ei
must have type
%% var
and represent function names. All the
%% Fi
must have type 'fun'
.
true
if Node
is an abstract
%% module definition, otherwise false
.
%%
%% @see type/1
is_c_module(#module{}) ->
true;
is_c_module(_) ->
false.
%% @spec module_name(Node::cerl()) -> cerl()
%%
%% @doc Returns the name subtree of an abstract module definition.
%%
%% @see c_module/4
module_name(Node) ->
Node#module.name.
%% @spec module_exports(Node::cerl()) -> [cerl()]
%%
%% @doc Returns the list of exports subtrees of an abstract module
%% definition.
%%
%% @see c_module/4
module_exports(Node) ->
Node#module.exports.
%% @spec module_attrs(Node::cerl()) -> [{cerl(), cerl()}]
%%
%% @doc Returns the list of pairs of attribute key/value subtrees of
%% an abstract module definition.
%%
%% @see c_module/4
module_attrs(Node) ->
Node#module.attrs.
%% @spec module_defs(Node::cerl()) -> [{cerl(), cerl()}]
%%
%% @doc Returns the list of function definitions of an abstract module
%% definition.
%%
%% @see c_module/4
module_defs(Node) ->
Node#module.defs.
%% @spec module_vars(Node::cerl()) -> [cerl()]
%%
%% @doc Returns the list of left-hand side function variable subtrees
%% of an abstract module definition.
%%
%% @see c_module/4
module_vars(Node) ->
[F || {F, _} <- module_defs(Node)].
%% ---------------------------------------------------------------------
%% @spec c_int(Value::integer()) -> cerl()
%%
%%
%% @doc Creates an abstract integer literal. The lexical
%% representation is the canonical decimal numeral of
%% Value
.
%%
%% @see ann_c_int/2
%% @see is_c_int/1
%% @see int_val/1
%% @see int_lit/1
%% @see c_char/1
c_int(Value) ->
#literal{val = Value}.
%% @spec ann_c_int(As::[term()], Value::integer()) -> cerl()
%% @see c_int/1
ann_c_int(As, Value) ->
#literal{val = Value, ann = As}.
%% @spec is_c_int(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
represents an
%% integer literal, otherwise false
.
%% @see c_int/1
is_c_int(#literal{val = V}) when integer(V) ->
true;
is_c_int(_) ->
false.
%% @spec int_val(cerl()) -> integer()
%%
%% @doc Returns the value represented by an integer literal node.
%% @see c_int/1
int_val(Node) ->
Node#literal.val.
%% @spec int_lit(cerl()) -> string()
%%
%% @doc Returns the numeral string represented by an integer literal
%% node.
%% @see c_int/1
int_lit(Node) ->
integer_to_list(int_val(Node)).
%% ---------------------------------------------------------------------
%% @spec c_float(Value::float()) -> cerl()
%%
%% @doc Creates an abstract floating-point literal. The lexical
%% representation is the decimal floating-point numeral of
%% Value
.
%%
%% @see ann_c_float/2
%% @see is_c_float/1
%% @see float_val/1
%% @see float_lit/1
%% Note that not all floating-point numerals can be represented with
%% full precision.
c_float(Value) ->
#literal{val = Value}.
%% @spec ann_c_float(As::[term()], Value::float()) -> cerl()
%% @see c_float/1
ann_c_float(As, Value) ->
#literal{val = Value, ann = As}.
%% @spec is_c_float(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
represents a
%% floating-point literal, otherwise false
.
%% @see c_float/1
is_c_float(#literal{val = V}) when float(V) ->
true;
is_c_float(_) ->
false.
%% @spec float_val(cerl()) -> float()
%%
%% @doc Returns the value represented by a floating-point literal
%% node.
%% @see c_float/1
float_val(Node) ->
Node#literal.val.
%% @spec float_lit(cerl()) -> string()
%%
%% @doc Returns the numeral string represented by a floating-point
%% literal node.
%% @see c_float/1
float_lit(Node) ->
float_to_list(float_val(Node)).
%% ---------------------------------------------------------------------
%% @spec c_atom(Name) -> cerl()
%% Name = atom() | string()
%%
%% @doc Creates an abstract atom literal. The print name of the atom
%% is the character sequence represented by Name
.
%%
%% Note: passing a string as argument to this function causes a %% corresponding atom to be created for the internal representation.
%% %% @see ann_c_atom/2 %% @see is_c_atom/1 %% @see atom_val/1 %% @see atom_name/1 %% @see atom_lit/1 c_atom(Name) when atom(Name) -> #literal{val = Name}; c_atom(Name) -> #literal{val = list_to_atom(Name)}. %% @spec ann_c_atom(As::[term()], Name) -> cerl() %% Name = atom() | string() %% @see c_atom/1 ann_c_atom(As, Name) when atom(Name) -> #literal{val = Name, ann = As}; ann_c_atom(As, Name) -> #literal{val = list_to_atom(Name), ann = As}. %% @spec is_c_atom(Node::cerl()) -> boolean() %% %% @doc Returnstrue
if Node
represents an
%% atom literal, otherwise false
.
%%
%% @see c_atom/1
is_c_atom(#literal{val = V}) when atom(V) ->
true;
is_c_atom(_) ->
false.
%% @spec atom_val(cerl())-> atom()
%%
%% @doc Returns the value represented by an abstract atom.
%%
%% @see c_atom/1
atom_val(Node) ->
Node#literal.val.
%% @spec atom_name(cerl()) -> string()
%%
%% @doc Returns the printname of an abstract atom.
%%
%% @see c_atom/1
atom_name(Node) ->
atom_to_list(atom_val(Node)).
%% @spec atom_lit(cerl()) -> string()
%%
%% @doc Returns the literal string represented by an abstract
%% atom. This always includes surrounding single-quote characters.
%%
%% Note that an abstract atom may have several literal
%% representations, and that the representation yielded by this
%% function is not fixed; e.g.,
%% atom_lit(c_atom("a\012b"))
could yield the string
%% "\'a\\nb\'"
.
char()
as a subset of
%% integer()
, this function is equivalent to
%% c_int/1
. Otherwise, if the given value is an integer,
%% it will be converted to the character with the corresponding
%% code. The lexical representation of a character is
%% "$Char
", where Char
is a single
%% printing character or an escape sequence.
%%
%% @see c_int/1
%% @see c_string/1
%% @see ann_c_char/2
%% @see is_c_char/1
%% @see char_val/1
%% @see char_lit/1
%% @see is_print_char/1
c_char(Value) when integer(Value), Value >= 0 ->
#literal{val = Value}.
%% @spec ann_c_char(As::[term()], Value::char()) -> cerl()
%% @see c_char/1
ann_c_char(As, Value) ->
#literal{val = Value, ann = As}.
%% @spec is_c_char(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
may represent a
%% character literal, otherwise false
.
%%
%% If the local implementation of Erlang defines
%% char()
as a subset of integer()
, then
%% is_c_int(Node)
will also yield
%% true
.
true
if Node
may represent a
%% "printing" character, otherwise false
. (Cf.
%% is_c_char/1
.) A "printing" character has either a
%% given graphical representation, or a "named" escape sequence such
%% as "\n
". Currently, only ISO 8859-1 (Latin-1)
%% character values are recognized.
%%
%% @see c_char/1
%% @see is_c_char/1
is_print_char(#literal{val = V}) when integer(V), V >= 0 ->
is_print_char_value(V);
is_print_char(_) ->
false.
%% @spec char_val(cerl()) -> char()
%%
%% @doc Returns the value represented by an abstract character literal.
%%
%% @see c_char/1
char_val(Node) ->
Node#literal.val.
%% @spec char_lit(cerl()) -> string()
%%
%% @doc Returns the literal string represented by an abstract
%% character. This includes a leading $
%% character. Currently, all characters that are not in the set of ISO
%% 8859-1 (Latin-1) "printing" characters will be escaped.
%%
%% @see c_char/1
char_lit(Node) ->
io_lib:write_char(char_val(Node)).
%% ---------------------------------------------------------------------
%% @spec c_string(Value::string()) -> cerl()
%%
%% @doc Creates an abstract string literal. Equivalent to creating an
%% abstract list of the corresponding character literals
%% (cf. is_c_string/1
), but is typically more
%% efficient. The lexical representation of a string is
%% ""Chars"
", where Chars
is a
%% sequence of printing characters or spaces.
%%
%% @see c_char/1
%% @see ann_c_string/2
%% @see is_c_string/1
%% @see string_val/1
%% @see string_lit/1
%% @see is_print_string/1
c_string(Value) ->
#literal{val = Value}.
%% @spec ann_c_string(As::[term()], Value::string()) -> cerl()
%% @see c_string/1
ann_c_string(As, Value) ->
#literal{val = Value, ann = As}.
%% @spec is_c_string(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
may represent a
%% string literal, otherwise false
. Strings are defined
%% as lists of characters; see is_c_char/1
for details.
%%
%% @see c_string/1
%% @see is_c_char/1
%% @see is_print_string/1
is_c_string(#literal{val = V}) ->
is_char_list(V);
is_c_string(_) ->
false.
%% @spec is_print_string(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
may represent a
%% string literal containing only "printing" characters, otherwise
%% false
. See is_c_string/1
and
%% is_print_char/1
for details. Currently, only ISO
%% 8859-1 (Latin-1) character values are recognized.
%%
%% @see c_string/1
%% @see is_c_string/1
%% @see is_print_char/1
is_print_string(#literal{val = V}) ->
is_print_char_list(V);
is_print_string(_) ->
false.
%% @spec string_val(cerl()) -> string()
%%
%% @doc Returns the value represented by an abstract string literal.
%%
%% @see c_string/1
string_val(Node) ->
Node#literal.val.
%% @spec string_lit(cerl()) -> string()
%%
%% @doc Returns the literal string represented by an abstract string.
%% This includes surrounding double-quote characters
%% "..."
. Currently, characters that are not in the set
%% of ISO 8859-1 (Latin-1) "printing" characters will be escaped,
%% except for spaces.
%%
%% @see c_string/1
string_lit(Node) ->
io_lib:write_string(string_val(Node)).
%% ---------------------------------------------------------------------
%% @spec c_nil() -> cerl()
%%
%% @doc Creates an abstract empty list. The result represents
%% "[]
". The empty list is traditionally called "nil".
%%
%% @see ann_c_nil/1
%% @see is_c_list/1
%% @see c_cons/2
c_nil() ->
#literal{val = []}.
%% @spec ann_c_nil(As::[term()]) -> cerl()
%% @see c_nil/0
ann_c_nil(As) ->
#literal{val = [], ann = As}.
%% @spec is_c_nil(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% empty list, otherwise false
.
is_c_nil(#literal{val = []}) ->
true;
is_c_nil(_) ->
false.
%% ---------------------------------------------------------------------
%% @spec c_cons(Head::cerl(), Tail::cerl()) -> cerl()
%%
%% @doc Creates an abstract list constructor. The result represents
%% "[Head | Tail]
". Note that if both
%% Head
and Tail
have type
%% literal
, then the result will also have type
%% literal
, and annotations on Head
and
%% Tail
are lost.
%%
%% Recall that in Erlang, the tail element of a list constructor is %% not necessarily a list.
%% %% @see ann_c_cons/3 %% @see update_c_cons/3 %% @see c_cons_skel/2 %% @see is_c_cons/1 %% @see cons_hd/1 %% @see cons_tl/1 %% @see is_c_list/1 %% @see c_nil/0 %% @see list_elements/1 %% @see list_length/1 %% @see make_list/2 -record(cons, {ann = [], hd, tl}). %% *Always* collapse literals. c_cons(#literal{val = Head}, #literal{val = Tail}) -> #literal{val = [Head | Tail]}; c_cons(Head, Tail) -> #cons{hd = Head, tl = Tail}. %% @spec ann_c_cons(As::[term()], Head::cerl(), Tail::cerl()) -> cerl() %% @see c_cons/2 ann_c_cons(As, #literal{val = Head}, #literal{val = Tail}) -> #literal{val = [Head | Tail], ann = As}; ann_c_cons(As, Head, Tail) -> #cons{hd = Head, tl = Tail, ann = As}. %% @spec update_c_cons(Old::cerl(), Head::cerl(), Tail::cerl()) -> %% cerl() %% @see c_cons/2 update_c_cons(Node, #literal{val = Head}, #literal{val = Tail}) -> #literal{val = [Head | Tail], ann = get_ann(Node)}; update_c_cons(Node, Head, Tail) -> #cons{hd = Head, tl = Tail, ann = get_ann(Node)}. %% @spec c_cons_skel(Head::cerl(), Tail::cerl()) -> cerl() %% %% @doc Creates an abstract list constructor skeleton. Does not fold %% constant literals, i.e., the result always has type %%cons
, representing "[Head |
%% Tail]
".
%%
%% This function is occasionally useful when it is necessary to have
%% annotations on the subnodes of a list constructor node, even when the
%% subnodes are constant literals. Note however that
%% is_literal/1
will yield false
and
%% concrete/1
will fail if passed the result from this
%% function.
fold_literal/1
can be used to revert a node to the
%% normal-form representation.
true
if Node
is an abstract
%% list constructor, otherwise false
.
is_c_cons(#cons{}) ->
true;
is_c_cons(#literal{val = [_ | _]}) ->
true;
is_c_cons(_) ->
false.
%% @spec cons_hd(cerl()) -> cerl()
%%
%% @doc Returns the head subtree of an abstract list constructor.
%%
%% @see c_cons/2
cons_hd(#cons{hd = Head}) ->
Head;
cons_hd(#literal{val = [Head | _]}) ->
#literal{val = Head}.
%% @spec cons_tl(cerl()) -> cerl()
%%
%% @doc Returns the tail subtree of an abstract list constructor.
%%
%% Recall that the tail does not necessarily represent a proper %% list.
%% %% @see c_cons/2 cons_tl(#cons{tl = Tail}) -> Tail; cons_tl(#literal{val = [_ | Tail]}) -> #literal{val = Tail}. %% @spec is_c_list(Node::cerl()) -> boolean() %% %% @doc Returnstrue
if Node
represents a
%% proper list, otherwise false
. A proper list is either
%% the empty list []
, or a cons cell [Head |
%% Tail]
, where recursively Tail
is a
%% proper list.
%%
%% Note: Because Node
is a syntax tree, the actual
%% run-time values corresponding to its subtrees may often be partially
%% or completely unknown. Thus, if Node
represents e.g.
%% "[... | Ns]
" (where Ns
is a variable), then
%% the function will return false
, because it is not known
%% whether Ns
will be bound to a list at run-time. If
%% Node
instead represents e.g. "[1, 2, 3]
" or
%% "[A | []]
", then the function will return
%% true
.
Node
must represent a proper list. E.g., if
%% Node
represents "[X1, X2 |
%% [X3, X4 | []]
", then
%% list_elements(Node)
yields the list [X1, X2, X3,
%% X4]
.
%%
%% @see c_cons/2
%% @see c_nil/1
%% @see is_c_list/1
%% @see list_length/1
%% @see make_list/2
list_elements(#cons{hd = Head, tl = Tail}) ->
[Head | list_elements(Tail)];
list_elements(#literal{val = V}) ->
abstract_list(V).
abstract_list([X | Xs]) ->
[abstract(X) | abstract_list(Xs)];
abstract_list([]) ->
[].
%% @spec list_length(Node::cerl()) -> integer()
%%
%% @doc Returns the number of element subtrees of an abstract list.
%% Node
must represent a proper list. E.g., if
%% Node
represents "[X1 | [X2, X3 | [X4, X5,
%% X6]]]
", then list_length(Node)
returns the
%% integer 6.
%%
%% Note: this is equivalent to
%% length(list_elements(Node))
, but potentially more
%% efficient.
List
%% and the optional Tail
. If Tail
is
%% none
, the result will represent a nil-terminated list,
%% otherwise it represents "[... | Tail]
".
%%
%% @see c_cons/2
%% @see c_nil/0
%% @see ann_make_list/3
%% @see update_list/3
%% @see list_elements/1
make_list(List, Tail) ->
ann_make_list([], List, Tail).
%% @spec update_list(Old::cerl(), List::[cerl()]) -> cerl()
%% @equiv update_list(Old, List, none)
update_list(Node, List) ->
ann_make_list(get_ann(Node), List).
%% @spec update_list(Old::cerl(), List::[cerl()], Tail) -> cerl()
%%
%% Tail = cerl() | none
%%
%% @see make_list/2
%% @see update_list/2
update_list(Node, List, Tail) ->
ann_make_list(get_ann(Node), List, Tail).
%% @spec ann_make_list(As::[term()], List::[cerl()]) -> cerl()
%% @equiv ann_make_list(As, List, none)
ann_make_list(As, List) ->
ann_make_list(As, List, none).
%% @spec ann_make_list(As::[term()], List::[cerl()], Tail) -> cerl()
%%
%% Tail = cerl() | none
%%
%% @see make_list/2
%% @see ann_make_list/2
ann_make_list(As, [H | T], Tail) ->
ann_c_cons(As, H, make_list(T, Tail)); % `c_cons' folds literals
ann_make_list(As, [], none) ->
ann_c_nil(As);
ann_make_list(_, [], Node) ->
Node.
%% ---------------------------------------------------------------------
%% @spec c_tuple(Elements::[cerl()]) -> cerl()
%%
%% @doc Creates an abstract tuple. If Elements
is
%% [E1, ..., En]
, the result represents
%% "{E1, ..., En}
". Note that if all
%% nodes in Elements
have type literal
, or if
%% Elements
is empty, then the result will also have type
%% literal
and annotations on nodes in
%% Elements
are lost.
%%
%% Recall that Erlang has distinct 1-tuples, i.e., {X}
%% is always distinct from X
itself.
tuple
,
%% representing "{E1, ..., En}
", if
%% Elements
is [E1, ..., En]
.
%%
%% This function is occasionally useful when it is necessary to have
%% annotations on the subnodes of a tuple node, even when all the
%% subnodes are constant literals. Note however that
%% is_literal/1
will yield false
and
%% concrete/1
will fail if passed the result from this
%% function.
fold_literal/1
can be used to revert a node to the
%% normal-form representation.
true
if Node
is an abstract
%% tuple, otherwise false
.
%%
%% @see c_tuple/1
is_c_tuple(#tuple{}) ->
true;
is_c_tuple(#literal{val = V}) when tuple(V) ->
true;
is_c_tuple(_) ->
false.
%% @spec tuple_es(cerl()) -> [cerl()]
%%
%% @doc Returns the list of element subtrees of an abstract tuple.
%%
%% @see c_tuple/1
tuple_es(#tuple{es = Es}) ->
Es;
tuple_es(#literal{val = V}) ->
make_lit_list(tuple_to_list(V)).
%% @spec tuple_arity(Node::cerl()) -> integer()
%%
%% @doc Returns the number of element subtrees of an abstract tuple.
%%
%% Note: this is equivalent to length(tuple_es(Node))
,
%% but potentially more efficient.
Name
parameter.
%%
%% If a name is given by a single atom, it should either be a
%% "simple" atom which does not need to be single-quoted in Erlang, or
%% otherwise its print name should correspond to a proper Erlang
%% variable, i.e., begin with an uppercase character or an
%% underscore. Names on the form {A, N}
represent
%% function name variables "A/N
"; these
%% are special variables which may be bound only in the function
%% definitions of a module or a letrec
. They may not be
%% bound in let
expressions and cannot occur in clause
%% patterns. The atom A
in a function name may be any
%% atom; the integer N
must be nonnegative. The functions
%% c_fname/2
etc. are utilities for handling function
%% name variables.
When printing variable names, they must have the form of proper
%% Core Erlang variables and function names. E.g., a name represented
%% by an integer such as 42
could be formatted as
%% "_42
", an atom 'Xxx'
simply as
%% "Xxx
", and an atom foo
as
%% "_foo
". However, one must assure that any two valid
%% distinct names are never mapped to the same strings. Tuples such
%% as {foo, 2}
representing function names can simply by
%% formatted as "'foo'/2
", with no risk of conflicts.
true
if Node
is an abstract
%% variable, otherwise false
.
%%
%% @see c_var/1
is_c_var(#var{}) ->
true;
is_c_var(_) ->
false.
%% @spec c_fname(Name::atom(), Arity::integer()) -> cerl()
%% @equiv c_var({Name, Arity})
%% @see fname_id/1
%% @see fname_arity/1
%% @see is_c_fname/1
%% @see ann_c_fname/3
%% @see update_c_fname/3
c_fname(Atom, Arity) ->
c_var({Atom, Arity}).
%% @spec ann_c_fname(As::[term()], Name::atom(), Arity::integer()) ->
%% cerl()
%% @equiv ann_c_var(As, {Atom, Arity})
%% @see c_fname/2
ann_c_fname(As, Atom, Arity) ->
ann_c_var(As, {Atom, Arity}).
%% @spec update_c_fname(Old::cerl(), Name::atom()) -> cerl()
%% @doc Like update_c_fname/3
, but takes the arity from
%% Node
.
%% @see update_c_fname/3
%% @see c_fname/2
update_c_fname(#var{name = {_, Arity}, ann = As}, Atom) ->
#var{name = {Atom, Arity}, ann = As}.
%% @spec update_c_fname(Old::cerl(), Name::atom(), Arity::integer()) ->
%% cerl()
%% @equiv update_c_var(Old, {Atom, Arity})
%% @see update_c_fname/2
%% @see c_fname/2
update_c_fname(Node, Atom, Arity) ->
update_c_var(Node, {Atom, Arity}).
%% @spec is_c_fname(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% function name variable, otherwise false
.
%%
%% @see c_fname/2
%% @see c_var/1
%% @see c_var_name/1
is_c_fname(#var{name = {A, N}}) when atom(A), integer(N), N >= 0 ->
true;
is_c_fname(_) ->
false.
%% @spec var_name(cerl()) -> var_name()
%%
%% @doc Returns the name of an abstract variable.
%%
%% @see c_var/1
var_name(Node) ->
Node#var.name.
%% @spec fname_id(cerl()) -> atom()
%%
%% @doc Returns the identifier part of an abstract function name
%% variable.
%%
%% @see fname_arity/1
%% @see c_fname/2
fname_id(#var{name={A,_}}) ->
A.
%% @spec fname_arity(cerl()) -> integer()
%%
%% @doc Returns the arity part of an abstract function name variable.
%%
%% @see fname_id/1
%% @see c_fname/2
fname_arity(#var{name={_,N}}) ->
N.
%% ---------------------------------------------------------------------
%% @spec c_values(Elements::[cerl()]) -> cerl()
%%
%% @doc Creates an abstract value list. If Elements
is
%% [E1, ..., En]
, the result represents
%% "<E1, ..., En>
".
%%
%% @see ann_c_values/2
%% @see update_c_values/2
%% @see is_c_values/1
%% @see values_es/1
%% @see values_arity/1
-record(values, {ann = [], es}).
c_values(Es) ->
#values{es = Es}.
%% @spec ann_c_values(As::[term()], Elements::[cerl()]) -> cerl()
%% @see c_values/1
ann_c_values(As, Es) ->
#values{es = Es, ann = As}.
%% @spec update_c_values(Old::cerl(), Elements::[cerl()]) -> cerl()
%% @see c_values/1
update_c_values(Node, Es) ->
#values{es = Es, ann = get_ann(Node)}.
%% @spec is_c_values(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% value list; otherwise false
.
%%
%% @see c_values/1
is_c_values(#values{}) ->
true;
is_c_values(_) ->
false.
%% @spec values_es(cerl()) -> [cerl()]
%%
%% @doc Returns the list of element subtrees of an abstract value
%% list.
%%
%% @see c_values/1
%% @see values_arity/1
values_es(Node) ->
Node#values.es.
%% @spec values_arity(Node::cerl()) -> integer()
%%
%% @doc Returns the number of element subtrees of an abstract value
%% list.
%%
%% Note: This is equivalent to
%% length(values_es(Node))
, but potentially more
%% efficient.
Segments
is [S1, ..., Sn]
, the result
%% represents "#{S1, ..., Sn}#
". All the
%% Si
must have type bitstr
.
%%
%% @see ann_c_binary/2
%% @see update_c_binary/2
%% @see is_c_binary/1
%% @see binary_segments/1
%% @see c_bitstr/5
-record(binary, {ann = [], segments}).
c_binary(Segments) ->
#binary{segments = Segments}.
%% @spec ann_c_binary(As::[term()], Segments::[cerl()]) -> cerl()
%% @see c_binary/1
ann_c_binary(As, Segments) ->
#binary{segments = Segments, ann = As}.
%% @spec update_c_binary(Old::cerl(), Segments::[cerl()]) -> cerl()
%% @see c_binary/1
update_c_binary(Node, Segments) ->
#binary{segments = Segments, ann = get_ann(Node)}.
%% @spec is_c_binary(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% binary-template; otherwise false
.
%%
%% @see c_binary/1
is_c_binary(#binary{}) ->
true;
is_c_binary(_) ->
false.
%% @spec binary_segments(cerl()) -> [cerl()]
%%
%% @doc Returns the list of segment subtrees of an abstract
%% binary-template.
%%
%% @see c_binary/1
%% @see c_bitstr/5
binary_segments(Node) ->
Node#binary.segments.
%% @spec c_bitstr(Value::cerl(), Size::cerl(), Unit::cerl(),
%% Type::cerl(), Flags::cerl()) -> cerl()
%%
%% @doc Creates an abstract bit-string template. These can only occur as
%% components of an abstract binary-template (see {@link c_binary/1}).
%% The result represents "#<Value>(Size,
%% Unit, Type, Flags)
", where
%% Unit
must represent a positive integer constant,
%% Type
must represent a constant atom (one of
%% 'integer'
, 'float'
, or
%% 'binary'
), and Flags
must represent a
%% constant list "[F1, ..., Fn]"
where
%% all the Fi
are atoms.
%%
%% @see c_binary/1
%% @see ann_c_bitstr/6
%% @see update_c_bitstr/6
%% @see is_c_bitstr/1
%% @see bitstr_val/1
%% @see bitstr_size/1
%% @see bitstr_unit/1
%% @see bitstr_type/1
%% @see bitstr_flags/1
-record(bitstr, {ann = [], val, size, unit, type, flags}).
c_bitstr(Val, Size, Unit, Type, Flags) ->
#bitstr{val = Val, size = Size, unit = Unit, type = Type,
flags = Flags}.
%% @spec c_bitstr(Value::cerl(), Size::cerl(), Type::cerl(),
%% Flags::cerl()) -> cerl()
%% @equiv c_bitstr(Value, Size, abstract(1), Type, Flags)
c_bitstr(Val, Size, Type, Flags) ->
c_bitstr(Val, Size, abstract(1), Type, Flags).
%% @spec c_bitstr(Value::cerl(), Type::cerl(),
%% Flags::cerl()) -> cerl()
%% @equiv c_bitstr(Value, abstract(all), abstract(1), Type, Flags)
c_bitstr(Val, Type, Flags) ->
c_bitstr(Val, abstract(all), abstract(1), Type, Flags).
%% @spec ann_c_bitstr(As::[term()], Value::cerl(), Size::cerl(),
%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> cerl()
%% @see c_bitstr/5
%% @see ann_c_bitstr/5
ann_c_bitstr(As, Val, Size, Unit, Type, Flags) ->
#bitstr{val = Val, size = Size, unit = Unit, type = Type,
flags = Flags, ann = As}.
%% @spec ann_c_bitstr(As::[term()], Value::cerl(), Size::cerl(),
%% Type::cerl(), Flags::cerl()) -> cerl()
%% @equiv ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags)
ann_c_bitstr(As, Value, Size, Type, Flags) ->
ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags).
%% @spec update_c_bitstr(Old::cerl(), Value::cerl(), Size::cerl(),
%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> cerl()
%% @see c_bitstr/5
%% @see update_c_bitstr/5
update_c_bitstr(Node, Val, Size, Unit, Type, Flags) ->
#bitstr{val = Val, size = Size, unit = Unit, type = Type,
flags = Flags, ann = get_ann(Node)}.
%% @spec update_c_bitstr(Old::cerl(), Value::cerl(), Size::cerl(),
%% Type::cerl(), Flags::cerl()) -> cerl()
%% @equiv update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags)
update_c_bitstr(Node, Value, Size, Type, Flags) ->
update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags).
%% @spec is_c_bitstr(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% bit-string template; otherwise false
.
%%
%% @see c_bitstr/5
is_c_bitstr(#bitstr{}) ->
true;
is_c_bitstr(_) ->
false.
%% @spec bitstr_val(cerl()) -> cerl()
%%
%% @doc Returns the value subtree of an abstract bit-string template.
%%
%% @see c_bitstr/5
bitstr_val(Node) ->
Node#bitstr.val.
%% @spec bitstr_size(cerl()) -> cerl()
%%
%% @doc Returns the size subtree of an abstract bit-string template.
%%
%% @see c_bitstr/5
bitstr_size(Node) ->
Node#bitstr.size.
%% @spec bitstr_bitsize(cerl()) -> integer() | any | all
%%
%% @doc Returns the total size in bits of an abstract bit-string
%% template. If the size field is an integer literal, the result is the
%% product of the size and unit values; if the size field is the atom
%% literal all
, the atom all
is returned; in
%% all other cases, the atom any
is returned.
%%
%% @see c_bitstr/5
bitstr_bitsize(Node) ->
Size = Node#bitstr.size,
case is_literal(Size) of
true ->
case concrete(Size) of
all ->
all;
S when integer(S) ->
S*concrete(Node#bitstr.unit);
true ->
any
end;
false ->
any
end.
%% @spec bitstr_unit(cerl()) -> cerl()
%%
%% @doc Returns the unit subtree of an abstract bit-string template.
%%
%% @see c_bitstr/5
bitstr_unit(Node) ->
Node#bitstr.unit.
%% @spec bitstr_type(cerl()) -> cerl()
%%
%% @doc Returns the type subtree of an abstract bit-string template.
%%
%% @see c_bitstr/5
bitstr_type(Node) ->
Node#bitstr.type.
%% @spec bitstr_flags(cerl()) -> cerl()
%%
%% @doc Returns the flags subtree of an abstract bit-string template.
%%
%% @see c_bitstr/5
bitstr_flags(Node) ->
Node#bitstr.flags.
%% ---------------------------------------------------------------------
%% @spec c_fun(Variables::[cerl()], Body::cerl()) -> cerl()
%%
%% @doc Creates an abstract fun-expression. If Variables
%% is [V1, ..., Vn]
, the result represents "fun
%% (V1, ..., Vn) -> Body
". All the
%% Vi
must have type var
.
%%
%% @see ann_c_fun/3
%% @see update_c_fun/3
%% @see is_c_fun/1
%% @see fun_vars/1
%% @see fun_body/1
%% @see fun_arity/1
-record('fun', {ann = [], vars, body}).
c_fun(Variables, Body) ->
#'fun'{vars = Variables, body = Body}.
%% @spec ann_c_fun(As::[term()], Variables::[cerl()], Body::cerl()) ->
%% cerl()
%% @see c_fun/2
ann_c_fun(As, Variables, Body) ->
#'fun'{vars = Variables, body = Body, ann = As}.
%% @spec update_c_fun(Old::cerl(), Variables::[cerl()],
%% Body::cerl()) -> cerl()
%% @see c_fun/2
update_c_fun(Node, Variables, Body) ->
#'fun'{vars = Variables, body = Body, ann = get_ann(Node)}.
%% @spec is_c_fun(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% fun-expression, otherwise false
.
%%
%% @see c_fun/2
is_c_fun(#'fun'{}) ->
true; % Now this is fun!
is_c_fun(_) ->
false.
%% @spec fun_vars(cerl()) -> [cerl()]
%%
%% @doc Returns the list of parameter subtrees of an abstract
%% fun-expression.
%%
%% @see c_fun/2
%% @see fun_arity/1
fun_vars(Node) ->
Node#'fun'.vars.
%% @spec fun_body(cerl()) -> cerl()
%%
%% @doc Returns the body subtree of an abstract fun-expression.
%%
%% @see c_fun/2
fun_body(Node) ->
Node#'fun'.body.
%% @spec fun_arity(Node::cerl()) -> integer()
%%
%% @doc Returns the number of parameter subtrees of an abstract
%% fun-expression.
%%
%% Note: this is equivalent to length(fun_vars(Node))
,
%% but potentially more efficient.
do Argument Body
".
%%
%% @see ann_c_seq/3
%% @see update_c_seq/3
%% @see is_c_seq/1
%% @see seq_arg/1
%% @see seq_body/1
-record(seq, {ann = [], arg, body}).
c_seq(Argument, Body) ->
#seq{arg = Argument, body = Body}.
%% @spec ann_c_seq(As::[term()], Argument::cerl(), Body::cerl()) ->
%% cerl()
%% @see c_seq/2
ann_c_seq(As, Argument, Body) ->
#seq{arg = Argument, body = Body, ann = As}.
%% @spec update_c_seq(Old::cerl(), Argument::cerl(), Body::cerl()) ->
%% cerl()
%% @see c_seq/2
update_c_seq(Node, Argument, Body) ->
#seq{arg = Argument, body = Body, ann = get_ann(Node)}.
%% @spec is_c_seq(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% sequencing expression, otherwise false
.
%%
%% @see c_seq/2
is_c_seq(#seq{}) ->
true;
is_c_seq(_) ->
false.
%% @spec seq_arg(cerl()) -> cerl()
%%
%% @doc Returns the argument subtree of an abstract sequencing
%% expression.
%%
%% @see c_seq/2
seq_arg(Node) ->
Node#seq.arg.
%% @spec seq_body(cerl()) -> cerl()
%%
%% @doc Returns the body subtree of an abstract sequencing expression.
%%
%% @see c_seq/2
seq_body(Node) ->
Node#seq.body.
%% ---------------------------------------------------------------------
%% @spec c_let(Variables::[cerl()], Argument::cerl(), Body::cerl()) ->
%% cerl()
%%
%% @doc Creates an abstract let-expression. If Variables
%% is [V1, ..., Vn]
, the result represents "let
%% <V1, ..., Vn> = Argument in
%% Body
". All the Vi
must have type
%% var
.
%%
%% @see ann_c_let/4
%% @see update_c_let/4
%% @see is_c_let/1
%% @see let_vars/1
%% @see let_arg/1
%% @see let_body/1
%% @see let_arity/1
-record('let', {ann = [], vars, arg, body}).
c_let(Variables, Argument, Body) ->
#'let'{vars = Variables, arg = Argument, body = Body}.
%% ann_c_let(As, Variables, Argument, Body) -> Node
%% @see c_let/3
ann_c_let(As, Variables, Argument, Body) ->
#'let'{vars = Variables, arg = Argument, body = Body, ann = As}.
%% update_c_let(Old, Variables, Argument, Body) -> Node
%% @see c_let/3
update_c_let(Node, Variables, Argument, Body) ->
#'let'{vars = Variables, arg = Argument, body = Body,
ann = get_ann(Node)}.
%% @spec is_c_let(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% let-expression, otherwise false
.
%%
%% @see c_let/3
is_c_let(#'let'{}) ->
true;
is_c_let(_) ->
false.
%% @spec let_vars(cerl()) -> [cerl()]
%%
%% @doc Returns the list of left-hand side variables of an abstract
%% let-expression.
%%
%% @see c_let/3
%% @see let_arity/1
let_vars(Node) ->
Node#'let'.vars.
%% @spec let_arg(cerl()) -> cerl()
%%
%% @doc Returns the argument subtree of an abstract let-expression.
%%
%% @see c_let/3
let_arg(Node) ->
Node#'let'.arg.
%% @spec let_body(cerl()) -> cerl()
%%
%% @doc Returns the body subtree of an abstract let-expression.
%%
%% @see c_let/3
let_body(Node) ->
Node#'let'.body.
%% @spec let_arity(Node::cerl()) -> integer()
%%
%% @doc Returns the number of left-hand side variables of an abstract
%% let-expression.
%%
%% Note: this is equivalent to length(let_vars(Node))
,
%% but potentially more efficient.
Definitions
is [{V1, F1}, ..., {Vn, Fn}]
,
%% the result represents "letrec V1 = F1
%% ... Vn = Fn in Body
. All the
%% Vi
must have type var
and represent
%% function names. All the Fi
must have type
%% 'fun'
.
%%
%% @see ann_c_letrec/3
%% @see update_c_letrec/3
%% @see is_c_letrec/1
%% @see letrec_defs/1
%% @see letrec_body/1
%% @see letrec_vars/1
-record(letrec, {ann = [], defs, body}).
c_letrec(Defs, Body) ->
#letrec{defs = Defs, body = Body}.
%% @spec ann_c_letrec(As::[term()], Definitions::[{cerl(), cerl()}],
%% Body::cerl()) -> cerl()
%% @see c_letrec/2
ann_c_letrec(As, Defs, Body) ->
#letrec{defs = Defs, body = Body, ann = As}.
%% @spec update_c_letrec(Old::cerl(),
%% Definitions::[{cerl(), cerl()}],
%% Body::cerl()) -> cerl()
%% @see c_letrec/2
update_c_letrec(Node, Defs, Body) ->
#letrec{defs = Defs, body = Body, ann = get_ann(Node)}.
%% @spec is_c_letrec(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% letrec-expression, otherwise false
.
%%
%% @see c_letrec/2
is_c_letrec(#letrec{}) ->
true;
is_c_letrec(_) ->
false.
%% @spec letrec_defs(Node::cerl()) -> [{cerl(), cerl()}]
%%
%% @doc Returns the list of definitions of an abstract
%% letrec-expression. If Node
represents "letrec
%% V1 = F1 ... Vn = Fn in
%% Body
", the returned value is [{V1, F1}, ...,
%% {Vn, Fn}]
.
%%
%% @see c_letrec/2
letrec_defs(Node) ->
Node#letrec.defs.
%% @spec letrec_body(cerl()) -> cerl()
%%
%% @doc Returns the body subtree of an abstract letrec-expression.
%%
%% @see c_letrec/2
letrec_body(Node) ->
Node#letrec.body.
%% @spec letrec_vars(cerl()) -> [cerl()]
%%
%% @doc Returns the list of left-hand side function variable subtrees
%% of a letrec-expression. If Node
represents
%% "letrec V1 = F1 ... Vn =
%% Fn in Body
", the returned value is
%% [V1, ..., Vn]
.
%%
%% @see c_letrec/2
letrec_vars(Node) ->
[F || {F, _} <- letrec_defs(Node)].
%% ---------------------------------------------------------------------
%% @spec c_case(Argument::cerl(), Clauses::[cerl()]) -> cerl()
%%
%% @doc Creates an abstract case-expression. If Clauses
%% is [C1, ..., Cn]
, the result represents "case
%% Argument of C1 ... Cn
%% end
". Clauses
must not be empty.
%%
%% @see ann_c_case/3
%% @see update_c_case/3
%% @see is_c_case/1
%% @see c_clause/3
%% @see case_arg/1
%% @see case_clauses/1
%% @see case_arity/1
-record('case', {ann = [], arg, clauses}).
c_case(Expr, Clauses) ->
#'case'{arg = Expr, clauses = Clauses}.
%% @spec ann_c_case(As::[term()], Argument::cerl(),
%% Clauses::[cerl()]) -> cerl()
%% @see c_case/2
ann_c_case(As, Expr, Clauses) ->
#'case'{arg = Expr, clauses = Clauses, ann = As}.
%% @spec update_c_case(Old::cerl(), Argument::cerl(),
%% Clauses::[cerl()]) -> cerl()
%% @see c_case/2
update_c_case(Node, Expr, Clauses) ->
#'case'{arg = Expr, clauses = Clauses, ann = get_ann(Node)}.
%% is_c_case(Node) -> boolean()
%%
%% Node = cerl()
%%
%% @doc Returns true
if Node
is an abstract
%% case-expression; otherwise false
.
%%
%% @see c_case/2
is_c_case(#'case'{}) ->
true;
is_c_case(_) ->
false.
%% @spec case_arg(cerl()) -> cerl()
%%
%% @doc Returns the argument subtree of an abstract case-expression.
%%
%% @see c_case/2
case_arg(Node) ->
Node#'case'.arg.
%% @spec case_clauses(cerl()) -> [cerl()]
%%
%% @doc Returns the list of clause subtrees of an abstract
%% case-expression.
%%
%% @see c_case/2
%% @see case_arity/1
case_clauses(Node) ->
Node#'case'.clauses.
%% @spec case_arity(Node::cerl()) -> integer()
%%
%% @doc Equivalent to
%% clause_arity(hd(case_clauses(Node)))
, but potentially
%% more efficient.
%%
%% @see c_case/2
%% @see case_clauses/1
%% @see clause_arity/1
case_arity(Node) ->
clause_arity(hd(case_clauses(Node))).
%% ---------------------------------------------------------------------
%% @spec c_clause(Patterns::[cerl()], Body::cerl()) -> cerl()
%% @equiv c_clause(Patterns, c_atom(true), Body)
%% @see c_atom/1
c_clause(Patterns, Body) ->
c_clause(Patterns, c_atom(true), Body).
%% @spec c_clause(Patterns::[cerl()], Guard::cerl(), Body::cerl()) ->
%% cerl()
%%
%% @doc Creates an an abstract clause. If Patterns
is
%% [P1, ..., Pn]
, the result represents
%% "<P1, ..., Pn> when Guard ->
%% Body
".
%%
%% @see c_clause/2
%% @see ann_c_clause/4
%% @see update_c_clause/4
%% @see is_c_clause/1
%% @see c_case/2
%% @see c_receive/3
%% @see clause_pats/1
%% @see clause_guard/1
%% @see clause_body/1
%% @see clause_arity/1
%% @see clause_vars/1
-record(clause, {ann = [], pats, guard, body}).
c_clause(Patterns, Guard, Body) ->
#clause{pats = Patterns, guard = Guard, body = Body}.
%% @spec ann_c_clause(As::[term()], Patterns::[cerl()],
%% Body::cerl()) -> cerl()
%% @equiv ann_c_clause(As, Patterns, c_atom(true), Body)
%% @see c_clause/3
ann_c_clause(As, Patterns, Body) ->
ann_c_clause(As, Patterns, c_atom(true), Body).
%% @spec ann_c_clause(As::[term()], Patterns::[cerl()], Guard::cerl(),
%% Body::cerl()) -> cerl()
%% @see ann_c_clause/3
%% @see c_clause/3
ann_c_clause(As, Patterns, Guard, Body) ->
#clause{pats = Patterns, guard = Guard, body = Body, ann = As}.
%% @spec update_c_clause(Old::cerl(), Patterns::[cerl()],
%% Guard::cerl(), Body::cerl()) -> cerl()
%% @see c_clause/3
update_c_clause(Node, Patterns, Guard, Body) ->
#clause{pats = Patterns, guard = Guard, body = Body,
ann = get_ann(Node)}.
%% @spec is_c_clause(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% clause, otherwise false
.
%%
%% @see c_clause/3
is_c_clause(#clause{}) ->
true;
is_c_clause(_) ->
false.
%% @spec clause_pats(cerl()) -> [cerl()]
%%
%% @doc Returns the list of pattern subtrees of an abstract clause.
%%
%% @see c_clause/3
%% @see clause_arity/1
clause_pats(Node) ->
Node#clause.pats.
%% @spec clause_guard(cerl()) -> cerl()
%%
%% @doc Returns the guard subtree of an abstract clause.
%%
%% @see c_clause/3
clause_guard(Node) ->
Node#clause.guard.
%% @spec clause_body(cerl()) -> cerl()
%%
%% @doc Returns the body subtree of an abstract clause.
%%
%% @see c_clause/3
clause_body(Node) ->
Node#clause.body.
%% @spec clause_arity(Node::cerl()) -> integer()
%%
%% @doc Returns the number of pattern subtrees of an abstract clause.
%%
%% Note: this is equivalent to
%% length(clause_pats(Node))
, but potentially more
%% efficient.
Node
does not represent a
%% well-formed Core Erlang clause pattern. The order of listing is not
%% defined.
%%
%% @see pat_list_vars/1
%% @see clause_vars/1
pat_vars(Node) ->
pat_vars(Node, []).
pat_vars(Node, Vs) ->
case type(Node) of
var ->
[Node | Vs];
literal ->
Vs;
cons ->
pat_vars(cons_hd(Node), pat_vars(cons_tl(Node), Vs));
tuple ->
pat_list_vars(tuple_es(Node), Vs);
binary ->
pat_list_vars(binary_segments(Node), Vs);
bitstr ->
pat_vars(bitstr_val(Node), Vs);
alias ->
pat_vars(alias_pat(Node), [alias_var(Node) | Vs])
end.
%% @spec pat_list_vars(Patterns::[cerl()]) -> [cerl()]
%%
%% @doc Returns the list of all abstract variables in the given
%% patterns. An exception is thrown if some element in
%% Patterns
does not represent a well-formed Core Erlang
%% clause pattern. The order of listing is not defined.
%%
%% @see pat_vars/1
%% @see clause_vars/1
pat_list_vars(Ps) ->
pat_list_vars(Ps, []).
pat_list_vars([P | Ps], Vs) ->
pat_list_vars(Ps, pat_vars(P, Vs));
pat_list_vars([], Vs) ->
Vs.
%% ---------------------------------------------------------------------
%% @spec c_alias(Variable::cerl(), Pattern::cerl()) -> cerl()
%%
%% @doc Creates an abstract pattern alias. The result represents
%% "Variable = Pattern
".
%%
%% @see ann_c_alias/3
%% @see update_c_alias/3
%% @see is_c_alias/1
%% @see alias_var/1
%% @see alias_pat/1
%% @see c_clause/3
-record(alias, {ann = [], var, pat}).
c_alias(Var, Pattern) ->
#alias{var = Var, pat = Pattern}.
%% @spec ann_c_alias(As::[term()], Variable::cerl(),
%% Pattern::cerl()) -> cerl()
%% @see c_alias/2
ann_c_alias(As, Var, Pattern) ->
#alias{var = Var, pat = Pattern, ann = As}.
%% @spec update_c_alias(Old::cerl(), Variable::cerl(),
%% Pattern::cerl()) -> cerl()
%% @see c_alias/2
update_c_alias(Node, Var, Pattern) ->
#alias{var = Var, pat = Pattern, ann = get_ann(Node)}.
%% @spec is_c_alias(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% pattern alias, otherwise false
.
%%
%% @see c_alias/2
is_c_alias(#alias{}) ->
true;
is_c_alias(_) ->
false.
%% @spec alias_var(cerl()) -> cerl()
%%
%% @doc Returns the variable subtree of an abstract pattern alias.
%%
%% @see c_alias/2
alias_var(Node) ->
Node#alias.var.
%% @spec alias_pat(cerl()) -> cerl()
%%
%% @doc Returns the pattern subtree of an abstract pattern alias.
%%
%% @see c_alias/2
alias_pat(Node) ->
Node#alias.pat.
%% ---------------------------------------------------------------------
%% @spec c_receive(Clauses::[cerl()]) -> cerl()
%% @equiv c_receive(Clauses, c_atom(infinity), c_atom(true))
%% @see c_atom/1
c_receive(Clauses) ->
c_receive(Clauses, c_atom(infinity), c_atom(true)).
%% @spec c_receive(Clauses::[cerl()], Timeout::cerl(),
%% Action::cerl()) -> cerl()
%%
%% @doc Creates an abstract receive-expression. If
%% Clauses
is [C1, ..., Cn]
, the result
%% represents "receive C1 ... Cn after
%% Timeout -> Action end
".
%%
%% @see c_receive/1
%% @see ann_c_receive/4
%% @see update_c_receive/4
%% @see is_c_receive/1
%% @see receive_clauses/1
%% @see receive_timeout/1
%% @see receive_action/1
-record('receive', {ann = [], clauses, timeout, action}).
c_receive(Clauses, Timeout, Action) ->
#'receive'{clauses = Clauses, timeout = Timeout, action = Action}.
%% @spec ann_c_receive(As::[term()], Clauses::[cerl()]) -> cerl()
%% @equiv ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true))
%% @see c_receive/3
%% @see c_atom/1
ann_c_receive(As, Clauses) ->
ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true)).
%% @spec ann_c_receive(As::[term()], Clauses::[cerl()],
%% Timeout::cerl(), Action::cerl()) -> cerl()
%% @see ann_c_receive/2
%% @see c_receive/3
ann_c_receive(As, Clauses, Timeout, Action) ->
#'receive'{clauses = Clauses, timeout = Timeout, action = Action,
ann = As}.
%% @spec update_c_receive(Old::cerl(), Clauses::[cerl()],
%% Timeout::cerl(), Action::cerl()) -> cerl()
%% @see c_receive/3
update_c_receive(Node, Clauses, Timeout, Action) ->
#'receive'{clauses = Clauses, timeout = Timeout, action = Action,
ann = get_ann(Node)}.
%% @spec is_c_receive(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% receive-expression, otherwise false
.
%%
%% @see c_receive/3
is_c_receive(#'receive'{}) ->
true;
is_c_receive(_) ->
false.
%% @spec receive_clauses(cerl()) -> [cerl()]
%%
%% @doc Returns the list of clause subtrees of an abstract
%% receive-expression.
%%
%% @see c_receive/3
receive_clauses(Node) ->
Node#'receive'.clauses.
%% @spec receive_timeout(cerl()) -> cerl()
%%
%% @doc Returns the timeout subtree of an abstract receive-expression.
%%
%% @see c_receive/3
receive_timeout(Node) ->
Node#'receive'.timeout.
%% @spec receive_action(cerl()) -> cerl()
%%
%% @doc Returns the action subtree of an abstract receive-expression.
%%
%% @see c_receive/3
receive_action(Node) ->
Node#'receive'.action.
%% ---------------------------------------------------------------------
%% @spec c_apply(Operator::cerl(), Arguments::[cerl()]) -> cerl()
%%
%% @doc Creates an abstract function application. If
%% Arguments
is [A1, ..., An]
, the result
%% represents "apply Operator(A1, ...,
%% An)
".
%%
%% @see ann_c_apply/3
%% @see update_c_apply/3
%% @see is_c_apply/1
%% @see apply_op/1
%% @see apply_args/1
%% @see apply_arity/1
%% @see c_call/3
%% @see c_primop/2
-record(apply, {ann = [], op, args}).
c_apply(Operator, Arguments) ->
#apply{op = Operator, args = Arguments}.
%% @spec ann_c_apply(As::[term()], Operator::cerl(),
%% Arguments::[cerl()]) -> cerl()
%% @see c_apply/2
ann_c_apply(As, Operator, Arguments) ->
#apply{op = Operator, args = Arguments, ann = As}.
%% @spec update_c_apply(Old::cerl(), Operator::cerl(),
%% Arguments::[cerl()]) -> cerl()
%% @see c_apply/2
update_c_apply(Node, Operator, Arguments) ->
#apply{op = Operator, args = Arguments, ann = get_ann(Node)}.
%% @spec is_c_apply(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% function application, otherwise false
.
%%
%% @see c_apply/2
is_c_apply(#apply{}) ->
true;
is_c_apply(_) ->
false.
%% @spec apply_op(cerl()) -> cerl()
%%
%% @doc Returns the operator subtree of an abstract function
%% application.
%%
%% @see c_apply/2
apply_op(Node) ->
Node#apply.op.
%% @spec apply_args(cerl()) -> [cerl()]
%%
%% @doc Returns the list of argument subtrees of an abstract function
%% application.
%%
%% @see c_apply/2
%% @see apply_arity/1
apply_args(Node) ->
Node#apply.args.
%% @spec apply_arity(Node::cerl()) -> integer()
%%
%% @doc Returns the number of argument subtrees of an abstract
%% function application.
%%
%% Note: this is equivalent to
%% length(apply_args(Node))
, but potentially more
%% efficient.
Arguments
is [A1, ..., An]
, the result
%% represents "call Module:Name(A1,
%% ..., An)
".
%%
%% @see ann_c_call/4
%% @see update_c_call/4
%% @see is_c_call/1
%% @see call_module/1
%% @see call_name/1
%% @see call_args/1
%% @see call_arity/1
%% @see c_apply/2
%% @see c_primop/2
-record(call, {ann = [], module, name, args}).
c_call(Module, Name, Arguments) ->
#call{module = Module, name = Name, args = Arguments}.
%% @spec ann_c_call(As::[term()], Module::cerl(), Name::cerl(),
%% Arguments::[cerl()]) -> cerl()
%% @see c_call/3
ann_c_call(As, Module, Name, Arguments) ->
#call{module = Module, name = Name, args = Arguments, ann = As}.
%% @spec update_c_call(Old::cerl(), Module::cerl(), Name::cerl(),
%% Arguments::[cerl()]) -> cerl()
%% @see c_call/3
update_c_call(Node, Module, Name, Arguments) ->
#call{module = Module, name = Name, args = Arguments,
ann = get_ann(Node)}.
%% @spec is_c_call(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% inter-module call expression; otherwise false
.
%%
%% @see c_call/3
is_c_call(#call{}) ->
true;
is_c_call(_) ->
false.
%% @spec call_module(cerl()) -> cerl()
%%
%% @doc Returns the module subtree of an abstract inter-module call.
%%
%% @see c_call/3
call_module(Node) ->
Node#call.module.
%% @spec call_name(cerl()) -> cerl()
%%
%% @doc Returns the name subtree of an abstract inter-module call.
%%
%% @see c_call/3
call_name(Node) ->
Node#call.name.
%% @spec call_args(cerl()) -> [cerl()]
%%
%% @doc Returns the list of argument subtrees of an abstract
%% inter-module call.
%%
%% @see c_call/3
%% @see call_arity/1
call_args(Node) ->
Node#call.args.
%% @spec call_arity(Node::cerl()) -> integer()
%%
%% @doc Returns the number of argument subtrees of an abstract
%% inter-module call.
%%
%% Note: this is equivalent to
%% length(call_args(Node))
, but potentially more
%% efficient.
Arguments
is [A1, ..., An]
, the result
%% represents "primop Name(A1, ...,
%% An)
". Name
must be an atom literal.
%%
%% @see ann_c_primop/3
%% @see update_c_primop/3
%% @see is_c_primop/1
%% @see primop_name/1
%% @see primop_args/1
%% @see primop_arity/1
%% @see c_apply/2
%% @see c_call/3
-record(primop, {ann = [], name, args}).
c_primop(Name, Arguments) ->
#primop{name = Name, args = Arguments}.
%% @spec ann_c_primop(As::[term()], Name::cerl(),
%% Arguments::[cerl()]) -> cerl()
%% @see c_primop/2
ann_c_primop(As, Name, Arguments) ->
#primop{name = Name, args = Arguments, ann = As}.
%% @spec update_c_primop(Old::cerl(), Name::cerl(),
%% Arguments::[cerl()]) -> cerl()
%% @see c_primop/2
update_c_primop(Node, Name, Arguments) ->
#primop{name = Name, args = Arguments, ann = get_ann(Node)}.
%% @spec is_c_primop(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% primitive operation call, otherwise false
.
%%
%% @see c_primop/2
is_c_primop(#primop{}) ->
true;
is_c_primop(_) ->
false.
%% @spec primop_name(cerl()) -> cerl()
%%
%% @doc Returns the name subtree of an abstract primitive operation
%% call.
%%
%% @see c_primop/2
primop_name(Node) ->
Node#primop.name.
%% @spec primop_args(cerl()) -> [cerl()]
%%
%% @doc Returns the list of argument subtrees of an abstract primitive
%% operation call.
%%
%% @see c_primop/2
%% @see primop_arity/1
primop_args(Node) ->
Node#primop.args.
%% @spec primop_arity(Node::cerl()) -> integer()
%%
%% @doc Returns the number of argument subtrees of an abstract
%% primitive operation call.
%%
%% Note: this is equivalent to
%% length(primop_args(Node))
, but potentially more
%% efficient.
Variables
is
%% [V1, ..., Vn]
and ExceptionVars
is
%% [X1, ..., Xm]
, the result represents "try
%% Argument of <V1, ..., Vn> ->
%% Body catch <X1, ..., Xm> ->
%% Handler
". All the Vi
and Xi
%% must have type var
.
%%
%% @see ann_c_try/6
%% @see update_c_try/6
%% @see is_c_try/1
%% @see try_arg/1
%% @see try_vars/1
%% @see try_body/1
%% @see c_catch/1
-record('try', {ann = [], arg, vars, body, evars, handler}).
c_try(Expr, Vs, Body, Evs, Handler) ->
#'try'{arg = Expr, vars = Vs, body = Body,
evars = Evs, handler = Handler}.
%% @spec ann_c_try(As::[term()], Expression::cerl(),
%% Variables::[cerl()], Body::cerl(),
%% EVars::[cerl()], EBody::[cerl()]) -> cerl()
%% @see c_try/3
ann_c_try(As, Expr, Vs, Body, Evs, Handler) ->
#'try'{arg = Expr, vars = Vs, body = Body,
evars = Evs, handler = Handler, ann = As}.
%% @spec update_c_try(Old::cerl(), Expression::cerl(),
%% Variables::[cerl()], Body::cerl(),
%% EVars::[cerl()], EBody::[cerl()]) -> cerl()
%% @see c_try/3
update_c_try(Node, Expr, Vs, Body, Evs, Handler) ->
#'try'{arg = Expr, vars = Vs, body = Body,
evars = Evs, handler = Handler, ann = get_ann(Node)}.
%% @spec is_c_try(Node::cerl()) -> boolean()
%%
%% @doc Returns true
if Node
is an abstract
%% try-expression, otherwise false
.
%%
%% @see c_try/3
is_c_try(#'try'{}) ->
true;
is_c_try(_) ->
false.
%% @spec try_arg(cerl()) -> cerl()
%%
%% @doc Returns the expression subtree of an abstract try-expression.
%%
%% @see c_try/3
try_arg(Node) ->
Node#'try'.arg.
%% @spec try_vars(cerl()) -> [cerl()]
%%
%% @doc Returns the list of success variable subtrees of an abstract
%% try-expression.
%%
%% @see c_try/3
try_vars(Node) ->
Node#'try'.vars.
%% @spec try_body(cerl()) -> cerl()
%%
%% @doc Returns the success body subtree of an abstract try-expression.
%%
%% @see c_try/3
try_body(Node) ->
Node#'try'.body.
%% @spec try_evars(cerl()) -> [cerl()]
%%
%% @doc Returns the list of exception variable subtrees of an abstract
%% try-expression.
%%
%% @see c_try/3
try_evars(Node) ->
Node#'try'.evars.
%% @spec try_handler(cerl()) -> cerl()
%%
%% @doc Returns the exception body subtree of an abstract
%% try-expression.
%%
%% @see c_try/3
try_handler(Node) ->
Node#'try'.handler.
%% ---------------------------------------------------------------------
%% @spec c_catch(Body::cerl()) -> cerl()
%%
%% @doc Creates an abstract catch-expression. The result represents
%% "catch Body
".
%%
%% Note: catch-expressions can be rewritten as try-expressions, and %% will eventually be removed from Core Erlang.
%% %% @see ann_c_catch/2 %% @see update_c_catch/2 %% @see is_c_catch/1 %% @see catch_body/1 %% @see c_try/3 -record('catch', {ann = [], body}). c_catch(Body) -> #'catch'{body = Body}. %% @spec ann_c_catch(As::[term()], Body::cerl()) -> cerl() %% @see c_catch/1 ann_c_catch(As, Body) -> #'catch'{body = Body, ann = As}. %% @spec update_c_catch(Old::cerl(), Body::cerl()) -> cerl() %% @see c_catch/1 update_c_catch(Node, Body) -> #'catch'{body = Body, ann = get_ann(Node)}. %% @spec is_c_catch(Node::cerl()) -> boolean() %% %% @doc Returnstrue
if Node
is an abstract
%% catch-expression, otherwise false
.
%%
%% @see c_catch/1
is_c_catch(#'catch'{}) ->
true;
is_c_catch(_) ->
false.
%% @spec catch_body(Node::cerl()) -> cerl()
%%
%% @doc Returns the body subtree of an abstract catch-expression.
%%
%% @see c_catch/1
catch_body(Node) ->
Node#'catch'.body.
%% ---------------------------------------------------------------------
%% @spec to_records(Tree::cerl()) -> record(record_types())
%%
%% @doc Translates an abstract syntax tree to a corresponding explicit
%% record representation. The records are defined in the file
%% "cerl.hrl
".
%%
%% Note: Compound constant literals are always unfolded in the %% record representation.
%% %% @see type/1 %% @see from_records/1 to_records(Node) -> A = get_ann(Node), case type(Node) of literal -> lit_to_records(concrete(Node), A); binary -> #c_binary{anno = A, segments = list_to_records(binary_segments(Node))}; bitstr -> #c_bitstr{anno = A, val = to_records(bitstr_val(Node)), size = to_records(bitstr_size(Node)), unit = to_records(bitstr_unit(Node)), type = to_records(bitstr_type(Node)), flags = to_records(bitstr_flags(Node))}; cons -> #c_cons{anno = A, hd = to_records(cons_hd(Node)), tl = to_records(cons_tl(Node))}; tuple -> #c_tuple{anno = A, es = list_to_records(tuple_es(Node))}; var -> case is_c_fname(Node) of true -> #c_fname{anno = A, id = fname_id(Node), arity = fname_arity(Node)}; false -> #c_var{anno = A, name = var_name(Node)} end; values -> #c_values{anno = A, es = list_to_records(values_es(Node))}; 'fun' -> #c_fun{anno = A, vars = list_to_records(fun_vars(Node)), body = to_records(fun_body(Node))}; seq -> #c_seq{anno = A, arg = to_records(seq_arg(Node)), body = to_records(seq_body(Node))}; 'let' -> #c_let{anno = A, vars = list_to_records(let_vars(Node)), arg = to_records(let_arg(Node)), body = to_records(let_body(Node))}; letrec -> #c_letrec{anno = A, defs = [#c_def{name = to_records(N), val = to_records(F)} || {N, F} <- letrec_defs(Node)], body = to_records(letrec_body(Node))}; 'case' -> #c_case{anno = A, arg = to_records(case_arg(Node)), clauses = list_to_records(case_clauses(Node))}; clause -> #c_clause{anno = A, pats = list_to_records(clause_pats(Node)), guard = to_records(clause_guard(Node)), body = to_records(clause_body(Node))}; alias -> #c_alias{anno = A, var = to_records(alias_var(Node)), pat = to_records(alias_pat(Node))}; 'receive' -> #c_receive{anno = A, clauses = list_to_records(receive_clauses(Node)), timeout = to_records(receive_timeout(Node)), action = to_records(receive_action(Node))}; apply -> #c_apply{anno = A, op = to_records(apply_op(Node)), args = list_to_records(apply_args(Node))}; call -> #c_call{anno = A, module = to_records(call_module(Node)), name = to_records(call_name(Node)), args = list_to_records(call_args(Node))}; primop -> #c_primop{anno = A, name = to_records(primop_name(Node)), args = list_to_records(primop_args(Node))}; 'try' -> #c_try{anno = A, arg = to_records(try_arg(Node)), vars = list_to_records(try_vars(Node)), body = to_records(try_body(Node)), evars = list_to_records(try_evars(Node)), handler = to_records(try_handler(Node))}; 'catch' -> #c_catch{anno = A, body = to_records(catch_body(Node))}; module -> #c_module{anno = A, name = to_records(module_name(Node)), exports = list_to_records( module_exports(Node)), attrs = [#c_def{name = to_records(K), val = to_records(V)} || {K, V} <- module_attrs(Node)], defs = [#c_def{name = to_records(N), val = to_records(F)} || {N, F} <- module_defs(Node)]} end. list_to_records([T | Ts]) -> [to_records(T) | list_to_records(Ts)]; list_to_records([]) -> []. lit_to_records(V, A) when integer(V) -> #c_int{anno = A, val = V}; lit_to_records(V, A) when float(V) -> #c_float{anno = A, val = V}; lit_to_records(V, A) when atom(V) -> #c_atom{anno = A, val = V}; lit_to_records([H | T] = V, A) -> case is_print_char_list(V) of true -> #c_string{anno = A, val = V}; false -> #c_cons{anno = A, hd = lit_to_records(H, []), tl = lit_to_records(T, [])} end; lit_to_records([], A) -> #c_nil{anno = A}; lit_to_records(V, A) when tuple(V) -> #c_tuple{anno = A, es = lit_list_to_records(tuple_to_list(V))}. lit_list_to_records([T | Ts]) -> [lit_to_records(T, []) | lit_list_to_records(Ts)]; lit_list_to_records([]) -> []. %% @spec from_records(Tree::record(record_types())) -> cerl() %% %% record_types() = c_alias | c_apply | c_call | c_case | c_catch | %% c_clause | c_cons | c_def| c_fun | c_let | %% c_letrec |c_lit | c_module | c_primop | %% c_receive | c_seq | c_try | c_tuple | %% c_values | c_var %% %% @doc Translates an explicit record representation to a %% corresponding abstract syntax tree. The records are defined in the %% file "cerl.hrl
".
%%
%% Note: Compound constant literals are folded, discarding
%% annotations on subtrees. There are no c_def
nodes in
%% the abstract representation; annotations on c_def
%% records are discarded.
true
if Node
represents a
%% data constructor, otherwise false
. Data constructors
%% are cons cells, tuples, and atomic literals.
%%
%% @see data_type/1
%% @see data_es/1
%% @see data_arity/1
is_data(#literal{}) ->
true;
is_data(#cons{}) ->
true;
is_data(#tuple{}) ->
true;
is_data(_) ->
false.
%% @spec data_type(Node::cerl()) -> dtype()
%%
%% dtype() = cons | tuple | {'atomic', Value}
%% Value = integer() | float() | atom() | []
%%
%% @doc Returns a type descriptor for a data constructor
%% node. (Cf. is_data/1
.) This is mainly useful for
%% comparing types and for constructing new nodes of the same type
%% (cf. make_data/2
). If Node
represents an
%% integer, floating-point number, atom or empty list, the result is
%% {'atomic', Value}
, where Value
is the value
%% of concrete(Node)
, otherwise the result is either
%% cons
or tuple
.
%%
%% Type descriptors can be compared for equality or order (in the %% Erlang term order), but remember that floating-point values should %% in general never be tested for equality.
%% %% @see is_data/1 %% @see make_data/2 %% @see type/1 %% @see concrete/1 data_type(#literal{val = V}) -> case V of [_ | _] -> cons; _ when tuple(V) -> tuple; _ -> {'atomic', V} end; data_type(#cons{}) -> cons; data_type(#tuple{}) -> tuple. %% @spec data_es(Node::cerl()) -> [cerl()] %% %% @doc Returns the list of subtrees of a data constructor node. If %% the arity of the constructor is zero, the result is the empty list. %% %%Note: if data_type(Node)
is cons
, the
%% number of subtrees is exactly two. If data_type(Node)
%% is {'atomic', Value}
, the number of subtrees is
%% zero.
length(data_es(Node))
, but
%% potentially more efficient.
%%
%% @see is_data/1
%% @see data_es/1
data_arity(#literal{val = V}) ->
case V of
[_ | _] ->
2;
_ when tuple(V) ->
size(V);
_ ->
0
end;
data_arity(#cons{}) ->
2;
data_arity(#tuple{es = Es}) ->
length(Es).
%% @spec make_data(Type::dtype(), Elements::[cerl()]) -> cerl()
%%
%% @doc Creates a data constructor node with the specified type and
%% subtrees. (Cf. data_type/1
.) An exception is thrown
%% if the length of Elements
is invalid for the given
%% Type
; see data_es/1
for arity constraints
%% on constructor types.
%%
%% @see data_type/1
%% @see data_es/1
%% @see ann_make_data/3
%% @see update_data/3
%% @see make_data_skel/2
make_data(CType, Es) ->
ann_make_data([], CType, Es).
%% @spec ann_make_data(As::[term()], Type::dtype(),
%% Elements::[cerl()]) -> cerl()
%% @see make_data/2
ann_make_data(As, {'atomic', V}, []) -> #literal{val = V, ann = As};
ann_make_data(As, cons, [H, T]) -> ann_c_cons(As, H, T);
ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es).
%% @spec update_data(Old::cerl(), Type::dtype(),
%% Elements::[cerl()]) -> cerl()
%% @see make_data/2
update_data(Node, CType, Es) ->
ann_make_data(get_ann(Node), CType, Es).
%% @spec make_data_skel(Type::dtype(), Elements::[cerl()]) -> cerl()
%%
%% @doc Like make_data/2
, but analogous to
%% c_tuple_skel/1
and c_cons_skel/2
.
%%
%% @see ann_make_data_skel/3
%% @see update_data_skel/3
%% @see make_data/2
%% @see c_tuple_skel/1
%% @see c_cons_skel/2
make_data_skel(CType, Es) ->
ann_make_data_skel([], CType, Es).
%% @spec ann_make_data_skel(As::[term()], Type::dtype(),
%% Elements::[cerl()]) -> cerl()
%% @see make_data_skel/2
ann_make_data_skel(As, {'atomic', V}, []) -> #literal{val = V, ann = As};
ann_make_data_skel(As, cons, [H, T]) -> ann_c_cons_skel(As, H, T);
ann_make_data_skel(As, tuple, Es) -> ann_c_tuple_skel(As, Es).
%% @spec update_data_skel(Old::cerl(), Type::dtype(),
%% Elements::[cerl()]) -> cerl()
%% @see make_data_skel/2
update_data_skel(Node, CType, Es) ->
ann_make_data_skel(get_ann(Node), CType, Es).
%% ---------------------------------------------------------------------
%% @spec subtrees(Node::cerl()) -> [[cerl()]]
%%
%% @doc Returns the grouped list of all subtrees of a node. If
%% Node
is a leaf node (cf. is_leaf/1
), this
%% is the empty list, otherwise the result is always a nonempty list,
%% containing the lists of subtrees of Node
, in
%% left-to-right order as they occur in the printed program text, and
%% grouped by category. Often, each group contains only a single
%% subtree.
%%
%% Depending on the type of Node
, the size of some
%% groups may be variable (e.g., the group consisting of all the
%% elements of a tuple), while others always contain the same number
%% of elements - usually exactly one (e.g., the group containing the
%% argument expression of a case-expression). Note, however, that the
%% exact structure of the returned list (for a given node type) should
%% in general not be depended upon, since it might be subject to
%% change without notice.
The function subtrees/1
and the constructor functions
%% make_tree/2
and update_tree/2
can be a
%% great help if one wants to traverse a syntax tree, visiting all its
%% subtrees, but treat nodes of the tree in a uniform way in most or all
%% cases. Using these functions makes this simple, and also assures that
%% your code is not overly sensitive to extensions of the syntax tree
%% data type, because any node types not explicitly handled by your code
%% can be left to a default case.
For example: %%
%% postorder(F, Tree) -> %% F(case subtrees(Tree) of %% [] -> Tree; %% List -> update_tree(Tree, %% [[postorder(F, Subtree) %% || Subtree <- Group] %% || Group <- List]) %% end). %%%% maps the function
F
on Tree
and all its
%% subtrees, doing a post-order traversal of the syntax tree. (Note
%% the use of update_tree/2
to preserve annotations.) For
%% a simple function like:
%% %% f(Node) -> %% case type(Node) of %% atom -> atom("a_" ++ atom_name(Node)); %% _ -> Node %% end. %%%% the call
postorder(fun f/1, Tree)
will yield a new
%% representation of Tree
in which all atom names have
%% been extended with the prefix "a_", but nothing else (including
%% annotations) has been changed.
%%
%% @see is_leaf/1
%% @see make_tree/2
%% @see update_tree/2
subtrees(T) ->
case is_leaf(T) of
true ->
[];
false ->
case type(T) of
values ->
[values_es(T)];
binary ->
[binary_segments(T)];
bitstr ->
[[bitstr_val(T)], [bitstr_size(T)],
[bitstr_unit(T)], [bitstr_type(T)],
[bitstr_flags(T)]];
cons ->
[[cons_hd(T)], [cons_tl(T)]];
tuple ->
[tuple_es(T)];
'let' ->
[let_vars(T), [let_arg(T)], [let_body(T)]];
seq ->
[[seq_arg(T)], [seq_body(T)]];
apply ->
[[apply_op(T)], apply_args(T)];
call ->
[[call_module(T)], [call_name(T)],
call_args(T)];
primop ->
[[primop_name(T)], primop_args(T)];
'case' ->
[[case_arg(T)], case_clauses(T)];
clause ->
[clause_pats(T), [clause_guard(T)],
[clause_body(T)]];
alias ->
[[alias_var(T)], [alias_pat(T)]];
'fun' ->
[fun_vars(T), [fun_body(T)]];
'receive' ->
[receive_clauses(T), [receive_timeout(T)],
[receive_action(T)]];
'try' ->
[[try_arg(T)], try_vars(T), [try_body(T)],
try_evars(T), [try_handler(T)]];
'catch' ->
[[catch_body(T)]];
letrec ->
Es = unfold_tuples(letrec_defs(T)),
[Es, [letrec_body(T)]];
module ->
As = unfold_tuples(module_attrs(T)),
Es = unfold_tuples(module_defs(T)),
[[module_name(T)], module_exports(T), As, Es]
end
end.
%% @spec update_tree(Old::cerl(), Groups::[[cerl()]]) -> cerl()
%%
%% @doc Creates a syntax tree with the given subtrees, and the same
%% type and annotations as the Old
node. This is
%% equivalent to ann_make_tree(get_ann(Node), type(Node),
%% Groups)
, but potentially more efficient.
%%
%% @see update_tree/3
%% @see ann_make_tree/3
%% @see get_ann/1
%% @see type/1
update_tree(Node, Gs) ->
ann_make_tree(get_ann(Node), type(Node), Gs).
%% @spec update_tree(Old::cerl(), Type::atom(), Groups::[[cerl()]]) ->
%% cerl()
%%
%% @doc Creates a syntax tree with the given type and subtrees, and
%% the same annotations as the Old
node. This is
%% equivalent to ann_make_tree(get_ann(Node), Type,
%% Groups)
, but potentially more efficient.
%%
%% @see update_tree/2
%% @see ann_make_tree/3
%% @see get_ann/1
update_tree(Node, Type, Gs) ->
ann_make_tree(get_ann(Node), Type, Gs).
%% @spec make_tree(Type::atom(), Groups::[[cerl()]]) -> cerl()
%%
%% @doc Creates a syntax tree with the given type and subtrees.
%% Type
must be a node type name
%% (cf. type/1
) that does not denote a leaf node type
%% (cf. is_leaf/1
). Groups
must be a
%% nonempty list of groups of syntax trees, representing the
%% subtrees of a node of the given type, in left-to-right order as
%% they would occur in the printed program text, grouped by category
%% as done by subtrees/1
.
%%
%% The result of ann_make_tree(get_ann(Node), type(Node),
%% subtrees(Node))
(cf. update_tree/2
) represents
%% the same source code text as the original Node
,
%% assuming that subtrees(Node)
yields a nonempty
%% list. However, it does not necessarily have the exact same data
%% representation as Node
.
make_tree/2
for details.
%%
%% @see make_tree/2
ann_make_tree(As, values, [Es]) -> ann_c_values(As, Es);
ann_make_tree(As, binary, [Ss]) -> ann_c_binary(As, Ss);
ann_make_tree(As, bitstr, [[V],[S],[U],[T],[Fs]]) ->
ann_c_bitstr(As, V, S, U, T, Fs);
ann_make_tree(As, cons, [[H], [T]]) -> ann_c_cons(As, H, T);
ann_make_tree(As, tuple, [Es]) -> ann_c_tuple(As, Es);
ann_make_tree(As, 'let', [Vs, [A], [B]]) -> ann_c_let(As, Vs, A, B);
ann_make_tree(As, seq, [[A], [B]]) -> ann_c_seq(As, A, B);
ann_make_tree(As, apply, [[Op], Es]) -> ann_c_apply(As, Op, Es);
ann_make_tree(As, call, [[M], [N], Es]) -> ann_c_call(As, M, N, Es);
ann_make_tree(As, primop, [[N], Es]) -> ann_c_primop(As, N, Es);
ann_make_tree(As, 'case', [[A], Cs]) -> ann_c_case(As, A, Cs);
ann_make_tree(As, clause, [Ps, [G], [B]]) -> ann_c_clause(As, Ps, G, B);
ann_make_tree(As, alias, [[V], [P]]) -> ann_c_alias(As, V, P);
ann_make_tree(As, 'fun', [Vs, [B]]) -> ann_c_fun(As, Vs, B);
ann_make_tree(As, 'receive', [Cs, [T], [A]]) ->
ann_c_receive(As, Cs, T, A);
ann_make_tree(As, 'try', [[E], Vs, [B], Evs, [H]]) ->
ann_c_try(As, E, Vs, B, Evs, H);
ann_make_tree(As, 'catch', [[B]]) -> ann_c_catch(As, B);
ann_make_tree(As, letrec, [Es, [B]]) ->
ann_c_letrec(As, fold_tuples(Es), B);
ann_make_tree(As, module, [[N], Xs, Es, Ds]) ->
ann_c_module(As, N, Xs, fold_tuples(Es), fold_tuples(Ds)).
%% ---------------------------------------------------------------------
%% @spec meta(Tree::cerl()) -> cerl()
%%
%% @doc Creates a meta-representation of a syntax tree. The result
%% represents an Erlang expression "MetaTree
"
%% which, if evaluated, will yield a new syntax tree representing the
%% same source code text as Tree
(although the actual
%% data representation may be different). The expression represented
%% by MetaTree
is implementation independent
%% with regard to the data structures used by the abstract syntax tree
%% implementation.
%%
%% Any node in Tree
whose node type is
%% var
(cf. type/1
), and whose list of
%% annotations (cf. get_ann/1
) contains the atom
%% meta_var
, will remain unchanged in the resulting tree,
%% except that exactly one occurrence of meta_var
is
%% removed from its annotation list.
The main use of the function meta/1
is to transform
%% a data structure Tree
, which represents a piece of
%% program code, into a form that is representation independent
%% when printed. E.g., suppose Tree
represents a
%% variable named "V". Then (assuming a function print/1
%% for printing syntax trees), evaluating
%% print(abstract(Tree))
- simply using
%% abstract/1
to map the actual data structure onto a
%% syntax tree representation - would output a string that might look
%% something like "{var, ..., 'V'}
", which is obviously
%% dependent on the implementation of the abstract syntax trees. This
%% could e.g. be useful for caching a syntax tree in a file. However,
%% in some situations like in a program generator generator (with two
%% "generator"), it may be unacceptable. Using
%% print(meta(Tree))
instead would output a
%% representation independent syntax tree generating
%% expression; in the above case, something like
%% "cerl:c_var('V')
".
The implementation tries to generate compact code with respect %% to literals and lists.
%% %% @see abstract/1 %% @see type/1 %% @see get_ann/1 meta(Node) -> %% First of all we check for metavariables: case type(Node) of var -> case lists:member(meta_var, get_ann(Node)) of false -> meta_0(var, Node); true -> %% A meta-variable: remove the first found %% 'meta_var' annotation, but otherwise leave %% the node unchanged. set_ann(Node, lists:delete(meta_var, get_ann(Node))) end; Type -> meta_0(Type, Node) end. meta_0(Type, Node) -> case get_ann(Node) of [] -> meta_1(Type, Node); As -> meta_call(set_ann, [meta_1(Type, Node), abstract(As)]) end. meta_1(literal, Node) -> %% We handle atomic literals separately, to get a bit %% more compact code. For the rest, we use 'abstract'. case concrete(Node) of V when atom(V) -> meta_call(c_atom, [Node]); V when integer(V) -> meta_call(c_int, [Node]); V when float(V) -> meta_call(c_float, [Node]); [] -> meta_call(c_nil, []); _ -> meta_call(abstract, [Node]) end; meta_1(var, Node) -> %% A normal variable or function name. meta_call(c_var, [abstract(var_name(Node))]); meta_1(values, Node) -> meta_call(c_values, [make_list(meta_list(values_es(Node)))]); meta_1(binary, Node) -> meta_call(c_binary, [make_list(meta_list(binary_segments(Node)))]); meta_1(bitstr, Node) -> meta_call(c_bitstr, [meta(bitstr_val(Node)), meta(bitstr_size(Node)), meta(bitstr_unit(Node)), meta(bitstr_type(Node)), meta(bitstr_flags(Node))]); meta_1(cons, Node) -> %% The list is split up if some sublist has annotatations. If %% we get exactly one element, we generate a 'c_cons' call %% instead of 'make_list' to reconstruct the node. case split_list(Node) of {[H], none} -> meta_call(c_cons, [meta(H), meta(c_nil())]); {[H], Node1} -> meta_call(c_cons, [meta(H), meta(Node1)]); {L, none} -> meta_call(make_list, [make_list(meta_list(L))]); {L, Node1} -> meta_call(make_list, [make_list(meta_list(L)), meta(Node1)]) end; meta_1(tuple, Node) -> meta_call(c_tuple, [make_list(meta_list(tuple_es(Node)))]); meta_1('let', Node) -> meta_call(c_let, [make_list(meta_list(let_vars(Node))), meta(let_arg(Node)), meta(let_body(Node))]); meta_1(seq, Node) -> meta_call(c_seq, [meta(seq_arg(Node)), meta(seq_body(Node))]); meta_1(apply, Node) -> meta_call(c_apply, [meta(apply_op(Node)), make_list(meta_list(apply_args(Node)))]); meta_1(call, Node) -> meta_call(c_call, [meta(call_module(Node)), meta(call_name(Node)), make_list(meta_list(call_args(Node)))]); meta_1(primop, Node) -> meta_call(c_primop, [meta(primop_name(Node)), make_list(meta_list(primop_args(Node)))]); meta_1('case', Node) -> meta_call(c_case, [meta(case_arg(Node)), make_list(meta_list(case_clauses(Node)))]); meta_1(clause, Node) -> meta_call(c_clause, [make_list(meta_list(clause_pats(Node))), meta(clause_guard(Node)), meta(clause_body(Node))]); meta_1(alias, Node) -> meta_call(c_alias, [meta(alias_var(Node)), meta(alias_pat(Node))]); meta_1('fun', Node) -> meta_call(c_fun, [make_list(meta_list(fun_vars(Node))), meta(fun_body(Node))]); meta_1('receive', Node) -> meta_call(c_receive, [make_list(meta_list(receive_clauses(Node))), meta(receive_timeout(Node)), meta(receive_action(Node))]); meta_1('try', Node) -> meta_call(c_try, [meta(try_arg(Node)), make_list(meta_list(try_vars(Node))), meta(try_body(Node)), make_list(meta_list(try_evars(Node))), meta(try_handler(Node))]); meta_1('catch', Node) -> meta_call(c_catch, [meta(catch_body(Node))]); meta_1(letrec, Node) -> meta_call(c_letrec, [make_list([c_tuple([meta(N), meta(F)]) || {N, F} <- letrec_defs(Node)]), meta(letrec_body(Node))]); meta_1(module, Node) -> meta_call(c_module, [meta(module_name(Node)), make_list(meta_list(module_exports(Node))), make_list([c_tuple([meta(A), meta(V)]) || {A, V} <- module_attrs(Node)]), make_list([c_tuple([meta(N), meta(F)]) || {N, F} <- module_defs(Node)])]). meta_call(F, As) -> c_call(c_atom(?MODULE), c_atom(F), As). meta_list([T | Ts]) -> [meta(T) | meta_list(Ts)]; meta_list([]) -> []. split_list(Node) -> split_list(set_ann(Node, []), []). split_list(Node, L) -> A = get_ann(Node), case type(Node) of cons when A == [] -> split_list(cons_tl(Node), [cons_hd(Node) | L]); nil when A == [] -> {lists:reverse(L), none}; _ -> {lists:reverse(L), Node} end. %% --------------------------------------------------------------------- %% General utilities is_lit_list([#literal{} | Es]) -> is_lit_list(Es); is_lit_list([_ | _]) -> false; is_lit_list([]) -> true. lit_list_vals([#literal{val = V} | Es]) -> [V | lit_list_vals(Es)]; lit_list_vals([]) -> []. make_lit_list([V | Vs]) -> [#literal{val = V} | make_lit_list(Vs)]; make_lit_list([]) -> []. %% The following tests are the same as done by 'io_lib:char_list' and %% 'io_lib:printable_list', respectively, but for a single character. is_char_value(V) when V >= $\000, V =< $\377 -> true; is_char_value(_) -> false. is_print_char_value(V) when V >= $\040, V =< $\176 -> true; is_print_char_value(V) when V >= $\240, V =< $\377 -> true; is_print_char_value(V) when V =:= $\b -> true; is_print_char_value(V) when V =:= $\d -> true; is_print_char_value(V) when V =:= $\e -> true; is_print_char_value(V) when V =:= $\f -> true; is_print_char_value(V) when V =:= $\n -> true; is_print_char_value(V) when V =:= $\r -> true; is_print_char_value(V) when V =:= $\s -> true; is_print_char_value(V) when V =:= $\t -> true; is_print_char_value(V) when V =:= $\v -> true; is_print_char_value(V) when V =:= $\" -> true; is_print_char_value(V) when V =:= $\' -> true; is_print_char_value(V) when V =:= $\\ -> true; is_print_char_value(_) -> false. is_char_list([V | Vs]) when integer(V) -> case is_char_value(V) of true -> is_char_list(Vs); false -> false end; is_char_list([]) -> true; is_char_list(_) -> false. is_print_char_list([V | Vs]) when integer(V) -> case is_print_char_value(V) of true -> is_print_char_list(Vs); false -> false end; is_print_char_list([]) -> true; is_print_char_list(_) -> false. unfold_tuples([{X, Y} | Ps]) -> [X, Y | unfold_tuples(Ps)]; unfold_tuples([]) -> []. fold_tuples([X, Y | Es]) -> [{X, Y} | fold_tuples(Es)]; fold_tuples([]) -> [].