%% ===================================================================== %% This library is free software; you can redistribute it and/or modify %% it under the terms of the GNU Lesser General Public License as %% published by the Free Software Foundation; either version 2 of the %% License, or (at your option) any later version. %% %% This library is distributed in the hope that it will be useful, but %% WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %% Lesser General Public License for more details. %% %% You should have received a copy of the GNU Lesser General Public %% License along with this library; if not, write to the Free Software %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% %% @copyright 1997-2006 Richard Carlsson %% @author Richard Carlsson %% @end %% ===================================================================== %% @doc Abstract Erlang syntax trees. %% %% This module defines an abstract data type for representing Erlang %% source code as syntax trees, in a way that is backwards compatible %% with the data structures created by the Erlang standard library %% parser module `erl_parse' (often referred to as "parse %% trees", which is a bit of a misnomer). This means that all %% `erl_parse' trees are valid abstract syntax trees, but the %% reverse is not true: abstract syntax trees can in general not be used %% as input to functions expecting an `erl_parse' tree. %% However, as long as an abstract syntax tree represents a correct %% Erlang program, the function {@link revert/1} should be able to %% transform it to the corresponding `erl_parse' %% representation. %% %% A recommended starting point for the first-time user is the documentation %% of the {@link syntaxTree()} data type, and the function {@link type/1}. %% %% == NOTES: == %% %% 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. %% %% With the exception of the {@link erl_parse()} data structures, %% 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. %% @type syntaxTree(). An abstract syntax tree. The {@link erl_parse()} %% "parse tree" representation is a proper subset of the `syntaxTree()' %% representation. %% %% Every abstract syntax tree node has a type, given by the %% function {@link type/1}. Each node also has associated %% attributes; see {@link get_attrs/1} for details. The functions %% {@link make_tree/2} and {@link subtrees/1} are generic %% constructor/decomposition functions for abstract syntax trees. The %% functions {@link abstract/1} and {@link concrete/1} convert between %% constant Erlang terms and their syntactic representations. The set of %% syntax tree nodes is extensible through the {@link tree/2} function. %% %% A syntax tree can be transformed to the {@link erl_parse()} %% representation with the {@link revert/1} function. -module(erl_syntax). -export([type/1, is_leaf/1, is_form/1, is_literal/1, abstract/1, concrete/1, revert/1, revert_forms/1, subtrees/1, make_tree/2, update_tree/2, meta/1, get_pos/1, set_pos/2, copy_pos/2, get_precomments/1, set_precomments/2, add_precomments/2, get_postcomments/1, set_postcomments/2, add_postcomments/2, has_comments/1, remove_comments/1, copy_comments/2, join_comments/2, get_ann/1, set_ann/2, add_ann/2, copy_ann/2, get_attrs/1, set_attrs/2, copy_attrs/2, flatten_form_list/1, cons/2, list_head/1, list_tail/1, is_list_skeleton/1, is_proper_list/1, list_elements/1, list_length/1, normalize_list/1, compact_list/1, application/2, application/3, application_arguments/1, application_operator/1, arity_qualifier/2, arity_qualifier_argument/1, arity_qualifier_body/1, atom/1, is_atom/2, atom_value/1, atom_literal/1, atom_name/1, attribute/1, attribute/2, attribute_arguments/1, attribute_name/1, binary/1, binary_comp/2, binary_comp_template/1, binary_comp_body/1, binary_field/1, binary_field/2, binary_field/3, binary_field_body/1, binary_field_types/1, binary_field_size/1, binary_fields/1, binary_generator/2, binary_generator_body/1, binary_generator_pattern/1, block_expr/1, block_expr_body/1, case_expr/2, case_expr_argument/1, case_expr_clauses/1, catch_expr/1, catch_expr_body/1, char/1, is_char/2, char_value/1, char_literal/1, char_literal/2, clause/2, clause/3, clause_body/1, clause_guard/1, clause_patterns/1, comment/1, comment/2, comment_padding/1, comment_text/1, cond_expr/1, cond_expr_clauses/1, conjunction/1, conjunction_body/1, disjunction/1, disjunction_body/1, eof_marker/0, error_marker/1, error_marker_info/1, float/1, float_value/1, float_literal/1, form_list/1, form_list_elements/1, fun_expr/1, fun_expr_arity/1, fun_expr_clauses/1, function/2, function_arity/1, function_clauses/1, function_name/1, generator/2, generator_body/1, generator_pattern/1, if_expr/1, if_expr_clauses/1, implicit_fun/1, implicit_fun/2, implicit_fun/3, implicit_fun_name/1, infix_expr/3, infix_expr_left/1, infix_expr_operator/1, infix_expr_right/1, integer/1, is_integer/2, integer_value/1, integer_literal/1, list/1, list/2, list_comp/2, list_comp_body/1, list_comp_template/1, list_prefix/1, list_suffix/1, macro/1, macro/2, macro_arguments/1, macro_name/1, match_expr/2, match_expr_body/1, match_expr_pattern/1, module_qualifier/2, module_qualifier_argument/1, module_qualifier_body/1, nil/0, operator/1, operator_literal/1, operator_name/1, parentheses/1, parentheses_body/1, prefix_expr/2, prefix_expr_argument/1, prefix_expr_operator/1, receive_expr/1, receive_expr/3, receive_expr_action/1, receive_expr_clauses/1, receive_expr_timeout/1, record_access/2, record_access/3, record_access_argument/1, record_access_field/1, record_access_type/1, record_expr/2, record_expr/3, record_expr_argument/1, record_expr_fields/1, record_expr_type/1, record_field/1, record_field/2, record_field_name/1, record_field_value/1, record_index_expr/2, record_index_expr_field/1, record_index_expr_type/1, rule/2, rule_arity/1, rule_clauses/1, rule_name/1, size_qualifier/2, size_qualifier_argument/1, size_qualifier_body/1, string/1, is_string/2, string_value/1, string_literal/1, string_literal/2, text/1, text_string/1, try_expr/2, try_expr/3, try_expr/4, try_after_expr/2, try_expr_body/1, try_expr_clauses/1, try_expr_handlers/1, try_expr_after/1, class_qualifier/2, class_qualifier_argument/1, class_qualifier_body/1, tuple/1, tuple_elements/1, tuple_size/1, underscore/0, variable/1, variable_name/1, variable_literal/1, warning_marker/1, warning_marker_info/1, tree/1, tree/2, data/1, is_tree/1]). -export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0, padding/0]). %% ===================================================================== %% IMPLEMENTATION NOTES: %% %% All nodes are represented by tuples of arity 2 or greater, whose %% first element is an atom which uniquely identifies the type of the %% node. (In the backwards-compatible representation, the interpretation %% is also often dependent on the context; the second element generally %% holds the position information - with a couple of exceptions; see %% `get_pos' and `set_pos' for details). In the documentation of this %% module, `Pos' is the source code position information associated with %% a node; usually, this is a positive integer indicating the original %% source code line, but no assumptions are made in this module %% regarding the format or interpretation of position information. When %% a syntax tree node is constructed, its associated position is by %% default set to the integer zero. %% ===================================================================== -define(NO_UNUSED, true). %% ===================================================================== %% Declarations of globally used internal data structures %% ===================================================================== %% `com' records are used to hold comment information attached to a %% syntax tree node or a wrapper structure. %% %% #com{pre :: Pre, post :: Post} %% %% Pre = Post = [Com] %% Com = syntaxTree() %% %% type(Com) = comment -record(com, {pre = [] :: [syntaxTree()], post = [] :: [syntaxTree()]}). %% `attr' records store node attributes as an aggregate. %% %% #attr{pos :: Pos, ann :: Ann, com :: Comments} %% %% Pos = term() %% Ann = [term()] %% Comments = none | #com{} %% %% where `Pos' `Ann' and `Comments' are the corresponding values of a %% `tree' or `wrapper' record. -record(attr, {pos = 0 :: term(), ann = [] :: [term()], com = none :: 'none' | #com{}}). -type syntaxTreeAttributes() :: #attr{}. %% `tree' records represent new-form syntax tree nodes. %% %% Tree = #tree{type :: Type, attr :: Attr, data :: Data} %% %% Type = atom() %% Attr = #attr{} %% Data = term() %% %% is_tree(Tree) = true -record(tree, {type :: atom(), attr = #attr{} :: #attr{}, data :: term()}). %% `wrapper' records are used for attaching new-form node information to %% `erl_parse' trees. %% %% Wrapper = #wrapper{type :: Type, attr :: Attr, tree :: ParseTree} %% %% Type = atom() %% Attr = #attr{} %% ParseTree = term() %% %% is_tree(Wrapper) = false -record(wrapper, {type :: atom(), attr = #attr{} :: #attr{}, tree :: erl_parse()}). %% ===================================================================== -type syntaxTree() :: #tree{} | #wrapper{} | erl_parse(). -type erl_parse() :: erl_parse:abstract_form() | erl_parse:abstract_expr(). %% The representation built by the Erlang standard library parser %% `erl_parse'. This is a subset of the {@link syntaxTree()} type. %% ===================================================================== %% %% Exported functions %% %% ===================================================================== %% ===================================================================== %% @doc Returns the type tag of `Node'. If `Node' %% does not represent a syntax tree, evaluation fails with reason %% `badarg'. Node types currently defined by this module are: %% %%
%% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %%
applicationarity_qualifieratomattribute
binarybinary_fieldblock_exprcase_expr
catch_exprcharclass_qualifierclause
commentcond_exprconjunctiondisjunction
eof_markererror_markerfloatform_list
fun_exprfunctiongeneratorif_expr
implicit_funinfix_exprintegerlist
list_compmacromatch_exprmodule_qualifier
niloperatorparenthesesprefix_expr
receive_exprrecord_access
record_exprrecord_fieldrecord_index_exprrule
size_qualifierstringtexttry_expr
tupleunderscorevariablewarning_marker
%% %% The user may (for special purposes) create additional nodes %% with other type tags, using the {@link tree/2} function. %% %% Note: The primary constructor functions for a node type should %% always have the same name as the node type itself. %% %% @see tree/2 %% @see application/3 %% @see arity_qualifier/2 %% @see atom/1 %% @see attribute/2 %% @see binary/1 %% @see binary_field/2 %% @see block_expr/1 %% @see case_expr/2 %% @see catch_expr/1 %% @see char/1 %% @see class_qualifier/2 %% @see clause/3 %% @see comment/2 %% @see cond_expr/1 %% @see conjunction/1 %% @see disjunction/1 %% @see eof_marker/0 %% @see error_marker/1 %% @see float/1 %% @see form_list/1 %% @see fun_expr/1 %% @see function/2 %% @see generator/2 %% @see if_expr/1 %% @see implicit_fun/2 %% @see infix_expr/3 %% @see integer/1 %% @see list/2 %% @see list_comp/2 %% @see macro/2 %% @see match_expr/2 %% @see module_qualifier/2 %% @see nil/0 %% @see operator/1 %% @see parentheses/1 %% @see prefix_expr/2 %% @see receive_expr/3 %% @see record_access/3 %% @see record_expr/2 %% @see record_field/2 %% @see record_index_expr/2 %% @see rule/2 %% @see size_qualifier/2 %% @see string/1 %% @see text/1 %% @see try_expr/3 %% @see tuple/1 %% @see underscore/0 %% @see variable/1 %% @see warning_marker/1 -spec type(syntaxTree()) -> atom(). type(#tree{type = T}) -> T; type(#wrapper{type = T}) -> T; type(Node) -> %% Check for `erl_parse'-compatible nodes, and otherwise fail. case Node of %% Leaf types {atom, _, _} -> atom; {char, _, _} -> char; {float, _, _} -> float; {integer, _, _} -> integer; {nil, _} -> nil; {string, _, _} -> string; {var, _, Name} -> if Name =:= '_' -> underscore; true -> variable end; {error, _} -> error_marker; {warning, _} -> warning_marker; {eof, _} -> eof_marker; %% Composite types {'case', _, _, _} -> case_expr; {'catch', _, _} -> catch_expr; {'cond', _, _} -> cond_expr; {'fun', _, {clauses, _}} -> fun_expr; {'fun', _, {function, _, _}} -> implicit_fun; {'fun', _, {function, _, _, _}} -> implicit_fun; {'if', _, _} -> if_expr; {'receive', _, _, _, _} -> receive_expr; {'receive', _, _} -> receive_expr; {attribute, _, _, _} -> attribute; {bin, _, _} -> binary; {bin_element, _, _, _, _} -> binary_field; {block, _, _} -> block_expr; {call, _, _, _} -> application; {clause, _, _, _, _} -> clause; {cons, _, _, _} -> list; {function, _, _, _, _} -> function; {b_generate, _, _, _} -> binary_generator; {generate, _, _, _} -> generator; {lc, _, _, _} -> list_comp; {bc, _, _, _} -> binary_comp; {match, _, _, _} -> match_expr; {op, _, _, _, _} -> infix_expr; {op, _, _, _} -> prefix_expr; {record, _, _, _, _} -> record_expr; {record, _, _, _} -> record_expr; {record_field, _, _, _, _} -> record_access; {record_field, _, _, _} -> record_access; {record_index, _, _, _} -> record_index_expr; {remote, _, _, _} -> module_qualifier; {rule, _, _, _, _} -> rule; {'try', _, _, _, _, _} -> try_expr; {tuple, _, _} -> tuple; _ -> erlang:error({badarg, Node}) end. %% ===================================================================== %% @doc Returns `true' if `Node' is a leaf node, %% otherwise `false'. The currently recognised leaf node %% types are: %% %%
%% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %%
`atom'`char'`comment'`eof_marker'`error_marker'
`float'`integer'`nil'`operator'`string'
`text'`underscore'`variable'`warning_marker'
%% %% A node of type `tuple' is a leaf node if and only if its arity is zero. %% %% Note: not all literals are leaf nodes, and vice versa. E.g., %% tuples with nonzero arity and nonempty lists may be literals, but are %% not leaf nodes. Variables, on the other hand, are leaf nodes but not %% literals. %% %% @see type/1 %% @see is_literal/1 -spec is_leaf(syntaxTree()) -> boolean(). is_leaf(Node) -> case type(Node) of atom -> true; char -> true; comment -> true; % nonstandard type eof_marker -> true; error_marker -> true; float -> true; integer -> true; nil -> true; operator -> true; % nonstandard type string -> true; text -> true; % nonstandard type tuple -> tuple_elements(Node) =:= []; underscore -> true; variable -> true; warning_marker -> true; _ -> false end. %% ===================================================================== %% @doc Returns `true' if `Node' is a syntax tree %% representing a so-called "source code form", otherwise %% `false'. Forms are the Erlang source code units which, %% placed in sequence, constitute an Erlang program. Current form types %% are: %% %%
%% %% %% %% %% %% %% %% %% %% %% %% %%
`attribute'`comment'`error_marker'`eof_marker'`form_list'
`function'`rule'`warning_marker'`text'
%% %% @see type/1 %% @see attribute/2 %% @see comment/2 %% @see eof_marker/0 %% @see error_marker/1 %% @see form_list/1 %% @see function/2 %% @see rule/2 %% @see warning_marker/1 -spec is_form(syntaxTree()) -> boolean(). is_form(Node) -> case type(Node) of attribute -> true; comment -> true; function -> true; eof_marker -> true; error_marker -> true; form_list -> true; rule -> true; warning_marker -> true; text -> true; _ -> false end. %% ===================================================================== %% @doc Returns the position information associated with %% `Node'. This is usually a nonnegative integer (indicating %% the source code line number), but may be any term. By default, all %% new tree nodes have their associated position information set to the %% integer zero. %% %% @see set_pos/2 %% @see get_attrs/1 %% All `erl_parse' tree nodes are represented by tuples whose second %% field is the position information (usually an integer), *with the %% exceptions of* `{error, ...}' (type `error_marker') and `{warning, %% ...}' (type `warning_marker'), which only contain the associated line %% number *of the error descriptor*; this is all handled transparently %% by `get_pos' and `set_pos'. -spec get_pos(syntaxTree()) -> term(). get_pos(#tree{attr = Attr}) -> Attr#attr.pos; get_pos(#wrapper{attr = Attr}) -> Attr#attr.pos; get_pos({error, {Pos, _, _}}) -> Pos; get_pos({warning, {Pos, _, _}}) -> Pos; get_pos(Node) -> %% Here, we assume that we have an `erl_parse' node with position %% information in element 2. element(2, Node). %% ===================================================================== %% @doc Sets the position information of `Node' to `Pos'. %% %% @see get_pos/1 %% @see copy_pos/2 -spec set_pos(syntaxTree(), term()) -> syntaxTree(). set_pos(Node, Pos) -> case Node of #tree{attr = Attr} -> Node#tree{attr = Attr#attr{pos = Pos}}; #wrapper{attr = Attr} -> Node#wrapper{attr = Attr#attr{pos = Pos}}; _ -> %% We then assume we have an `erl_parse' node, and create a %% wrapper around it to make things more uniform. set_pos(wrap(Node), Pos) end. %% ===================================================================== %% @doc Copies the position information from `Source' to `Target'. %% %% This is equivalent to `set_pos(Target, %% get_pos(Source))', but potentially more efficient. %% %% @see get_pos/1 %% @see set_pos/2 -spec copy_pos(syntaxTree(), syntaxTree()) -> syntaxTree(). copy_pos(Source, Target) -> set_pos(Target, get_pos(Source)). %% ===================================================================== %% `get_com' and `set_com' are for internal use only. get_com(#tree{attr = Attr}) -> Attr#attr.com; get_com(#wrapper{attr = Attr}) -> Attr#attr.com; get_com(_) -> none. set_com(Node, Com) -> case Node of #tree{attr = Attr} -> Node#tree{attr = Attr#attr{com = Com}}; #wrapper{attr = Attr} -> Node#wrapper{attr = Attr#attr{com = Com}}; _ -> set_com(wrap(Node), Com) end. %% ===================================================================== %% @doc Returns the associated pre-comments of a node. This is a %% possibly empty list of abstract comments, in top-down textual order. %% When the code is formatted, pre-comments are typically displayed %% directly above the node. For example: %% ```% Pre-comment of function %% foo(X) -> {bar, X}.''' %% %% If possible, the comment should be moved before any preceding %% separator characters on the same line. E.g.: %% ```foo([X | Xs]) -> %% % Pre-comment of 'bar(X)' node %% [bar(X) | foo(Xs)]; %% ...''' %% (where the comment is moved before the "`['"). %% %% @see comment/2 %% @see set_precomments/2 %% @see get_postcomments/1 %% @see get_attrs/1 -spec get_precomments(syntaxTree()) -> [syntaxTree()]. get_precomments(#tree{attr = Attr}) -> get_precomments_1(Attr); get_precomments(#wrapper{attr = Attr}) -> get_precomments_1(Attr); get_precomments(_) -> []. get_precomments_1(#attr{com = none}) -> []; get_precomments_1(#attr{com = #com{pre = Cs}}) -> Cs. %% ===================================================================== %% @doc Sets the pre-comments of `Node' to %% `Comments'. `Comments' should be a possibly %% empty list of abstract comments, in top-down textual order. %% %% @see comment/2 %% @see get_precomments/1 %% @see add_precomments/2 %% @see set_postcomments/2 %% @see copy_comments/2 %% @see remove_comments/1 %% @see join_comments/2 -spec set_precomments(syntaxTree(), [syntaxTree()]) -> syntaxTree(). set_precomments(Node, Cs) -> case Node of #tree{attr = Attr} -> Node#tree{attr = set_precomments_1(Attr, Cs)}; #wrapper{attr = Attr} -> Node#wrapper{attr = set_precomments_1(Attr, Cs)}; _ -> set_precomments(wrap(Node), Cs) end. set_precomments_1(#attr{com = none} = Attr, Cs) -> Attr#attr{com = #com{pre = Cs}}; set_precomments_1(#attr{com = Com} = Attr, Cs) -> Attr#attr{com = Com#com{pre = Cs}}. %% ===================================================================== %% @doc Appends `Comments' to the pre-comments of `Node'. %% %% Note: This is equivalent to `set_precomments(Node, %% get_precomments(Node) ++ Comments)', but potentially more %% efficient. %% %% @see comment/2 %% @see get_precomments/1 %% @see set_precomments/2 %% @see add_postcomments/2 %% @see join_comments/2 -spec add_precomments([syntaxTree()], syntaxTree()) -> syntaxTree(). add_precomments(Cs, Node) -> case Node of #tree{attr = Attr} -> Node#tree{attr = add_precomments_1(Cs, Attr)}; #wrapper{attr = Attr} -> Node#wrapper{attr = add_precomments_1(Cs, Attr)}; _ -> add_precomments(Cs, wrap(Node)) end. add_precomments_1(Cs, #attr{com = none} = Attr) -> Attr#attr{com = #com{pre = Cs}}; add_precomments_1(Cs, #attr{com = Com} = Attr) -> Attr#attr{com = Com#com{pre = Com#com.pre ++ Cs}}. %% ===================================================================== %% @doc Returns the associated post-comments of a node. This is a %% possibly empty list of abstract comments, in top-down textual order. %% When the code is formatted, post-comments are typically displayed to %% the right of and/or below the node. For example: %% ```{foo, X, Y} % Post-comment of tuple''' %% %% If possible, the comment should be moved past any following %% separator characters on the same line, rather than placing the %% separators on the following line. E.g.: %% ```foo([X | Xs], Y) -> %% foo(Xs, bar(X)); % Post-comment of 'bar(X)' node %% ...''' %% (where the comment is moved past the rightmost "`)'" and %% the "`;'"). %% %% @see comment/2 %% @see set_postcomments/2 %% @see get_precomments/1 %% @see get_attrs/1 -spec get_postcomments(syntaxTree()) -> [syntaxTree()]. get_postcomments(#tree{attr = Attr}) -> get_postcomments_1(Attr); get_postcomments(#wrapper{attr = Attr}) -> get_postcomments_1(Attr); get_postcomments(_) -> []. get_postcomments_1(#attr{com = none}) -> []; get_postcomments_1(#attr{com = #com{post = Cs}}) -> Cs. %% ===================================================================== %% @doc Sets the post-comments of `Node' to %% `Comments'. `Comments' should be a possibly %% empty list of abstract comments, in top-down textual order %% %% @see comment/2 %% @see get_postcomments/1 %% @see add_postcomments/2 %% @see set_precomments/2 %% @see copy_comments/2 %% @see remove_comments/1 %% @see join_comments/2 -spec set_postcomments(syntaxTree(), [syntaxTree()]) -> syntaxTree(). set_postcomments(Node, Cs) -> case Node of #tree{attr = Attr} -> Node#tree{attr = set_postcomments_1(Attr, Cs)}; #wrapper{attr = Attr} -> Node#wrapper{attr = set_postcomments_1(Attr, Cs)}; _ -> set_postcomments(wrap(Node), Cs) end. set_postcomments_1(#attr{com = none} = Attr, Cs) -> Attr#attr{com = #com{post = Cs}}; set_postcomments_1(#attr{com = Com} = Attr, Cs) -> Attr#attr{com = Com#com{post = Cs}}. %% ===================================================================== %% @doc Appends `Comments' to the post-comments of `Node'. %% %% Note: This is equivalent to `set_postcomments(Node, %% get_postcomments(Node) ++ Comments)', but potentially more %% efficient. %% %% @see comment/2 %% @see get_postcomments/1 %% @see set_postcomments/2 %% @see add_precomments/2 %% @see join_comments/2 -spec add_postcomments([syntaxTree()], syntaxTree()) -> syntaxTree(). add_postcomments(Cs, Node) -> case Node of #tree{attr = Attr} -> Node#tree{attr = add_postcomments_1(Cs, Attr)}; #wrapper{attr = Attr} -> Node#wrapper{attr = add_postcomments_1(Cs, Attr)}; _ -> add_postcomments(Cs, wrap(Node)) end. add_postcomments_1(Cs, #attr{com = none} = Attr) -> Attr#attr{com = #com{post = Cs}}; add_postcomments_1(Cs, #attr{com = Com} = Attr) -> Attr#attr{com = Com#com{post = Com#com.post ++ Cs}}. %% ===================================================================== %% @doc Yields `false' if the node has no associated %% comments, and `true' otherwise. %% %% Note: This is equivalent to `(get_precomments(Node) == []) %% and (get_postcomments(Node) == [])', but potentially more %% efficient. %% %% @see get_precomments/1 %% @see get_postcomments/1 %% @see remove_comments/1 -spec has_comments(syntaxTree()) -> boolean(). has_comments(#tree{attr = Attr}) -> case Attr#attr.com of none -> false; #com{pre = [], post = []} -> false; _ -> true end; has_comments(#wrapper{attr = Attr}) -> case Attr#attr.com of none -> false; #com{pre = [], post = []} -> false; _ -> true end; has_comments(_) -> false. %% ===================================================================== %% @doc Clears the associated comments of `Node'. %% %% Note: This is equivalent to %% `set_precomments(set_postcomments(Node, []), [])', but %% potentially more efficient. %% %% @see set_precomments/2 %% @see set_postcomments/2 -spec remove_comments(syntaxTree()) -> syntaxTree(). remove_comments(Node) -> case Node of #tree{attr = Attr} -> Node#tree{attr = Attr#attr{com = none}}; #wrapper{attr = Attr} -> Node#wrapper{attr = Attr#attr{com = none}}; _ -> Node end. %% ===================================================================== %% @doc Copies the pre- and postcomments from `Source' to `Target'. %% %% Note: This is equivalent to %% `set_postcomments(set_precomments(Target, %% get_precomments(Source)), get_postcomments(Source))', but %% potentially more efficient. %% %% @see comment/2 %% @see get_precomments/1 %% @see get_postcomments/1 %% @see set_precomments/2 %% @see set_postcomments/2 -spec copy_comments(syntaxTree(), syntaxTree()) -> syntaxTree(). copy_comments(Source, Target) -> set_com(Target, get_com(Source)). %% ===================================================================== %% @doc Appends the comments of `Source' to the current %% comments of `Target'. %% %% Note: This is equivalent to %% `add_postcomments(get_postcomments(Source), %% add_precomments(get_precomments(Source), Target))', but %% potentially more efficient. %% %% @see comment/2 %% @see get_precomments/1 %% @see get_postcomments/1 %% @see add_precomments/2 %% @see add_postcomments/2 -spec join_comments(syntaxTree(), syntaxTree()) -> syntaxTree(). join_comments(Source, Target) -> add_postcomments( get_postcomments(Source), add_precomments(get_precomments(Source), Target)). %% ===================================================================== %% @doc Returns the list of user annotations associated with a syntax %% tree node. For a newly created node, this is the empty list. The %% annotations may be any terms. %% %% @see set_ann/2 %% @see get_attrs/1 -spec get_ann(syntaxTree()) -> [term()]. get_ann(#tree{attr = Attr}) -> Attr#attr.ann; get_ann(#wrapper{attr = Attr}) -> Attr#attr.ann; get_ann(_) -> []. %% ===================================================================== %% @doc Sets the list of user annotations of `Node' to `Annotations'. %% %% @see get_ann/1 %% @see add_ann/2 %% @see copy_ann/2 -spec set_ann(syntaxTree(), [term()]) -> syntaxTree(). set_ann(Node, As) -> case Node of #tree{attr = Attr} -> Node#tree{attr = Attr#attr{ann = As}}; #wrapper{attr = Attr} -> Node#wrapper{attr = Attr#attr{ann = As}}; _ -> %% Assume we have an `erl_parse' node and create a wrapper %% structure to carry the annotation. set_ann(wrap(Node), As) end. %% ===================================================================== %% @doc Appends the term `Annotation' to the list of user %% annotations of `Node'. %% %% Note: this is equivalent to `set_ann(Node, [Annotation | %% get_ann(Node)])', but potentially more efficient. %% %% @see get_ann/1 %% @see set_ann/2 -spec add_ann(term(), syntaxTree()) -> syntaxTree(). add_ann(A, Node) -> case Node of #tree{attr = Attr} -> Node#tree{attr = Attr#attr{ann = [A | Attr#attr.ann]}}; #wrapper{attr = Attr} -> Node#wrapper{attr = Attr#attr{ann = [A | Attr#attr.ann]}}; _ -> %% Assume we have an `erl_parse' node and create a wrapper %% structure to carry the annotation. add_ann(A, wrap(Node)) end. %% ===================================================================== %% @doc Copies the list of user annotations from `Source' to `Target'. %% %% Note: this is equivalent to `set_ann(Target, %% get_ann(Source))', but potentially more efficient. %% %% @see get_ann/1 %% @see set_ann/2 -spec copy_ann(syntaxTree(), syntaxTree()) -> syntaxTree(). copy_ann(Source, Target) -> set_ann(Target, get_ann(Source)). %% ===================================================================== %% @doc Returns a representation of the attributes associated with a %% syntax tree node. The attributes are all the extra information that %% can be attached to a node. Currently, this includes position %% information, source code comments, and user annotations. The result %% of this function cannot be inspected directly; only attached to %% another node (see {@link set_attrs/2}). %% %% For accessing individual attributes, see {@link get_pos/1}, %% {@link get_ann/1}, {@link get_precomments/1} and %% {@link get_postcomments/1}. %% %% @type syntaxTreeAttributes(). This is an abstract representation of %% syntax tree node attributes; see the function {@link get_attrs/1}. %% %% @see set_attrs/2 %% @see get_pos/1 %% @see get_ann/1 %% @see get_precomments/1 %% @see get_postcomments/1 -spec get_attrs(syntaxTree()) -> syntaxTreeAttributes(). get_attrs(#tree{attr = Attr}) -> Attr; get_attrs(#wrapper{attr = Attr}) -> Attr; get_attrs(Node) -> #attr{pos = get_pos(Node), ann = get_ann(Node), com = get_com(Node)}. %% ===================================================================== %% @doc Sets the attributes of `Node' to `Attributes'. %% %% @see get_attrs/1 %% @see copy_attrs/2 -spec set_attrs(syntaxTree(), syntaxTreeAttributes()) -> syntaxTree(). set_attrs(Node, Attr) -> case Node of #tree{} -> Node#tree{attr = Attr}; #wrapper{} -> Node#wrapper{attr = Attr}; _ -> set_attrs(wrap(Node), Attr) end. %% ===================================================================== %% @doc Copies the attributes from `Source' to `Target'. %% %% Note: this is equivalent to `set_attrs(Target, %% get_attrs(Source))', but potentially more efficient. %% %% @see get_attrs/1 %% @see set_attrs/2 -spec copy_attrs(syntaxTree(), syntaxTree()) -> syntaxTree(). copy_attrs(S, T) -> set_attrs(T, get_attrs(S)). %% ===================================================================== %% @equiv comment(none, Strings) -spec comment([string()]) -> syntaxTree(). comment(Strings) -> comment(none, Strings). %% ===================================================================== %% @doc Creates an abstract comment with the given padding and text. If %% `Strings' is a (possibly empty) list %% ["Txt1", ..., "TxtN"], the result %% represents the source code text %%
%%    %Txt1
%%    ...
%%    %TxtN
%% `Padding' states the number of empty character positions %% to the left of the comment separating it horizontally from %% source code on the same line (if any). If `Padding' is %% `none', a default positive number is used. If %% `Padding' is an integer less than 1, there should be no %% separating space. Comments are in themselves regarded as source %% program forms. %% %% @see comment/1 %% @see is_form/1 -type padding() :: 'none' | integer(). -record(comment, {pad :: padding(), text :: [string()]}). %% type(Node) = comment %% data(Node) = #comment{pad :: Padding, text :: Strings} %% %% Padding = none | integer() %% Strings = [string()] -spec comment(padding(), [string()]) -> syntaxTree(). comment(Pad, Strings) -> tree(comment, #comment{pad = Pad, text = Strings}). %% ===================================================================== %% @doc Returns the lines of text of the abstract comment. %% %% @see comment/2 -spec comment_text(syntaxTree()) -> [string()]. comment_text(Node) -> (data(Node))#comment.text. %% ===================================================================== %% @doc Returns the amount of padding before the comment, or %% `none'. The latter means that a default padding may be used. %% %% @see comment/2 -spec comment_padding(syntaxTree()) -> padding(). comment_padding(Node) -> (data(Node))#comment.pad. %% ===================================================================== %% @doc Creates an abstract sequence of "source code forms". If %% `Forms' is `[F1, ..., Fn]', where each %% `Fi' is a form (see {@link is_form/1}, the result %% represents %%
%%    F1
%%    ...
%%    Fn
%% where the `Fi' are separated by one or more line breaks. A %% node of type `form_list' is itself regarded as a source %% code form; see {@link flatten_form_list/1}. %% %% Note: this is simply a way of grouping source code forms as a %% single syntax tree, usually in order to form an Erlang module %% definition. %% %% @see form_list_elements/1 %% @see is_form/1 %% @see flatten_form_list/1 %% type(Node) = form_list %% data(Node) = [Form] %% %% Form = syntaxTree() %% is_form(Form) = true -spec form_list([syntaxTree()]) -> syntaxTree(). form_list(Forms) -> tree(form_list, Forms). %% ===================================================================== %% @doc Returns the list of subnodes of a `form_list' node. %% %% @see form_list/1 -spec form_list_elements(syntaxTree()) -> [syntaxTree()]. form_list_elements(Node) -> data(Node). %% ===================================================================== %% @doc Flattens sublists of a `form_list' node. Returns %% `Node' with all subtrees of type `form_list' %% recursively expanded, yielding a single "flat" abstract form %% sequence. %% %% @see form_list/1 -spec flatten_form_list(syntaxTree()) -> syntaxTree(). flatten_form_list(Node) -> Fs = form_list_elements(Node), Fs1 = lists:reverse(flatten_form_list_1(Fs, [])), copy_attrs(Node, form_list(Fs1)). flatten_form_list_1([F | Fs], As) -> case type(F) of form_list -> As1 = flatten_form_list_1(form_list_elements(F), As), flatten_form_list_1(Fs, As1); _ -> flatten_form_list_1(Fs, [F | As]) end; flatten_form_list_1([], As) -> As. %% ===================================================================== %% @doc Creates an abstract piece of source code text. The result %% represents exactly the sequence of characters in `String'. %% This is useful in cases when one wants full control of the resulting %% output, e.g., for the appearance of floating-point numbers or macro %% definitions. %% %% @see text_string/1 %% type(Node) = text %% data(Node) = string() -spec text(string()) -> syntaxTree(). text(String) -> tree(text, String). %% ===================================================================== %% @doc Returns the character sequence represented by a `text' node. %% %% @see text/1 -spec text_string(syntaxTree()) -> string(). text_string(Node) -> data(Node). %% ===================================================================== %% @doc Creates an abstract variable with the given name. %% `Name' may be any atom or string that represents a %% lexically valid variable name, but not a single underscore %% character; see {@link underscore/0}. %% %% Note: no checking is done whether the character sequence %% represents a proper variable name, i.e., whether or not its first %% character is an uppercase Erlang character, or whether it does not %% contain control characters, whitespace, etc. %% %% @see variable_name/1 %% @see variable_literal/1 %% @see underscore/0 %% type(Node) = variable %% data(Node) = atom() %% %% `erl_parse' representation: %% %% {var, Pos, Name} %% %% Name = atom() \ '_' -spec variable(atom() | string()) -> syntaxTree(). variable(Name) when is_atom(Name) -> tree(variable, Name); variable(Name) -> tree(variable, list_to_atom(Name)). revert_variable(Node) -> Pos = get_pos(Node), Name = variable_name(Node), {var, Pos, Name}. %% ===================================================================== %% @doc Returns the name of a `variable' node as an atom. %% %% @see variable/1 -spec variable_name(syntaxTree()) -> atom(). variable_name(Node) -> case unwrap(Node) of {var, _, Name} -> Name; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Returns the name of a `variable' node as a string. %% %% @see variable/1 -spec variable_literal(syntaxTree()) -> string(). variable_literal(Node) -> case unwrap(Node) of {var, _, Name} -> atom_to_list(Name); Node1 -> atom_to_list(data(Node1)) end. %% ===================================================================== %% @doc Creates an abstract universal pattern ("`_'"). The %% lexical representation is a single underscore character. Note that %% this is not a variable, lexically speaking. %% %% @see variable/1 %% type(Node) = underscore %% data(Node) = [] %% %% `erl_parse' representation: %% %% {var, Pos, '_'} -spec underscore() -> syntaxTree(). underscore() -> tree(underscore, []). revert_underscore(Node) -> Pos = get_pos(Node), {var, Pos, '_'}. %% ===================================================================== %% @doc Creates an abstract integer literal. The lexical representation %% is the canonical decimal numeral of `Value'. %% %% @see integer_value/1 %% @see integer_literal/1 %% @see is_integer/2 %% type(Node) = integer %% data(Node) = integer() %% %% `erl_parse' representation: %% %% {integer, Pos, Value} %% %% Value = integer() -spec integer(integer()) -> syntaxTree(). integer(Value) -> tree(integer, Value). revert_integer(Node) -> Pos = get_pos(Node), {integer, Pos, integer_value(Node)}. %% ===================================================================== %% @doc Returns `true' if `Node' has type %% `integer' and represents `Value', otherwise `false'. %% %% @see integer/1 -spec is_integer(syntaxTree(), integer()) -> boolean(). is_integer(Node, Value) -> case unwrap(Node) of {integer, _, Value} -> true; #tree{type = integer, data = Value} -> true; _ -> false end. %% ===================================================================== %% @doc Returns the value represented by an `integer' node. %% %% @see integer/1 -spec integer_value(syntaxTree()) -> integer(). integer_value(Node) -> case unwrap(Node) of {integer, _, Value} -> Value; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Returns the numeral string represented by an `integer' node. %% %% @see integer/1 -spec integer_literal(syntaxTree()) -> string(). integer_literal(Node) -> integer_to_list(integer_value(Node)). %% ===================================================================== %% @doc Creates an abstract floating-point literal. The lexical %% representation is the decimal floating-point numeral of `Value'. %% %% @see float_value/1 %% @see float_literal/1 %% type(Node) = float %% data(Node) = Value %% %% Value = float() %% %% `erl_parse' representation: %% %% {float, Pos, Value} %% %% Value = float() %% Note that under current versions of Erlang, the name `float/1' cannot %% be used for local calls (i.e., within the module) - it will be %% overridden by the type conversion BIF of the same name, so always use %% `make_float/1' for local calls. -spec float(float()) -> syntaxTree(). float(Value) -> make_float(Value). make_float(Value) -> tree(float, Value). revert_float(Node) -> Pos = get_pos(Node), {float, Pos, float_value(Node)}. %% ===================================================================== %% @doc Returns the value represented by a `float' node. Note %% that floating-point values should usually not be compared for %% equality. %% %% @see float/1 -spec float_value(syntaxTree()) -> float(). float_value(Node) -> case unwrap(Node) of {float, _, Value} -> Value; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Returns the numeral string represented by a `float' node. %% %% @see float/1 -spec float_literal(syntaxTree()) -> string(). float_literal(Node) -> float_to_list(float_value(Node)). %% ===================================================================== %% @doc Creates an abstract character literal. The result represents %% "$Name", where `Name' corresponds to %% `Value'. %% %% Note: the literal corresponding to a particular character value is %% not uniquely defined. E.g., the character "`a'" can be %% written both as "`$a'" and "`$\141'", and a Tab %% character can be written as "`$\11'", "`$\011'" %% or "`$\t'". %% %% @see char_value/1 %% @see char_literal/1 %% @see char_literal/2 %% @see is_char/2 %% type(Node) = char %% data(Node) = char() %% %% `erl_parse' representation: %% %% {char, Pos, Code} %% %% Code = integer() -spec char(char()) -> syntaxTree(). char(Char) -> tree(char, Char). revert_char(Node) -> Pos = get_pos(Node), {char, Pos, char_value(Node)}. %% ===================================================================== %% @doc Returns `true' if `Node' has type %% `char' and represents `Value', otherwise `false'. %% %% @see char/1 -spec is_char(syntaxTree(), char()) -> boolean(). is_char(Node, Value) -> case unwrap(Node) of {char, _, Value} -> true; #tree{type = char, data = Value} -> true; _ -> false end. %% ===================================================================== %% @doc Returns the value represented by a `char' node. %% %% @see char/1 -spec char_value(syntaxTree()) -> char(). char_value(Node) -> case unwrap(Node) of {char, _, Char} -> Char; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Returns the literal string represented by a `char' %% node. This includes the leading "`$'" character. %% Characters beyond 255 will be escaped. %% %% @see char/1 -spec char_literal(syntaxTree()) -> nonempty_string(). char_literal(Node) -> char_literal(Node, latin1). %% ===================================================================== %% @doc Returns the literal string represented by a `char' %% node. This includes the leading "`$'" character. %% Depending on the encoding a character beyond 255 will be escaped %% ('latin1') or copied as is ('utf8'). %% %% @see char/1 -type encoding() :: 'utf8' | 'unicode' | 'latin1'. -spec char_literal(syntaxTree(), encoding()) -> nonempty_string(). char_literal(Node, unicode) -> io_lib:write_char(char_value(Node)); char_literal(Node, utf8) -> io_lib:write_char(char_value(Node)); char_literal(Node, latin1) -> io_lib:write_char_as_latin1(char_value(Node)). %% ===================================================================== %% @doc Creates an abstract string literal. The result represents %% "Text" (including the surrounding %% double-quotes), where `Text' corresponds to the sequence %% of characters in `Value', but not representing a %% specific string literal. %% %% For example, the result of `string("x\ny")' represents any and all of %% `"x\ny"', `"x\12y"', `"x\012y"' and `"x\^Jy"'; see {@link char/1}. %% %% @see string_value/1 %% @see string_literal/1 %% @see string_literal/2 %% @see is_string/2 %% @see char/1 %% type(Node) = string %% data(Node) = string() %% %% `erl_parse' representation: %% %% {string, Pos, Chars} %% %% Chars = string() -spec string(string()) -> syntaxTree(). string(String) -> tree(string, String). revert_string(Node) -> Pos = get_pos(Node), {string, Pos, string_value(Node)}. %% ===================================================================== %% @doc Returns `true' if `Node' has type %% `string' and represents `Value', otherwise `false'. %% %% @see string/1 -spec is_string(syntaxTree(), string()) -> boolean(). is_string(Node, Value) -> case unwrap(Node) of {string, _, Value} -> true; #tree{type = string, data = Value} -> true; _ -> false end. %% ===================================================================== %% @doc Returns the value represented by a `string' node. %% %% @see string/1 -spec string_value(syntaxTree()) -> string(). string_value(Node) -> case unwrap(Node) of {string, _, List} -> List; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Returns the literal string represented by a `string' %% node. This includes surrounding double-quote characters. %% Characters beyond 255 will be escaped. %% %% @see string/1 -spec string_literal(syntaxTree()) -> nonempty_string(). string_literal(Node) -> string_literal(Node, latin1). %% ===================================================================== %% @doc Returns the literal string represented by a `string' %% node. This includes surrounding double-quote characters. %% Depending on the encoding characters beyond 255 will be escaped %% ('latin1') or copied as is ('utf8'). %% %% @see string/1 -spec string_literal(syntaxTree(), encoding()) -> nonempty_string(). string_literal(Node, utf8) -> io_lib:write_string(string_value(Node)); string_literal(Node, unicode) -> io_lib:write_string(string_value(Node)); string_literal(Node, latin1) -> io_lib:write_string_as_latin1(string_value(Node)). %% ===================================================================== %% @doc Creates an abstract atom literal. The print name of the atom is %% the character sequence represented by `Name'. %% %% @see atom_value/1 %% @see atom_name/1 %% @see atom_literal/1 %% @see is_atom/2 %% type(Node) = atom %% data(Node) = atom() %% %% `erl_parse' representation: %% %% {atom, Pos, Value} %% %% Value = atom() -spec atom(atom() | string()) -> syntaxTree(). atom(Name) when is_atom(Name) -> tree(atom, Name); atom(Name) -> tree(atom, list_to_atom(Name)). revert_atom(Node) -> Pos = get_pos(Node), {atom, Pos, atom_value(Node)}. %% ===================================================================== %% @doc Returns `true' if `Node' has type %% `atom' and represents `Value', otherwise `false'. %% %% @see atom/1 -spec is_atom(syntaxTree(), atom()) -> boolean(). is_atom(Node, Value) -> case unwrap(Node) of {atom, _, Value} -> true; #tree{type = atom, data = Value} -> true; _ -> false end. %% ===================================================================== %% @doc Returns the value represented by an `atom' node. %% %% @see atom/1 -spec atom_value(syntaxTree()) -> atom(). atom_value(Node) -> case unwrap(Node) of {atom, _, Name} -> Name; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Returns the printname of an `atom' node. %% %% @see atom/1 -spec atom_name(syntaxTree()) -> string(). atom_name(Node) -> atom_to_list(atom_value(Node)). %% ===================================================================== %% @doc Returns the literal string represented by an `atom' %% node. This includes surrounding single-quote characters if necessary. %% %% Note that e.g. the result of `atom("x\ny")' represents %% any and all of `'x\ny'', `'x\12y'', %% `'x\012y'' and `'x\^Jy\''; see {@link string/1}. %% %% @see atom/1 %% @see string/1 -spec atom_literal(syntaxTree()) -> string(). atom_literal(Node) -> io_lib:write_atom(atom_value(Node)). %% ===================================================================== %% @doc Creates an abstract tuple. If `Elements' is %% `[X1, ..., Xn]', the result represents %% "{X1, ..., Xn}". %% %% Note: The Erlang language has distinct 1-tuples, i.e., %% `{X}' is always distinct from `X' itself. %% %% @see tuple_elements/1 %% @see tuple_size/1 %% type(Node) = tuple %% data(Node) = Elements %% %% Elements = [syntaxTree()] %% %% `erl_parse' representation: %% %% {tuple, Pos, Elements} %% %% Elements = [erl_parse()] -spec tuple([syntaxTree()]) -> syntaxTree(). tuple(List) -> tree(tuple, List). revert_tuple(Node) -> Pos = get_pos(Node), {tuple, Pos, tuple_elements(Node)}. %% ===================================================================== %% @doc Returns the list of element subtrees of a `tuple' node. %% %% @see tuple/1 -spec tuple_elements(syntaxTree()) -> [syntaxTree()]. tuple_elements(Node) -> case unwrap(Node) of {tuple, _, List} -> List; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Returns the number of elements of a `tuple' node. %% %% Note: this is equivalent to %% `length(tuple_elements(Node))', but potentially more %% efficient. %% %% @see tuple/1 %% @see tuple_elements/1 -spec tuple_size(syntaxTree()) -> non_neg_integer(). tuple_size(Node) -> length(tuple_elements(Node)). %% ===================================================================== %% @equiv list(List, none) -spec list([syntaxTree()]) -> syntaxTree(). list(List) -> list(List, none). %% ===================================================================== %% @doc Constructs an abstract list skeleton. The result has type %% `list' or `nil'. If `List' is a %% nonempty list `[E1, ..., En]', the result has type %% `list' and represents either "[E1, ..., %% En]", if `Tail' is `none', or %% otherwise "[E1, ..., En | %% Tail]". If `List' is the empty list, %% `Tail' must be `none', and in that %% case the result has type `nil' and represents %% "`[]'" (see {@link nil/0}). %% %% The difference between lists as semantic objects (built up of %% individual "cons" and "nil" terms) and the various syntactic forms %% for denoting lists may be bewildering at first. This module provides %% functions both for exact control of the syntactic representation as %% well as for the simple composition and deconstruction in terms of %% cons and head/tail operations. %% %% Note: in `list(Elements, none)', the "nil" list %% terminator is implicit and has no associated information (see %% {@link get_attrs/1}), while in the seemingly equivalent %% `list(Elements, Tail)' when `Tail' has type %% `nil', the list terminator subtree `Tail' may %% have attached attributes such as position, comments, and annotations, %% which will be preserved in the result. %% %% @see nil/0 %% @see list/1 %% @see list_prefix/1 %% @see list_suffix/1 %% @see cons/2 %% @see list_head/1 %% @see list_tail/1 %% @see is_list_skeleton/1 %% @see is_proper_list/1 %% @see list_elements/1 %% @see list_length/1 %% @see normalize_list/1 %% @see compact_list/1 %% @see get_attrs/1 -record(list, {prefix :: [syntaxTree()], suffix :: 'none' | syntaxTree()}). %% type(Node) = list %% data(Node) = #list{prefix :: Elements, suffix :: Tail} %% %% Elements = [syntaxTree()] %% Tail = none | syntaxTree() %% %% `erl_parse' representation: %% %% {cons, Pos, Head, Tail} %% %% Head = Tail = [erl_parse()] %% %% This represents `[ | ]', or more generally `[ %% ]' where the form of can depend on the %% structure of ; there is no fixed printed form. -spec list([syntaxTree()], 'none' | syntaxTree()) -> syntaxTree(). list([], none) -> nil(); list(Elements, Tail) when Elements =/= [] -> tree(list, #list{prefix = Elements, suffix = Tail}). revert_list(Node) -> Pos = get_pos(Node), P = list_prefix(Node), S = case list_suffix(Node) of none -> revert_nil(set_pos(nil(), Pos)); S1 -> S1 end, lists:foldr(fun (X, A) -> {cons, Pos, X, A} end, S, P). %% ===================================================================== %% @doc Creates an abstract empty list. The result represents %% "`[]'". The empty list is traditionally called "nil". %% %% @see list/2 %% @see is_list_skeleton/1 %% type(Node) = nil %% data(Node) = term() %% %% `erl_parse' representation: %% %% {nil, Pos} -spec nil() -> syntaxTree(). nil() -> tree(nil). revert_nil(Node) -> Pos = get_pos(Node), {nil, Pos}. %% ===================================================================== %% @doc Returns the prefix element subtrees of a `list' node. %% If `Node' represents "[E1, ..., %% En]" or "[E1, ..., En | %% Tail]", the returned value is `[E1, ..., %% En]'. %% %% @see list/2 -spec list_prefix(syntaxTree()) -> [syntaxTree()]. list_prefix(Node) -> case unwrap(Node) of {cons, _, Head, Tail} -> [Head | cons_prefix(Tail)]; Node1 -> (data(Node1))#list.prefix end. %% collects sequences of conses; cf. cons_suffix/1 below cons_prefix({cons, _, Head, Tail}) -> [Head | cons_prefix(Tail)]; cons_prefix(_) -> []. %% ===================================================================== %% @doc Returns the suffix subtree of a `list' node, if one %% exists. If `Node' represents "[E1, ..., %% En | Tail]", the returned value is %% `Tail', otherwise, i.e., if `Node' represents %% "[E1, ..., En]", `none' is %% returned. %% %% Note that even if this function returns some `Tail' %% that is not `none', the type of `Tail' can be %% `nil', if the tail has been given explicitly, and the list %% skeleton has not been compacted (see {@link compact_list/1}). %% %% @see list/2 %% @see nil/0 %% @see compact_list/1 -spec list_suffix(syntaxTree()) -> 'none' | syntaxTree(). list_suffix(Node) -> case unwrap(Node) of {cons, _, _, Tail} -> case cons_suffix(Tail) of {nil, _} -> none; Tail1 -> Tail1 end; Node1 -> (data(Node1))#list.suffix end. %% skips sequences of conses; cf. cons_prefix/1 above cons_suffix({cons, _, _, Tail}) -> cons_suffix(Tail); cons_suffix(Tail) -> Tail. %% ===================================================================== %% @doc "Optimising" list skeleton cons operation. Creates an abstract %% list skeleton whose first element is `Head' and whose tail %% corresponds to `Tail'. This is similar to %% `list([Head], Tail)', except that `Tail' may %% not be `none', and that the result does not necessarily %% represent exactly "[Head | Tail]", but %% may depend on the `Tail' subtree. E.g., if %% `Tail' represents `[X, Y]', the result may %% represent "[Head, X, Y]", rather than %% "[Head | [X, Y]]". Annotations on %% `Tail' itself may be lost if `Tail' represents %% a list skeleton, but comments on `Tail' are propagated to %% the result. %% %% @see list/2 %% @see list_head/1 %% @see list_tail/1 -spec cons(syntaxTree(), syntaxTree()) -> syntaxTree(). cons(Head, Tail) -> case type(Tail) of list -> copy_comments(Tail, list([Head | list_prefix(Tail)], list_suffix(Tail))); nil -> copy_comments(Tail, list([Head])); _ -> list([Head], Tail) end. %% ===================================================================== %% @doc Returns the head element subtree of a `list' node. If %% `Node' represents "[Head ...]", the %% result will represent "Head". %% %% @see list/2 %% @see list_tail/1 %% @see cons/2 -spec list_head(syntaxTree()) -> syntaxTree(). list_head(Node) -> hd(list_prefix(Node)). %% ===================================================================== %% @doc Returns the tail of a `list' node. If %% `Node' represents a single-element list %% "[E]", then the result has type %% `nil', representing "`[]'". If %% `Node' represents "[E1, E2 %% ...]", the result will represent "[E2 %% ...]", and if `Node' represents %% "[Head | Tail]", the result will %% represent "Tail". %% %% @see list/2 %% @see list_head/1 %% @see cons/2 -spec list_tail(syntaxTree()) -> syntaxTree(). list_tail(Node) -> Tail = list_suffix(Node), case tl(list_prefix(Node)) of [] -> if Tail =:= none -> nil(); % implicit list terminator. true -> Tail end; Es -> list(Es, Tail) % `Es' is nonempty. end. %% ===================================================================== %% @doc Returns `true' if `Node' has type %% `list' or `nil', otherwise `false'. %% %% @see list/2 %% @see nil/0 -spec is_list_skeleton(syntaxTree()) -> boolean(). is_list_skeleton(Node) -> case type(Node) of list -> true; nil -> true; _ -> false end. %% ===================================================================== %% @doc Returns `true' if `Node' represents a %% proper list, and `false' otherwise. A proper list is a %% list skeleton either on the form "`[]'" or %% "[E1, ..., En]", or "[... | %% Tail]" where recursively `Tail' also %% represents a proper list. %% %% Note: Since `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'. %% %% @see list/2 -spec is_proper_list(syntaxTree()) -> boolean(). is_proper_list(Node) -> case type(Node) of list -> case list_suffix(Node) of none -> true; Tail -> is_proper_list(Tail) end; nil -> true; _ -> false end. %% ===================================================================== %% @doc Returns the list of element subtrees of a list skeleton. %% `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 list/2 %% @see is_proper_list/1 -spec list_elements(syntaxTree()) -> [syntaxTree()]. list_elements(Node) -> lists:reverse(list_elements(Node, [])). list_elements(Node, As) -> case type(Node) of list -> As1 = lists:reverse(list_prefix(Node)) ++ As, case list_suffix(Node) of none -> As1; Tail -> list_elements(Tail, As1) end; nil -> As end. %% ===================================================================== %% @doc Returns the number of element subtrees of a list skeleton. %% `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. %% %% @see list/2 %% @see is_proper_list/1 %% @see list_elements/1 -spec list_length(syntaxTree()) -> non_neg_integer(). list_length(Node) -> list_length(Node, 0). list_length(Node, A) -> case type(Node) of list -> A1 = length(list_prefix(Node)) + A, case list_suffix(Node) of none -> A1; Tail -> list_length(Tail, A1) end; nil -> A end. %% ===================================================================== %% @doc Expands an abstract list skeleton to its most explicit form. If %% `Node' represents "[E1, ..., En | %% Tail]", the result represents "[E1 | %% ... [En | Tail1] ... ]", where %% `Tail1' is the result of %% `normalize_list(Tail)'. If `Node' represents %% "[E1, ..., En]", the result simply %% represents "[E1 | ... [En | []] ... %% ]". If `Node' does not represent a list skeleton, %% `Node' itself is returned. %% %% @see list/2 %% @see compact_list/1 -spec normalize_list(syntaxTree()) -> syntaxTree(). normalize_list(Node) -> case type(Node) of list -> P = list_prefix(Node), case list_suffix(Node) of none -> copy_attrs(Node, normalize_list_1(P, nil())); Tail -> Tail1 = normalize_list(Tail), copy_attrs(Node, normalize_list_1(P, Tail1)) end; _ -> Node end. normalize_list_1(Es, Tail) -> lists:foldr(fun (X, A) -> list([X], A) % not `cons'! end, Tail, Es). %% ===================================================================== %% @doc Yields the most compact form for an abstract list skeleton. The %% result either represents "[E1, ..., En | %% Tail]", where `Tail' is not a list %% skeleton, or otherwise simply "[E1, ..., %% En]". Annotations on subtrees of `Node' %% that represent list skeletons may be lost, but comments will be %% propagated to the result. Returns `Node' itself if %% `Node' does not represent a list skeleton. %% %% @see list/2 %% @see normalize_list/1 -spec compact_list(syntaxTree()) -> syntaxTree(). compact_list(Node) -> case type(Node) of list -> case list_suffix(Node) of none -> Node; Tail -> case type(Tail) of list -> Tail1 = compact_list(Tail), Node1 = list(list_prefix(Node) ++ list_prefix(Tail1), list_suffix(Tail1)), join_comments(Tail1, copy_attrs(Node, Node1)); nil -> Node1 = list(list_prefix(Node)), join_comments(Tail, copy_attrs(Node, Node1)); _ -> Node end end; _ -> Node end. %% ===================================================================== %% @doc Creates an abstract binary-object template. If %% `Fields' is `[F1, ..., Fn]', the result %% represents "<<F1, ..., %% Fn>>". %% %% @see binary_fields/1 %% @see binary_field/2 %% type(Node) = binary %% data(Node) = Fields %% %% Fields = [syntaxTree()] %% %% `erl_parse' representation: %% %% {bin, Pos, Fields} %% %% Fields = [Field] %% Field = {bin_element, ...} %% %% See `binary_field' for documentation on `erl_parse' binary %% fields (or "elements"). -spec binary([syntaxTree()]) -> syntaxTree(). binary(List) -> tree(binary, List). revert_binary(Node) -> Pos = get_pos(Node), {bin, Pos, binary_fields(Node)}. %% ===================================================================== %% @doc Returns the list of field subtrees of a `binary' node. %% %% @see binary/1 %% @see binary_field/2 -spec binary_fields(syntaxTree()) -> [syntaxTree()]. binary_fields(Node) -> case unwrap(Node) of {bin, _, List} -> List; Node1 -> data(Node1) end. %% ===================================================================== %% @equiv binary_field(Body, []) -spec binary_field(syntaxTree()) -> syntaxTree(). binary_field(Body) -> binary_field(Body, []). %% ===================================================================== %% @doc Creates an abstract binary template field. %% If `Size' is `none', this is equivalent to %% "`binary_field(Body, Types)'", otherwise it is %% equivalent to "`binary_field(size_qualifier(Body, Size), %% Types)'". %% %% (This is a utility function.) %% %% @see binary/1 %% @see binary_field/2 %% @see size_qualifier/2 -spec binary_field(syntaxTree(), 'none' | syntaxTree(), [syntaxTree()]) -> syntaxTree(). binary_field(Body, none, Types) -> binary_field(Body, Types); binary_field(Body, Size, Types) -> binary_field(size_qualifier(Body, Size), Types). %% ===================================================================== %% @doc Creates an abstract binary template field. If %% `Types' is the empty list, the result simply represents %% "Body", otherwise, if `Types' is %% `[T1, ..., Tn]', the result represents %% "Body/T1-...-Tn". %% %% @see binary/1 %% @see binary_field/1 %% @see binary_field/3 %% @see binary_field_body/1 %% @see binary_field_types/1 %% @see binary_field_size/1 -record(binary_field, {body :: syntaxTree(), types :: [syntaxTree()]}). %% type(Node) = binary_field %% data(Node) = #binary_field{body :: Body, types :: Types} %% %% Body = syntaxTree() %% Types = [syntaxTree()] %% %% `erl_parse' representation: %% %% {bin_element, Pos, Expr, Size, TypeList} %% %% Expr = erl_parse() %% Size = default | erl_parse() %% TypeList = default | [Type] \ [] %% Type = atom() | {atom(), integer()} -spec binary_field(syntaxTree(), [syntaxTree()]) -> syntaxTree(). binary_field(Body, Types) -> tree(binary_field, #binary_field{body = Body, types = Types}). revert_binary_field(Node) -> Pos = get_pos(Node), Body = binary_field_body(Node), {Expr, Size} = case type(Body) of size_qualifier -> %% Note that size qualifiers are not %% revertible out of context. {size_qualifier_body(Body), size_qualifier_argument(Body)}; _ -> {Body, default} end, Types = case binary_field_types(Node) of [] -> default; Ts -> fold_binary_field_types(Ts) end, {bin_element, Pos, Expr, Size, Types}. %% ===================================================================== %% @doc Returns the body subtree of a `binary_field'. %% %% @see binary_field/2 -spec binary_field_body(syntaxTree()) -> syntaxTree(). binary_field_body(Node) -> case unwrap(Node) of {bin_element, _, Body, Size, _} -> if Size =:= default -> Body; true -> size_qualifier(Body, Size) end; Node1 -> (data(Node1))#binary_field.body end. %% ===================================================================== %% @doc Returns the list of type-specifier subtrees of a %% `binary_field' node. If `Node' represents %% ".../T1, ..., Tn", the result is %% `[T1, ..., Tn]', otherwise the result is the empty list. %% %% @see binary_field/2 -spec binary_field_types(syntaxTree()) -> [syntaxTree()]. binary_field_types(Node) -> case unwrap(Node) of {bin_element, Pos, _, _, Types} -> if Types =:= default -> []; true -> unfold_binary_field_types(Types, Pos) end; Node1 -> (data(Node1))#binary_field.types end. %% ===================================================================== %% @doc Returns the size specifier subtree of a %% `binary_field' node, if any. If `Node' %% represents "Body:Size" or %% "Body:Size/T1, ..., %% Tn", the result is `Size', otherwise %% `none' is returned. %% %% (This is a utility function.) %% %% @see binary_field/2 %% @see binary_field/3 -spec binary_field_size(syntaxTree()) -> 'none' | syntaxTree(). binary_field_size(Node) -> case unwrap(Node) of {bin_element, _, _, Size, _} -> if Size =:= default -> none; true -> Size end; Node1 -> Body = (data(Node1))#binary_field.body, case type(Body) of size_qualifier -> size_qualifier_argument(Body); _ -> none end end. %% ===================================================================== %% @doc Creates an abstract size qualifier. The result represents %% "Body:Size". %% %% @see size_qualifier_body/1 %% @see size_qualifier_argument/1 -record(size_qualifier, {body :: syntaxTree(), size :: syntaxTree()}). %% type(Node) = size_qualifier %% data(Node) = #size_qualifier{body :: Body, size :: Size} %% %% Body = Size = syntaxTree() -spec size_qualifier(syntaxTree(), syntaxTree()) -> syntaxTree(). size_qualifier(Body, Size) -> tree(size_qualifier, #size_qualifier{body = Body, size = Size}). %% ===================================================================== %% @doc Returns the body subtree of a `size_qualifier' node. %% %% @see size_qualifier/2 -spec size_qualifier_body(syntaxTree()) -> syntaxTree(). size_qualifier_body(Node) -> (data(Node))#size_qualifier.body. %% ===================================================================== %% @doc Returns the argument subtree (the size) of a %% `size_qualifier' node. %% %% @see size_qualifier/2 -spec size_qualifier_argument(syntaxTree()) -> syntaxTree(). size_qualifier_argument(Node) -> (data(Node))#size_qualifier.size. %% ===================================================================== %% @doc Creates an abstract error marker. The result represents an %% occurrence of an error in the source code, with an associated Erlang %% I/O ErrorInfo structure given by `Error' (see module %% {@link //stdlib/io} for details). Error markers are regarded as source %% code forms, but have no defined lexical form. %% %% Note: this is supported only for backwards compatibility with %% existing parsers and tools. %% %% @see error_marker_info/1 %% @see warning_marker/1 %% @see eof_marker/0 %% @see is_form/1 %% type(Node) = error_marker %% data(Node) = term() %% %% `erl_parse' representation: %% %% {error, Error} %% %% Error = term() %% %% Note that there is no position information for the node %% itself: `get_pos' and `set_pos' handle this as a special case. -spec error_marker(term()) -> syntaxTree(). error_marker(Error) -> tree(error_marker, Error). revert_error_marker(Node) -> %% Note that the position information of the node itself is not %% preserved. {error, error_marker_info(Node)}. %% ===================================================================== %% @doc Returns the ErrorInfo structure of an `error_marker' node. %% %% @see error_marker/1 -spec error_marker_info(syntaxTree()) -> term(). error_marker_info(Node) -> case unwrap(Node) of {error, Error} -> Error; T -> data(T) end. %% ===================================================================== %% @doc Creates an abstract warning marker. The result represents an %% occurrence of a possible problem in the source code, with an %% associated Erlang I/O ErrorInfo structure given by `Error' %% (see module {@link //stdlib/io} for details). Warning markers are %% regarded as source code forms, but have no defined lexical form. %% %% Note: this is supported only for backwards compatibility with %% existing parsers and tools. %% %% @see warning_marker_info/1 %% @see error_marker/1 %% @see eof_marker/0 %% @see is_form/1 %% type(Node) = warning_marker %% data(Node) = term() %% %% `erl_parse' representation: %% %% {warning, Error} %% %% Error = term() %% %% Note that there is no position information for the node %% itself: `get_pos' and `set_pos' handle this as a special case. -spec warning_marker(term()) -> syntaxTree(). warning_marker(Warning) -> tree(warning_marker, Warning). revert_warning_marker(Node) -> %% Note that the position information of the node itself is not %% preserved. {warning, warning_marker_info(Node)}. %% ===================================================================== %% @doc Returns the ErrorInfo structure of a `warning_marker' node. %% %% @see warning_marker/1 -spec warning_marker_info(syntaxTree()) -> term(). warning_marker_info(Node) -> case unwrap(Node) of {warning, Error} -> Error; T -> data(T) end. %% ===================================================================== %% @doc Creates an abstract end-of-file marker. This represents the %% end of input when reading a sequence of source code forms. An %% end-of-file marker is itself regarded as a source code form %% (namely, the last in any sequence in which it occurs). It has no %% defined lexical form. %% %% Note: this is retained only for backwards compatibility with %% existing parsers and tools. %% %% @see error_marker/1 %% @see warning_marker/1 %% @see is_form/1 %% type(Node) = eof_marker %% data(Node) = term() %% %% `erl_parse' representation: %% %% {eof, Pos} -spec eof_marker() -> syntaxTree(). eof_marker() -> tree(eof_marker). revert_eof_marker(Node) -> Pos = get_pos(Node), {eof, Pos}. %% ===================================================================== %% @equiv attribute(Name, none) -spec attribute(syntaxTree()) -> syntaxTree(). attribute(Name) -> attribute(Name, none). %% ===================================================================== %% @doc Creates an abstract program attribute. If %% `Arguments' is `[A1, ..., An]', the result %% represents "-Name(A1, ..., %% An).". Otherwise, if `Arguments' is %% `none', the result represents %% "-Name.". The latter form makes it possible %% to represent preprocessor directives such as %% "`-endif.'". Attributes are source code forms. %% %% Note: The preprocessor macro definition directive %% "-define(Name, Body)." has relatively %% few requirements on the syntactical form of `Body' (viewed %% as a sequence of tokens). The `text' node type can be used %% for a `Body' that is not a normal Erlang construct. %% %% @see attribute/1 %% @see attribute_name/1 %% @see attribute_arguments/1 %% @see text/1 %% @see is_form/1 -record(attribute, {name :: syntaxTree(), args :: 'none' | [syntaxTree()]}). %% type(Node) = attribute %% data(Node) = #attribute{name :: Name, args :: Arguments} %% %% Name = syntaxTree() %% Arguments = none | [syntaxTree()] %% %% `erl_parse' representation: %% %% {attribute, Pos, module, {Name,Vars}} %% {attribute, Pos, module, Name} %% %% Name = atom() | [atom()] %% Vars = [atom()] %% %% Representing `-module(M).', or `-module(M, Vs).', where M is %% `A1.A2.....An' if Name is `[A1, A2, ..., An]', and Vs is `[V1, %% ..., Vm]' if Vars is `[V1, ..., Vm]'. %% %% {attribute, Pos, export, Exports} %% %% Exports = [{atom(), integer()}] %% %% Representing `-export([A1/N1, ..., Ak/Nk]).', if `Exports' is %% `[{A1, N1}, ..., {Ak, Nk}]'. %% %% {attribute, Pos, import, Imports} %% %% Imports = {atom(), Pairs} | [atom()] %% Pairs = [{atom(), integer()] %% %% Representing `-import(Module, [A1/N1, ..., Ak/Nk]).', if %% `Imports' is `{Module, [{A1, N1}, ..., {Ak, Nk}]}', or %% `-import(A1.....An).', if `Imports' is `[A1, ..., An]'. %% %% {attribute, Pos, file, Position} %% %% Position = {filename(), integer()} %% %% Representing `-file(Name, Line).', if `Position' is `{Name, %% Line}'. %% %% {attribute, Pos, record, Info} %% %% Info = {Name, [Entries]} %% Name = atom() %% Entries = {record_field, Pos, atom()} %% | {record_field, Pos, atom(), erl_parse()} %% %% Representing `-record(Name, {, ..., }).', if `Info' is %% `{Name, [D1, ..., D1]}', where each `Fi' is either `Ai = ', %% if the corresponding `Di' is `{record_field, Pos, Ai, Ei}', or %% otherwise simply `Ai', if `Di' is `{record_field, Pos, Ai}'. %% %% {attribute, L, Name, Term} %% %% Name = atom() \ StandardName %% StandardName = module | export | import | file | record %% Term = term() %% %% Representing `-Name(Term).'. -spec attribute(syntaxTree(), 'none' | [syntaxTree()]) -> syntaxTree(). attribute(Name, Args) -> tree(attribute, #attribute{name = Name, args = Args}). revert_attribute(Node) -> Name = attribute_name(Node), Args = attribute_arguments(Node), Pos = get_pos(Node), case type(Name) of atom -> revert_attribute_1(atom_value(Name), Args, Pos, Node); _ -> Node end. %% All the checking makes this part a bit messy: revert_attribute_1(module, [M], Pos, Node) -> case revert_module_name(M) of {ok, A} -> {attribute, Pos, module, A}; error -> Node end; revert_attribute_1(module, [M, List], Pos, Node) -> Vs = case is_list_skeleton(List) of true -> case is_proper_list(List) of true -> fold_variable_names(list_elements(List)); false -> Node end; false -> Node end, case revert_module_name(M) of {ok, A} -> {attribute, Pos, module, {A, Vs}}; error -> Node end; revert_attribute_1(export, [List], Pos, Node) -> case is_list_skeleton(List) of true -> case is_proper_list(List) of true -> Fs = fold_function_names(list_elements(List)), {attribute, Pos, export, Fs}; false -> Node end; false -> Node end; revert_attribute_1(import, [M], Pos, Node) -> case revert_module_name(M) of {ok, A} -> {attribute, Pos, import, A}; error -> Node end; revert_attribute_1(import, [M, List], Pos, Node) -> case revert_module_name(M) of {ok, A} -> case is_list_skeleton(List) of true -> case is_proper_list(List) of true -> Fs = fold_function_names( list_elements(List)), {attribute, Pos, import, {A, Fs}}; false -> Node end; false -> Node end; error -> Node end; revert_attribute_1(file, [A, Line], Pos, Node) -> case type(A) of string -> case type(Line) of integer -> {attribute, Pos, file, {concrete(A), concrete(Line)}}; _ -> Node end; _ -> Node end; revert_attribute_1(record, [A, Tuple], Pos, Node) -> case type(A) of atom -> case type(Tuple) of tuple -> Fs = fold_record_fields( tuple_elements(Tuple)), {attribute, Pos, record, {concrete(A), Fs}}; _ -> Node end; _ -> Node end; revert_attribute_1(N, [T], Pos, _) -> {attribute, Pos, N, concrete(T)}; revert_attribute_1(_, _, _, Node) -> Node. revert_module_name(A) -> case type(A) of atom -> {ok, concrete(A)}; _ -> error end. %% ===================================================================== %% @doc Returns the name subtree of an `attribute' node. %% %% @see attribute/1 -spec attribute_name(syntaxTree()) -> syntaxTree(). attribute_name(Node) -> case unwrap(Node) of {attribute, Pos, Name, _} -> set_pos(atom(Name), Pos); Node1 -> (data(Node1))#attribute.name end. %% ===================================================================== %% @doc Returns the list of argument subtrees of an %% `attribute' node, if any. If `Node' %% represents "-Name.", the result is %% `none'. Otherwise, if `Node' represents %% "-Name(E1, ..., En).", %% `[E1, ..., E1]' is returned. %% %% @see attribute/1 -spec attribute_arguments(syntaxTree()) -> none | [syntaxTree()]. attribute_arguments(Node) -> case unwrap(Node) of {attribute, Pos, Name, Data} -> case Name of module -> {M1, Vs} = case Data of {M0, Vs0} -> {M0, unfold_variable_names(Vs0, Pos)}; M0 -> {M0, none} end, M2 = atom(M1), M = set_pos(M2, Pos), if Vs == none -> [M]; true -> [M, set_pos(list(Vs), Pos)] end; export -> [set_pos( list(unfold_function_names(Data, Pos)), Pos)]; import -> {Module, Imports} = Data, [set_pos(atom(Module), Pos), set_pos( list(unfold_function_names(Imports, Pos)), Pos)]; file -> {File, Line} = Data, [set_pos(string(File), Pos), set_pos(integer(Line), Pos)]; record -> %% Note that we create a tuple as container %% for the second argument! {Type, Entries} = Data, [set_pos(atom(Type), Pos), set_pos(tuple(unfold_record_fields(Entries)), Pos)]; _ -> %% Standard single-term generic attribute. [set_pos(abstract(Data), Pos)] end; Node1 -> (data(Node1))#attribute.args end. %% ===================================================================== %% @doc Creates an abstract arity qualifier. The result represents %% "Body/Arity". %% %% @see arity_qualifier_body/1 %% @see arity_qualifier_argument/1 -record(arity_qualifier, {body :: syntaxTree(), arity :: syntaxTree()}). %% type(Node) = arity_qualifier %% data(Node) = #arity_qualifier{body :: Body, arity :: Arity} %% %% Body = Arity = syntaxTree() -spec arity_qualifier(syntaxTree(), syntaxTree()) -> syntaxTree(). arity_qualifier(Body, Arity) -> tree(arity_qualifier, #arity_qualifier{body = Body, arity = Arity}). %% ===================================================================== %% @doc Returns the body subtree of an `arity_qualifier' node. %% %% @see arity_qualifier/2 -spec arity_qualifier_body(syntaxTree()) -> syntaxTree(). arity_qualifier_body(Node) -> (data(Node))#arity_qualifier.body. %% ===================================================================== %% @doc Returns the argument (the arity) subtree of an %% `arity_qualifier' node. %% %% @see arity_qualifier/2 -spec arity_qualifier_argument(syntaxTree()) -> syntaxTree(). arity_qualifier_argument(Node) -> (data(Node))#arity_qualifier.arity. %% ===================================================================== %% @doc Creates an abstract module qualifier. The result represents %% "Module:Body". %% %% @see module_qualifier_argument/1 %% @see module_qualifier_body/1 -record(module_qualifier, {module :: syntaxTree(), body :: syntaxTree()}). %% type(Node) = module_qualifier %% data(Node) = #module_qualifier{module :: Module, body :: Body} %% %% Module = Body = syntaxTree() %% %% `erl_parse' representation: %% %% {remote, Pos, Module, Arg} %% %% Module = Arg = erl_parse() -spec module_qualifier(syntaxTree(), syntaxTree()) -> syntaxTree(). module_qualifier(Module, Body) -> tree(module_qualifier, #module_qualifier{module = Module, body = Body}). revert_module_qualifier(Node) -> Pos = get_pos(Node), Module = module_qualifier_argument(Node), Body = module_qualifier_body(Node), {remote, Pos, Module, Body}. %% ===================================================================== %% @doc Returns the argument (the module) subtree of a %% `module_qualifier' node. %% %% @see module_qualifier/2 -spec module_qualifier_argument(syntaxTree()) -> syntaxTree(). module_qualifier_argument(Node) -> case unwrap(Node) of {remote, _, Module, _} -> Module; Node1 -> (data(Node1))#module_qualifier.module end. %% ===================================================================== %% @doc Returns the body subtree of a `module_qualifier' node. %% %% @see module_qualifier/2 -spec module_qualifier_body(syntaxTree()) -> syntaxTree(). module_qualifier_body(Node) -> case unwrap(Node) of {remote, _, _, Body} -> Body; Node1 -> (data(Node1))#module_qualifier.body end. %% ===================================================================== %% @doc Creates an abstract function definition. If `Clauses' %% is `[C1, ..., Cn]', the result represents %% "Name C1; ...; Name %% Cn.". More exactly, if each `Ci' %% represents "(Pi1, ..., Pim) Gi -> %% Bi", then the result represents %% "Name(P11, ..., P1m) G1 -> %% B1; ...; Name(Pn1, ..., Pnm) %% Gn -> Bn.". Function definitions are source %% code forms. %% %% @see function_name/1 %% @see function_clauses/1 %% @see function_arity/1 %% @see is_form/1 %% @see rule/2 %% Don't use the name 'function' for this record, to avoid confusion with %% the tuples on the form {function,Name,Arity} used by erl_parse. -record(func, {name :: syntaxTree(), clauses :: [syntaxTree()]}). %% type(Node) = function %% data(Node) = #func{name :: Name, clauses :: Clauses} %% %% Name = syntaxTree() %% Clauses = [syntaxTree()] %% %% (There's no real point in precomputing and storing the arity, %% and passing it as a constructor argument makes it possible to %% end up with an inconsistent value. Besides, some people might %% want to check all clauses, and not just the first, so the %% computation is not obvious.) %% %% `erl_parse' representation: %% %% {function, Pos, Name, Arity, Clauses} %% %% Name = atom() %% Arity = integer() %% Clauses = [Clause] \ [] %% Clause = {clause, ...} %% %% where the number of patterns in each clause should be equal to %% the integer `Arity'; see `clause' for documentation on %% `erl_parse' clauses. -spec function(syntaxTree(), [syntaxTree()]) -> syntaxTree(). function(Name, Clauses) -> tree(function, #func{name = Name, clauses = Clauses}). revert_function(Node) -> Name = function_name(Node), Clauses = [revert_clause(C) || C <- function_clauses(Node)], Pos = get_pos(Node), case type(Name) of atom -> A = function_arity(Node), {function, Pos, concrete(Name), A, Clauses}; _ -> Node end. %% ===================================================================== %% @doc Returns the name subtree of a `function' node. %% %% @see function/2 -spec function_name(syntaxTree()) -> syntaxTree(). function_name(Node) -> case unwrap(Node) of {function, Pos, Name, _, _} -> set_pos(atom(Name), Pos); Node1 -> (data(Node1))#func.name end. %% ===================================================================== %% @doc Returns the list of clause subtrees of a `function' node. %% %% @see function/2 -spec function_clauses(syntaxTree()) -> [syntaxTree()]. function_clauses(Node) -> case unwrap(Node) of {function, _, _, _, Clauses} -> Clauses; Node1 -> (data(Node1))#func.clauses end. %% ===================================================================== %% @doc Returns the arity of a `function' node. The result %% is the number of parameter patterns in the first clause of the %% function; subsequent clauses are ignored. %% %% An exception is thrown if `function_clauses(Node)' %% returns an empty list, or if the first element of that list is not %% a syntax tree `C' of type `clause' such that %% `clause_patterns(C)' is a nonempty list. %% %% @see function/2 %% @see function_clauses/1 %% @see clause/3 %% @see clause_patterns/1 -spec function_arity(syntaxTree()) -> arity(). function_arity(Node) -> %% Note that this never accesses the arity field of `erl_parse' %% function nodes. length(clause_patterns(hd(function_clauses(Node)))). %% ===================================================================== %% @equiv clause([], Guard, Body) -type guard() :: 'none' | syntaxTree() | [syntaxTree()] | [[syntaxTree()]]. -spec clause(guard(), [syntaxTree()]) -> syntaxTree(). clause(Guard, Body) -> clause([], Guard, Body). %% ===================================================================== %% @doc Creates an abstract clause. If `Patterns' is %% `[P1, ..., Pn]' and `Body' is `[B1, ..., %% Bm]', then if `Guard' is `none', the %% result represents "(P1, ..., Pn) -> %% B1, ..., Bm", otherwise, unless %% `Guard' is a list, the result represents %% "(P1, ..., Pn) when Guard -> %% B1, ..., Bm". %% %% For simplicity, the `Guard' argument may also be any %% of the following: %%
    %%
  • An empty list `[]'. This is equivalent to passing %% `none'.
  • %%
  • A nonempty list `[E1, ..., Ej]' of syntax trees. %% This is equivalent to passing `conjunction([E1, ..., %% Ej])'.
  • %%
  • A nonempty list of lists of syntax trees `[[E1_1, ..., %% E1_k1], ..., [Ej_1, ..., Ej_kj]]', which is equivalent %% to passing `disjunction([conjunction([E1_1, ..., %% E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])])'.
  • %%
%% %% @see clause/2 %% @see clause_patterns/1 %% @see clause_guard/1 %% @see clause_body/1 -record(clause, {patterns :: [syntaxTree()], guard :: guard(), body :: [syntaxTree()]}). %% type(Node) = clause %% data(Node) = #clause{patterns :: Patterns, guard :: Guard, %% body :: Body} %% %% Patterns = [syntaxTree()] %% Guard = syntaxTree() | none %% Body = [syntaxTree()] %% %% `erl_parse' representation: %% %% {clause, Pos, Patterns, Guard, Body} %% %% Patterns = [erl_parse()] %% Guard = [[erl_parse()]] | [erl_parse()] %% Body = [erl_parse()] \ [] %% %% Taken out of context, if `Patterns' is `[P1, ..., Pn]' and %% `Body' is `[B1, ..., Bm]', this represents `(, ..., ) -> %% , ..., ' if `Guard' is `[]', or otherwise `(, ..., %% ) when -> ', where `G' is `, ..., ; %% ...; , ..., ', if `Guard' is a list of lists %% `[[E1_1, ..., E1_k1], ..., [Ej_1, ..., Ej_kj]]'. In older %% versions, `Guard' was simply a list `[E1, ..., En]' of parse %% trees, which is equivalent to the new form `[[E1, ..., En]]'. -spec clause([syntaxTree()], guard(), [syntaxTree()]) -> syntaxTree(). clause(Patterns, Guard, Body) -> Guard1 = case Guard of [] -> none; [X | _] when is_list(X) -> disjunction(conjunction_list(Guard)); [_ | _] -> %% Handle older forms also. conjunction(Guard); _ -> %% This should be `none' or a syntax tree. Guard end, tree(clause, #clause{patterns = Patterns, guard = Guard1, body = Body}). conjunction_list([L | Ls]) -> [conjunction(L) | conjunction_list(Ls)]; conjunction_list([]) -> []. revert_clause(Node) -> Pos = get_pos(Node), Guard = case clause_guard(Node) of none -> []; E -> case type(E) of disjunction -> revert_clause_disjunction(E); conjunction -> %% Only the top level expression is %% unfolded here; no recursion. [conjunction_body(E)]; _ -> [[E]] % a single expression end end, {clause, Pos, clause_patterns(Node), Guard, clause_body(Node)}. revert_clause_disjunction(D) -> %% We handle conjunctions within a disjunction, but only at %% the top level; no recursion. [case type(E) of conjunction -> conjunction_body(E); _ -> [E] end || E <- disjunction_body(D)]. revert_try_clause(Node) -> fold_try_clause(revert_clause(Node)). fold_try_clause({clause, Pos, [P], Guard, Body}) -> P1 = case type(P) of class_qualifier -> {tuple, Pos, [class_qualifier_argument(P), class_qualifier_body(P), {var, Pos, '_'}]}; _ -> {tuple, Pos, [{atom, Pos, throw}, P, {var, Pos, '_'}]} end, {clause, Pos, [P1], Guard, Body}. unfold_try_clauses(Cs) -> [unfold_try_clause(C) || C <- Cs]. unfold_try_clause({clause, Pos, [{tuple, _, [{atom, _, throw}, V, _]}], Guard, Body}) -> {clause, Pos, [V], Guard, Body}; unfold_try_clause({clause, Pos, [{tuple, _, [C, V, _]}], Guard, Body}) -> {clause, Pos, [class_qualifier(C, V)], Guard, Body}. %% ===================================================================== %% @doc Returns the list of pattern subtrees of a `clause' node. %% %% @see clause/3 -spec clause_patterns(syntaxTree()) -> [syntaxTree()]. clause_patterns(Node) -> case unwrap(Node) of {clause, _, Patterns, _, _} -> Patterns; Node1 -> (data(Node1))#clause.patterns end. %% ===================================================================== %% @doc Returns the guard subtree of a `clause' node, if %% any. If `Node' represents "(P1, ..., %% Pn) when Guard -> B1, ..., %% Bm", `Guard' is returned. Otherwise, the %% result is `none'. %% %% @see clause/3 -spec clause_guard(syntaxTree()) -> 'none' | syntaxTree(). clause_guard(Node) -> case unwrap(Node) of {clause, _, _, Guard, _} -> case Guard of [] -> none; [L | _] when is_list(L) -> disjunction(conjunction_list(Guard)); [_ | _] -> conjunction(Guard) end; Node1 -> (data(Node1))#clause.guard end. %% ===================================================================== %% @doc Return the list of body subtrees of a `clause' node. %% %% @see clause/3 -spec clause_body(syntaxTree()) -> [syntaxTree()]. clause_body(Node) -> case unwrap(Node) of {clause, _, _, _, Body} -> Body; Node1 -> (data(Node1))#clause.body end. %% ===================================================================== %% @doc Creates an abstract disjunction. If `List' is %% `[E1, ..., En]', the result represents %% "E1; ...; En". %% %% @see disjunction_body/1 %% @see conjunction/1 %% type(Node) = disjunction %% data(Node) = [syntaxTree()] -spec disjunction([syntaxTree()]) -> syntaxTree(). disjunction(Tests) -> tree(disjunction, Tests). %% ===================================================================== %% @doc Returns the list of body subtrees of a %% `disjunction' node. %% %% @see disjunction/1 -spec disjunction_body(syntaxTree()) -> [syntaxTree()]. disjunction_body(Node) -> data(Node). %% ===================================================================== %% @doc Creates an abstract conjunction. If `List' is %% `[E1, ..., En]', the result represents %% "E1, ..., En". %% %% @see conjunction_body/1 %% @see disjunction/1 %% type(Node) = conjunction %% data(Node) = [syntaxTree()] -spec conjunction([syntaxTree()]) -> syntaxTree(). conjunction(Tests) -> tree(conjunction, Tests). %% ===================================================================== %% @doc Returns the list of body subtrees of a %% `conjunction' node. %% %% @see conjunction/1 -spec conjunction_body(syntaxTree()) -> [syntaxTree()]. conjunction_body(Node) -> data(Node). %% ===================================================================== %% @doc Creates an abstract catch-expression. The result represents %% "catch Expr". %% %% @see catch_expr_body/1 %% type(Node) = catch_expr %% data(Node) = syntaxTree() %% %% `erl_parse' representation: %% %% {'catch', Pos, Expr} %% %% Expr = erl_parse() -spec catch_expr(syntaxTree()) -> syntaxTree(). catch_expr(Expr) -> tree(catch_expr, Expr). revert_catch_expr(Node) -> Pos = get_pos(Node), Expr = catch_expr_body(Node), {'catch', Pos, Expr}. %% ===================================================================== %% @doc Returns the body subtree of a `catch_expr' node. %% %% @see catch_expr/1 -spec catch_expr_body(syntaxTree()) -> syntaxTree(). catch_expr_body(Node) -> case unwrap(Node) of {'catch', _, Expr} -> Expr; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Creates an abstract match-expression. The result represents %% "Pattern = Body". %% %% @see match_expr_pattern/1 %% @see match_expr_body/1 -record(match_expr, {pattern :: syntaxTree(), body :: syntaxTree()}). %% type(Node) = match_expr %% data(Node) = #match_expr{pattern :: Pattern, body :: Body} %% %% Pattern = Body = syntaxTree() %% %% `erl_parse' representation: %% %% {match, Pos, Pattern, Body} %% %% Pattern = Body = erl_parse() -spec match_expr(syntaxTree(), syntaxTree()) -> syntaxTree(). match_expr(Pattern, Body) -> tree(match_expr, #match_expr{pattern = Pattern, body = Body}). revert_match_expr(Node) -> Pos = get_pos(Node), Pattern = match_expr_pattern(Node), Body = match_expr_body(Node), {match, Pos, Pattern, Body}. %% ===================================================================== %% @doc Returns the pattern subtree of a `match_expr' node. %% %% @see match_expr/2 -spec match_expr_pattern(syntaxTree()) -> syntaxTree(). match_expr_pattern(Node) -> case unwrap(Node) of {match, _, Pattern, _} -> Pattern; Node1 -> (data(Node1))#match_expr.pattern end. %% ===================================================================== %% @doc Returns the body subtree of a `match_expr' node. %% %% @see match_expr/2 -spec match_expr_body(syntaxTree()) -> syntaxTree(). match_expr_body(Node) -> case unwrap(Node) of {match, _, _, Body} -> Body; Node1 -> (data(Node1))#match_expr.body end. %% ===================================================================== %% @doc Creates an abstract operator. The name of the operator is the %% character sequence represented by `Name'. This is %% analogous to the print name of an atom, but an operator is never %% written within single-quotes; e.g., the result of %% `operator('++')' represents "`++'" rather %% than "`'++''". %% %% @see operator_name/1 %% @see operator_literal/1 %% @see atom/1 %% type(Node) = operator %% data(Node) = atom() -spec operator(atom() | string()) -> syntaxTree(). operator(Name) when is_atom(Name) -> tree(operator, Name); operator(Name) -> tree(operator, list_to_atom(Name)). %% ===================================================================== %% @doc Returns the name of an `operator' node. Note that %% the name is returned as an atom. %% %% @see operator/1 -spec operator_name(syntaxTree()) -> atom(). operator_name(Node) -> data(Node). %% ===================================================================== %% @doc Returns the literal string represented by an %% `operator' node. This is simply the operator name as a string. %% %% @see operator/1 -spec operator_literal(syntaxTree()) -> string(). operator_literal(Node) -> atom_to_list(operator_name(Node)). %% ===================================================================== %% @doc Creates an abstract infix operator expression. The result %% represents "Left Operator %% Right". %% %% @see infix_expr_left/1 %% @see infix_expr_right/1 %% @see infix_expr_operator/1 %% @see prefix_expr/2 -record(infix_expr, {operator :: syntaxTree(), left :: syntaxTree(), right :: syntaxTree()}). %% type(Node) = infix_expr %% data(Node) = #infix_expr{left :: Left, operator :: Operator, %% right :: Right} %% %% Left = Operator = Right = syntaxTree() %% %% `erl_parse' representation: %% %% {op, Pos, Operator, Left, Right} %% %% Operator = atom() %% Left = Right = erl_parse() -spec infix_expr(syntaxTree(), syntaxTree(), syntaxTree()) -> syntaxTree(). infix_expr(Left, Operator, Right) -> tree(infix_expr, #infix_expr{operator = Operator, left = Left, right = Right}). revert_infix_expr(Node) -> Pos = get_pos(Node), Operator = infix_expr_operator(Node), Left = infix_expr_left(Node), Right = infix_expr_right(Node), case type(Operator) of operator -> %% Note that the operator itself is not revertible out %% of context. {op, Pos, operator_name(Operator), Left, Right}; _ -> Node end. %% ===================================================================== %% @doc Returns the left argument subtree of an %% `infix_expr' node. %% %% @see infix_expr/3 -spec infix_expr_left(syntaxTree()) -> syntaxTree(). infix_expr_left(Node) -> case unwrap(Node) of {op, _, _, Left, _} -> Left; Node1 -> (data(Node1))#infix_expr.left end. %% ===================================================================== %% @doc Returns the operator subtree of an `infix_expr' node. %% %% @see infix_expr/3 -spec infix_expr_operator(syntaxTree()) -> syntaxTree(). infix_expr_operator(Node) -> case unwrap(Node) of {op, Pos, Operator, _, _} -> set_pos(operator(Operator), Pos); Node1 -> (data(Node1))#infix_expr.operator end. %% ===================================================================== %% @doc Returns the right argument subtree of an %% `infix_expr' node. %% %% @see infix_expr/3 -spec infix_expr_right(syntaxTree()) -> syntaxTree(). infix_expr_right(Node) -> case unwrap(Node) of {op, _, _, _, Right} -> Right; Node1 -> (data(Node1))#infix_expr.right end. %% ===================================================================== %% @doc Creates an abstract prefix operator expression. The result %% represents "Operator Argument". %% %% @see prefix_expr_argument/1 %% @see prefix_expr_operator/1 %% @see infix_expr/3 -record(prefix_expr, {operator :: syntaxTree(), argument :: syntaxTree()}). %% type(Node) = prefix_expr %% data(Node) = #prefix_expr{operator :: Operator, %% argument :: Argument} %% %% Operator = Argument = syntaxTree() %% %% `erl_parse' representation: %% %% {op, Pos, Operator, Arg} %% %% Operator = atom() %% Argument = erl_parse() -spec prefix_expr(syntaxTree(), syntaxTree()) -> syntaxTree(). prefix_expr(Operator, Argument) -> tree(prefix_expr, #prefix_expr{operator = Operator, argument = Argument}). revert_prefix_expr(Node) -> Pos = get_pos(Node), Operator = prefix_expr_operator(Node), Argument = prefix_expr_argument(Node), case type(Operator) of operator -> %% Note that the operator itself is not revertible out %% of context. {op, Pos, operator_name(Operator), Argument}; _ -> Node end. %% ===================================================================== %% @doc Returns the operator subtree of a `prefix_expr' node. %% %% @see prefix_expr/2 -spec prefix_expr_operator(syntaxTree()) -> syntaxTree(). prefix_expr_operator(Node) -> case unwrap(Node) of {op, Pos, Operator, _} -> set_pos(operator(Operator), Pos); Node1 -> (data(Node1))#prefix_expr.operator end. %% ===================================================================== %% @doc Returns the argument subtree of a `prefix_expr' node. %% %% @see prefix_expr/2 -spec prefix_expr_argument(syntaxTree()) -> syntaxTree(). prefix_expr_argument(Node) -> case unwrap(Node) of {op, _, _, Argument} -> Argument; Node1 -> (data(Node1))#prefix_expr.argument end. %% ===================================================================== %% @equiv record_field(Name, none) -spec record_field(syntaxTree()) -> syntaxTree(). record_field(Name) -> record_field(Name, none). %% ===================================================================== %% @doc Creates an abstract record field specification. If %% `Value' is `none', the result represents %% simply "Name", otherwise it represents %% "Name = Value". %% %% @see record_field_name/1 %% @see record_field_value/1 %% @see record_expr/3 -record(record_field, {name :: syntaxTree(), value :: 'none' | syntaxTree()}). %% type(Node) = record_field %% data(Node) = #record_field{name :: Name, value :: Value} %% %% Name = Value = syntaxTree() -spec record_field(syntaxTree(), 'none' | syntaxTree()) -> syntaxTree(). record_field(Name, Value) -> tree(record_field, #record_field{name = Name, value = Value}). %% ===================================================================== %% @doc Returns the name subtree of a `record_field' node. %% %% @see record_field/2 -spec record_field_name(syntaxTree()) -> syntaxTree(). record_field_name(Node) -> (data(Node))#record_field.name. %% ===================================================================== %% @doc Returns the value subtree of a `record_field' node, %% if any. If `Node' represents %% "Name", `none' is %% returned. Otherwise, if `Node' represents %% "Name = Value", `Value' %% is returned. %% %% @see record_field/2 -spec record_field_value(syntaxTree()) -> 'none' | syntaxTree(). record_field_value(Node) -> (data(Node))#record_field.value. %% ===================================================================== %% @doc Creates an abstract record field index expression. The result %% represents "#Type.Field". %% %% (Note: the function name `record_index/2' is reserved %% by the Erlang compiler, which is why that name could not be used %% for this constructor.) %% %% @see record_index_expr_type/1 %% @see record_index_expr_field/1 %% @see record_expr/3 -record(record_index_expr, {type :: syntaxTree(), field :: syntaxTree()}). %% type(Node) = record_index_expr %% data(Node) = #record_index_expr{type :: Type, field :: Field} %% %% Type = Field = syntaxTree() %% %% `erl_parse' representation: %% %% {record_index, Pos, Type, Field} %% %% Type = atom() %% Field = erl_parse() -spec record_index_expr(syntaxTree(), syntaxTree()) -> syntaxTree(). record_index_expr(Type, Field) -> tree(record_index_expr, #record_index_expr{type = Type, field = Field}). revert_record_index_expr(Node) -> Pos = get_pos(Node), Type = record_index_expr_type(Node), Field = record_index_expr_field(Node), case type(Type) of atom -> {record_index, Pos, concrete(Type), Field}; _ -> Node end. %% ===================================================================== %% @doc Returns the type subtree of a `record_index_expr' node. %% %% @see record_index_expr/2 -spec record_index_expr_type(syntaxTree()) -> syntaxTree(). record_index_expr_type(Node) -> case unwrap(Node) of {record_index, Pos, Type, _} -> set_pos(atom(Type), Pos); Node1 -> (data(Node1))#record_index_expr.type end. %% ===================================================================== %% @doc Returns the field subtree of a `record_index_expr' node. %% %% @see record_index_expr/2 -spec record_index_expr_field(syntaxTree()) -> syntaxTree(). record_index_expr_field(Node) -> case unwrap(Node) of {record_index, _, _, Field} -> Field; Node1 -> (data(Node1))#record_index_expr.field end. %% ===================================================================== %% @equiv record_access(Argument, none, Field) -spec record_access(syntaxTree(), syntaxTree()) -> syntaxTree(). record_access(Argument, Field) -> record_access(Argument, none, Field). %% ===================================================================== %% @doc Creates an abstract record field access expression. If %% `Type' is not `none', the result represents %% "Argument#Type.Field". %% %% If `Type' is `none', the result represents %% "Argument.Field". This is a special %% form only allowed within Mnemosyne queries. %% %% @see record_access/2 %% @see record_access_argument/1 %% @see record_access_type/1 %% @see record_access_field/1 %% @see record_expr/3 -record(record_access, {argument :: syntaxTree(), type :: 'none' | syntaxTree(), field :: syntaxTree()}). %% type(Node) = record_access %% data(Node) = #record_access{argument :: Argument, type :: Type, %% field :: Field} %% %% Argument = Field = syntaxTree() %% Type = none | syntaxTree() %% %% `erl_parse' representation: %% %% {record_field, Pos, Argument, Type, Field} %% {record_field, Pos, Argument, Field} %% %% Argument = Field = erl_parse() %% Type = atom() -spec record_access(syntaxTree(), 'none' | syntaxTree(), syntaxTree()) -> syntaxTree(). record_access(Argument, Type, Field) -> tree(record_access,#record_access{argument = Argument, type = Type, field = Field}). revert_record_access(Node) -> Pos = get_pos(Node), Argument = record_access_argument(Node), Type = record_access_type(Node), Field = record_access_field(Node), if Type =:= none -> {record_field, Pos, Argument, Field}; true -> case type(Type) of atom -> {record_field, Pos, Argument, concrete(Type), Field}; _ -> Node end end. %% ===================================================================== %% @doc Returns the argument subtree of a `record_access' node. %% %% @see record_access/3 -spec record_access_argument(syntaxTree()) -> syntaxTree(). record_access_argument(Node) -> case unwrap(Node) of {record_field, _, Argument, _} -> Argument; {record_field, _, Argument, _, _} -> Argument; Node1 -> (data(Node1))#record_access.argument end. %% ===================================================================== %% @doc Returns the type subtree of a `record_access' node, %% if any. If `Node' represents %% "Argument.Field", `none' %% is returned, otherwise if `Node' represents %% "Argument#Type.Field", %% `Type' is returned. %% %% @see record_access/3 -spec record_access_type(syntaxTree()) -> 'none' | syntaxTree(). record_access_type(Node) -> case unwrap(Node) of {record_field, _, _, _} -> none; {record_field, Pos, _, Type, _} -> set_pos(atom(Type), Pos); Node1 -> (data(Node1))#record_access.type end. %% ===================================================================== %% @doc Returns the field subtree of a `record_access' node. %% %% @see record_access/3 -spec record_access_field(syntaxTree()) -> syntaxTree(). record_access_field(Node) -> case unwrap(Node) of {record_field, _, _, Field} -> Field; {record_field, _, _, _, Field} -> Field; Node1 -> (data(Node1))#record_access.field end. %% ===================================================================== %% @equiv record_expr(none, Type, Fields) -spec record_expr(syntaxTree(), [syntaxTree()]) -> syntaxTree(). record_expr(Type, Fields) -> record_expr(none, Type, Fields). %% ===================================================================== %% @doc Creates an abstract record expression. If `Fields' is %% `[F1, ..., Fn]', then if `Argument' is %% `none', the result represents %% "#Type{F1, ..., Fn}", %% otherwise it represents %% "Argument#Type{F1, ..., %% Fn}". %% %% @see record_expr/2 %% @see record_expr_argument/1 %% @see record_expr_fields/1 %% @see record_expr_type/1 %% @see record_field/2 %% @see record_index_expr/2 %% @see record_access/3 -record(record_expr, {argument :: 'none' | syntaxTree(), type :: syntaxTree(), fields :: [syntaxTree()]}). %% type(Node) = record_expr %% data(Node) = #record_expr{argument :: Argument, type :: Type, %% fields :: Fields} %% %% Argument = none | syntaxTree() %% Type = syntaxTree %% Fields = [syntaxTree()] %% %% `erl_parse' representation: %% %% {record, Pos, Type, Fields} %% {record, Pos, Argument, Type, Fields} %% %% Argument = erl_parse() %% Type = atom() %% Fields = [Entry] %% Entry = {record_field, Pos, Field, Value} %% | {record_field, Pos, Field} %% Field = Value = erl_parse() -spec record_expr('none' | syntaxTree(), syntaxTree(), [syntaxTree()]) -> syntaxTree(). record_expr(Argument, Type, Fields) -> tree(record_expr, #record_expr{argument = Argument, type = Type, fields = Fields}). revert_record_expr(Node) -> Pos = get_pos(Node), Argument = record_expr_argument(Node), Type = record_expr_type(Node), Fields = record_expr_fields(Node), case type(Type) of atom -> T = concrete(Type), Fs = fold_record_fields(Fields), case Argument of none -> {record, Pos, T, Fs}; _ -> {record, Pos, Argument, T, Fs} end; _ -> Node end. %% ===================================================================== %% @doc Returns the argument subtree of a `record_expr' node, %% if any. If `Node' represents %% "#Type{...}", `none' is returned. %% Otherwise, if `Node' represents %% "Argument#Type{...}", %% `Argument' is returned. %% %% @see record_expr/3 -spec record_expr_argument(syntaxTree()) -> 'none' | syntaxTree(). record_expr_argument(Node) -> case unwrap(Node) of {record, _, _, _} -> none; {record, _, Argument, _, _} -> Argument; Node1 -> (data(Node1))#record_expr.argument end. %% ===================================================================== %% @doc Returns the type subtree of a `record_expr' node. %% %% @see record_expr/3 -spec record_expr_type(syntaxTree()) -> syntaxTree(). record_expr_type(Node) -> case unwrap(Node) of {record, Pos, Type, _} -> set_pos(atom(Type), Pos); {record, Pos, _, Type, _} -> set_pos(atom(Type), Pos); Node1 -> (data(Node1))#record_expr.type end. %% ===================================================================== %% @doc Returns the list of field subtrees of a %% `record_expr' node. %% %% @see record_expr/3 -spec record_expr_fields(syntaxTree()) -> [syntaxTree()]. record_expr_fields(Node) -> case unwrap(Node) of {record, _, _, Fields} -> unfold_record_fields(Fields); {record, _, _, _, Fields} -> unfold_record_fields(Fields); Node1 -> (data(Node1))#record_expr.fields end. %% ===================================================================== %% @doc Creates an abstract function application expression. If %% `Module' is `none', this is call is equivalent %% to `application(Function, Arguments)', otherwise it is %% equivalent to `application(module_qualifier(Module, Function), %% Arguments)'. %% %% (This is a utility function.) %% %% @see application/2 %% @see module_qualifier/2 -spec application('none' | syntaxTree(), syntaxTree(), [syntaxTree()]) -> syntaxTree(). application(none, Name, Arguments) -> application(Name, Arguments); application(Module, Name, Arguments) -> application(module_qualifier(Module, Name), Arguments). %% ===================================================================== %% @doc Creates an abstract function application expression. If %% `Arguments' is `[A1, ..., An]', the result %% represents "Operator(A1, ..., %% An)". %% %% @see application_operator/1 %% @see application_arguments/1 %% @see application/3 -record(application, {operator :: syntaxTree(), arguments :: [syntaxTree()]}). %% type(Node) = application %% data(Node) = #application{operator :: Operator, %% arguments :: Arguments} %% %% Operator = syntaxTree() %% Arguments = [syntaxTree()] %% %% `erl_parse' representation: %% %% {call, Pos, Fun, Args} %% %% Operator = erl_parse() %% Arguments = [erl_parse()] -spec application(syntaxTree(), [syntaxTree()]) -> syntaxTree(). application(Operator, Arguments) -> tree(application, #application{operator = Operator, arguments = Arguments}). revert_application(Node) -> Pos = get_pos(Node), Operator = application_operator(Node), Arguments = application_arguments(Node), {call, Pos, Operator, Arguments}. %% ===================================================================== %% @doc Returns the operator subtree of an `application' node. %% %% Note: if `Node' represents %% "M:F(...)", then the result is the %% subtree representing "M:F". %% %% @see application/2 %% @see module_qualifier/2 -spec application_operator(syntaxTree()) -> syntaxTree(). application_operator(Node) -> case unwrap(Node) of {call, _, Operator, _} -> Operator; Node1 -> (data(Node1))#application.operator end. %% ===================================================================== %% @doc Returns the list of argument subtrees of an %% `application' node. %% %% @see application/2 -spec application_arguments(syntaxTree()) -> [syntaxTree()]. application_arguments(Node) -> case unwrap(Node) of {call, _, _, Arguments} -> Arguments; Node1 -> (data(Node1))#application.arguments end. %% ===================================================================== %% @doc Creates an abstract list comprehension. If `Body' is %% `[E1, ..., En]', the result represents %% "[Template || E1, ..., En]". %% %% @see list_comp_template/1 %% @see list_comp_body/1 %% @see generator/2 -record(list_comp, {template :: syntaxTree(), body :: [syntaxTree()]}). %% type(Node) = list_comp %% data(Node) = #list_comp{template :: Template, body :: Body} %% %% Template = Node = syntaxTree() %% Body = [syntaxTree()] %% %% `erl_parse' representation: %% %% {lc, Pos, Template, Body} %% %% Template = erl_parse() %% Body = [erl_parse()] \ [] -spec list_comp(syntaxTree(), [syntaxTree()]) -> syntaxTree(). list_comp(Template, Body) -> tree(list_comp, #list_comp{template = Template, body = Body}). revert_list_comp(Node) -> Pos = get_pos(Node), Template = list_comp_template(Node), Body = list_comp_body(Node), {lc, Pos, Template, Body}. %% ===================================================================== %% @doc Returns the template subtree of a `list_comp' node. %% %% @see list_comp/2 -spec list_comp_template(syntaxTree()) -> syntaxTree(). list_comp_template(Node) -> case unwrap(Node) of {lc, _, Template, _} -> Template; Node1 -> (data(Node1))#list_comp.template end. %% ===================================================================== %% @doc Returns the list of body subtrees of a `list_comp' node. %% %% @see list_comp/2 -spec list_comp_body(syntaxTree()) -> [syntaxTree()]. list_comp_body(Node) -> case unwrap(Node) of {lc, _, _, Body} -> Body; Node1 -> (data(Node1))#list_comp.body end. %% ===================================================================== %% @doc Creates an abstract binary comprehension. If `Body' is %% `[E1, ..., En]', the result represents %% "<<Template || E1, ..., En>>". %% %% @see binary_comp_template/1 %% @see binary_comp_body/1 %% @see generator/2 -record(binary_comp, {template :: syntaxTree(), body :: [syntaxTree()]}). %% type(Node) = binary_comp %% data(Node) = #binary_comp{template :: Template, body :: Body} %% %% Template = Node = syntaxTree() %% Body = [syntaxTree()] %% %% `erl_parse' representation: %% %% {bc, Pos, Template, Body} %% %% Template = erl_parse() %% Body = [erl_parse()] \ [] -spec binary_comp(syntaxTree(), [syntaxTree()]) -> syntaxTree(). binary_comp(Template, Body) -> tree(binary_comp, #binary_comp{template = Template, body = Body}). revert_binary_comp(Node) -> Pos = get_pos(Node), Template = binary_comp_template(Node), Body = binary_comp_body(Node), {bc, Pos, Template, Body}. %% ===================================================================== %% @doc Returns the template subtree of a `binary_comp' node. %% %% @see binary_comp/2 -spec binary_comp_template(syntaxTree()) -> syntaxTree(). binary_comp_template(Node) -> case unwrap(Node) of {bc, _, Template, _} -> Template; Node1 -> (data(Node1))#binary_comp.template end. %% ===================================================================== %% @doc Returns the list of body subtrees of a `binary_comp' node. %% %% @see binary_comp/2 -spec binary_comp_body(syntaxTree()) -> [syntaxTree()]. binary_comp_body(Node) -> case unwrap(Node) of {bc, _, _, Body} -> Body; Node1 -> (data(Node1))#binary_comp.body end. %% ===================================================================== %% @doc Creates an abstract Mnemosyne rule. If `Clauses' is %% `[C1, ..., Cn]', the results represents %% "Name C1; ...; Name %% Cn.". More exactly, if each `Ci' %% represents "(Pi1, ..., Pim) Gi -> %% Bi", then the result represents %% "Name(P11, ..., P1m) G1 :- %% B1; ...; Name(Pn1, ..., Pnm) %% Gn :- Bn.". Rules are source code forms. %% %% @see rule_name/1 %% @see rule_clauses/1 %% @see rule_arity/1 %% @see is_form/1 %% @see function/2 -record(rule, {name :: syntaxTree(), clauses :: [syntaxTree()]}). %% type(Node) = rule %% data(Node) = #rule{name :: Name, clauses :: Clauses} %% %% Name = syntaxTree() %% Clauses = [syntaxTree()] %% %% (See `function' for notes on why the arity is not stored.) %% %% `erl_parse' representation: %% %% {rule, Pos, Name, Arity, Clauses} %% %% Name = atom() %% Arity = integer() %% Clauses = [Clause] \ [] %% Clause = {clause, ...} %% %% where the number of patterns in each clause should be equal to %% the integer `Arity'; see `clause' for documentation on %% `erl_parse' clauses. -spec rule(syntaxTree(), [syntaxTree()]) -> syntaxTree(). rule(Name, Clauses) -> tree(rule, #rule{name = Name, clauses = Clauses}). revert_rule(Node) -> Name = rule_name(Node), Clauses = [revert_clause(C) || C <- rule_clauses(Node)], Pos = get_pos(Node), case type(Name) of atom -> A = rule_arity(Node), {rule, Pos, concrete(Name), A, Clauses}; _ -> Node end. %% ===================================================================== %% @doc Returns the name subtree of a `rule' node. %% %% @see rule/2 -spec rule_name(syntaxTree()) -> syntaxTree(). rule_name(Node) -> case unwrap(Node) of {rule, Pos, Name, _, _} -> set_pos(atom(Name), Pos); Node1 -> (data(Node1))#rule.name end. %% ===================================================================== %% @doc Returns the list of clause subtrees of a `rule' node. %% %% @see rule/2 -spec rule_clauses(syntaxTree()) -> [syntaxTree()]. rule_clauses(Node) -> case unwrap(Node) of {rule, _, _, _, Clauses} -> Clauses; Node1 -> (data(Node1))#rule.clauses end. %% ===================================================================== %% @doc Returns the arity of a `rule' node. The result is the %% number of parameter patterns in the first clause of the rule; %% subsequent clauses are ignored. %% %% An exception is thrown if `rule_clauses(Node)' returns %% an empty list, or if the first element of that list is not a syntax %% tree `C' of type `clause' such that %% `clause_patterns(C)' is a nonempty list. %% %% @see rule/2 %% @see rule_clauses/1 %% @see clause/3 %% @see clause_patterns/1 -spec rule_arity(syntaxTree()) -> arity(). rule_arity(Node) -> %% Note that this never accesses the arity field of %% `erl_parse' rule nodes. length(clause_patterns(hd(rule_clauses(Node)))). %% ===================================================================== %% @doc Creates an abstract generator. The result represents %% "Pattern <- Body". %% %% @see generator_pattern/1 %% @see generator_body/1 %% @see list_comp/2 %% @see binary_comp/2 -record(generator, {pattern :: syntaxTree(), body :: syntaxTree()}). %% type(Node) = generator %% data(Node) = #generator{pattern :: Pattern, body :: Body} %% %% Pattern = Argument = syntaxTree() %% %% `erl_parse' representation: %% %% {generate, Pos, Pattern, Body} %% %% Pattern = Body = erl_parse() -spec generator(syntaxTree(), syntaxTree()) -> syntaxTree(). generator(Pattern, Body) -> tree(generator, #generator{pattern = Pattern, body = Body}). revert_generator(Node) -> Pos = get_pos(Node), Pattern = generator_pattern(Node), Body = generator_body(Node), {generate, Pos, Pattern, Body}. %% ===================================================================== %% @doc Returns the pattern subtree of a `generator' node. %% %% @see generator/2 -spec generator_pattern(syntaxTree()) -> syntaxTree(). generator_pattern(Node) -> case unwrap(Node) of {generate, _, Pattern, _} -> Pattern; Node1 -> (data(Node1))#generator.pattern end. %% ===================================================================== %% @doc Returns the body subtree of a `generator' node. %% %% @see generator/2 -spec generator_body(syntaxTree()) -> syntaxTree(). generator_body(Node) -> case unwrap(Node) of {generate, _, _, Body} -> Body; Node1 -> (data(Node1))#generator.body end. %% ===================================================================== %% @doc Creates an abstract binary_generator. The result represents %% "Pattern <- Body". %% %% @see binary_generator_pattern/1 %% @see binary_generator_body/1 %% @see list_comp/2 %% @see binary_comp/2 -record(binary_generator, {pattern :: syntaxTree(), body :: syntaxTree()}). %% type(Node) = binary_generator %% data(Node) = #binary_generator{pattern :: Pattern, body :: Body} %% %% Pattern = Argument = syntaxTree() %% %% `erl_parse' representation: %% %% {b_generate, Pos, Pattern, Body} %% %% Pattern = Body = erl_parse() -spec binary_generator(syntaxTree(), syntaxTree()) -> syntaxTree(). binary_generator(Pattern, Body) -> tree(binary_generator, #binary_generator{pattern = Pattern, body = Body}). revert_binary_generator(Node) -> Pos = get_pos(Node), Pattern = binary_generator_pattern(Node), Body = binary_generator_body(Node), {b_generate, Pos, Pattern, Body}. %% ===================================================================== %% @doc Returns the pattern subtree of a `generator' node. %% %% @see binary_generator/2 -spec binary_generator_pattern(syntaxTree()) -> syntaxTree(). binary_generator_pattern(Node) -> case unwrap(Node) of {b_generate, _, Pattern, _} -> Pattern; Node1 -> (data(Node1))#binary_generator.pattern end. %% ===================================================================== %% @doc Returns the body subtree of a `generator' node. %% %% @see binary_generator/2 -spec binary_generator_body(syntaxTree()) -> syntaxTree(). binary_generator_body(Node) -> case unwrap(Node) of {b_generate, _, _, Body} -> Body; Node1 -> (data(Node1))#binary_generator.body end. %% ===================================================================== %% @doc Creates an abstract block expression. If `Body' is %% `[B1, ..., Bn]', the result represents "begin %% B1, ..., Bn end". %% %% @see block_expr_body/1 %% type(Node) = block_expr %% data(Node) = Body %% %% Body = [syntaxTree()] %% %% `erl_parse' representation: %% %% {block, Pos, Body} %% %% Body = [erl_parse()] \ [] -spec block_expr([syntaxTree()]) -> syntaxTree(). block_expr(Body) -> tree(block_expr, Body). revert_block_expr(Node) -> Pos = get_pos(Node), Body = block_expr_body(Node), {block, Pos, Body}. %% ===================================================================== %% @doc Returns the list of body subtrees of a `block_expr' node. %% %% @see block_expr/1 -spec block_expr_body(syntaxTree()) -> [syntaxTree()]. block_expr_body(Node) -> case unwrap(Node) of {block, _, Body} -> Body; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Creates an abstract if-expression. If `Clauses' is %% `[C1, ..., Cn]', the result represents "if %% C1; ...; Cn end". More exactly, if each %% `Ci' represents "() Gi -> %% Bi", then the result represents "if %% G1 -> B1; ...; Gn -> Bn %% end". %% %% @see if_expr_clauses/1 %% @see clause/3 %% @see case_expr/2 %% type(Node) = if_expr %% data(Node) = Clauses %% %% Clauses = [syntaxTree()] %% %% `erl_parse' representation: %% %% {'if', Pos, Clauses} %% %% Clauses = [Clause] \ [] %% Clause = {clause, ...} %% %% See `clause' for documentation on `erl_parse' clauses. -spec if_expr([syntaxTree()]) -> syntaxTree(). if_expr(Clauses) -> tree(if_expr, Clauses). revert_if_expr(Node) -> Pos = get_pos(Node), Clauses = [revert_clause(C) || C <- if_expr_clauses(Node)], {'if', Pos, Clauses}. %% ===================================================================== %% @doc Returns the list of clause subtrees of an `if_expr' node. %% %% @see if_expr/1 -spec if_expr_clauses(syntaxTree()) -> [syntaxTree()]. if_expr_clauses(Node) -> case unwrap(Node) of {'if', _, Clauses} -> Clauses; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Creates an abstract case-expression. If `Clauses' is %% `[C1, ..., Cn]', the result represents "case %% Argument of C1; ...; Cn end". More %% exactly, if each `Ci' represents "(Pi) %% Gi -> Bi", then the result represents %% "case Argument of P1 G1 -> %% B1; ...; Pn Gn -> Bn end". %% %% @see case_expr_clauses/1 %% @see case_expr_argument/1 %% @see clause/3 %% @see if_expr/1 %% @see cond_expr/1 -record(case_expr, {argument :: syntaxTree(), clauses :: [syntaxTree()]}). %% type(Node) = case_expr %% data(Node) = #case_expr{argument :: Argument, %% clauses :: Clauses} %% %% Argument = syntaxTree() %% Clauses = [syntaxTree()] %% %% `erl_parse' representation: %% %% {'case', Pos, Argument, Clauses} %% %% Argument = erl_parse() %% Clauses = [Clause] \ [] %% Clause = {clause, ...} %% %% See `clause' for documentation on `erl_parse' clauses. -spec case_expr(syntaxTree(), [syntaxTree()]) -> syntaxTree(). case_expr(Argument, Clauses) -> tree(case_expr, #case_expr{argument = Argument, clauses = Clauses}). revert_case_expr(Node) -> Pos = get_pos(Node), Argument = case_expr_argument(Node), Clauses = [revert_clause(C) || C <- case_expr_clauses(Node)], {'case', Pos, Argument, Clauses}. %% ===================================================================== %% @doc Returns the argument subtree of a `case_expr' node. %% %% @see case_expr/2 -spec case_expr_argument(syntaxTree()) -> syntaxTree(). case_expr_argument(Node) -> case unwrap(Node) of {'case', _, Argument, _} -> Argument; Node1 -> (data(Node1))#case_expr.argument end. %% ===================================================================== %% @doc Returns the list of clause subtrees of a `case_expr' node. %% %% @see case_expr/2 -spec case_expr_clauses(syntaxTree()) -> [syntaxTree()]. case_expr_clauses(Node) -> case unwrap(Node) of {'case', _, _, Clauses} -> Clauses; Node1 -> (data(Node1))#case_expr.clauses end. %% ===================================================================== %% @doc Creates an abstract cond-expression. If `Clauses' is %% `[C1, ..., Cn]', the result represents "cond %% C1; ...; Cn end". More exactly, if each %% `Ci' represents "() Ei -> %% Bi", then the result represents "cond %% E1 -> B1; ...; En -> Bn %% end". %% %% @see cond_expr_clauses/1 %% @see clause/3 %% @see case_expr/2 %% type(Node) = cond_expr %% data(Node) = Clauses %% %% Clauses = [syntaxTree()] %% %% `erl_parse' representation: %% %% {'cond', Pos, Clauses} %% %% Clauses = [Clause] \ [] %% Clause = {clause, ...} %% %% See `clause' for documentation on `erl_parse' clauses. -spec cond_expr([syntaxTree()]) -> syntaxTree(). cond_expr(Clauses) -> tree(cond_expr, Clauses). revert_cond_expr(Node) -> Pos = get_pos(Node), Clauses = [revert_clause(C) || C <- cond_expr_clauses(Node)], {'cond', Pos, Clauses}. %% ===================================================================== %% @doc Returns the list of clause subtrees of a `cond_expr' node. %% %% @see cond_expr/1 -spec cond_expr_clauses(syntaxTree()) -> [syntaxTree()]. cond_expr_clauses(Node) -> case unwrap(Node) of {'cond', _, Clauses} -> Clauses; Node1 -> data(Node1) end. %% ===================================================================== %% @equiv receive_expr(Clauses, none, []) -spec receive_expr([syntaxTree()]) -> syntaxTree(). receive_expr(Clauses) -> receive_expr(Clauses, none, []). %% ===================================================================== %% @doc Creates an abstract receive-expression. If `Timeout' %% is `none', the result represents "receive %% C1; ...; Cn end" (the `Action' %% argument is ignored). Otherwise, if `Clauses' is %% `[C1, ..., Cn]' and `Action' is `[A1, ..., %% Am]', the result represents "receive C1; ...; %% Cn after Timeout -> A1, ..., Am %% end". More exactly, if each `Ci' represents %% "(Pi) Gi -> Bi", then the %% result represents "receive P1 G1 -> %% B1; ...; Pn Gn -> Bn ... %% end". %% %% Note that in Erlang, a receive-expression must have at least one %% clause if no timeout part is specified. %% %% @see receive_expr_clauses/1 %% @see receive_expr_timeout/1 %% @see receive_expr_action/1 %% @see receive_expr/1 %% @see clause/3 %% @see case_expr/2 -record(receive_expr, {clauses :: [syntaxTree()], timeout :: 'none' | syntaxTree(), action :: [syntaxTree()]}). %% type(Node) = receive_expr %% data(Node) = #receive_expr{clauses :: Clauses, %% timeout :: Timeout, %% action :: Action} %% %% Clauses = [syntaxTree()] %% Timeout = none | syntaxTree() %% Action = [syntaxTree()] %% %% `erl_parse' representation: %% %% {'receive', Pos, Clauses} %% {'receive', Pos, Clauses, Timeout, Action} %% %% Clauses = [Clause] \ [] %% Clause = {clause, ...} %% Timeout = erl_parse() %% Action = [erl_parse()] \ [] %% %% See `clause' for documentation on `erl_parse' clauses. -spec receive_expr([syntaxTree()], 'none' | syntaxTree(), [syntaxTree()]) -> syntaxTree(). receive_expr(Clauses, Timeout, Action) -> %% If `Timeout' is `none', we always replace the actual %% `Action' argument with an empty list, since %% `receive_expr_action' should in that case return the empty %% list regardless. Action1 = case Timeout of none -> []; _ -> Action end, tree(receive_expr, #receive_expr{clauses = Clauses, timeout = Timeout, action = Action1}). revert_receive_expr(Node) -> Pos = get_pos(Node), Clauses = [revert_clause(C) || C <- receive_expr_clauses(Node)], Timeout = receive_expr_timeout(Node), Action = receive_expr_action(Node), case Timeout of none -> {'receive', Pos, Clauses}; _ -> {'receive', Pos, Clauses, Timeout, Action} end. %% ===================================================================== %% @doc Returns the list of clause subtrees of a %% `receive_expr' node. %% %% @see receive_expr/3 -spec receive_expr_clauses(syntaxTree()) -> [syntaxTree()]. receive_expr_clauses(Node) -> case unwrap(Node) of {'receive', _, Clauses} -> Clauses; {'receive', _, Clauses, _, _} -> Clauses; Node1 -> (data(Node1))#receive_expr.clauses end. %% ===================================================================== %% @doc Returns the timeout subtree of a `receive_expr' node, %% if any. If `Node' represents "receive C1; %% ...; Cn end", `none' is returned. %% Otherwise, if `Node' represents "receive %% C1; ...; Cn after Timeout -> ... end", %% `Timeout' is returned. %% %% @see receive_expr/3 -spec receive_expr_timeout(syntaxTree()) -> 'none' | syntaxTree(). receive_expr_timeout(Node) -> case unwrap(Node) of {'receive', _, _} -> none; {'receive', _, _, Timeout, _} -> Timeout; Node1 -> (data(Node1))#receive_expr.timeout end. %% ===================================================================== %% @doc Returns the list of action body subtrees of a %% `receive_expr' node. If `Node' represents %% "receive C1; ...; Cn end", this is the %% empty list. %% %% @see receive_expr/3 -spec receive_expr_action(syntaxTree()) -> [syntaxTree()]. receive_expr_action(Node) -> case unwrap(Node) of {'receive', _, _} -> []; {'receive', _, _, _, Action} -> Action; Node1 -> (data(Node1))#receive_expr.action end. %% ===================================================================== %% @equiv try_expr(Body, [], Handlers) -spec try_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree(). try_expr(Body, Handlers) -> try_expr(Body, [], Handlers). %% ===================================================================== %% @equiv try_expr(Body, Clauses, Handlers, []) -spec try_expr([syntaxTree()], [syntaxTree()], [syntaxTree()]) -> syntaxTree(). try_expr(Body, Clauses, Handlers) -> try_expr(Body, Clauses, Handlers, []). %% ===================================================================== %% @equiv try_expr(Body, [], [], After) -spec try_after_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree(). try_after_expr(Body, After) -> try_expr(Body, [], [], After). %% ===================================================================== %% @doc Creates an abstract try-expression. If `Body' is %% `[B1, ..., Bn]', `Clauses' is `[C1, ..., %% Cj]', `Handlers' is `[H1, ..., Hk]', and %% `After' is `[A1, ..., Am]', the result %% represents "try B1, ..., Bn of C1; %% ...; Cj catch H1; ...; Hk after %% A1, ..., Am end". More exactly, if each %% `Ci' represents "(CPi) CGi -> %% CBi", and each `Hi' represents %% "(HPi) HGi -> HBi", then the %% result represents "try B1, ..., Bn of %% CP1 CG1 -> CB1; ...; CPj %% CGj -> CBj catch HP1 HG1 -> %% HB1; ...; HPk HGk -> HBk after %% A1, ..., Am end"; see %% {@link case_expr/2}. If `Clauses' is the empty list, %% the `of ...' section is left out. If `After' is %% the empty list, the `after ...' section is left out. If %% `Handlers' is the empty list, and `After' is %% nonempty, the `catch ...' section is left out. %% %% @see try_expr_body/1 %% @see try_expr_clauses/1 %% @see try_expr_handlers/1 %% @see try_expr_after/1 %% @see try_expr/2 %% @see try_expr/3 %% @see try_after_expr/2 %% @see clause/3 %% @see class_qualifier/2 %% @see case_expr/2 -record(try_expr, {body :: [syntaxTree()], clauses :: [syntaxTree()], handlers :: [syntaxTree()], 'after' :: [syntaxTree()]}). %% type(Node) = try_expr %% data(Node) = #try_expr{body :: Body, %% clauses :: Clauses, %% handlers :: Clauses, %% after :: Body} %% %% Body = syntaxTree() %% Clauses = [syntaxTree()] %% %% `erl_parse' representation: %% %% {'try', Pos, Body, Clauses, Handlers, After} %% %% Body = [erl_parse()] %% Clauses = [Clause] %% Handlers = [Clause] \ [] %% Clause = {clause, ...} %% After = [erl_parse()] %% %% See `clause' for documentation on `erl_parse' clauses. -spec try_expr([syntaxTree()], [syntaxTree()], [syntaxTree()], [syntaxTree()]) -> syntaxTree(). try_expr(Body, Clauses, Handlers, After) -> tree(try_expr, #try_expr{body = Body, clauses = Clauses, handlers = Handlers, 'after' = After}). revert_try_expr(Node) -> Pos = get_pos(Node), Body = try_expr_body(Node), Clauses = [revert_clause(C) || C <- try_expr_clauses(Node)], Handlers = [revert_try_clause(C) || C <- try_expr_handlers(Node)], After = try_expr_after(Node), {'try', Pos, Body, Clauses, Handlers, After}. %% ===================================================================== %% @doc Returns the list of body subtrees of a `try_expr' node. %% %% @see try_expr/4 -spec try_expr_body(syntaxTree()) -> [syntaxTree()]. try_expr_body(Node) -> case unwrap(Node) of {'try', _, Body, _, _, _} -> Body; Node1 -> (data(Node1))#try_expr.body end. %% ===================================================================== %% @doc Returns the list of case-clause subtrees of a %% `try_expr' node. If `Node' represents %% "try Body catch H1; ...; Hn %% end", the result is the empty list. %% %% @see try_expr/4 -spec try_expr_clauses(syntaxTree()) -> [syntaxTree()]. try_expr_clauses(Node) -> case unwrap(Node) of {'try', _, _, Clauses, _, _} -> Clauses; Node1 -> (data(Node1))#try_expr.clauses end. %% ===================================================================== %% @doc Returns the list of handler-clause subtrees of a %% `try_expr' node. %% %% @see try_expr/4 -spec try_expr_handlers(syntaxTree()) -> [syntaxTree()]. try_expr_handlers(Node) -> case unwrap(Node) of {'try', _, _, _, Handlers, _} -> unfold_try_clauses(Handlers); Node1 -> (data(Node1))#try_expr.handlers end. %% ===================================================================== %% @doc Returns the list of "after" subtrees of a `try_expr' node. %% %% @see try_expr/4 -spec try_expr_after(syntaxTree()) -> [syntaxTree()]. try_expr_after(Node) -> case unwrap(Node) of {'try', _, _, _, _, After} -> After; Node1 -> (data(Node1))#try_expr.'after' end. %% ===================================================================== %% @doc Creates an abstract class qualifier. The result represents %% "Class:Body". %% %% @see class_qualifier_argument/1 %% @see class_qualifier_body/1 %% @see try_expr/4 -record(class_qualifier, {class :: syntaxTree(), body :: syntaxTree()}). %% type(Node) = class_qualifier %% data(Node) = #class_qualifier{class :: Class, body :: Body} %% %% Class = Body = syntaxTree() -spec class_qualifier(syntaxTree(), syntaxTree()) -> syntaxTree(). class_qualifier(Class, Body) -> tree(class_qualifier, #class_qualifier{class = Class, body = Body}). %% ===================================================================== %% @doc Returns the argument (the class) subtree of a %% `class_qualifier' node. %% %% @see class_qualifier/2 -spec class_qualifier_argument(syntaxTree()) -> syntaxTree(). class_qualifier_argument(Node) -> (data(Node))#class_qualifier.class. %% ===================================================================== %% @doc Returns the body subtree of a `class_qualifier' node. %% %% @see class_qualifier/2 -spec class_qualifier_body(syntaxTree()) -> syntaxTree(). class_qualifier_body(Node) -> (data(Node))#class_qualifier.body. %% ===================================================================== %% @doc Creates an abstract "implicit fun" expression. If %% `Arity' is `none', this is equivalent to %% `implicit_fun(Name)', otherwise it is equivalent to %% `implicit_fun(arity_qualifier(Name, Arity))'. %% %% (This is a utility function.) %% %% @see implicit_fun/1 %% @see implicit_fun/3 -spec implicit_fun(syntaxTree(), 'none' | syntaxTree()) -> syntaxTree(). implicit_fun(Name, none) -> implicit_fun(Name); implicit_fun(Name, Arity) -> implicit_fun(arity_qualifier(Name, Arity)). %% ===================================================================== %% @doc Creates an abstract module-qualified "implicit fun" expression. %% If `Module' is `none', this is equivalent to %% `implicit_fun(Name, Arity)', otherwise it is equivalent to %% `implicit_fun(module_qualifier(Module, arity_qualifier(Name, %% Arity))'. %% %% (This is a utility function.) %% %% @see implicit_fun/1 %% @see implicit_fun/2 -spec implicit_fun('none' | syntaxTree(), syntaxTree(), syntaxTree()) -> syntaxTree(). implicit_fun(none, Name, Arity) -> implicit_fun(Name, Arity); implicit_fun(Module, Name, Arity) -> implicit_fun(module_qualifier(Module, arity_qualifier(Name, Arity))). %% ===================================================================== %% @doc Creates an abstract "implicit fun" expression. The result %% represents "fun Name". `Name' should %% represent either F/A or %% M:F/A %% %% @see implicit_fun_name/1 %% @see implicit_fun/2 %% @see implicit_fun/3 %% @see arity_qualifier/2 %% @see module_qualifier/2 %% type(Node) = implicit_fun %% data(Node) = syntaxTree() %% %% `erl_parse' representation: %% %% {'fun', Pos, {function, Name, Arity}} %% {'fun', Pos, {function, Module, Name, Arity}} %% %% Module = atom() %% Name = atom() %% Arity = arity() -spec implicit_fun(syntaxTree()) -> syntaxTree(). implicit_fun(Name) -> tree(implicit_fun, Name). revert_implicit_fun(Node) -> Pos = get_pos(Node), Name = implicit_fun_name(Node), case type(Name) of arity_qualifier -> F = arity_qualifier_body(Name), A = arity_qualifier_argument(Name), case {type(F), type(A)} of {atom, integer} -> {'fun', Pos, {function, concrete(F), concrete(A)}}; _ -> Node end; module_qualifier -> M = module_qualifier_argument(Name), Name1 = module_qualifier_body(Name), case type(Name1) of arity_qualifier -> F = arity_qualifier_body(Name1), A = arity_qualifier_argument(Name1), {'fun', Pos, {function, M, F, A}}; _ -> Node end; _ -> Node end. %% ===================================================================== %% @doc Returns the name subtree of an `implicit_fun' node. %% %% Note: if `Node' represents "fun %% N/A" or "fun %% M:N/A", then the result is the %% subtree representing "N/A" or %% "M:N/A", respectively. %% %% @see implicit_fun/1 %% @see arity_qualifier/2 %% @see module_qualifier/2 -spec implicit_fun_name(syntaxTree()) -> syntaxTree(). implicit_fun_name(Node) -> case unwrap(Node) of {'fun', Pos, {function, Atom, Arity}} -> arity_qualifier(set_pos(atom(Atom), Pos), set_pos(integer(Arity), Pos)); {'fun', Pos, {function, Module, Atom, Arity}} when is_atom(Module), is_atom(Atom), is_integer(Arity) -> %% Backward compatibility with pre-R15 abstract format. module_qualifier(set_pos(atom(Module), Pos), arity_qualifier( set_pos(atom(Atom), Pos), set_pos(integer(Arity), Pos))); {'fun', _Pos, {function, Module, Atom, Arity}} -> %% New in R15: fun M:F/A. %% XXX: Perhaps set position for this as well? module_qualifier(Module, arity_qualifier(Atom, Arity)); Node1 -> data(Node1) end. %% ===================================================================== %% @doc Creates an abstract fun-expression. If `Clauses' is %% `[C1, ..., Cn]', the result represents "fun %% C1; ...; Cn end". More exactly, if each %% `Ci' represents "(Pi1, ..., Pim) %% Gi -> Bi", then the result represents %% "fun (P11, ..., P1m) G1 -> %% B1; ...; (Pn1, ..., Pnm) Gn -> %% Bn end". %% %% @see fun_expr_clauses/1 %% @see fun_expr_arity/1 %% type(Node) = fun_expr %% data(Node) = Clauses %% %% Clauses = [syntaxTree()] %% %% (See `function' for notes; e.g. why the arity is not stored.) %% %% `erl_parse' representation: %% %% {'fun', Pos, {clauses, Clauses}} %% %% Clauses = [Clause] \ [] %% Clause = {clause, ...} %% %% See `clause' for documentation on `erl_parse' clauses. -spec fun_expr([syntaxTree()]) -> syntaxTree(). fun_expr(Clauses) -> tree(fun_expr, Clauses). revert_fun_expr(Node) -> Clauses = [revert_clause(C) || C <- fun_expr_clauses(Node)], Pos = get_pos(Node), {'fun', Pos, {clauses, Clauses}}. %% ===================================================================== %% @doc Returns the list of clause subtrees of a `fun_expr' node. %% %% @see fun_expr/1 -spec fun_expr_clauses(syntaxTree()) -> [syntaxTree()]. fun_expr_clauses(Node) -> case unwrap(Node) of {'fun', _, {clauses, Clauses}} -> Clauses; Node1 -> data(Node1) end. %% ===================================================================== %% @doc Returns the arity of a `fun_expr' node. The result is %% the number of parameter patterns in the first clause of the %% fun-expression; subsequent clauses are ignored. %% %% An exception is thrown if `fun_expr_clauses(Node)' %% returns an empty list, or if the first element of that list is not a %% syntax tree `C' of type `clause' such that %% `clause_patterns(C)' is a nonempty list. %% %% @see fun_expr/1 %% @see fun_expr_clauses/1 %% @see clause/3 %% @see clause_patterns/1 -spec fun_expr_arity(syntaxTree()) -> arity(). fun_expr_arity(Node) -> length(clause_patterns(hd(fun_expr_clauses(Node)))). %% ===================================================================== %% @doc Creates an abstract parenthesised expression. The result %% represents "(Body)", independently of the %% context. %% %% @see parentheses_body/1 %% type(Node) = parentheses %% data(Node) = syntaxTree() -spec parentheses(syntaxTree()) -> syntaxTree(). parentheses(Expr) -> tree(parentheses, Expr). revert_parentheses(Node) -> parentheses_body(Node). %% ===================================================================== %% @doc Returns the body subtree of a `parentheses' node. %% %% @see parentheses/1 -spec parentheses_body(syntaxTree()) -> syntaxTree(). parentheses_body(Node) -> data(Node). %% ===================================================================== %% @equiv macro(Name, none) -spec macro(syntaxTree()) -> syntaxTree(). macro(Name) -> macro(Name, none). %% ===================================================================== %% @doc Creates an abstract macro application. If `Arguments' %% is `none', the result represents %% "?Name", otherwise, if `Arguments' %% is `[A1, ..., An]', the result represents %% "?Name(A1, ..., An)". %% %% Notes: if `Arguments' is the empty list, the result %% will thus represent "?Name()", including a pair %% of matching parentheses. %% %% The only syntactical limitation imposed by the preprocessor on the %% arguments to a macro application (viewed as sequences of tokens) is %% that they must be balanced with respect to parentheses, brackets, %% `begin ... end', `case ... end', etc. The %% `text' node type can be used to represent arguments which %% are not regular Erlang constructs. %% %% @see macro_name/1 %% @see macro_arguments/1 %% @see macro/1 %% @see text/1 -record(macro, {name :: syntaxTree(), arguments :: 'none' | [syntaxTree()]}). %% type(Node) = macro %% data(Node) = #macro{name :: Name, arguments :: Arguments} %% %% Name = syntaxTree() %% Arguments = none | [syntaxTree()] -spec macro(syntaxTree(), 'none' | [syntaxTree()]) -> syntaxTree(). macro(Name, Arguments) -> tree(macro, #macro{name = Name, arguments = Arguments}). %% ===================================================================== %% @doc Returns the name subtree of a `macro' node. %% %% @see macro/2 -spec macro_name(syntaxTree()) -> syntaxTree(). macro_name(Node) -> (data(Node))#macro.name. %% ===================================================================== %% @doc Returns the list of argument subtrees of a `macro' %% node, if any. If `Node' represents %% "?Name", `none' is returned. %% Otherwise, if `Node' represents %% "?Name(A1, ..., An)", %% `[A1, ..., An]' is returned. %% %% @see macro/2 -spec macro_arguments(syntaxTree()) -> 'none' | [syntaxTree()]. macro_arguments(Node) -> (data(Node))#macro.arguments. %% ===================================================================== %% @doc Returns the syntax tree corresponding to an Erlang term. %% `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. The function recognises printable strings, in order to get a %% compact and readable representation. Evaluation fails with reason %% `badarg' if `Term' is not a literal term. %% %% @see concrete/1 %% @see is_literal/1 -spec abstract(term()) -> syntaxTree(). abstract([H | T] = L) when is_integer(H) -> case is_printable(L) of true -> string(L); false -> abstract_tail(H, T) end; abstract([H | T]) -> abstract_tail(H, T); abstract(T) when is_atom(T) -> atom(T); abstract(T) when is_integer(T) -> integer(T); abstract(T) when is_float(T) -> make_float(T); % (not `float', which would call the BIF) abstract([]) -> nil(); abstract(T) when is_tuple(T) -> tuple(abstract_list(tuple_to_list(T))); abstract(T) when is_binary(T) -> binary([binary_field(integer(B)) || B <- binary_to_list(T)]); abstract(T) -> erlang:error({badarg, T}). abstract_list([T | Ts]) -> [abstract(T) | abstract_list(Ts)]; abstract_list([]) -> []. %% This is entered when we might have a sequence of conses that might or %% might not be a proper list, but which should not be considered as a %% potential string, to avoid unnecessary checking. This also avoids %% that a list like `[4711, 42, 10]' could be abstracted to represent %% `[4711 | "*\n"]'. abstract_tail(H1, [H2 | T]) -> %% Recall that `cons' does "intelligent" composition cons(abstract(H1), abstract_tail(H2, T)); abstract_tail(H, T) -> cons(abstract(H), abstract(T)). %% ===================================================================== %% @doc Returns the Erlang term represented by a syntax tree. Evaluation %% fails with reason `badarg' if `Node' does not %% represent a literal term. %% %% Note: Currently, the set of syntax trees which have a concrete %% representation is larger than the set of trees which can be built %% using the function {@link abstract/1}. An abstract character %% will be concretised as an integer, while {@link abstract/1} does %% not at present yield an abstract character for any input. (Use the %% {@link char/1} function to explicitly create an abstract %% character.) %% %% @see abstract/1 %% @see is_literal/1 %% @see char/1 -spec concrete(syntaxTree()) -> term(). concrete(Node) -> case type(Node) of atom -> atom_value(Node); integer -> integer_value(Node); float -> float_value(Node); char -> char_value(Node); string -> string_value(Node); nil -> []; list -> [concrete(list_head(Node)) | concrete(list_tail(Node))]; tuple -> list_to_tuple(concrete_list(tuple_elements(Node))); binary -> Fs = [revert_binary_field( binary_field(binary_field_body(F), case binary_field_size(F) of none -> none; S -> revert(S) end, binary_field_types(F))) || F <- binary_fields(Node)], {value, B, _} = eval_bits:expr_grp(Fs, [], fun(F, _) -> {value, concrete(F), []} end, [], true), B; _ -> erlang:error({badarg, Node}) end. concrete_list([E | Es]) -> [concrete(E) | concrete_list(Es)]; concrete_list([]) -> []. %% ===================================================================== %% @doc Returns `true' if `Node' represents a %% literal term, otherwise `false'. This function returns %% `true' if and only if the value of %% `concrete(Node)' is defined. %% %% @see abstract/1 %% @see concrete/1 -spec is_literal(syntaxTree()) -> boolean(). is_literal(T) -> case type(T) of atom -> true; integer -> true; float -> true; char-> true; string -> true; nil -> true; list -> is_literal(list_head(T)) andalso is_literal(list_tail(T)); tuple -> lists:all(fun is_literal/1, tuple_elements(T)); _ -> false end. %% ===================================================================== %% @doc Returns an `erl_parse'-compatible representation of a %% syntax tree, if possible. If `Tree' represents a %% well-formed Erlang program or expression, the conversion should work %% without problems. Typically, {@link is_tree/1} yields %% `true' if conversion failed (i.e., the result is still an %% abstract syntax tree), and `false' otherwise. %% %% The {@link is_tree/1} test is not completely foolproof. For a %% few special node types (e.g. `arity_qualifier'), if such a %% node occurs in a context where it is not expected, it will be left %% unchanged as a non-reverted subtree of the result. This can only %% happen if `Tree' does not actually represent legal Erlang %% code. %% %% @see revert_forms/1 %% @see //stdlib/erl_parse -spec revert(syntaxTree()) -> syntaxTree(). revert(Node) -> case is_tree(Node) of false -> %% Just remove any wrapper. `erl_parse' nodes never contain %% abstract syntax tree nodes as subtrees. unwrap(Node); true -> case is_leaf(Node) of true -> revert_root(Node); false -> %% First revert the subtrees, where possible. %% (Sometimes, subtrees cannot be reverted out of %% context, and the real work will be done when the %% parent node is reverted.) Gs = [[revert(X) || X <- L] || L <- subtrees(Node)], %% Then reconstruct the node from the reverted %% parts, and revert the node itself. Node1 = update_tree(Node, Gs), revert_root(Node1) end end. %% Note: The concept of "compatible root node" is not strictly defined. %% At a minimum, if `make_tree' is used to compose a node `T' from %% subtrees that are all completely backwards compatible, then the %% result of `revert_root(T)' should also be completely backwards %% compatible. revert_root(Node) -> case type(Node) of application -> revert_application(Node); atom -> revert_atom(Node); attribute -> revert_attribute(Node); binary -> revert_binary(Node); binary_comp -> revert_binary_comp(Node); binary_field -> revert_binary_field(Node); binary_generator -> revert_binary_generator(Node); block_expr -> revert_block_expr(Node); case_expr -> revert_case_expr(Node); catch_expr -> revert_catch_expr(Node); char -> revert_char(Node); clause -> revert_clause(Node); cond_expr -> revert_cond_expr(Node); eof_marker -> revert_eof_marker(Node); error_marker -> revert_error_marker(Node); float -> revert_float(Node); fun_expr -> revert_fun_expr(Node); function -> revert_function(Node); generator -> revert_generator(Node); if_expr -> revert_if_expr(Node); implicit_fun -> revert_implicit_fun(Node); infix_expr -> revert_infix_expr(Node); integer -> revert_integer(Node); list -> revert_list(Node); list_comp -> revert_list_comp(Node); match_expr -> revert_match_expr(Node); module_qualifier -> revert_module_qualifier(Node); nil -> revert_nil(Node); parentheses -> revert_parentheses(Node); prefix_expr -> revert_prefix_expr(Node); receive_expr -> revert_receive_expr(Node); record_access -> revert_record_access(Node); record_expr -> revert_record_expr(Node); record_index_expr -> revert_record_index_expr(Node); rule -> revert_rule(Node); string -> revert_string(Node); try_expr -> revert_try_expr(Node); tuple -> revert_tuple(Node); underscore -> revert_underscore(Node); variable -> revert_variable(Node); warning_marker -> revert_warning_marker(Node); _ -> %% Non-revertible new-form node Node end. %% ===================================================================== %% @doc Reverts a sequence of Erlang source code forms. The sequence can %% be given either as a `form_list' syntax tree (possibly %% nested), or as a list of "program form" syntax trees. If successful, %% the corresponding flat list of `erl_parse'-compatible %% syntax trees is returned (see {@link revert/1}). If some program %% form could not be reverted, `{error, Form}' is thrown. %% Standalone comments in the form sequence are discarded. %% %% @see revert/1 %% @see form_list/1 %% @see is_form/1 -type forms() :: syntaxTree() | [syntaxTree()]. -spec revert_forms(forms()) -> [erl_parse()]. revert_forms(Forms) when is_list(Forms) -> revert_forms(form_list(Forms)); revert_forms(T) -> case type(T) of form_list -> T1 = flatten_form_list(T), case catch {ok, revert_forms_1(form_list_elements(T1))} of {ok, Fs} -> Fs; {error, _} = Error -> erlang:error(Error); {'EXIT', R} -> exit(R); R -> throw(R) end; _ -> erlang:error({badarg, T}) end. revert_forms_1([T | Ts]) -> case type(T) of comment -> revert_forms_1(Ts); _ -> T1 = revert(T), case is_tree(T1) of true -> throw({error, T1}); false -> [T1 | revert_forms_1(Ts)] end end; revert_forms_1([]) -> []. %% ===================================================================== %% @doc Returns the grouped list of all subtrees of a syntax tree. If %% `Node' is a leaf node (see {@link 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 {@link subtrees/1} and the constructor functions %% {@link make_tree/2} and {@link 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 {@link update_tree/2} to preserve node attributes.) 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 comments, %% annotations and line numbers) has been changed. %% %% @see make_tree/2 %% @see type/1 %% @see is_leaf/1 %% @see copy_attrs/2 -spec subtrees(syntaxTree()) -> [[syntaxTree()]]. subtrees(T) -> case is_leaf(T) of true -> []; false -> case type(T) of application -> [[application_operator(T)], application_arguments(T)]; arity_qualifier -> [[arity_qualifier_body(T)], [arity_qualifier_argument(T)]]; attribute -> case attribute_arguments(T) of none -> [[attribute_name(T)]]; As -> [[attribute_name(T)], As] end; binary -> [binary_fields(T)]; binary_comp -> [[binary_comp_template(T)], binary_comp_body(T)]; binary_field -> case binary_field_types(T) of [] -> [[binary_field_body(T)]]; Ts -> [[binary_field_body(T)], Ts] end; binary_generator -> [[binary_generator_pattern(T)], [binary_generator_body(T)]]; block_expr -> [block_expr_body(T)]; case_expr -> [[case_expr_argument(T)], case_expr_clauses(T)]; catch_expr -> [[catch_expr_body(T)]]; class_qualifier -> [[class_qualifier_argument(T)], [class_qualifier_body(T)]]; clause -> case clause_guard(T) of none -> [clause_patterns(T), clause_body(T)]; G -> [clause_patterns(T), [G], clause_body(T)] end; cond_expr -> [cond_expr_clauses(T)]; conjunction -> [conjunction_body(T)]; disjunction -> [disjunction_body(T)]; form_list -> [form_list_elements(T)]; fun_expr -> [fun_expr_clauses(T)]; function -> [[function_name(T)], function_clauses(T)]; generator -> [[generator_pattern(T)], [generator_body(T)]]; if_expr -> [if_expr_clauses(T)]; implicit_fun -> [[implicit_fun_name(T)]]; infix_expr -> [[infix_expr_left(T)], [infix_expr_operator(T)], [infix_expr_right(T)]]; list -> case list_suffix(T) of none -> [list_prefix(T)]; S -> [list_prefix(T), [S]] end; list_comp -> [[list_comp_template(T)], list_comp_body(T)]; macro -> case macro_arguments(T) of none -> [[macro_name(T)]]; As -> [[macro_name(T)], As] end; match_expr -> [[match_expr_pattern(T)], [match_expr_body(T)]]; module_qualifier -> [[module_qualifier_argument(T)], [module_qualifier_body(T)]]; parentheses -> [[parentheses_body(T)]]; prefix_expr -> [[prefix_expr_operator(T)], [prefix_expr_argument(T)]]; receive_expr -> case receive_expr_timeout(T) of none -> [receive_expr_clauses(T)]; E -> [receive_expr_clauses(T), [E], receive_expr_action(T)] end; record_access -> case record_access_type(T) of none -> [[record_access_argument(T)], [record_access_field(T)]]; R -> [[record_access_argument(T)], [R], [record_access_field(T)]] end; record_expr -> case record_expr_argument(T) of none -> [[record_expr_type(T)], record_expr_fields(T)]; V -> [[V], [record_expr_type(T)], record_expr_fields(T)] end; record_field -> case record_field_value(T) of none -> [[record_field_name(T)]]; V -> [[record_field_name(T)], [V]] end; record_index_expr -> [[record_index_expr_type(T)], [record_index_expr_field(T)]]; rule -> [[rule_name(T)], rule_clauses(T)]; size_qualifier -> [[size_qualifier_body(T)], [size_qualifier_argument(T)]]; try_expr -> [try_expr_body(T), try_expr_clauses(T), try_expr_handlers(T), try_expr_after(T)]; tuple -> [tuple_elements(T)] end end. %% ===================================================================== %% @doc Creates a syntax tree with the same type and attributes as the %% given tree. This is equivalent to `copy_attrs(Node, %% make_tree(type(Node), Groups))'. %% %% @see make_tree/2 %% @see copy_attrs/2 %% @see type/1 -spec update_tree(syntaxTree(), [[syntaxTree()]]) -> syntaxTree(). update_tree(Node, Groups) -> copy_attrs(Node, make_tree(type(Node), Groups)). %% ===================================================================== %% @doc Creates a syntax tree with the given type and subtrees. %% `Type' must be a node type name (see {@link type/1}) %% that does not denote a leaf node type (see {@link 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 {@link subtrees/1}. %% %% The result of `copy_attrs(Node, make_tree(type(Node), %% subtrees(Node)))' (see {@link 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 same data representation as %% `Node'. %% %% @see update_tree/2 %% @see subtrees/1 %% @see type/1 %% @see is_leaf/1 %% @see copy_attrs/2 -spec make_tree(atom(), [[syntaxTree()]]) -> syntaxTree(). make_tree(application, [[F], A]) -> application(F, A); make_tree(arity_qualifier, [[N], [A]]) -> arity_qualifier(N, A); make_tree(attribute, [[N]]) -> attribute(N); make_tree(attribute, [[N], A]) -> attribute(N, A); make_tree(binary, [Fs]) -> binary(Fs); make_tree(binary_comp, [[T], B]) -> binary_comp(T, B); make_tree(binary_field, [[B]]) -> binary_field(B); make_tree(binary_field, [[B], Ts]) -> binary_field(B, Ts); make_tree(binary_generator, [[P], [E]]) -> binary_generator(P, E); make_tree(block_expr, [B]) -> block_expr(B); make_tree(case_expr, [[A], C]) -> case_expr(A, C); make_tree(catch_expr, [[B]]) -> catch_expr(B); make_tree(class_qualifier, [[A], [B]]) -> class_qualifier(A, B); make_tree(clause, [P, B]) -> clause(P, none, B); make_tree(clause, [P, [G], B]) -> clause(P, G, B); make_tree(cond_expr, [C]) -> cond_expr(C); make_tree(conjunction, [E]) -> conjunction(E); make_tree(disjunction, [E]) -> disjunction(E); make_tree(form_list, [E]) -> form_list(E); make_tree(fun_expr, [C]) -> fun_expr(C); make_tree(function, [[N], C]) -> function(N, C); make_tree(generator, [[P], [E]]) -> generator(P, E); make_tree(if_expr, [C]) -> if_expr(C); make_tree(implicit_fun, [[N]]) -> implicit_fun(N); make_tree(infix_expr, [[L], [F], [R]]) -> infix_expr(L, F, R); make_tree(list, [P]) -> list(P); make_tree(list, [P, [S]]) -> list(P, S); make_tree(list_comp, [[T], B]) -> list_comp(T, B); make_tree(macro, [[N]]) -> macro(N); make_tree(macro, [[N], A]) -> macro(N, A); make_tree(match_expr, [[P], [E]]) -> match_expr(P, E); make_tree(module_qualifier, [[M], [N]]) -> module_qualifier(M, N); make_tree(parentheses, [[E]]) -> parentheses(E); make_tree(prefix_expr, [[F], [A]]) -> prefix_expr(F, A); make_tree(receive_expr, [C]) -> receive_expr(C); make_tree(receive_expr, [C, [E], A]) -> receive_expr(C, E, A); make_tree(record_access, [[E], [F]]) -> record_access(E, F); make_tree(record_access, [[E], [T], [F]]) -> record_access(E, T, F); make_tree(record_expr, [[T], F]) -> record_expr(T, F); make_tree(record_expr, [[E], [T], F]) -> record_expr(E, T, F); make_tree(record_field, [[N]]) -> record_field(N); make_tree(record_field, [[N], [E]]) -> record_field(N, E); make_tree(record_index_expr, [[T], [F]]) -> record_index_expr(T, F); make_tree(rule, [[N], C]) -> rule(N, C); make_tree(size_qualifier, [[N], [A]]) -> size_qualifier(N, A); make_tree(try_expr, [B, C, H, A]) -> try_expr(B, C, H, A); make_tree(tuple, [E]) -> tuple(E). %% ===================================================================== %% @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. Comments attached to nodes of `Tree' will %% be preserved, but other attributes are lost. %% %% Any node in `Tree' whose node type is %% `variable' (see {@link type/1}), and whose list of %% annotations (see {@link 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 {@link abstract/1} to map the actual data %% structure onto a syntax tree representation - would output a string %% that might look something like "`{tree, variable, ..., "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 %% "`erl_syntax:variable("V")'". %% %% @see abstract/1 %% @see type/1 %% @see get_ann/1 -spec meta(syntaxTree()) -> syntaxTree(). meta(T) -> %% First of all we check for metavariables: case type(T) of variable -> case lists:member(meta_var, get_ann(T)) of false -> meta_precomment(T); true -> %% A meta-variable: remove the first found %% `meta_var' annotation, but otherwise leave %% the node unchanged. set_ann(T, lists:delete(meta_var, get_ann(T))) end; _ -> case has_comments(T) of true -> meta_precomment(T); false -> meta_1(T) end end. meta_precomment(T) -> case get_precomments(T) of [] -> meta_postcomment(T); Cs -> meta_call(set_precomments, [meta_postcomment(T), list(meta_list(Cs))]) end. meta_postcomment(T) -> case get_postcomments(T) of [] -> meta_0(T); Cs -> meta_call(set_postcomments, [meta_0(T), list(meta_list(Cs))]) end. meta_0(T) -> meta_1(remove_comments(T)). meta_1(T) -> %% First handle leaf nodes and other common cases, in order to %% generate compact code. case type(T) of atom -> meta_call(atom, [T]); char -> meta_call(char, [T]); comment -> meta_call(comment, [list([string(S) || S <- comment_text(T)])]); eof_marker -> meta_call(eof_marker, []); error_marker -> meta_call(error_marker, [abstract(error_marker_info(T))]); float -> meta_call(float, [T]); integer -> meta_call(integer, [T]); nil -> meta_call(nil, []); operator -> meta_call(operator, [atom(operator_name(T))]); string -> meta_call(string, [T]); text -> meta_call(text, [string(text_string(T))]); underscore -> meta_call(underscore, []); variable -> meta_call(variable, [string(atom_to_list(variable_name(T)))]); warning_marker -> meta_call(warning_marker, [abstract(warning_marker_info(T))]); list -> case list_suffix(T) of none -> meta_call(list, [list(meta_list(list_prefix(T)))]); S -> meta_call(list, [list(meta_list(list_prefix(T))), meta(S)]) end; tuple -> meta_call(tuple, [list(meta_list(tuple_elements(T)))]); Type -> %% All remaining cases are handled using `subtrees' %% and `make_tree' to decompose and reassemble the %% nodes. More cases could of course be handled %% directly to get a more compact output, but I can't %% be bothered right now. meta_call(make_tree, [abstract(Type), meta_subtrees(subtrees(T))]) end. meta_list([T | Ts]) -> [meta(T) | meta_list(Ts)]; meta_list([]) -> []. meta_subtrees(Gs) -> list([list([meta(T) || T <- G]) || G <- Gs]). meta_call(F, As) -> application(atom(?MODULE), atom(F), As). %% ===================================================================== %% Functions for abstraction of the syntax tree representation; may be %% used externally, but not intended for the normal user. %% ===================================================================== %% ===================================================================== %% @equiv tree(Type, []) -spec tree(atom()) -> #tree{}. tree(Type) -> tree(Type, []). %% ===================================================================== %% @doc For special purposes only. Creates an abstract syntax %% tree node with type tag `Type' and associated data %% `Data'. %% %% This function and the related {@link is_tree/1} and %% {@link data/1} provide a uniform way to extend the set of %% `erl_parse' node types. The associated data is any term, %% whose format may depend on the type tag. %% %% === Notes: === %%
    %%
  • Any nodes created outside of this module must have type tags %% distinct from those currently defined by this module; see %% {@link type/1} for a complete list.
  • %%
  • The type tag of a syntax tree node may also be used %% as a primary tag by the `erl_parse' representation; %% in that case, the selector functions for that node type %% must handle both the abstract syntax tree and the %% `erl_parse' form. The function `type(T)' %% should return the correct type tag regardless of the %% representation of `T', so that the user sees no %% difference between `erl_syntax' and %% `erl_parse' nodes.
  • %%
%% %% @see is_tree/1 %% @see data/1 %% @see type/1 -spec tree(atom(), term()) -> #tree{}. tree(Type, Data) -> #tree{type = Type, data = Data}. %% ===================================================================== %% @doc For special purposes only. Returns `true' if %% `Tree' is an abstract syntax tree and `false' %% otherwise. %% %% Note: this function yields `false' for all %% "old-style" `erl_parse'-compatible "parse trees". %% %% @see tree/2 -spec is_tree(syntaxTree()) -> boolean(). is_tree(#tree{}) -> true; is_tree(_) -> false. %% ===================================================================== %% @doc For special purposes only. Returns the associated data %% of a syntax tree node. Evaluation fails with reason %% `badarg' if `is_tree(Node)' does not yield %% `true'. %% %% @see tree/2 -spec data(syntaxTree()) -> term(). data(#tree{data = D}) -> D; data(T) -> erlang:error({badarg, T}). %% ===================================================================== %% Primitives for backwards compatibility; for internal use only %% ===================================================================== %% ===================================================================== %% @doc Creates a wrapper structure around an `erl_parse' %% "parse tree". %% %% This function and the related {@link unwrap/1} and %% {@link is_wrapper/1} provide a uniform way to attach arbitrary %% information to an `erl_parse' tree. Some information about %% the encapsuled tree may be cached in the wrapper, such as the node %% type. All functions on syntax trees must behave so that the user sees %% no difference between wrapped and non-wrapped `erl_parse' %% trees. Attaching a wrapper onto another wrapper structure is an %% error. -spec wrap(erl_parse()) -> #wrapper{}. wrap(Node) -> %% We assume that Node is an old-school `erl_parse' tree. #wrapper{type = type(Node), attr = #attr{pos = get_pos(Node)}, tree = Node}. %% ===================================================================== %% @doc Removes any wrapper structure, if present. If `Node' %% is a wrapper structure, this function returns the wrapped %% `erl_parse' tree; otherwise it returns `Node' %% itself. -spec unwrap(syntaxTree()) -> #tree{} | erl_parse(). unwrap(#wrapper{tree = Node}) -> Node; unwrap(Node) -> Node. % This could also be a new-form node. %% ===================================================================== %% @doc Returns `true' if the argument is a wrapper %% structure, otherwise `false'. -ifndef(NO_UNUSED). -spec is_wrapper(term()) -> boolean(). is_wrapper(#wrapper{}) -> true; is_wrapper(_) -> false. -endif. %% ===================================================================== %% General utility functions for internal use %% ===================================================================== is_printable(S) -> io_lib:printable_list(S). %% Support functions for transforming lists of function names %% specified as `arity_qualifier' nodes. unfold_function_names(Ns, Pos) -> F = fun ({Atom, Arity}) -> N = arity_qualifier(atom(Atom), integer(Arity)), set_pos(N, Pos) end, [F(N) || N <- Ns]. fold_function_names(Ns) -> [fold_function_name(N) || N <- Ns]. fold_function_name(N) -> Name = arity_qualifier_body(N), Arity = arity_qualifier_argument(N), true = ((type(Name) =:= atom) and (type(Arity) =:= integer)), {concrete(Name), concrete(Arity)}. fold_variable_names(Vs) -> [variable_name(V) || V <- Vs]. unfold_variable_names(Vs, Pos) -> [set_pos(variable(V), Pos) || V <- Vs]. %% Support functions for transforming lists of record field definitions. %% %% There is no unique representation for field definitions in the %% standard form. There, they may only occur in the "fields" part of a %% record expression or declaration, and are represented as %% `{record_field, Pos, Name, Value}', or as `{record_field, Pos, Name}' %% if the value part is left out. However, these cannot be distinguished %% out of context from the representation of record field access %% expressions (see `record_access'). fold_record_fields(Fs) -> [fold_record_field(F) || F <- Fs]. fold_record_field(F) -> Pos = get_pos(F), Name = record_field_name(F), case record_field_value(F) of none -> {record_field, Pos, Name}; Value -> {record_field, Pos, Name, Value} end. unfold_record_fields(Fs) -> [unfold_record_field(F) || F <- Fs]. unfold_record_field({typed_record_field, Field, _Type}) -> unfold_record_field_1(Field); unfold_record_field(Field) -> unfold_record_field_1(Field). unfold_record_field_1({record_field, Pos, Name}) -> set_pos(record_field(Name), Pos); unfold_record_field_1({record_field, Pos, Name, Value}) -> set_pos(record_field(Name, Value), Pos). fold_binary_field_types(Ts) -> [fold_binary_field_type(T) || T <- Ts]. fold_binary_field_type(Node) -> case type(Node) of size_qualifier -> {concrete(size_qualifier_body(Node)), concrete(size_qualifier_argument(Node))}; _ -> concrete(Node) end. unfold_binary_field_types(Ts, Pos) -> [unfold_binary_field_type(T, Pos) || T <- Ts]. unfold_binary_field_type({Type, Size}, Pos) -> set_pos(size_qualifier(atom(Type), integer(Size)), Pos); unfold_binary_field_type(Type, Pos) -> set_pos(atom(Type), Pos). %% =====================================================================