%% ===================================================================== %% 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 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 syntaxTree() data type, and %% the function 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 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 %% erl_parse "parse tree" representation is a subset of the %% syntaxTree() representation. %% %% Every abstract syntax tree node has a type, given by the %% function type/1. Each node also %% has associated attributes; see get_attrs/1 for details. The %% functions make_tree/2 and subtrees/1 are generic %% constructor/decomposition functions for abstract syntax trees. The %% functions abstract/1 and concrete/1 convert between %% constant Erlang terms and their syntactic representations. The set of %% syntax tree nodes is extensible through the tree/2 function. %% %% A syntax tree can be transformed to the erl_parse %% representation with the 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, 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, qualified_name/1, qualified_name_segments/1, query_expr/1, query_expr_body/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, 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 `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
qualified_namequery_exprreceive_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 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 qualified_name/1 %% @see query_expr/1 %% @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; {'query', _, _} -> query_expr; {record, _, _, _, _} -> record_expr; {record, _, _, _} -> record_expr; {record_field, _, _, _, _} -> record_access; {record_field, _, _, _} -> case is_qualified_name(Node) of true -> qualified_name; false -> record_access end; {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: %%

%% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %% %%
atomcharcommenteof_markererror_marker
floatintegerniloperatorstring
textunderscorevariablewarning_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: %%

%% %% %% %% %% %% %% %% %% %% %% %% %%
attributecommenterror_markereof_markerform_list
functionrulewarning_markertext

%% @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 (cf. set_attrs/2). %% %%

For accessing individual attributes, see get_pos/1, %% get_ann/1, get_precomments/1 and %% get_postcomments/1.

%% %% @type syntaxTreeAttributes(). This is an abstract representation of %% syntax tree node attributes; see the function 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 (cf. 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; cf. 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; cf. 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 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. %% %% @see char/1 -spec char_literal(syntaxTree()) -> nonempty_string(). char_literal(Node) -> io_lib:write_char(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. E.g., the result of %% string("x\ny") represents any and all of %% "x\ny", "x\12y", "x\012y" and %% "x\^Jy"; cf. char/1. %% %% @see string_value/1 %% @see string_literal/1 %% @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. %% %% @see string/1 -spec string_literal(syntaxTree()) -> nonempty_string(). string_literal(Node) -> io_lib:write_string(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\'; cf. %% 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 %% "[]" (cf. 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 (cf. %% 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, _} -> [Head]; Node1 -> (data(Node1))#list.prefix end. %% ===================================================================== %% @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 (cf. %% 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} -> %% If there could be comments/annotations on the tail node, %% we should not return `none' even if it has type `nil'. case Tail of {nil, _} -> none; % no interesting information is lost _ -> Tail end; Node1 -> (data(Node1))#list.suffix end. %% ===================================================================== %% @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)}; qualified_name -> Ss = qualified_name_segments(A), {ok, [concrete(S) || S <- Ss]}; _ -> 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 = if is_list(M1) -> qualified_name([atom(A) || A <- M1]); true -> atom(M1) end, 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 -> case Data of {Module, Imports} -> [if is_list(Module) -> qualified_name([atom(A) || A <- Module]); true -> set_pos(atom(Module), Pos) end, set_pos( list(unfold_function_names(Imports, Pos)), Pos)]; _ -> [qualified_name([atom(A) || A <- Data])] end; 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 qualified name. The result represents %% "S1.S2. ... .Sn", if %% Segments is [S1, S2, ..., Sn]. %% %% @see qualified_name_segments/1 %% type(Node) = qualified_name %% data(Node) = [syntaxTree()] %% %% `erl_parse' representation: %% %% {record_field, Pos, Node, Node} %% %% Node = {atom, Pos, Value} | {record_field, Pos, Node, Node} %% %% Note that if not all leaf subnodes are (abstract) atoms, then Node %% represents a Mnemosyne query record field access ('record_access'); %% see type/1 for details. -spec qualified_name([syntaxTree()]) -> syntaxTree(). qualified_name(Segments) -> tree(qualified_name, Segments). revert_qualified_name(Node) -> Pos = get_pos(Node), fold_qualified_name(qualified_name_segments(Node), Pos). %% ===================================================================== %% @doc Returns the list of name segments of a %% qualified_name node. %% %% @see qualified_name/1 -spec qualified_name_segments(syntaxTree()) -> [syntaxTree()]. qualified_name_segments(Node) -> case unwrap(Node) of {record_field, _, _, _} = Node1 -> unfold_qualified_name(Node1); Node1 -> data(Node1) 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 %% @see query_expr/1 -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 query expression. The result %% represents "query Body end". %% %% @see query_expr_body/1 %% @see record_access/2 %% @see rule/2 %% type(Node) = query_expr %% data(Node) = syntaxTree() %% %% `erl_parse' representation: %% %% {'query', Pos, Body} %% %% Body = erl_parse() -spec query_expr(syntaxTree()) -> syntaxTree(). query_expr(Body) -> tree(query_expr, Body). revert_query_expr(Node) -> Pos = get_pos(Node), Body = list_comp_body(Node), {'query', Pos, Body}. %% ===================================================================== %% @doc Returns the body subtree of a query_expr node. %% %% @see query_expr/1 -spec query_expr_body(syntaxTree()) -> syntaxTree(). query_expr_body(Node) -> case unwrap(Node) of {'query', _, Body} -> Body; Node1 -> data(Node1) 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"; cf. %% 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), F = arity_qualifier_body(Name1), A = arity_qualifier_argument(Name1), case {type(M), type(F), type(A)} of {atom, atom, integer} -> {'fun', Pos, {function, concrete(M), concrete(F), concrete(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 abstract/1. An abstract character %% will be concretised as an integer, while abstract/1 does %% not at present yield an abstract character for any input. (Use the %% 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, is_tree/1 yields %% true if conversion failed (i.e., the result is still an %% abstract syntax tree), and false otherwise. %% %%

The 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 and copy the position. `erl_parse' %% nodes never contain abstract syntax tree nodes as subtrees. case unwrap(Node) of {error, Info} -> {error, setelement(1,Info,get_pos(Node))}; {warning, Info} -> {warning, setelement(1,Info,get_pos(Node))}; Node1 -> setelement(2,Node1,get_pos(Node)) end; 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); qualified_name -> revert_qualified_name(Node); query_expr -> revert_query_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 (cf. 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 (cf. is_leaf/1), this %% is the empty list, otherwise the result is always a nonempty list, %% containing the lists of subtrees of Node, in %% left-to-right order as they occur in the printed program text, and %% grouped by category. Often, each group contains only a single %% subtree. %% %%

Depending on the type of Node, the size of some %% groups may be variable (e.g., the group consisting of all the %% elements of a tuple), while others always contain the same number of %% elements - usually exactly one (e.g., the group containing the %% argument expression of a case-expression). Note, however, that the %% exact structure of the returned list (for a given node type) should %% in general not be depended upon, since it might be subject to change %% without notice.

%% %%

The function subtrees/1 and the constructor functions %% make_tree/2 and update_tree/2 can be a %% great help if one wants to traverse a syntax tree, visiting all its %% subtrees, but treat nodes of the tree in a uniform way in most or all %% cases. Using these functions makes this simple, and also assures that %% your code is not overly sensitive to extensions of the syntax tree %% data type, because any node types not explicitly handled by your code %% can be left to a default case.

%% %%

For example: %%

%%   postorder(F, Tree) ->
%%       F(case subtrees(Tree) of
%%           [] -> Tree;
%%           List -> update_tree(Tree,
%%                               [[postorder(F, Subtree)
%%                                 || Subtree <- Group]
%%                                || Group <- List])
%%         end).
%% 
%% maps the function F on Tree and all its %% subtrees, doing a post-order traversal of the syntax tree. (Note the %% use of update_tree/2 to preserve 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)]]; qualified_name -> [qualified_name_segments(T)]; query_expr -> [[query_expr_body(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 (cf. type/1) %% that does not denote a leaf node type (cf. is_leaf/1). %% Groups must be a nonempty list of groups of %% syntax trees, representing the subtrees of a node of the given type, %% in left-to-right order as they would occur in the printed program %% text, grouped by category as done by subtrees/1. %% %%

The result of copy_attrs(Node, make_tree(type(Node), %% subtrees(Node))) (cf. update_tree/2) represents %% the same source code text as the original Node, assuming %% that subtrees(Node) yields a nonempty list. However, it %% does not necessarily have the 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(qualified_name, [S]) -> qualified_name(S); make_tree(query_expr, [[B]]) -> query_expr(B); 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 (cf. type/1), and whose list of %% annotations (cf. get_ann/1) contains the atom %% meta_var, will remain unchanged in the resulting tree, %% except that exactly one occurrence of meta_var is %% removed from its annotation list.

%% %%

The main use of the function meta/1 is to transform a %% data structure Tree, which represents a piece of program %% code, into a form that is representation independent when %% printed. E.g., suppose Tree represents a variable %% named "V". Then (assuming a function print/1 for %% printing syntax trees), evaluating print(abstract(Tree)) %% - simply using abstract/1 to map the actual data %% structure onto a syntax tree representation - would output a string %% that might look something like "{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 is_tree/1 and %% 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 %% 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 unwrap/1 and %% 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 qualified names ("foo.bar.baz", %% "erl.lang.lists", etc.). The representation overlaps with the weird %% "Mnesia query record access" operators. The '.' operator is left %% associative, so folding should nest on the left. is_qualified_name({record_field, _, L, R}) -> is_qualified_name(L) andalso is_qualified_name(R); is_qualified_name({atom, _, _}) -> true; is_qualified_name(_) -> false. unfold_qualified_name(Node) -> lists:reverse(unfold_qualified_name(Node, [])). unfold_qualified_name({record_field, _, L, R}, Ss) -> unfold_qualified_name(R, unfold_qualified_name(L, Ss)); unfold_qualified_name(S, Ss) -> [S | Ss]. fold_qualified_name([S | Ss], Pos) -> fold_qualified_name(Ss, Pos, {atom, Pos, atom_value(S)}). fold_qualified_name([S | Ss], Pos, Ack) -> fold_qualified_name(Ss, Pos, {record_field, Pos, Ack, {atom, Pos, atom_value(S)}}); fold_qualified_name([], _Pos, Ack) -> Ack. %% 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). %% =====================================================================