erl_syntax(3)
💖 erl_syntax — the Erlang Module Definition, bestie edition 💖
okay so this is the manual page for erl_syntax from Erlang, and it's a big one, so grab a strawberry 🍓 and settle in. This is part 1 of 9, so we're not even done yet. slay.
✨ NAME ✨
erl_syntax — abstract Erlang syntax trees. yep, trees. of syntax. abstract ones. it's giving forest energy 🌈
🦋 DESCRIPTION 🦋
okay so real talk: this module defines an abstract data type for representing Erlang source code as syntax trees, and it does it in a way that's backwards compatible with the data structures that the Erlang standard library parser module erl_parse creates. those are often called "parse trees", which is, and I quote, "a bit of a misnomer" 😭 — the manpage said it, not me.
So what that actually means, bestie: every erl_parse tree is a valid abstract syntax tree. but the reverse? absolutely not. 🙅 abstract syntax trees in general can not be used as input to functions that expect an erl_parse tree. however — and this is the good part — as long as an abstract syntax tree represents a correct Erlang program, the function revert/1 should be able to transform it into the corresponding erl_parse representation. glow up complete ✨
If you're a first-time user (welcome, we love you here 🫶), the recommended starting point is the docs for the syntaxTree() data type, plus the function type/1.
NOTE: this module deals with the composition and decomposition of syntactic entities, not semantic ones. its whole purpose is to hide all direct references to the data structures used to represent these entities. with few exceptions, the functions in here do absolutely zero semantic interpretation of their inputs, and in general you're assumed to pass type-correct arguments — and if you don't? the effects are not defined. no cap, don't do it. 💀
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. not even in your notes app, babe. 📵
Furthermore — and this is a big vibe-check moment — we do not give any guarantees on how an abstract syntax tree may or may not be represented, with these exceptions:
- no syntax tree is represented by a single atom, such as
none - no syntax tree is represented by a list constructor
[X | Y] - no syntax tree is represented by the empty list
[]
These three things you can rely on when you're writing functions that operate on syntax trees. periodt. 👑
🎀 DATA TYPES 🎀
annotation_or_location() :: erl_anno:anno() | erl_anno:location().encoding() :: utf8 | unicode | latin1.erl_parse() ::
erl_parse:abstract_clause() |
erl_parse:abstract_expr() |
erl_parse:abstract_form() |
erl_parse:abstract_type() |
erl_parse:form_info() |
erl_parse:af_binelement(term()) |
erl_parse:af_generator() |
[erl_parse:af_generator()] |
erl_parse:af_remote_function().
forms() :: syntaxTree() | [syntaxTree()].guard() :: none | syntaxTree() | [syntaxTree()] | [[syntaxTree()]].padding() :: none | integer().syntaxTree() :: tree() | wrapper() | erl_parse().syntaxTreeAttributes() :: #attr{}.tree() :: #tree{}.wrapper() :: #wrapper{}.
💅 FUNCTIONS 💅
abstract(term()) -> syntaxTree().
Returns the syntax tree corresponding to an Erlang term. iconic behaviour, honestly.
Term must be a literal term, meaning one that can be represented as a source code literal. so that means it must not contain a process identifier, a port, a reference, or a function value as a subterm. 🚫 The function recognises printable strings too, so that you get a compact and readable representation (thoughtful queen 💖). Evaluation fails with reason badarg if Term is not a literal term.
See also: concrete/1, is_literal/1.
add_ann(term(), syntaxTree()) -> syntaxTree().
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. same result, less work, we love efficiency 💅
See also: get_ann/1, set_ann/2.
add_postcomments([syntaxTree()], syntaxTree()) -> syntaxTree().
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 also: add_precomments/2, comment/2, get_postcomments/1, join_comments/2, set_postcomments/2.
add_precomments([syntaxTree()], syntaxTree()) -> syntaxTree().
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 also: add_postcomments/2, comment/2, get_precomments/1, join_comments/2, set_precomments/2.
annotated_type(syntaxTree(), syntaxTree()) -> syntaxTree().
Creates an abstract annotated type expression. 🦋
The result represents "Name :: Type".
See also: annotated_type_body/1, annotated_type_name/1.
annotated_type_body(syntaxTree()) -> syntaxTree().
Returns the type subtrees of an annotated_type node.
See also: annotated_type/2.
annotated_type_name(syntaxTree()) -> syntaxTree().
Returns the name subtree of an annotated_type node.
See also: annotated_type/2.
application(syntaxTree(), [syntaxTree()]) -> syntaxTree().
Creates an abstract function application expression. we're applying functions, bestie, it's giving ✨
If Arguments is [A1, ..., An], the result represents "Operator(A1, ..., An)".
See also: application/3, application_arguments/1, application_operator/1.
application(none | syntaxTree(), syntaxTree(), [syntaxTree()]) -> syntaxTree().
Creates an abstract function application expression.
If Module is none, this call is equivalent to application(Function, Arguments), otherwise it is equivalent to application(module_qualifier(Module, Function), Arguments).
(This is a utility function.)
See also: application/2, module_qualifier/2.
application_arguments(syntaxTree()) -> [syntaxTree()].
Returns the list of argument subtrees of an application node.
See also: application/2.
application_operator(syntaxTree()) -> syntaxTree().
Returns the operator subtree of an application node.
If Node represents "M:F(...)", then the result is the subtree representing "M:F".
See also: application/2, module_qualifier/2.
arity_qualifier(syntaxTree(), syntaxTree()) -> syntaxTree().
Creates an abstract arity qualifier.
The result represents "Body/Arity".
See also: arity_qualifier_argument/1, arity_qualifier_body/1.
arity_qualifier_argument(syntaxTree()) -> syntaxTree().
Returns the argument (the arity) subtree of an arity_qualifier node.
See also: arity_qualifier/2.
arity_qualifier_body(syntaxTree()) -> syntaxTree().
Returns the body subtree of an arity_qualifier node.
See also: arity_qualifier/2.
atom(atom() | string()) -> syntaxTree().
Creates an abstract atom literal. 💜
The print name of the atom is the character sequence represented by Name.
See also: atom_literal/1, atom_literal/2, atom_name/1, atom_value/1, is_atom/2.
atom_literal(syntaxTree()) -> string().
Returns the literal string represented by an atom node.
This includes surrounding single-quote characters if necessary. Characters beyond 255 will be escaped.
Note: for example, the result ofatom("x\ny")represents any and all of'x\ny','x\12y','x\012y', and'x\^Jy'; seestring/1. multiple forms, one vibe 💫
See also: atom/1, string/1.
atom_literal(syntaxTree(), utf8 | unicode | latin1) -> string().
Returns the literal string represented by an atom node.
This includes surrounding single-quote characters if necessary. Depending on the encoding, a character beyond 255 will be escaped (latin1) or copied as is (utf8).
See also: atom/1, atom_literal/1, string/1.
atom_name(syntaxTree()) -> string().
Returns the printname of an atom node.
See also: atom/1.
atom_value(syntaxTree()) -> atom().
Returns the value represented by an atom node.
See also: atom/1.
attribute(syntaxTree(), none | [syntaxTree()]) -> syntaxTree().
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 ofBody(viewed as a sequence of tokens). Thetextnode type can be used for aBodythat is not a normal Erlang construct. yep, there's an escape hatch for the weird stuff 🫠
See also: attribute/1, attribute_arguments/1, attribute_name/1, is_form/1, text/1.
attribute_arguments(syntaxTree()) -> none | [syntaxTree()].
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. (yes, the manual literally says E1 twice there, and we copy it character for character because that's the rule, bestie 💅)
See also: attribute/1.
attribute_name(syntaxTree()) -> syntaxTree().
Returns the name subtree of an attribute node.
See also: attribute/1.
binary([syntaxTree()]) -> syntaxTree().
Creates an abstract binary-object template. <<iconic>> 🌸
If Fields is [F1, ..., Fn], the result represents "<<F1, ..., Fn>>".
See also: binary_field/2, binary_fields/1.
binary_comp(syntaxTree(), [syntaxTree()]) -> syntaxTree().
Creates an abstract binary comprehension.
If Body is [E1, ..., En], the result represents "<<Template || E1, ..., En>>".
See also: binary_comp_body/1, binary_comp_template/1, generator/2.
binary_comp_body(syntaxTree()) -> [syntaxTree()].
Returns the list of body subtrees of a binary_comp node.
See also: binary_comp/2.
binary_comp_template(syntaxTree()) -> syntaxTree().
Returns the template subtree of a binary_comp node.
See also: binary_comp/2.
binary_field(syntaxTree(), [syntaxTree()]) -> syntaxTree().
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 also: binary/1, binary_field/1, binary_field/3, binary_field_body/1, binary_field_size/1, binary_field_types/1.
binary_field(syntaxTree(), none | syntaxTree(), [syntaxTree()]) -> syntaxTree().
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.)
okay that's it for part 1, don't go anywhere, there's more 🫶
See also:binary/1,binary_field/2,size_qualifier/2.
binary_field_body/1 🌸
binary_field_body(syntaxTree()) -> syntaxTree().
Hands you back the body subtree of a binary_field, bestie. That's the whole job.
See also: binary_field/2.
binary_field_size/1 📏
binary_field_size(syntaxTree()) -> none | syntaxTree().
Gives you the size-specifier subtree of a binary_field node — if there is one, no pressure.
So like, if Node is doing "Body:Size" or the whole "Body:Size/T1, ..., Tn" situation, you get Size back. Otherwise? You get none. That's it. That's the vibe. 🫶
(This one is a utility function, btw.)
See also:binary_field/2,binary_field/3.
binary_field_types/1 🎀
binary_field_types(syntaxTree()) -> [syntaxTree()].
Returns the list of type-specifier subtrees of a binary_field node.
If Node is representing ".../T1, ..., Tn" then you get [T1, ..., Tn] — slay. In literally every other case the result is the empty list, and that's on periodt.
See also: binary_field/2.
binary_fields/1 🧚
binary_fields(syntaxTree()) -> [syntaxTree()].
Returns the list of field subtrees of a binary node. All of them. Every last one. 💅
See also:binary/1,binary_field/2.
binary_generator/2 ✨
binary_generator(syntaxTree(), syntaxTree()) -> syntaxTree().
Creates an abstract binary_generator. The result represents "Pattern <= Body".
See also:binary_comp/2,binary_generator_body/1,binary_generator_pattern/1,list_comp/2,map_comp/2.
binary_generator_body/1 🌈
binary_generator_body(syntaxTree()) -> syntaxTree().
Returns the body subtree of a generator node. Just the body, nothing extra.
See also: binary_generator/2.
binary_generator_pattern/1 🦋
binary_generator_pattern(syntaxTree()) -> syntaxTree().
Returns the pattern subtree of a generator node. The pattern's the main character here, fr fr.
See also: binary_generator/2.
bitstring_type/2 ⭐
bitstring_type(syntaxTree(), syntaxTree()) -> syntaxTree().
Creates an abstract bitstring type. The result represents "<<_:M, _:_N>>" — yes it's giving bit-syntax and yes it ate.
See also:bitstring_type_m/1,bitstring_type_n/1.
bitstring_type_m/1 💜
bitstring_type_m(syntaxTree()) -> syntaxTree().
Returns the number of start bits, M, of a bitstring_type node. The M. The start bits. Iconic behaviour.
See also: bitstring_type/2.
bitstring_type_n/1 🍓
bitstring_type_n(syntaxTree()) -> syntaxTree().
Returns the segment size, N, of a bitstring_type node. Not the start bits — the segment size. Different girl, different job. 😌
See also: bitstring_type/2.
block_expr/1 🎀
block_expr([syntaxTree()]) -> syntaxTree().
Creates an abstract block expression. If Body is [B1, ..., Bn], the result represents "begin B1, ..., Bn end". It's giving wrapped-up-and-ready.
See also: block_expr_body/1.
block_expr_body/1 🌸
block_expr_body(syntaxTree()) -> [syntaxTree()].
Returns the list of body subtrees of a block_expr node.
See also: block_expr/1.
case_expr/2 💖
case_expr(syntaxTree(), [syntaxTree()]) -> syntaxTree().
Creates an abstract case-expression, bestie. If Clauses is [C1, ..., Cn], the result represents "case Argument of C1; ...; Cn end".
More exactly — and I do mean exactly — if each Ci represents "(Pi) Gi -> Bi", then the result represents "case Argument of P1G1 -> B1; ...; PnGn -> Bn end". Lowkey a mouthful, highkey accurate.
See also:case_expr_argument/1,case_expr_clauses/1,clause/3,if_expr/1.
case_expr_argument/1 🔥
case_expr_argument(syntaxTree()) -> syntaxTree().
Returns the argument subtree of a case_expr node. You know, the thing you're casing on. 💅
See also: case_expr/2.
case_expr_clauses/1 🧚
case_expr_clauses(syntaxTree()) -> [syntaxTree()].
Returns the list of clause subtrees of a case_expr node. All the clauses, in order, no notes. 👑
See also: case_expr/2.
catch_expr/1 🦋
catch_expr(syntaxTree()) -> syntaxTree().
Creates an abstract catch-expression. The result represents "catch Expr". Simple. Clean. Caught. 😌
See also: catch_expr_body/1.
catch_expr_body/1 🌈
catch_expr_body(syntaxTree()) -> syntaxTree().
Returns the body subtree of a catch_expr node — i.e. Expr, the thing being caught.
See also: catch_expr/1.
char/1 🍓
char(char()) -> syntaxTree().
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. Not even close. For example, the character"a"can be written both as"$a"and"$\141", and a Tab character can be written as"$\11","$\011", or"$\t". So many outfits for one little character, honestly obsessed.
See also:char_literal/1,char_literal/2,char_value/1,is_char/2.
char_literal/1 ✨
char_literal(syntaxTree()) -> nonempty_string().
Returns the literal string represented by a char node.
This includes the leading "$" character. Characters beyond 255 will be escaped. No cap.
See also: char/1.
char_literal/2 💜
char_literal(syntaxTree(), encoding()) -> nonempty_string().
Returns the literal string represented by a char node — same deal, leading "$" included.
Depending on the encoding, a character beyond 255 will either be escaped (latin1) or copied as is (utf8). Pick your fighter. 🎀
See also: char/1.
char_value/1 🌟
char_value(syntaxTree()) -> char().
Returns the value represented by a char node. Decimals of the char world.
See also: char/1.
class_qualifier/2 🎀
class_qualifier(syntaxTree(), syntaxTree()) -> syntaxTree().
Creates an abstract class qualifier. The result represents "Class:Body".
See also:class_qualifier_argument/1,class_qualifier_body/1,class_qualifier_stacktrace/1,try_expr/4.
class_qualifier/3 💅
class_qualifier(syntaxTree(), syntaxTree(), syntaxTree()) ->
syntaxTree().
Creates an abstract class qualifier — the three-argument edition. The result represents "Class:Body:Stacktrace". Yes she brought the stacktrace too. 😭
See also:class_qualifier_argument/1,class_qualifier_body/1,try_expr/4.
class_qualifier_argument/1 🦋
class_qualifier_argument(syntaxTree()) -> syntaxTree().
Returns the argument (the class) subtree of a class_qualifier node. That's the class part, bestie.
See also: class_qualifier/2.
class_qualifier_body/1 🌸
class_qualifier_body(syntaxTree()) -> syntaxTree().
Returns the body subtree of a class_qualifier node. Just the body.
See also: class_qualifier/2.
class_qualifier_stacktrace/1 🌈
class_qualifier_stacktrace(syntaxTree()) -> syntaxTree().
Returns the stacktrace subtree of a class_qualifier node. There she is. 👑
See also: class_qualifier/2.
clause/3 🔥
clause([syntaxTree()], guard(), [syntaxTree()]) -> syntaxTree().
Creates an abstract clause — the building block of case branches and function heads everywhere.
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".
And for simplicity, the Guard argument may also be any of the following, because we're flexible like that:
- An empty list
[]. This is equivalent to passingnone. Same energy. - A nonempty list
[E1, ..., Ej]of syntax trees. This is equivalent to passingconjunction([E1, ..., Ej]). - A nonempty list of lists of syntax trees
[[E1_1, ..., E1_k1], ..., [Ej_1, ..., Ej_kj]], which is equivalent to passingdisjunction([conjunction([E1_1, ..., E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])]).
See also:clause/2,clause_body/1,clause_guard/1,clause_patterns/1.
clause_body/1 🍓
clause_body(syntaxTree()) -> [syntaxTree()].
Returns the list of body subtrees of a clause node. (Yes the original said "Return", we're all out here living our truth. 💅)
See also: clause/3.
clause_guard/1 ⭐
clause_guard(syntaxTree()) -> none | syntaxTree().
Returns the guard subtree of a clause node, if any.
If Node represents "(P1, ..., Pn) when Guard -> B1, ..., Bm", then Guard is returned. Otherwise the result is none. Guard's not home, leave a message. 🙈
See also: clause/3.
clause_patterns/1 💜
clause_patterns(syntaxTree()) -> [syntaxTree()].
Returns the list of pattern subtrees of a clause node. All the patterns, matched up like besties. 🫶
See also: clause/3.
comment/2 🎀
comment(padding(), [string()]) -> syntaxTree().
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. And get this — comments are in themselves regarded as source program forms. They're valid, they're serving, they count. 💅
See also:comment/1,is_form/1.
comment_padding/1 🌸
comment_padding(syntaxTree()) -> padding().
Returns the amount of padding before the comment, or none.
none means that a default padding may be used. Chill, it'll sort itself out.
See also: comment/2.
comment_text/1 ✨
comment_text(syntaxTree()) -> [string()].
Returns the lines of text of the abstract comment. Every line. No skipping. 💖
See also: comment/2.
compact_list/1 🧚
compact_list(syntaxTree()) -> syntaxTree().
Yields the most compact form for an abstract list skeleton. Glow-down, basically.
The result either represents "[E1, ..., En | Tail]", where Tail is not a list skeleton — or otherwise simply "[E1, ..., En]".
Heads up tho: annotations on subtrees of Node that represent list skeletons may be lost, but comments will be propagated to the result. (We keep the commentary, we drop the notes. 🙈) If Node doesn't represent a list skeleton, you just get Node itself back. No notes.
See also:list/2,normalize_list/1.
concrete/1 🔥
concrete(syntaxTree()) -> term().
Returns the Erlang term represented by a syntax tree. Bringing it from abstract to real. ✨
Evaluation fails with reason badarg if Node does not represent a literal term. So yeah — only literal terms, or it's a whole badarg moment. 💀
NOTE: the set of syntax trees which have a concrete representation is larger than the set of trees which can be built using the functionabstract/1. An abstract character will be concretised as an integer, whileabstract/1does not at present yield an abstract character for any input. (Use thechar/1function to explicitly create an abstract character.)
NOTE:arity_qualifiernodes are recognized. This is to follow the Erlang Parser when it comes to wild attributes: both{F, A}andF/Aare recognized, which makes it possible to turn wild attributes into recognized attributes without at the same time making it impossible to compile files using the new syntax with the old version of the Erlang Compiler. Iconic compatibility behaviour honestly. 👑
See also:abstract/1,char/1,is_literal/1.
conjunction/1 🌈
conjunction([syntaxTree()]) -> syntaxTree().
Creates an abstract conjunction. If List is [E1, ..., En], the result represents "E1, ..., En". All of them, together, holding hands. 🫶
See also:conjunction_body/1,disjunction/1.
conjunction_body/1 💜
conjunction_body(syntaxTree()) -> [syntaxTree()].
Returns the list of body subtrees of a conjunction node.
See also: conjunction/1.
cons/2 🦋
cons(syntaxTree(), syntaxTree()) -> syntaxTree().
The "Optimizing" list skeleton cons operation. (And that's where this part of the page cuts off, bestie — more coming. 💖)
okay bestie, continuing where we left off 💖
This one's creates an abstract list skeleton energy — the first element is Head and the tail corresponds to Tail. It's basically like list([Head], Tail), except that Tail must not be none, and the result doesn't necessarily represent exactly "[Head | Tail]" — it may depend on the Tail subtree. 🦋
Like, for example: if Tail represents [X, Y], the result may represent "[Head, X, Y]", rather than "[Head | [X, Y]]". And the way that annotations on Tail itself may be lost if Tail represents a list skeleton, but comments on Tail are propagated to the result. lowkey important. ✨
See also:list/2,list_head/1,list_tail/1.
constrained_function_type(syntaxTree(), [syntaxTree()]) -> syntaxTree().
Creates an abstract constrained function type. 🌸
If FunctionConstraint is [C1, ..., Cn], the result represents "FunctionType when C1, ...Cn".
See also:constrained_function_type_argument/1,constrained_function_type_body/1.
constrained_function_type_argument(syntaxTree()) -> syntaxTree().
Returns the function constraint subtree of a constrained_function_type node. 💅
See also: constrained_function_type/2.
constrained_function_type_body(syntaxTree()) -> syntaxTree().
Returns the function type subtree of a constrained_function_type node.
See also: constrained_function_type/2.
constraint(syntaxTree(), [syntaxTree()]) -> syntaxTree().
Creates an abstract (subtype) constraint. ✨
The result represents "Name :: Type".
See also:constraint_argument/1,constraint_body/1.
constraint_argument(syntaxTree()) -> syntaxTree().
Returns the name subtree of a constraint node.
See also: constraint/2.
constraint_body(syntaxTree()) -> [syntaxTree()].
Returns the type subtree of a constraint node. 🎀
See also: constraint/2.
the copy crew is here and they are ICONIC for not duplicating work 👑
copy_ann(syntaxTree(), syntaxTree()) -> syntaxTree().
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 also:get_ann/1,set_ann/2.
copy_attrs(syntaxTree(), syntaxTree()) -> syntaxTree().
Copies the attributes from Source to Target.
Note: this is equivalent to set_attrs(Target, get_attrs(Source)), but potentially more efficient.
See also:get_attrs/1,set_attrs/2.
copy_comments(syntaxTree(), syntaxTree()) -> syntaxTree().
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 also:comment/2,get_postcomments/1,get_precomments/1,set_postcomments/2,set_precomments/2.
copy_pos(syntaxTree(), syntaxTree()) -> syntaxTree().
Copies the annotation from Source to Target. 🦋
This is equivalent to set_pos(Target, get_pos(Source)), but potentially more efficient.
See also:get_pos/1,set_pos/2.
data(syntaxTree()) -> term().
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 also: tree/2.
disjunction([syntaxTree()]) -> syntaxTree().
Creates an abstract disjunction. ✨
If List is [E1, ..., En], the result represents "E1; ...; En".
See also:conjunction/1,disjunction_body/1.
disjunction_body(syntaxTree()) -> [syntaxTree()].
Returns the list of body subtrees of a disjunction node.
See also: disjunction/1.
else_expr([syntaxTree()]) -> syntaxTree().
Creates an abstract else-expression. 💜
If Clauses is [C1, ..., Cn], the result represents "else C1; ...; Cn end". More exactly, if each Ci represents "(Pi) Gi -> Bi", then the result represents "else (P1) G1 -> B1; ...; (Pn) Gn -> Bn end".
See also:clause/3,else_expr_clauses/1,maybe_expr/2.
else_expr_clauses(syntaxTree()) -> [syntaxTree()].
Returns the list of clause subtrees of an else_expr node.
See also: else_expr/1.
eof_marker() -> syntaxTree().
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 also:error_marker/1,is_form/1,warning_marker/1.
error_marker(term()) -> syntaxTree().
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 //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 also:eof_marker/0,error_marker_info/1,is_form/1,warning_marker/1.
error_marker_info(syntaxTree()) -> term().
Returns the ErrorInfo structure of an error_marker node.
See also: error_marker/1.
flatten_form_list(syntaxTree()) -> syntaxTree().
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 also: form_list/1.
float(float()) -> syntaxTree().
Creates an abstract floating-point literal. ⭐
The lexical representation is the decimal floating-point numeral of Value.
See also:float_literal/1,float_value/1.
float_literal(syntaxTree()) -> string().
Returns the numeral string represented by a float node.
See also: float/1.
float_value(syntaxTree()) -> float().
Returns the value represented by a float node.
Note that floating-point values should usually not be compared for equality. 👀
See also: float/1.
form_list([syntaxTree()]) -> syntaxTree().
Creates an abstract sequence of "source code forms". 🎀
If Forms is [F1, ..., Fn], where each Fi is a form (see is_form/1), the result represents:
F1
Fn
where the Fi are separated by one or more line breaks. A node of type form_list is itself regarded as a source code form; see flatten_form_list/1.
NOTE: This is simply a way of grouping source code forms into a single syntax tree, usually to form an Erlang module definition.
See also:flatten_form_list/1,form_list_elements/1,is_form/1.
form_list_elements(syntaxTree()) -> [syntaxTree()].
Returns the list of subnodes of a form_list node.
See also: form_list/1.
fun_expr([syntaxTree()]) -> syntaxTree().
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 also:fun_expr_arity/1,fun_expr_clauses/1.
fun_expr_arity(syntaxTree()) -> arity().
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 also:clause/3,clause_patterns/1,fun_expr/1,fun_expr_clauses/1.
fun_expr_clauses(syntaxTree()) -> [syntaxTree()].
Returns the list of clause subtrees of a fun_expr node.
See also: fun_expr/1.
fun_type() -> syntaxTree().
Creates an abstract fun of any type. ✨
The result represents "fun()".
function(syntaxTree(), [syntaxTree()]) -> syntaxTree().
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 also:function_arity/1,function_clauses/1,function_name/1,is_form/1.
function_arity(syntaxTree()) -> arity().
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 also:clause/3,clause_patterns/1,function/2,function_clauses/1.
function_clauses(syntaxTree()) -> [syntaxTree()].
Returns the list of clause subtrees of a function node.
See also: function/2.
function_name(syntaxTree()) -> syntaxTree().
Returns the name subtree of a function node. 🌸
See also: function/2.
function_type(any_arity | [syntaxTree()], syntaxTree()) -> syntaxTree().
Creates an abstract function type. 🦋
If Arguments is [T1, ..., Tn] and it occurs within a function specification, the result represents "(T1, ...Tn) -> Return"; otherwise it represents "fun((T1, ...Tn) -> Return)". If Arguments is any_arity, it represents "fun((...) -> Return)".
Note that theerl_parserepresentation is identical for"FunctionType"and"fun(FunctionType)". 🤯
See also:function_type_arguments/1,function_type_return/1.
function_type_arguments(syntaxTree()) -> any_arity | [syntaxTree()].
Returns the argument types subtrees of a function_type node.
If Node represents "fun((...) -> Return)", any_arity is returned; otherwise, if Node represents "(T1, ...Tn) -> Return" or "fun((T1, ...Tn) -> Return)", [T1, ..., Tn] is returned.
See also:function_type/1,function_type/2.
function_type_return(syntaxTree()) -> syntaxTree()— bestie, this gives you the return type subtrees of afunction_typenode. that's it, that's the vibe 🎀
See also: function_type/1, function_type/2.
generator(syntaxTree(), syntaxTree()) -> syntaxTree()— builds an abstract list generator ✨
the result is literally giving "Pattern <- Body".
See also: binary_comp/2, generator_body/1, generator_pattern/1, map_comp/2, list_comp/2.
generator_body(syntaxTree()) -> syntaxTree()— hands you back the body subtree of ageneratornode 🫶
See also: generator/2.
generator_pattern(syntaxTree()) -> syntaxTree()— returns the pattern subtree of ageneratornode 💜
See also: generator/2.
get_ann(syntaxTree()) -> [term()]— returns the list of user annotations hanging out with a syntax tree 🌸
for a freshly-created node? that's the empty list, periodt. the annotations can be literally any terms.
See also: get_attrs/1, set_ann/2.
get_attrs(syntaxTree()) -> syntaxTreeAttributes()— gives you a representation of the attributes attached to a syntax tree node ✨
attributes are all the extra info that can be glued onto a node. rn that includes position information, source code comments, and user annotations. the result of this function? you can't inspect it directly, bestie — you can only attach it to another node (peep set_attrs/2).
for getting at the individual attributes, go see get_pos/1, get_ann/1, get_precomments/1 and get_postcomments/1.
See also: get_ann/1, get_pos/1, get_postcomments/1, get_precomments/1, set_attrs/2.
get_pos(syntaxTree()) -> annotation_or_location()— returns the annotation (check//stdlib/erl_anno) associated with Node 👑
by default, all brand-new tree nodes have their position info set to the integer zero. use //stdlib/erl_anno:location/1 or //stdlib/erl_anno:line/1 to actually get the position info out.
See also: get_attrs/1, set_pos/2.
get_postcomments(syntaxTree()) -> [syntaxTree()]— returns the post-comments attached to a node 💬
this is a possibly empty list of abstract comments, in top-down textual order. when the code gets formatted, post-comments typically show up to the right of and/or below the node. like:
{foo, X, Y} % Post-comment of tuple
if it's possible, the comment should get moved past any following separator characters on the same line, rather than dumping the separators onto the next line. for example:
foo([X | Xs], Y) ->
foo(Xs, bar(X)); % Post-comment of 'bar(X)' node
...
(where the comment gets moved past the rightmost ")" and the ";").
See also: comment/2, get_attrs/1, get_precomments/1, set_postcomments/2.
get_precomments(syntaxTree()) -> [syntaxTree()]— returns the pre-comments attached to a node 🌸
this is a possibly empty list of abstract comments, in top-down textual order. when the code gets formatted, pre-comments usually sit directly above the node. like:
% Pre-comment of function
foo(X) -> {bar, X}.
if it's possible, the comment should get moved before any preceding separator characters on the same line. for example:
foo([X | Xs]) ->
% Pre-comment of 'bar(X)' node
[bar(X) | foo(Xs)];
(where the comment gets moved before the "[").
See also: comment/2, get_attrs/1, get_postcomments/1, set_precomments/2.
has_comments(syntaxTree()) -> boolean()— yieldsfalseif the node has no comments attached, andtrueotherwise 🫶
note: this is equivalent to (get_precomments(Node) == []) and (get_postcomments(Node) == []), but potentially more efficient. iykyk 💅
See also: get_postcomments/1, get_precomments/1, remove_comments/1.
if_expr([syntaxTree()]) -> syntaxTree()— 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 also: case_expr/2, clause/3, if_expr_clauses/1.
if_expr_clauses(syntaxTree()) -> [syntaxTree()]— returns the list of clause subtrees of anif_exprnode 💖
See also: if_expr/1.
implicit_fun(syntaxTree()) -> syntaxTree()— creates an abstract "implicit fun" expression 🌈
the result represents "fun Name". Name should represent either F/A or M:F/A.
See also: arity_qualifier/2, implicit_fun/2, implicit_fun/3, implicit_fun_name/1, module_qualifier/2.
implicit_fun(syntaxTree(), none | syntaxTree()) -> syntaxTree()— also creates an abstract "implicit fun" expression 👑
if Arity is none, this is equivalent to implicit_fun(Name), otherwise it's equivalent to implicit_fun(arity_qualifier(Name, Arity)).
(this is a utility function.)
See also: implicit_fun/1, implicit_fun/3.
implicit_fun(none | syntaxTree(), syntaxTree(), syntaxTree()) -> syntaxTree()— creates an abstract module-qualified "implicit fun" expression 💜
if Module is none, this is equivalent to implicit_fun(Name, Arity), otherwise it's equivalent to implicit_fun(module_qualifier(Module, arity_qualifier(Name, Arity)).
(this is a utility function.)
See also: implicit_fun/1, implicit_fun/2.
implicit_fun_name(syntaxTree()) -> syntaxTree()— returns the name subtree of animplicit_funnode 🌟
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 also: arity_qualifier/2, implicit_fun/1, module_qualifier/2.
infix_expr(syntaxTree(), syntaxTree(), syntaxTree()) -> syntaxTree()— creates an abstract infix operator expression 🔥
the result represents "Left Operator Right".
See also: infix_expr_left/1, infix_expr_operator/1, infix_expr_right/1, prefix_expr/2.
infix_expr_left(syntaxTree()) -> syntaxTree()— returns the left argument subtree of aninfix_exprnode 🫶
See also: infix_expr/3.
infix_expr_operator(syntaxTree()) -> syntaxTree()— returns the operator subtree of aninfix_exprnode 💅
See also: infix_expr/3.
infix_expr_right(syntaxTree()) -> syntaxTree()— returns the right argument subtree of aninfix_exprnode ✨
See also: infix_expr/3.
integer(integer()) -> syntaxTree()— creates an abstract integer literal 🔢
the lexical representation is the canonical decimal numeral of Value.
See also: integer_literal/1, integer_value/1, is_integer/2.
integer_literal(syntaxTree()) -> string()— returns the numeral string represented by anintegernode 🎀
See also: integer/1.
integer_range_type(syntaxTree(), syntaxTree()) -> syntaxTree()— creates an abstract range type 🌈
the result represents "Low .. High".
See also: integer_range_type_high/1, integer_range_type_low/1.
integer_range_type_high(syntaxTree()) -> syntaxTree()— returns the high limit of aninteger_range_typenode ⬆️
See also: integer_range_type/2.
integer_range_type_low(syntaxTree()) -> syntaxTree()— returns the low limit of aninteger_range_typenode ⬇️
See also: integer_range_type/2.
integer_value(syntaxTree()) -> integer()— returns the value represented by anintegernode 💖
See also: integer/1.
is_atom(syntaxTree(), atom()) -> boolean()— returnstrueif Node has typeatomand represents Value, otherwisefalse⭐
See also: atom/1.
is_char(syntaxTree(), char()) -> boolean()— returnstrueif Node has typecharand represents Value, otherwisefalse🌸
See also: char/1.
is_form(syntaxTree()) -> boolean()— returnstrueif Node is a syntax tree representing a so-called "source code form", otherwisefalse📜
forms are the Erlang source code units which, placed in sequence, constitute an Erlang program. current form types are:
attributecommenterror_markereof_markerform_listfunctionwarning_markertext
See also: attribute/2, comment/2, eof_marker/0, error_marker/1, form_list/1, function/2, type/1, warning_marker/1.
is_integer(syntaxTree(), integer()) -> boolean()— returnstrueif Node has typeintegerand represents Value, otherwisefalse💅
See also: integer/1.
is_leaf(syntaxTree()) -> boolean()— returnstrueif Node is a leaf node, otherwisefalse🍃
the currently recognised leaf node types are:
atomcharcommenteof_markererror_markerfloatfun_typeintegerniloperatorstringtextunderscorevariablewarning_marker
a node of type map_expr is a leaf node if and only if it has no argument and no fields. a node of type map_type is a leaf node if and only if it has no fields (any_size). a node of type tuple is a leaf node if and only if its arity is zero. a node of type tuple_type is a leaf node if and only if it has no elements (any_size).
note: not all literals are leaf nodes, and vice versa. for example, 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 also: is_literal/1, type/1.
is_list_skeleton(syntaxTree()) -> boolean()— returnstrueif Node has typelistornil, otherwisefalse🧚
See also: list/2, nil/0.
is_literal(syntaxTree()) -> boolean()— returnstrueif Node represents a literal term, otherwisefalse🔥
this function returns true if and only if the value of concrete(Node) is defined.
See also: abstract/1, concrete/1.
is_proper_list(syntaxTree()) -> boolean()—
so real talk 💖 this one hands you back true if Node represents a proper list, and false if it doesn't — no in-between, no vibes-only answers, ok?
A proper list, for the record, is a list skeleton that's either shaped like [] or [E1, ..., En], or shaped like [... | Tail] where — and yes this part is recursive, bestie, hold my hand 🫶 — Tail also represents a proper list. Down and down and down until something bottoms out. 🌀
NOTE: SinceNodeis a syntax tree, the actual run-time values hanging off its subtrees can often be partially or completely unknown. Like, ifNoderepresents[... | Ns](whereNsis a variable), the function returnsfalse, because nobody — not even the compiler — knows whetherNswill end up bound to a list at run-time. It's giving mystery 🔮. Flip side: ifNoderepresents something like[1, 2, 3]or[A | []], you gettrue. Periodt.
See also: list/2.
is_string(syntaxTree(), string()) -> boolean().
Returns true if Node has type string and represents Value. Anything else? false. Simple as. ✨
See also: string/1.
is_tree(syntaxTree()) -> boolean().
For special purposes only. 👑 Like, don't just casually reach for this one at a party. It returns true if Tree is an abstract syntax tree, and false if it isn't.
NOTE: This one yieldsfalsefor all "old-style"erl_parse-compatible "parse trees". So if you're coming here with a vintage parse tree expecting a warm welcome… it's not coming, bestie. 💀
See also: tree/2.
join_comments(syntaxTree(), syntaxTree()) -> syntaxTree().
Glues the comments of Source onto the current comments of Target. Comment merge arc unlocked. 🎀
Note: this is equivalent to add_postcomments(get_postcomments(Source), add_precomments(get_precomments(Source), Target)), but potentially more efficient — same result, less work. Slay.
See also: add_postcomments/2, add_precomments/2, comment/2, get_postcomments/1, get_precomments/1.
list([syntaxTree()], none | syntaxTree()) -> syntaxTree().
Builds you an abstract list skeleton. The construction era. 🔨
The result has type list or nil. If List is a nonempty list [E1, ..., En], the result has type list, and it represents either [E1, ..., En] when Tail is none, or otherwise [E1, ..., En | Tail]. And if List is the empty list, then Tail must be none, and in that case the result has type nil and represents [] (peep nil/0).
Okay so, the difference between lists as semantic objects — built up out of individual "cons" and "nil" terms — and the various syntactic forms for denoting lists… yeah, that can be lowkey bewildering at first, no cap. 😵💫 That's why this module gives you functions for both: exact control over the syntactic representation, and the simple composition/deconstruction in terms of cons and head/tail operations. Both energies, one module.
NOTE: Inlist(Elements, none), the "nil" list terminator is implicit and has no associated information (checkget_attrs/1). But in the seemingly equivalentlist(Elements, Tail)whereTailhas the typenil, that list terminator subtreeTailcan have attached attributes — position, comments, annotations — and those will be preserved in the result. So they are not actually identical, bestie. ⚠️
See also: compact_list/1, cons/2, get_attrs/1, is_list_skeleton/1, is_proper_list/1, list/1, list_elements/1, list_head/1, list_length/1, list_prefix/1, list_suffix/1, list_tail/1, nil/0, normalize_list/1.
list_comp(syntaxTree() | [syntaxTree()], [syntaxTree()]) -> syntaxTree().
Creates an abstract list comprehension. If Body is [E1, ..., En], the result represents [Template || E1, ..., En]. Chef's kiss. 😘
It also supports comprehensions that emit multiple elements per iteration, from EEP 78 — in those cases, Template is a list of expressions. Multiple emissions, one iteration, we love to see it. 🔥
See also: generator/2, list_comp_body/1, list_comp_template/1.
list_comp_body(syntaxTree()) -> [syntaxTree()].
Hands you the list of body subtrees of a list_comp node. Gimme the body, body. 💃
See also: list_comp/2.
list_comp_template(syntaxTree()) -> syntaxTree() | [syntaxTree()].
Returns the template subtree of a list_comp node.
Supports comprehensions with multiple emitted elements per iteration, from EEP 78 — in such cases, template will be a list of expressions. Yeah, it can be a single thing or a whole list of them, she contains multitudes. 🦋
See also: list_comp/2.
list_elements(syntaxTree()) -> [syntaxTree()].
Returns the list of element subtrees of a list skeleton.
Node must represent a proper list. For example, if Node represents [X1, X2 | [X3, X4 | []], then list_elements(Node) yields the list [X1, X2, X3, X4]. Un-nesting everything so you don't have to. 💅
See also: is_proper_list/1, list/2.
list_head(syntaxTree()) -> syntaxTree().
Returns the head element subtree of a list node. If Node represents [Head ...], then the result will represent Head. Head = got. 🫡
See also: cons/2, list/2, list_tail/1.
list_length(syntaxTree()) -> non_neg_integer().
Returns the number of element subtrees of a list skeleton. Node must represent a proper list. For example, if Node represents [X1 | [X2, X3 | [X4, X5, X6]]], then list_length(Node) returns the integer 6. Go on, count them, I'll wait. 👑
NOTE: This is equivalent to length(list_elements(Node)), but potentially more efficient. Faster, same tea. ⚡
See also: is_proper_list/1, list/2, list_elements/1.
list_prefix(syntaxTree()) -> [syntaxTree()].
Returns the prefix element subtrees of a list node. If Node represents [E1, ..., En] or [E1, ..., En | Tail], the returned value is [E1, ..., En]. Everything before the tail, bestie. ✂️
See also: list/2.
list_suffix(syntaxTree()) -> none | syntaxTree().
Returns the suffix subtree of a list node, if one exists. If Node represents [E1, ..., En | Tail], the returned value is Tail. Otherwise, if Node represents [E1, ..., En], you get none back. No tail, no problem. 🙈
NOTE: Even when this function returns aTailthat is notnone, the type ofTailcan benil— that happens if the tail was given explicitly and the list skeleton hasn't been compacted (seecompact_list/1). So don't let the type throw you off. 😭
See also: compact_list/1, list/2, nil/0.
list_tail(syntaxTree()) -> syntaxTree().
Returns the tail of a list node. If Node represents a single-element list [E], then the result has type nil and represents []. If Node represents [E1, E2 ...], the result will represent [E2 ...]. And if Node represents [Head | Tail], the result will represent Tail. Tail acquired, mission complete. 🌈
See also: cons/2, list/2, list_head/1.
macro(syntaxTree(), none | [syntaxTree()]) -> syntaxTree().
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 represent ?Name() — parentheses included, matching pair and all. So empty-list-args and no-args are not the same look, iykyk. 👀
The only syntactical limitation the preprocessor imposes on the arguments to a macro application (when you look at them as sequences of tokens) is that they must be balanced with respect to parentheses, brackets, begin ... end, case ... end, and so on. And the text node type exists so you can represent arguments that aren't regular Erlang constructs at all. Rolodex of possibilities. 📚
See also: macro/1, macro_arguments/1, macro_name/1, text/1.
macro_arguments(syntaxTree()) -> none | [syntaxTree()].
Returns the list of argument subtrees of a macro node, if there are any. If Node represents ?Name, you get none. Otherwise, if Node represents ?Name(A1, ..., An), you get [A1, ..., An]. Give me the receipts (the arguments). 🧾
See also: macro/2.
macro_name(syntaxTree()) -> syntaxTree().
Returns the name subtree of a macro node. The name. That's it. That's the tweet. 🐦
See also: macro/2.
make_tree(atom(), [[syntaxTree()]]) -> syntaxTree().
Creates a syntax tree with the given type and subtrees. 🎨
Type must be a node type name (peep type/1) that does not denote a leaf node type (peep 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 exactly as they would occur in the printed program text, grouped by category the same way subtrees/1 does it.
So: the result of copy_attrs(Node, make_tree(type(Node), subtrees(Node))) (check update_tree/2) represents the same source code text as the original Node, assuming that subtrees(Node) yields a nonempty list. However — and this is important, don't skip it — it does not necessarily have the same data representation as Node. Same text, different internals. 👯
See also: copy_attrs/2, is_leaf/1, subtrees/1, type/1, update_tree/2.
map_comp(syntaxTree() | [syntaxTree()], [syntaxTree()]) -> syntaxTree().
Creates an abstract map comprehension. If Body is [E1, ..., En], the result represents #{Template || E1, ..., En}. 💖
Supports comprehensions with multiple emitted elements per iteration, from EEP 78 — in such cases, Template is a list of key-value associations. Keys, values, all of it, multiple per loop. Obsessed. 💅
See also: generator/2, map_comp_body/1, map_comp_template/1.
map_comp_body(syntaxTree()) -> [syntaxTree()].
Returns the list of body subtrees of a map_comp node. The whole body, served on a plate. 🍓
See also: map_comp/2.
map_comp_template(syntaxTree()) -> syntaxTree() | [syntaxTree()].
Returns the template subtree of a map_comp node.
Supports comprehensions with multiple emitted elements per iteration, from EEP 78 — in such cases, template will be list of key-value associations. Yes, the template itself can be a whole lil' lineup. 🧚
See also: map_comp/2.
map_expr(none | syntaxTree(), [syntaxTree()]) -> syntaxTree().
Creates an abstract map expression. If Fields is [F1, ..., Fn], then: if Argument is none, the result represents #{F1, ..., Fn}; otherwise it represents Argument#{F1, ..., Fn}. With or without a thing in front of the #, your call. 🔥
See also: map_expr/1, map_expr_argument/1, map_expr_fields/1, map_field_assoc/2, map_field_exact/2.
map_expr_argument(syntaxTree()) -> none | syntaxTree().
Returns the argument subtree of a map_expr node, if there is one. If Node represents #{...}, you get none. Otherwise, if Node represents Argument#{...}, you get Argument. The thing before the #, retrieved. 🫶
See also: map_expr/2.
map_expr_fields(syntaxTree()) -> [syntaxTree()].
Returns the list of field subtrees of a map_expr node. All the fields, in a neat list. 🌸
See also: map_expr/2.
map_field_assoc(syntaxTree(), syntaxTree()) -> syntaxTree().
Creates an abstract map assoc field. The result represents Name => Value. The => energy, specifically. ➡️
See also: map_expr/2, map_field_assoc_name/1, map_field_assoc_value/1.
map_field_assoc_name(syntaxTree()) -> syntaxTree().
Returns the name subtree of a map_field_assoc node. The Name in Name => Value. 👑
See also: map_field_assoc/2.
map_field_assoc_value(syntaxTree()) -> syntaxTree().
Returns the value subtree of a map_field_assoc node. And that's the Value side of Name => Value. 💜
See also: map_field_assoc/2. ✨
map_field_exact(syntaxTree(), syntaxTree()) -> syntaxTree()
Makes an abstract map exact field bestie 💖
What you get back represents "Name := Value".
See also: map_expr/2, map_field_exact_name/1, map_field_exact_value/1.
map_field_exact_name(syntaxTree()) -> syntaxTree()
Gives you the name subtree of a map_field_exact node 🎀
See also: map_field_exact/2.
map_field_exact_value(syntaxTree()) -> syntaxTree()
Gives you the value subtree of a map_field_exact node 🎀
See also: map_field_exact/2.
map_generator(syntaxTree(), syntaxTree()) -> syntaxTree()
Makes an abstract map_generator ✨
The result represents "Pattern <- Body".
See also: binary_comp/2, list_comp/2, map_comp/2, map_generator_body/1, map_generator_pattern/1.
map_generator_body(syntaxTree()) -> syntaxTree()
Gives you the body subtree of a map_generator node 🦋
See also: map_generator/2.
map_generator_pattern(syntaxTree()) -> syntaxTree()
Gives you the pattern subtree of a map_generator node 🦋
See also: map_generator/2.
map_type(any_size | [syntaxTree()]) -> syntaxTree()
Makes an abstract type map 👑
If Fields is [F1, ..., Fn], your result represents "#{F1, ..., Fn}"; but if Fields is any_size instead, it represents "map/0". Two completely different vibes, one function, iconic 💅
See also: map_type_fields/1.
map_type_assoc(syntaxTree(), syntaxTree()) -> syntaxTree()
Makes an abstract map type assoc field 💖
The result represents "Name => Value".
See also: map_type/1, map_type_assoc_name/1, map_type_assoc_value/1.
map_type_assoc_name(syntaxTree()) -> syntaxTree()
Gives you the name subtree of a map_type_assoc node 🌸
See also: map_type_assoc/2.
map_type_assoc_value(syntaxTree()) -> syntaxTree()
Gives you the value subtree of a map_type_assoc node 🌸
See also: map_type_assoc/2.
map_type_exact(syntaxTree(), syntaxTree()) -> syntaxTree()
Makes an abstract map type exact field 💖
The result represents "Name := Value".
See also: map_type/1, map_type_exact_name/1, map_type_exact_value/1.
map_type_exact_name(syntaxTree()) -> syntaxTree()
Gives you the name subtree of a map_type_exact node 🧚
See also: map_type_exact/2.
map_type_exact_value(syntaxTree()) -> syntaxTree()
Gives you the value subtree of a map_type_exact node 🧚
See also: map_type_exact/2.
map_type_fields(syntaxTree()) -> any_size | [syntaxTree()]
Gives you the list of field subtrees of a map_type node 🌈
If Node represents "map/0", you get any_size back; but if Node represents "#{F1, ..., Fn}", you get [F1, ..., Fn] back instead. Yes it's a two-for-one return type, we love a versatile queen 👑
See also: map_type/0, map_type/1.
match_expr(syntaxTree(), syntaxTree()) -> syntaxTree()
Makes an abstract match-expression ✨
The result represents "Pattern = Body".
See also: match_expr_body/1, match_expr_pattern/1.
match_expr_body(syntaxTree()) -> syntaxTree()
Gives you the body subtree of a match_expr node 🦋
See also: match_expr/2.
match_expr_pattern(syntaxTree()) -> syntaxTree()
Gives you the pattern subtree of a match_expr node 🦋
See also: match_expr/2.
maybe_expr([syntaxTree()], none | syntaxTree()) -> syntaxTree()
Makes an abstract maybe-expression 😭 the plot device of expressions fr fr
If Body is [B1, ..., Bn] and OptionalElse is none, the result represents "maybe B1, ..., Bn end". But if Body is [B1, ..., Bn] and OptionalElse represents an else_expr node with clauses [C1, ..., Cn], then the result represents "maybe B1, ..., Bn else C1; ..., Cn end". Pick your fighter 💅
See clause for documentation on erl_parse clauses.
See also: maybe_expr_body/1, maybe_expr_else/1.
maybe_expr_body(syntaxTree()) -> [syntaxTree()]
Gives you the list of body subtrees of a maybe_expr node 🎀
See also: maybe_expr/2.
maybe_expr_else(syntaxTree()) -> none | syntaxTree()
Gives you the else subtree of a maybe_expr node — or none, no pressure 💖
See also: maybe_expr/2.
maybe_match_expr(syntaxTree(), syntaxTree()) -> syntaxTree()
Makes an abstract maybe-expression, the kind used inside maybe blocks 🌸
The result represents "Pattern ?= Body".
See also: maybe_expr/2, maybe_match_expr_body/1, maybe_match_expr_pattern/1.
maybe_match_expr_body(syntaxTree()) -> syntaxTree()
Gives you the body subtree of a maybe_expr node 🦋
See also: maybe_match_expr/2.
maybe_match_expr_pattern(syntaxTree()) -> syntaxTree()
Gives you the pattern subtree of a maybe_expr node 🦋
See also: maybe_match_expr/2.
meta(syntaxTree()) -> syntaxTree()
Makes a meta-representation of a syntax tree 👑 this one is SO cool, sit down for it
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, bestie, don't @ me. The expression represented by MetaTree is implementation independent with regard to the data structures used by the abstract syntax tree implementation. Comments attached to nodes of Tree will be preserved, but other attributes are lost. 💀
Any node in Tree whose node type is variable (see type/1), and whose list of annotations (see 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. Not zero, not two. One. Periodt.
The main use of 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. For example, 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, for example, 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")". Slay 🌈
See also: abstract/1, get_ann/1, type/1.
module_qualifier(syntaxTree(), syntaxTree()) -> syntaxTree()
Makes an abstract module qualifier ✨
The result represents "Module:Body".
See also: module_qualifier_argument/1, module_qualifier_body/1.
module_qualifier_argument(syntaxTree()) -> syntaxTree()
Gives you the argument (aka the module) subtree of a module_qualifier node 🎀
See also: module_qualifier/2.
module_qualifier_body(syntaxTree()) -> syntaxTree()
Gives you the body subtree of a module_qualifier node 🎀
See also: module_qualifier/2.
named_fun_expr(syntaxTree(), [syntaxTree()]) -> syntaxTree()
Makes an abstract named fun-expression 👑
If Clauses is [C1, ..., Cn], the result represents "fun Name C1; ...; Name Cn end". More exactly — and bestie they mean exactly exactly — if each Ci represents "(Pi1, ..., Pim) Gi -> Bi", then the result represents "fun Name(P11, ..., P1m) G1 -> B1; ...; Name(Pn1, ..., Pnm) Gn -> Bn end". It's giving recursive self-reference, we love to see it 💅
See also: named_fun_expr_arity/1, named_fun_expr_clauses/1, named_fun_expr_name/1.
named_fun_expr_arity(syntaxTree()) -> arity()
Gives you the arity of a named_fun_expr node 🌸
The result is the number of parameter patterns in the first clause of the named fun-expression; subsequent clauses are ignored. So don't come at it with clause number two, it's not looking 💀
An exception is thrown if named_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. Yeah it's picky, it has standards ✨
See also: clause/3, clause_patterns/1, named_fun_expr/2, named_fun_expr_clauses/1.
named_fun_expr_clauses(syntaxTree()) -> [syntaxTree()]
Gives you the list of clause subtrees of a named_fun_expr node 🦋
See also: named_fun_expr/2.
named_fun_expr_name(syntaxTree()) -> syntaxTree()
Gives you the name subtree of a named_fun_expr node 🦋
See also: named_fun_expr/2.
nil() -> syntaxTree()
Makes an abstract empty list 🎀
The result represents "[]". And fun fact bestie, the empty list is traditionally called "nil" — hence the function name, iykyk 💖
See also: is_list_skeleton/1, list/2.
normalize_list(syntaxTree()) -> syntaxTree()
Expands an abstract list skeleton to its most explicit form 🌈 maximum transparency era
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 | []] ... ]". And if Node does not represent a list skeleton, then Node itself is returned. It said "not my problem, moving on" 💅
See also: compact_list/1, list/2.
operator(atom() | string()) -> syntaxTree()
Makes 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; for example, the result of operator('++') represents "++" rather than "'++'". No quotes, it's giving minimalist 🤌
See also: atom/1, operator_literal/1, operator_name/1.
operator_literal(syntaxTree()) -> string()
Gives you the literal string represented by an operator node 🎀
This is simply the operator name as a string. Simple. Clean. Iconic. 💖
See also: operator/1.
operator_name(syntaxTree()) -> atom()
Gives you the name of an operator node 🌸
Note that the name is returned as an atom. Yes, an atom, not a string — read it again bestie, it matters 👑
See also: operator/1.
parentheses(syntaxTree()) -> syntaxTree()
Makes an abstract parenthesised expression 🎀
The result represents "(Body)", independently of the context. Context? Never heard of her. The parentheses are staying 💅
See also: parentheses_body/1.
parentheses_body(syntaxTree()) -> syntaxTree()
Gives you the body subtree of a parentheses node 🦋
See also: parentheses/1.
prefix_expr(syntaxTree(), syntaxTree()) -> syntaxTree()
Makes an abstract prefix operator expression ✨
The result represents "Operator Argument". 💫
See also: infix_expr/3, prefix_expr_argument/1, prefix_expr_operator/1.
prefix_expr_argument(syntaxTree()) -> syntaxTree().
Hands you the argument subtree of a prefix_expr node. bestie's inner child, exposed. 🎀
See also: prefix_expr/2.
prefix_expr_operator(syntaxTree()) -> syntaxTree().
Hands you the operator subtree of a prefix_expr node. the little symbol doing all the work. 💅
See also: prefix_expr/2.
receive_expr([syntaxTree()], none | syntaxTree(), [syntaxTree()]) -> syntaxTree().
Makes an abstract receive-expression. iconic mailbox behavior, no cap. 📬
If Timeout is none, the result represents receive C1; ...; Cn end (the Action argument gets ignored, rip). Otherwise, if Clauses is [C1, ..., Cn] and Action is [A1, ..., Am], the result represents receive C1; ...; Cn after Timeout -> A1, ..., Am end. To be more exact: if each Ci represents "(Pi) Gi -> Bi", then the result represents "receive P1 G1 -> B1; ...; Pn G1 -> Bn ... end".
Heads up bestie: in Erlang, a receive-expression must have at least one clause if no timeout part is specified. that's the rule, periodt. 🙈
See also: case_expr/2, clause/3, receive_expr/1, receive_expr_action/1, receive_expr_clauses/1, receive_expr_timeout/1.
receive_expr_action(syntaxTree()) -> [syntaxTree()].
Gives you the list of action body subtrees of a receive_expr node. ✨
If Node represents receive C1; ...; Cn end, this is the empty list. nothing to see here, literally. 🫶
See also: receive_expr/3.
receive_expr_clauses(syntaxTree()) -> [syntaxTree()].
Gives you the list of clause subtrees of a receive_expr node. the whole crew. 👑
See also: receive_expr/3.
receive_expr_timeout(syntaxTree()) -> none | syntaxTree().
Gives you the timeout subtree of a receive_expr node, if there even is one. ⏰
If Node represents receive C1; ...; Cn end, none is returned. Otherwise, if Node represents receive C1; ...; Cn after Timeout -> ... end, then Timeout is returned.
See also: receive_expr/3.
record_access(syntaxTree(), syntaxTree(), syntaxTree()) -> syntaxTree().
Makes an abstract record field access expression. 🍓
The result represents Argument#Type.Field.
See also: record_access_argument/1, record_access_field/1, record_access_type/1, record_expr/3.
record_access_argument(syntaxTree()) -> syntaxTree().
Gives you the argument subtree of a record_access node.
See also: record_access/3.
record_access_field(syntaxTree()) -> syntaxTree().
Gives you the field subtree of a record_access node.
See also: record_access/3.
record_access_type(syntaxTree()) -> syntaxTree().
Gives you the type subtree of a record_access node.
See also: record_access/3.
record_expr(none | syntaxTree(), syntaxTree() | [syntaxTree()], [syntaxTree()]) -> syntaxTree().
Makes 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 also: record_access/3, record_expr/2, record_expr_argument/1, record_expr_fields/1, record_expr_type/1, record_field/2, record_index_expr/2.
record_expr_argument(syntaxTree()) -> none | syntaxTree().
Gives you the argument subtree of a record_expr node, if there is one. 🙈
If Node represents #Type{...}, none is returned. Otherwise, if Node represents Argument#Type{...}, Argument is returned.
See also: record_expr/3.
record_expr_fields(syntaxTree()) -> [syntaxTree()].
Gives you the list of field subtrees of a record_expr node.
See also: record_expr/3.
record_expr_type(syntaxTree()) -> syntaxTree().
Gives you the type subtree of a record_expr node.
See also: record_expr/3.
record_field(syntaxTree(), none | syntaxTree()) -> syntaxTree().
Makes an abstract record field specification. 🎀
If Value is none, the result represents simply Name, otherwise it represents Name = Value.
See also: record_expr/3, record_field_name/1, record_field_value/1.
record_field_name(syntaxTree()) -> syntaxTree().
Gives you the name subtree of a record_field node.
See also: record_field/2.
record_field_value(syntaxTree()) -> none | syntaxTree().
Gives you 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 also: record_field/2.
record_index_expr(syntaxTree(), syntaxTree()) -> syntaxTree().
Makes 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. names were taken, we made do. 🙃
See also: record_expr/3, record_index_expr_field/1, record_index_expr_type/1.
record_index_expr_field(syntaxTree()) -> syntaxTree().
Gives you the field subtree of a record_index_expr node.
See also: record_index_expr/2.
record_index_expr_type(syntaxTree()) -> syntaxTree().
Gives you the type subtree of a record_index_expr node.
See also: record_index_expr/2.
record_type(syntaxTree(), [syntaxTree()]) -> syntaxTree().
Makes an abstract record type. 🌈
If Fields is [F1, ..., Fn], the result represents #Name{F1, ..., Fn}.
See also: record_type_fields/1, record_type_name/1.
record_type_field(syntaxTree(), syntaxTree()) -> syntaxTree().
Makes an abstract record type field. ✨
The result represents Name :: Type.
See also: record_type_field_name/1, record_type_field_type/1.
record_type_field_name(syntaxTree()) -> syntaxTree().
Gives you the name subtree of a record_type_field node.
See also: record_type_field/2.
record_type_field_type(syntaxTree()) -> syntaxTree().
Gives you the type subtree of a record_type_field node.
See also: record_type_field/2.
record_type_fields(syntaxTree()) -> [syntaxTree()].
Gives you the fields subtree of a record_type node.
See also: record_type/2.
record_type_name(syntaxTree()) -> syntaxTree().
Gives you the name subtree of a record_type node.
See also: record_type/2.
remove_comments(syntaxTree()) -> syntaxTree().
Clears the associated comments of Node. poof, gone, cleansing era. 🧚
Note: this is equivalent to set_precomments(set_postcomments(Node, []), []), but potentially more efficient. same result, less drama. 💅
See also: set_postcomments/2, set_precomments/2.
revert(syntaxTree()) -> syntaxTree().
Returns an erl_parse-compatible representation of a syntax tree, if possible. the glow-up back to normal form. ✨
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 (that is, 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 (for example 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. so like, garbage in, garbage still there. 😭
See also: //stdlib/erl_parse, revert_forms/1.
revert_forms(forms()) -> [erl_parse()].
Reverts a sequence of Erlang source code forms. 🦋
The sequence can be given either as a form_list syntax tree (possibly nested), or as a list of "program form" syntax trees. If successful, the corresponding flat list of erl_parse-compatible syntax trees is returned (see revert/1). If some program form could not be reverted, {error, Form} is thrown. Standalone comments in the form sequence are discarded — buh-bye, commentary. 💀
See also: form_list/1, is_form/1, revert/1.
set_ann(syntaxTree(), [term()]) -> syntaxTree().
Sets the list of user annotations of Node to Annotations.
See also: add_ann/2, copy_ann/2, get_ann/1.
set_attrs(syntaxTree(), syntaxTreeAttributes()) -> syntaxTree().
Sets the attributes of Node to Attributes.
See also: copy_attrs/2, get_attrs/1.
set_pos(syntaxTree(), annotation_or_location()) -> syntaxTree().
Sets the position information of Node to Pos. 📍
See also: copy_pos/2, get_pos/1.
set_postcomments(syntaxTree(), [syntaxTree()]) -> syntaxTree().
Sets the post-comments of Node to Comments.
Comments should be a possibly empty list of abstract comments, in top-down textual order.
See also: add_postcomments/2, comment/2, copy_comments/2, get_postcomments/1, join_comments/2, remove_comments/1, set_precomments/2.
set_precomments(syntaxTree(), [syntaxTree()]) -> syntaxTree().
Sets the pre-comments of Node to Comments.
Comments should be a possibly empty list of abstract comments, in top-down textual order.
See also: add_precomments/2, comment/2, copy_comments/2, get_precomments/1, join_comments/2, remove_comments/1, set_postcomments/2.
size_qualifier(syntaxTree(), syntaxTree()) -> syntaxTree().
Makes an abstract size qualifier. 📏
The result represents Body:Size.
See also: size_qualifier_argument/1, size_qualifier_body/1.
size_qualifier_argument(syntaxTree()) -> syntaxTree().
Gives you the argument subtree (the size, bestie) of a size_qualifier node.
See also: size_qualifier/2.
size_qualifier_body(syntaxTree()) -> syntaxTree().
Gives you the body subtree of a size_qualifier node.
See also: size_qualifier/2.
strict_binary_generator(syntaxTree(), syntaxTree()) -> syntaxTree().
Makes an abstract strict binary_generator. 💖
The result represents *Pattern*<:- *Body*.
See also: binary_comp/2, strict_binary_generator_body/1, strict_binary_generator_pattern/1, list_comp/2.
strict_binary_generator_body(syntaxTree()) -> syntaxTree().
Gives you the body subtree of a generator node.
See also: strict_binary_generator/2.
strict_binary_generator_pattern(syntaxTree()) -> syntaxTree().
Gives you the pattern subtree of a generator node.
See also: strict_binary_generator/2.
strict_generator(syntaxTree(), syntaxTree()) -> syntaxTree().
Creates an abstract strict list generator. 💖 The result represents *Pattern*<:- *Body*.
See also: binary_comp/2, strict_generator_body/1, strict_generator_pattern/1, list_comp/2.
strict_generator_body(syntaxTree()) -> syntaxTree()
Hands you the body subtree of a generator node. the vibes? extracted. 🎀
See also: strict_generator/2.
strict_generator_pattern(syntaxTree()) -> syntaxTree()
And this one gives you the pattern subtree of a generator node. matching pairs, periodt. ✨
See also: strict_generator/2.
strict_map_generator(syntaxTree(), syntaxTree()) -> syntaxTree()
Builds an abstract strict map_generator, bestie. The result represents *Pattern*<- *Body*. 💅
See also: list_comp/2, map_comp/2, strict_map_generator_body/1, strict_map_generator_pattern/1.
strict_map_generator_body(syntaxTree()) -> syntaxTree()
Returns the body subtree of a generator node. same energy as the list one, different node. 🦋
See also: strict_map_generator/2.
strict_map_generator_pattern(syntaxTree()) -> syntaxTree()
Returns the pattern subtree of a generator node. 💜
See also: strict_map_generator/2.
string(string()) -> syntaxTree()
Creates an abstract string literal — not me making a string node out of thin air, iconic. 🌸
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. so like, the idea of the string, not one exact spelling of it.
For example, the result of string("x\ny") represents any and all of "x\ny", "x\12y", "x\012y" and "x\^Jy"; see char/1.
See also: char/1, is_string/2, string_literal/1, string_literal/2, string_value/1.
string_literal(syntaxTree()) -> nonempty_string()
Returns the literal string represented by a string node. 🫶
This includes surrounding double-quote characters. Characters beyond 255 will be escaped. no cap, they get escaped.
See also: string/1.
string_literal(syntaxTree(), encoding()) -> nonempty_string()
Returns the literal string represented by a string node. 💖
This includes surrounding double-quote characters. Depending on the encoding, characters beyond 255 will be escaped (latin1) or copied as is (utf8).
See also: string/1.
string_value(syntaxTree()) -> string()
Returns the value represented by a string node. the actual content, no quotes drama. ✨
See also: string/1.
subtrees(syntaxTree()) -> [[syntaxTree()]]
Returns the grouped list of all subtrees of a syntax tree. this one's lowkey a banger, let me explain. 🌈
If Node is a leaf node (see 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 (for example, the group consisting of all the elements of a tuple), while others always contain the same number of elements — usually exactly one (for example, 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. so don't hardcode assumptions about the shape, fr fr. 🙈
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. chef's kiss, honestly. 👑
See also: copy_attrs/2, is_leaf/1, make_tree/2, type/1.
text(string()) -> syntaxTree()
Creates an abstract piece of source code text. 🎀
The result represents exactly the sequence of characters in String. This is useful in cases where one wants full control of the resulting output, such as the appearance of floating-point numbers or macro definitions.
See also: text_string/1.
text_string(syntaxTree()) -> string()
Returns the character sequence represented by a text node. exactly what you put in, comes back out. 💅
See also: text/1.
tree(atom(), term()) -> tree()
For special purposes only. Creates an abstract syntax tree node with type tag Type and associated data Data. ⚠️ like, don't just casually be making these, bestie.
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; seetype/1for a complete list. - The type tag of a syntax tree node may also be used as a primary tag by theerl_parserepresentation; in that case, the selector functions for that node type must handle both the abstract syntax tree and theerl_parseform. The functiontype(T)should return the correct type tag regardless of the representation ofT, so that the user sees no difference betweenerl_syntaxanderl_parsenodes.
See also: data/1, is_tree/1, type/1.
try_expr/4
try_expr([syntaxTree()],
[syntaxTree()],
[syntaxTree()],
[syntaxTree()]) ->
syntaxTree().
Creates an abstract try-expression. 🔥
If Body is [B1, ..., Bn], Clauses is [C1, ..., Cj], Handlers is [H1, ..., Hk], and After is [A1, ..., Am], the result represents "try B1, ..., Bn of C1; ...; Cj catch H1; ...; Hk after A1, ..., Am end". More exactly, if each Ci represents "(CPi) CGi -> CBi", and each Hi represents "(HPi) HGi -> HBi", then the result represents "try B1, ..., Bn of CP1 CG1 -> CB1; ...; CPj CGj -> CBj catch HP1 HG1 -> HB1; ...; HPk HGk -> HBk after A1, ..., Am end"; see 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 also: case_expr/2, class_qualifier/2, clause/3, try_after_expr/2, try_expr/2, try_expr/3, try_expr_after/1, try_expr_body/1, try_expr_clauses/1, try_expr_handlers/1.
try_expr_after(syntaxTree()) -> [syntaxTree()]
Returns the list of after subtrees of a try_expr node. 🌸
See also: try_expr/4.
try_expr_body(syntaxTree()) -> [syntaxTree()]
Returns the list of body subtrees of a try_expr node. ✨
See also: try_expr/4.
try_expr_clauses(syntaxTree()) -> [syntaxTree()]
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. 💀 no of section, no clauses, simple math.
See also: try_expr/4.
try_expr_handlers(syntaxTree()) -> [syntaxTree()]
Returns the list of handler-clause subtrees of a try_expr node. 🫶
See also: try_expr/4.
tuple([syntaxTree()]) -> syntaxTree()
Creates an abstract tuple. 💜
If Elements is [X1, ..., Xn], the result represents "{X1, ..., Xn}".
NOTE: The Erlang language has distinct 1-tuples, meaning{X}is always distinct fromXitself.
See also: tuple_elements/1, tuple_size/1.
tuple_elements(syntaxTree()) -> [syntaxTree()]
Returns the list of element subtrees of a tuple node. all the little guys inside. 🎀
See also: tuple/1.
tuple_size(syntaxTree()) -> non_neg_integer()
Returns the number of elements of a tuple node. ✨
NOTE: This is equivalent to length(tuple_elements(Node)), but potentially more efficient.
See also: tuple/1, tuple_elements/1.
tuple_type(any_size | [syntaxTree()]) -> syntaxTree()
Creates an abstract type tuple. 🌈
If Elements is [T1, ..., Tn], the result represents "{T1, ..., Tn}"; otherwise, if Elements is any_size, it represents "tuple/0".
See also: tuple_type_elements/1.
tuple_type_elements(syntaxTree()) -> any_size | [syntaxTree()]
Returns the list of type element subtrees of a tuple_type node. 💖
If Node represents "tuple/0", any_size is returned; otherwise, if Node represents "{T1, ..., Tn}", [T1, ..., Tn] is returned.
See also: tuple_type/0, tuple_type/1.
type(syntaxTree()) -> atom()
Returns the type tag of Node. the "who even are you" function of the whole module, ngl. 👑
If Node does not represent a syntax tree, evaluation fails with reason badarg. 🚨
Node types currently defined by this module are:
applicationannotated_typearity_qualifieratomattributebinarybinary_fieldbitstring_typeblock_exprcase_exprcatch_exprcharclass_qualifierclausecommentconjunctionconstrained_function_typeconstraintdisjunctionelse_expreof_markererror_markerfloatform_listfun_exprfun_typefunctionfunction_typegeneratorif_exprimplicit_funinfix_exprintegerinteger_range_typelistlist_compmacromap_exprmap_field_assocmap_field_exactmap_typemap_type_assocmap_type_exactmatch_exprmaybe_exprmaybe_match_exprmodule_qualifiernamed_fun_exprniloperatorparenthesesprefix_exprreceive_exprrecord_accessrecord_exprrecord_fieldrecord_index_exprrecord_typerecord_type_fieldsize_qualifierstringtexttry_exprtupletuple_typetyped_record_fieldtype_applicationtype_unionunderscoreuser_type_applicationvariablewarning_markerzip_generator
Bestie, that's the whole tag lineup, and the doors are open: if you've got special purposes (aesthetic, chaotic, both 💅), you can make your own extra nodes with other type tags using the tree/2 function. It's giving "go off, choose your own adventure" energy. 🦋
📌 Note from the syllabus: the primary constructor functions for a node type should always have the same name as the node type itself. No nicknames. Periodt.
See also: annotated_type/2, application/3, arity_qualifier/2, atom/1, attribute/2, binary/1, binary_field/2, bitstring_type/2, block_expr/1, case_expr/2, catch_expr/1, char/1, class_qualifier/2, clause/3, comment/2, conjunction/1, constrained_function_type/2, constraint/2, disjunction/1, else_expr/1, eof_marker/0, error_marker/1, float/1, form_list/1, fun_expr/1, fun_type/0, function/2, function_type/1, function_type/2, generator/2, if_expr/1, implicit_fun/2, infix_expr/3, integer/1, integer_range_type/2, list/2, list_comp/2, macro/2, map_expr/2, map_field_assoc/2, map_field_exact/2, map_type/0, map_type/1, map_type_assoc/2, map_type_exact/2, match_expr/2, maybe_expr/1, maybe_expr/2, maybe_match_expr/2, module_qualifier/2, named_fun_expr/2, nil/0, operator/1, parentheses/1, prefix_expr/2, receive_expr/3, record_access/3, record_expr/2, record_field/2, record_index_expr/2, record_type/2, record_type_field/2, size_qualifier/2, string/1, text/1, tree/2, try_expr/3, tuple/1, tuple_type/0, tuple_type/1, type_application/2, type_union/1, typed_record_field/2, underscore/0, user_type_application/2, variable/1, warning_marker/1, zip_generator/1.
type_application(syntaxTree(), [syntaxTree()]) -> syntaxTree().
type_application(syntaxTree(), [syntaxTree()]) -> syntaxTree().
Makes you an abstract type application expression, slay. ✨
So if Arguments is [T1, ..., Tn], the result you get back represents "TypeName(T1, ...Tn)".
See also: type_application/3, type_application_arguments/1, type_application_name/1, user_type_application/2.
type_application(none | syntaxTree(), syntaxTree(), [syntaxTree()]) -> syntaxTree().
type_application(none | syntaxTree(),
syntaxTree(),
[syntaxTree()]) ->
syntaxTree().
Also makes you an abstract type application expression — the deluxe version. 👑
If Module is none, this call is equivalent to type_application(TypeName, Arguments). Otherwise it's equivalent to type_application(module_qualifier(Module, TypeName), Arguments). Cute, right? That's the whole point.
(This is a utility function.)
See also: module_qualifier/2, type_application/2.
type_application_arguments(syntaxTree()) -> [syntaxTree()].
type_application_arguments(syntaxTree()) -> [syntaxTree()].
Hands you the arguments subtrees of a type_application node. The receipts. 🧾
See also: type_application/2.
type_application_name(syntaxTree()) -> syntaxTree().
type_application_name(syntaxTree()) -> syntaxTree().
Gives you the type name subtree of a type_application node. The name, the whole name, nothing but the name. 💖
See also: type_application/2.
type_union([syntaxTree()]) -> syntaxTree().
type_union([syntaxTree()]) -> syntaxTree().
Creates an abstract type union. 🌈
If Types is [T1, ..., Tn], the result represents "T1 | ... | Tn".
See also: type_union_types/1.
type_union_types(syntaxTree()) -> [syntaxTree()].
type_union_types(syntaxTree()) -> [syntaxTree()].
Returns the list of type subtrees of a type_union node. All of them. The full set. Periodt.
See also: type_union/1.
typed_record_field(syntaxTree(), syntaxTree()) -> syntaxTree().
typed_record_field(syntaxTree(), syntaxTree()) -> syntaxTree().
Creates an abstract typed record field specification. 📝
The result represents "Field :: Type".
See also: typed_record_field_body/1, typed_record_field_type/1.
typed_record_field_body(syntaxTree()) -> syntaxTree().
typed_record_field_body(syntaxTree()) -> syntaxTree().
Returns the field subtree of a typed_record_field node. It's giving "just the body, bestie". 🫶
See also: typed_record_field/2.
typed_record_field_type(syntaxTree()) -> syntaxTree().
typed_record_field_type(syntaxTree()) -> syntaxTree().
Returns the type subtree of a typed_record_field node. The type, and nothing else. ✨
See also: typed_record_field/2.
underscore() -> syntaxTree().
underscore() -> syntaxTree().
Creates an abstract universal pattern — that's "_", the icon, the legend. 💅
The lexical representation is a single underscore character. And heads up: this is not a variable, lexically speaking. It's built different, literally.
See also: variable/1.
update_tree(syntaxTree(), [[syntaxTree()]]) -> syntaxTree().
update_tree(syntaxTree(), [[syntaxTree()]]) -> syntaxTree().
Creates a syntax tree with the same type and attributes as the given tree. Same vibe, new guts. 🔄
This is equivalent to copy_attrs(Node, make_tree(type(Node), Groups)).
See also: copy_attrs/2, make_tree/2, type/1.
user_type_application(syntaxTree(), [syntaxTree()]) -> syntaxTree().
user_type_application(syntaxTree(), [syntaxTree()]) ->
syntaxTree().
Creates an abstract user type. 🎀
If Arguments is [T1, ..., Tn], the result represents "TypeName(T1, ...Tn)".
See also: type_application/2, user_type_application_arguments/1, user_type_application_name/1.
user_type_application_arguments(syntaxTree()) -> [syntaxTree()].
user_type_application_arguments(syntaxTree()) -> [syntaxTree()].
Returns the arguments subtrees of a user_type_application node. The whole squad of arguments. 🌸
See also: user_type_application/2.
user_type_application_name(syntaxTree()) -> syntaxTree().
user_type_application_name(syntaxTree()) -> syntaxTree().
Returns the type name subtree of a user_type_application node. Main character energy, one name only. 👑
See also: user_type_application/2.
variable(atom() | string()) -> syntaxTree().
variable(atom() | string()) -> syntaxTree().
Creates an abstract variable with the given name. Simple, clean, iconic. 💜
Name may be any atom or string that represents a lexically valid variable name — but not a single underscore character, bestie. For that one, go see underscore/0. 🙈
⚠️ NOTE, and this one matters: no check is performed to verify whether the character sequence represents a proper variable name. Like, it does not check whether its first character is an uppercase Erlang character, and it does not check whether it contains illegal characters such as control characters or whitespace. It's just gonna believe you. No cap, the trust is real and it will not save you. 😭
See also: underscore/0, variable_literal/1, variable_name/1.
variable_literal(syntaxTree()) -> string().
variable_literal(syntaxTree()) -> string().
Returns the name of a variable node as a string. Text-flavoured. ✨
See also: variable/1.
variable_name(syntaxTree()) -> atom().
variable_name(syntaxTree()) -> atom().
Returns the name of a variable node as an atom. Atom-flavoured. 🍓
See also: variable/1.
warning_marker(term()) -> syntaxTree().
warning_marker(term()) -> syntaxTree().
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 //stdlib/io for details, iykyk). Warning markers are regarded as source code forms, but heads up: they have no defined lexical form.
⚠️ NOTE: this is supported only for backwards compatibility with existing parsers and tools. So like, it's legacy-core. Respect the elders, but don't build your dream house on it. 💀
See also: eof_marker/0, error_marker/1, is_form/1, warning_marker_info/1.
warning_marker_info(syntaxTree()) -> term().
warning_marker_info(syntaxTree()) -> term().
Returns the ErrorInfo structure of a warning_marker node. The tea, in structured form. ☕
See also: warning_marker/1.
zip_generator([syntaxTree()]) -> syntaxTree().
zip_generator([syntaxTree()]) -> syntaxTree().
Creates an abstract zip_generator. 🦋
The result represents G1 && ... Gn, where each G is a generator. The way it just zips them together, gorgeous.
See also: binary_comp/2, list_comp/2, map_comp/2, map_generator_body/1, map_generator_pattern/1.
zip_generator_body(syntaxTree()) -> syntaxTree().
zip_generator_body(syntaxTree()) -> syntaxTree().
Returns the body subtree of a zip_generator node. That's a wrap on the whole tree, bestie. 🎬
See also: zip_generator/1.
And that's the entire page, start to finish, no skips, no summarising, we read the whole thing like the academic weapon you are. 🎓💖
This one lives in Ericsson AB's syntax_tools 4.1, under section erl_syntax(3) — so if you're citing it in the group chat, that's the receipt. 🌟 Love u, go write some syntax trees, mwah. 💅✨
📦 where this page comes from
- ubuntu stonking: erlang-doc 1:29.0.6+dfsg-1 · Apache-2.0
- 💖 girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms