cerl(3)
๐ cerl โ Core Erlang abstract syntax trees ๐
okay bestie, welcome to cerl(3), the Erlang module definition for Core Erlang abstract syntax trees. this is the Erlang compiler's inner sanctum, so buckle up ๐
โจ NAME โจ
cerl โ Core Erlang abstract syntax trees. that's the whole vibe. trees of syntax. abstract ones. ๐
๐ธ DESCRIPTION ๐ธ
โ ๏ธ NOTE: the public interface of the Erlang compiler lives in module compile. This module is an internal part of the compiler. Its API is not guaranteed to remain compatible between releases. so likeโฆ use it, love it, but don't build your whole personality on it staying the same, fr fr ๐ญ
so this module defines an abstract data type for representing Core Erlang source code as syntax trees. trees! ๐ณโจ
if you're a first-time user, the recommended starting point is the documentation of the function type/1. that's your entry point, bestie. start there ๐ซถ
โ ๏ธ NOTE: This module deals with the composition and decomposition of syntactic entities (as opposed to 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 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. ๐ so like, you gotta do your part okay??
Currently, the internal data structure used is the same as the record-based data structures used traditionally in the Beam compiler. 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 - no syntax tree is represented by a list constructor
[X | Y] - no syntax tree is represented by the empty list
[]
This can be relied on when writing functions that operate on syntax trees. those three things are your safe little constants, periodt ๐
๐ฆ DATA TYPES ๐ฆ
okay so here's the whole roster. every single one. no skipping, we don't do that here ๐
c_alias() :: #c_alias{}.
c_apply() :: #c_apply{}.
c_binary() :: #c_binary{}.
c_bitstr() :: #c_bitstr{}.
c_call() :: #c_call{}.
c_case() :: #c_case{}.
c_catch() :: #c_catch{}.
c_clause() :: #c_clause{}.
c_cons() :: #c_cons{}.
c_fun() :: #c_fun{}.
c_lct() :: c_literal() | c_cons() | c_tuple().
c_let() :: #c_let{}.
c_letrec() :: #c_letrec{}.
c_literal() :: #c_literal{}.
c_map() :: #c_map{}.
c_map_pair() :: #c_map_pair{}.
c_module() :: #c_module{}.
c_opaque() :: #c_opaque{}.
c_primop() :: #c_primop{}.
c_receive() :: #c_receive{}.
c_record() :: #c_record{}.
c_record_pair() :: #c_record_pair{}.
c_seq() :: #c_seq{}.
c_try() :: #c_try{}.
c_tuple() :: #c_tuple{}.
c_values() :: #c_values{}.
c_var() :: #c_var{}.
and now the big union type, the main character herself ๐:
cerl() ::
c_alias() |
c_apply() |
c_binary() |
c_bitstr() |
c_call() |
c_case() |
c_catch() |
c_clause() |
c_cons() |
c_fun() |
c_let() |
c_letrec() |
c_literal() |
c_map() |
c_map_pair() |
c_record() |
c_record_pair() |
c_module() |
c_opaque() |
c_primop() |
c_receive() |
c_seq() |
c_try() |
c_tuple() |
c_values() |
c_var().
the type tags, so you know what flavor of node you're holding ๐:
ctype() ::
alias | apply | binary | bitstr | call | 'case' | 'catch' |
clause | cons | 'fun' | 'let' | letrec | literal | map |
map_pair | module | opaque | primop | 'receive' | seq |
record | record_pair | 'try' | tuple | values | var.
data types for data types, iykyk:
dtype() :: cons | tuple | {atomic, value()}.
map_op() :: #c_literal{val :: assoc} | #c_literal{val :: exact}.
record_id() :: c_literal().
value() :: integer() | float() | atom() | [].
var_name() :: integer() | atom() | {atom(), integer()}.
๐ฅ FUNCTIONS ๐ฅ
abstract(Term :: term()) -> c_literal()โ Creates a syntax tree corresponding to an Erlang term. ๐ณ
Term must be a literal term, that is, one that can be represented as a source code literal. Thus, it may not contain a process identifier, port, reference, binary or function value as a subterm. ๐ซ
Note: This is a constant time operation. โก
See also: ann_abstract/2, concrete/1, is_literal/1, is_literal_term/1.
add_ann(Annotations :: [term()], Node :: cerl()) -> cerl()โ AppendsAnnotationsto the list of user annotations ofNode. ๐
Note: this is equivalent to set_ann(Node, Annotations ++ get_ann(Node)), but potentially more efficient. โจ
See also: get_ann/1, set_ann/2.
alias_pat(Node :: c_alias()) -> cerl()โ Returns the pattern subtree of an abstract pattern alias. ๐
See also: c_alias/2.
alias_var(Node :: c_alias()) -> c_var()โ Returns the variable subtree of an abstract pattern alias. ๐ฆ
See also: c_alias/2.
ann_abstract(Annotations :: [term()], Term :: term()) -> c_literal()โ the annotated version, bestie.
See also: abstract/1.
ann_c_alias(Annotations :: [term()], Variable :: c_var(), Pattern :: cerl()) -> c_alias()โ annotated alias constructor.
See also: c_alias/2.
ann_c_apply(Annotations :: [term()], Operator :: cerl(), Arguments :: [cerl()]) -> c_apply()โ annotated apply constructor.
See also: c_apply/2.
ann_c_atom(Annotations :: [term()], Name :: atom() | string()) -> c_literal()โ annotated atom constructor.
See also: c_atom/1.
ann_c_binary(Annotations :: [term()], Segments :: [cerl()]) -> c_binary()โ annotated binary constructor.
See also: c_binary/1.
ann_c_bitstr(Annotations :: [term()], Value :: cerl(), Size :: cerl(), Type :: cerl(), Flags :: cerl()) -> c_bitstr()โ Equivalent toann_c_bitstr(As, Value, Size, abstract(1), Type, Flags). ๐ซann_c_bitstr(Annotations :: [term()], Value :: cerl(), Size :: cerl(), Unit :: cerl(), Type :: cerl(), Flags :: cerl()) -> c_bitstr()โ the six-arg glow-up version.
See also: ann_c_bitstr/5, c_bitstr/5.
ann_c_call(Annotations :: [term()], Module :: cerl(), Name :: cerl(), Arguments :: [cerl()]) -> c_call()โ annotated call constructor.
See also: c_call/3.
ann_c_case(Annotations :: [term()], Argument :: cerl(), Clauses :: [cerl()]) -> c_case()โ annotated case constructor.
See also: c_case/2.
ann_c_catch(Annotations :: [term()], Body :: cerl()) -> c_catch()โ annotated catch constructor.
See also: c_catch/1.
ann_c_char(Annotations :: [term()], Value :: char()) -> c_literal()โ annotated char constructor.
See also: c_char/1.
ann_c_clause(Annotations :: [term()], Patterns :: [cerl()], Body :: cerl()) -> c_clause()โ Equivalent toann_c_clause(As, Patterns, c_atom(true), Body). ๐
See also: c_clause/3.
ann_c_clause(Annotations :: [term()], Patterns :: [cerl()], Guard :: cerl(), Body :: cerl()) -> c_clause()โ the four-arg version, with the guard front and center.
See also: ann_c_clause/3, c_clause/3.
ann_c_cons(Annotations :: [term()], Head :: cerl(), Tail :: cerl()) -> c_literal() | c_cons()โ annotated cons constructor.
See also: c_cons/2.
ann_c_cons_skel(Annotations :: [term()], Head :: cerl(), Tail :: cerl()) -> c_cons()โ annotated cons skeleton, the skele-bones version. ๐ฆด
See also: c_cons_skel/2.
ann_c_float(Annotations :: [term()], Value :: float()) -> c_literal()โ annotated float constructor.
See also: c_float/1.
ann_c_fname(Annotations :: [term()], Name :: atom(), Arity :: arity()) -> c_var()โ Equivalent toann_c_var(As, {Atom, Arity}). โจ
See also: c_fname/2.
ann_c_fun(Annotations :: [term()], Variables :: [cerl()], Body :: cerl()) -> c_fun()โ annotated fun constructor.
See also: c_fun/2.
ann_c_int(Annotations :: [term()], Value :: integer()) -> c_literal()โ annotated int constructor.
See also: c_int/1.
ann_c_let(Annotations :: [term()], Variables :: [cerl()], Argument :: cerl(), Body :: cerl()) -> c_let()โ annotated let constructor.
See also: c_let/3.
ann_c_letrec(Annotations :: [term()], Definitions :: [{cerl(), cerl()}], Body :: cerl()) -> c_letrec()โ annotated letrec constructor.
See also: c_letrec/2.
ann_c_map(Annotations :: [term()], Pairs :: [c_map_pair()]) -> c_map() | c_literal()โ annotated map constructor.
See also: c_map/1.
ann_c_map(Annotations :: [term()], Argument :: c_map() | c_literal(), Pairs :: [c_map_pair()]) -> c_map() | c_literal()โ the two-arg, with-argument version.
See also: c_map/2
ann_c_map_pair(Annotations :: [term()], Operation :: cerl(), Key :: cerl(), Value :: cerl()) -> c_map_pair()โ annotated map pair constructor.
See also: c_map_pair/2, c_map_pair_exact/2.
ann_c_map_pattern(Annotations :: [term()], Pairs :: [c_map_pair()]) -> c_map()โ annotated map pattern constructor.
See also: c_map_pattern/2
ann_c_module(Annotations :: [term()], Name :: cerl(), Exports :: [cerl()], Definitions :: [{cerl(), cerl()}]) -> c_module()โ annotated module constructor.
See also: ann_c_module/5, c_module/3.
ann_c_module(Annotations :: [term()], Name :: cerl(), Exports :: [cerl()], Attributes :: [{cerl(), cerl()}], Definitions :: [{cerl(), cerl()}]) -> c_module()โ the five-arg version, now with attributes ๐
See also: ann_c_module/4, c_module/4.
ann_c_nil(Annotations :: [term()]) -> c_literal()โ annotated nil constructor. the fancy version of nothing. ๐
See also: c_nil/0.
ann_c_primop(Annotations :: [term()], Name :: cerl(), Arguments :: [cerl()]) -> c_primop()โ annotated primop constructor.
See also: c_primop/2.
ann_c_receive(Annotations :: [term()], Clauses :: [cerl()]) -> c_receive()โ Equivalent toann_c_receive(As, Clauses, c_atom(infinity), c_atom(true)). ๐จ
See also: c_atom/1, c_receive/3.
okay we're not done yet bestie, there's MORE ๐
ann_c_receive(Annotations :: [term()], Clauses :: [cerl()], Timeout :: cerl(), Actions :: cerl()) -> c_receive()โจ See also:ann_c_receive/2,c_receive/3.ann_c_record(Annotations :: [term()], Argument :: c_var() | c_record() | c_literal(), Id :: term(), Pairs :: [c_record_pair()]) -> #c_record{}๐ See also:c_record/2.ann_c_record_pair(Annotations :: [term()], Key :: cerl(), Value :: cerl()) -> c_record_pair()๐ See also:c_record/2.ann_c_seq(Annotations :: [term()], Argument :: cerl(), Body :: cerl()) -> c_seq()๐ธ See also:c_seq/2.ann_c_string(Annotations :: [term()], Value :: string()) -> c_literal()๐ฆ See also:c_string/1.ann_c_try(Annotations :: [term()], Argument :: cerl(), Variables :: [cerl()], Body :: cerl(), ExceptionVars :: [cerl()], Handler :: cerl()) -> c_try()๐ See also:c_try/5.ann_c_tuple(Annotations :: [term()], Elements :: [cerl()]) -> c_tuple() | c_literal()โจ See also:c_tuple/1.ann_c_tuple_skel(Annotations :: [term()], Elements :: [cerl()]) -> c_tuple()๐ซ See also:c_tuple_skel/1.ann_c_values(Annotations :: [term()], Values :: [cerl()]) -> c_values()๐ See also:c_values/1.ann_c_var(Annotations :: [term()], Name :: var_name()) -> c_var()๐ See also:c_var/1.ann_make_data(Annotations :: [term()], Type :: dtype(), Elementes :: [cerl()]) -> c_lct()๐ See also:make_data/2.ann_make_data_skel(Annotations :: [term()], Type :: dtype(), Elements :: [cerl()]) -> c_lct()๐ซถ See also:make_data_skel/2.ann_make_list(Annotations :: [term()], List :: [cerl()], Tail :: cerl() | none) -> cerl()๐ See also:ann_make_list/2,make_list/2.ann_make_tree(Annotations :: [term()], Type :: ctype(), Groups :: [[cerl()], ...]) -> cerl()โ Makes a syntax tree with those annotations, type, and subtrees. โจ (Yeah, it's literally building a tree, no cap.) Peepmake_tree/2for the deets. See also:make_tree/2.apply_args(Node :: c_apply()) -> [cerl()]โ Returns the list of argument subtrees of an abstract function application. ๐ฅ See also:apply_arity/1,c_apply/2.apply_arity(Node :: c_apply()) -> arity()โ Returns the number of argument subtrees of an abstract function application. ๐งฎ
Note: this is equivalent to length(apply_args(Node)), but potentially more efficient.
See also: apply_args/1, c_apply/2.
apply_op(Node :: c_apply()) -> cerl()โ Returns the operator subtree of an abstract function application. ๐ See also:c_apply/2.atom_lit(Node :: cerl()) -> nonempty_string()โ Returns the literal string represented by an abstract atom. This always includes surrounding single-quote characters. ๐
Note that an abstract atom may have several literal representations, and that the representation yielded by this function is not fixed; for example,atom_lit(c_atom("a\012b"))could yield the string"\'a\\nb\'".
See also: c_atom/1.
atom_name(Node :: c_literal()) -> string()โ Returns the printname of an abstract atom. ๐ท๏ธ See also:c_atom/1.atom_val(Node :: c_literal()) -> atom()โ Returns the value represented by an abstract atom. ๐ See also:c_atom/1.binary_segments(Node :: c_binary()) -> [cerl()]โ Returns the list of segment subtrees of an abstract binary-template. ๐งฉ See also:c_binary/1,c_bitstr/5.bitstr_bitsize(Node :: c_bitstr()) -> all | any | utf | non_neg_integer()โ Returns the total size in bits of an abstract bit-string template. ๐ If the size field is an integer literal, the result is the product of the size and unit values; if the size field is the atom literalall, the atomallis returned. If the size is not a literal, the atomanyis returned. If the type of the bit-string segment is one ofutf8,utf16orutf32, the atomutfis returned. See also:c_bitstr/5.bitstr_flags(Node :: c_bitstr()) -> cerl()โ Returns the flags subtree of an abstract bit-string template. ๐ฉ See also:c_bitstr/5.bitstr_size(Node :: c_bitstr()) -> cerl()โ Returns the size subtree of an abstract bit-string template. ๐ See also:c_bitstr/5.bitstr_type(Node :: c_bitstr()) -> cerl()โ Returns the type subtree of an abstract bit-string template. ๐ท๏ธ See also:c_bitstr/5.bitstr_unit(Node :: c_bitstr()) -> cerl()โ Returns the unit subtree of an abstract bit-string template. ๐ See also:c_bitstr/5.bitstr_val(Node :: c_bitstr()) -> cerl()โ Returns the value subtree of an abstract bit-string template. ๐ See also:c_bitstr/5.c_alias(Variable :: c_var(), Pattern :: cerl()) -> c_alias()โ Creates an abstract pattern alias. ๐ The result represents"Variable = Pattern". See also:alias_pat/1,alias_var/1,ann_c_alias/3,c_clause/3,is_c_alias/1,update_c_alias/3.c_apply(Operator :: cerl(), Arguments :: [cerl()]) -> c_apply()โ Creates an abstract function application. ๐ IfArgumentsis[A1, ..., An], the result represents"apply Operator(A1, ..., An)". See also:ann_c_apply/3,apply_args/1,apply_arity/1,apply_op/1,c_call/3,c_primop/2,is_c_apply/1,update_c_apply/3.c_atom(Name :: atom() | string()) -> c_literal()โ Creates an abstract atom literal. โ๏ธ The print name of the atom is the character sequence represented byName.
Note: passing a string as argument to this function causes a corresponding atom to be created for the internal representation.
See also: ann_c_atom/2, atom_lit/1, atom_name/1, atom_val/1, is_c_atom/1.
c_binary(Segments :: [cerl()]) -> c_binary()โ Creates an abstract binary-template. ๐งฌ A binary object is in this context is a sequence of an arbitrary number of bits. (The number of bits used to be evenly divisible by 8, but after the introduction of bit strings in the Erlang language, the choice was made to use the binary template for all bit strings.) It is specified by zero or more bit-string template segments of arbitrary lengths (in number of bits). IfSegmentsis[S1, ..., Sn], the result represents"#{S1, ..., Sn}#". All theSimust have typebitstr. See also:ann_c_binary/2,binary_segments/1,c_bitstr/5,is_c_binary/1,update_c_binary/2.c_bitstr(Value :: cerl(), Type :: cerl(), Flags :: cerl()) -> c_bitstr()โ Equivalent toc_bitstr(Value, abstract(all), abstract(1), Type, Flags). โจc_bitstr(Value :: cerl(), Size :: cerl(), Unit :: cerl(), Type :: cerl(), Flags :: cerl()) -> c_bitstr()โ Creates an abstract bit-string template. ๐งต These can only occur as components of an abstract binary-template (seec_binary/1). The result represents"#<Value>(Size, Unit, Type, Flags)", whereUnitmust represent a positive integer constant,Typemust represent a constant atom (one of'integer','float','binary','utf8','utf16'or'utf32'), andFlagsmust represent a constant list"[F1, ..., Fn]"where all theFiare atoms. See also:ann_c_bitstr/6,bitstr_flags/1,bitstr_size/1,bitstr_type/1,bitstr_unit/1,bitstr_val/1,c_binary/1,is_c_bitstr/1,update_c_bitstr/6.c_call(Module :: cerl(), Name :: cerl(), Arguments :: [cerl()]) -> c_call()โ Creates an abstract inter-module call. ๐ IfArgumentsis[A1, ..., An], the result represents"call Module:Name(A1, ..., An)". See also:ann_c_call/4,c_apply/2,c_primop/2,call_args/1,call_arity/1,call_module/1,call_name/1,is_c_call/1,update_c_call/4.c_case(Argument :: cerl(), Clauses :: [cerl()]) -> c_case()โ Creates an abstract case-expression. ๐ญ IfClausesis[C1, ..., Cn], the result represents"case Argument of C1 ... Cn end".Clausesmust not be empty. See also:ann_c_case/3,c_clause/3,case_arg/1,case_arity/1,case_clauses/1,is_c_case/1,update_c_case/3.c_catch(Body :: cerl()) -> c_catch()โ Creates an abstract catch-expression. ๐ฅ The result represents"catch Body".
Note: catch-expressions can be rewritten as try-expressions, and will eventually be removed from Core Erlang.
See also: ann_c_catch/2, c_try/5, catch_body/1, is_c_catch/1, update_c_catch/2.
c_char(Value :: non_neg_integer()) -> c_literal()โ Creates an abstract character literal. ๐ค If the local implementation of Erlang defineschar/0as a subset ofinteger/0, this function is equivalent toc_int/1. Otherwise, if the given value is an integer, it will be converted to the character with the corresponding code. The lexical representation of a character is"$Char", whereCharis a single printing character or an escape sequence. See also:ann_c_char/2,c_int/1,c_string/1,char_lit/1,char_val/1,is_c_char/1,is_print_char/1.c_clause(Patterns :: [cerl()], Body :: cerl()) -> c_clause()โ Equivalent toc_clause(Patterns, c_atom(true), Body). โจ See also:c_atom/1.c_clause(Patterns :: [cerl()], Guard :: cerl(), Body :: cerl()) -> c_clause()โ Creates an abstract clause. ๐ IfPatternsis[P1, ..., Pn], the result represents"<P1, ..., Pn> when Guard -> Body".
See also: ann_c_clause/4, c_case/2, c_clause/2, c_receive/3, clause_arity/1, clause_body/1, clause_guard/1, clause_pats/1, clause_vars/1, is_c_clause/1, update_c_clause/4.
๐ c_cons/2 ๐
c_cons(Head :: cerl(), Tail :: cerl()) -> c_literal() | c_cons().
Makes u an abstract list constructor, bestie. The result is giving [Head | Tail] energy. Heads up though: if both Head and Tail have type literal, then the result is also type literal, and the annotations on Head and Tail get yeeted into the void โ gone, lost, no cap.
And remember what we learned in Erlang class: the tail element of a list constructor is not necessarily a list. The way that's just how it works. ๐ฆ
See also: ann_c_cons/3, c_cons_skel/2, c_nil/0, cons_hd/1, cons_tl/1, is_c_cons/1, is_c_list/1, list_elements/1, list_length/1, make_list/2, update_c_cons/3.
๐
c_cons_skel/2 ๐
c_cons_skel(Head :: cerl(), Tail :: cerl()) -> c_cons().
Creates an abstract list constructor skeleton, and this one is so different from c_cons/2, iykyk. It does not fold constant literals โ like, ever. The result always has type cons, and it represents [Head | Tail]. Periodt.
So when is this iconic? Occasionally when u genuinely need annotations on the subnodes of a list constructor node, even when those subnodes are constant literals. Very niche, very main character.
โ ๏ธ But bestie, listen:is_literal/1will yieldfalseandconcrete/1will fail if u pass it the result from this function. Not a vibe. If u need to go back,fold_literal/1can revert a node to the normal-form representation. ๐ซถ
See also: ann_c_cons_skel/3, c_cons/2, c_nil/0, concrete/1, fold_literal/1, is_c_cons/1, is_c_list/1, is_literal/1, update_c_cons_skel/3.
โจ c_float/1 โจ
c_float(Value :: float()) -> c_literal().
Makes an abstract floating-point literal. The lexical representation is just the decimal floating-point numeral of Value. Slay, decimals. ๐ฅ
See also: ann_c_float/2, float_lit/1, float_val/1, is_c_float/1.
๐ธ c_fname/2 ๐ธ
c_fname(Name :: atom(), Arity :: arity()) -> c_var().
Literally the same as calling c_var({Name, Arity}). That's it, that's the whole function. ๐
See also: ann_c_fname/3, fname_arity/1, fname_id/1, is_c_fname/1, update_c_fname/3.
๐ง c_fun/2 ๐ง
c_fun(Variables :: [cerl()], Body :: cerl()) -> c_fun().
Creates an abstract fun-expression. If Variables is [V1, ..., Vn], the result represents fun (V1, ..., Vn) -> Body. And all the Vi must have type var โ like, mandatory, no exceptions. ๐
See also: ann_c_fun/3, fun_arity/1, fun_body/1, fun_vars/1, is_c_fun/1, update_c_fun/3.
โญ c_int/1 โญ
c_int(Value :: integer()) -> c_literal().
Creates an abstract integer literal. The lexical representation is the canonical decimal numeral of Value. Canonical, bestie โ we don't do messy numerals here. ๐
See also: ann_c_int/2, c_char/1, int_lit/1, int_val/1, is_c_int/1.
๐ c_let/3 ๐
c_let(Variables :: [cerl()], Argument :: cerl(), Body :: cerl()) ->
c_let().
Creates an abstract let-expression. If Variables is [V1, ..., Vn], the result represents let <V1, ..., Vn> = Argument in Body. All the Vi must have type var. Yes all of them. Every single one. ๐
See also: ann_c_let/4, is_c_let/1, let_arg/1, let_arity/1, let_body/1, let_vars/1, update_c_let/4.
๐ฆ c_letrec/2 ๐ฆ
c_letrec(Definitions :: [{cerl(), cerl()}], Body :: cerl()) ->
c_letrec().
Creates an abstract letrec-expression. If Definitions is [{V1, F1}, ..., {Vn, Fn}], the result represents letrec V1 = F1 ... Vn = Fn in Body. Rules of the house: all the Vi must have type var and represent function names, and all the Fi must have type 'fun'. Recursion but make it abstract. ๐
See also: ann_c_letrec/3, is_c_letrec/1, letrec_body/1, letrec_defs/1, letrec_vars/1, update_c_letrec/3.
๐ c_map/1 ๐
c_map(Pairs :: [c_map_pair()]) -> c_map().
Creates an abstract map constructor. If Pairs is [E1, ..., EN], the result represents "~{E1, ..., EN}~" โ that's the creating a new map flavour, obvs. ๐บ๏ธ
Important tea: if all pairs in Pairs have type literal for both the key and the value, or if Pairs is empty, then the result will also have type literal and the annotations on nodes in Pairs are lost forever. ๐ญ
All Ei must be abstract pairs constructed by c_map_pair/2. Don't try anything cute.
See also: ann_c_map/2, is_c_map/1, is_c_map_empty/1, is_c_map_pattern/1, map_es/1, c_map_pair/2, c_map_pair_exact/2.
๐ฅ c_map/2 ๐ฅ
c_map(Argument :: cerl(), Pairs :: [c_map_pair()]) -> c_map().
Creates an abstract map update expression, the sequel to c_map/1. If Pairs is [E1, ..., EN], the result represents "~{E1, ..., EN | Argument}~" โ updating an existing map, periodt.
Same warning energy: if Argument is a literal and all pairs in Pairs have type literal for both key and value, or if Pairs is empty, then the result will also have type literal and annotations on nodes in Pairs are gone. ๐
All Ei must be abstract pairs constructed by either c_map_pair/2 or c_map_pair_exact/2. Both are welcome here.
See also: ann_c_map/2, is_c_map/1, is_c_map_empty/1, is_c_map_pattern/1, map_es/1, c_map_pair/2, c_map_pair_exact/2.
๐ c_map_pair/2 ๐
c_map_pair(Key :: cerl(), Value :: cerl()) -> c_map_pair().
Creates an abstract map pair using the assoc operator. These can only occur as components of an abstract map creation expression or an abstract update expression (peep c_map/1 and c_map/2). The result represents Key => Value. Classic. ๐
See also: map_pair_key/1, map_pair_op/1, map_pair_val/1.
๐ง c_map_pair_exact/2 ๐ง
c_map_pair_exact(Key :: cerl(), Value :: cerl()) -> c_map_pair().
Creates an abstract map pair using the exact operator. These can only occur as components of an abstract map update expression or an abstract map pattern (peep c_map/1 and c_map_pattern/1). The result represents Key := Value.
Note the difference bestie: exact operator = :=, assoc = =>. They are not interchangeable, and I need u to know that. โจ
See also: map_pair_key/1, map_pair_op/1, map_pair_val/1.
๐ c_map_pattern/1 ๐
c_map_pattern(Pairs :: [c_map_pair()]) -> c_map().
Creates an abstract map pattern. If Pairs is [E1, ..., EN], the result represents "~{E1, ..., EN}~". Yes it looks the same as c_map/1 but bestie it is not the same โ trust.
All Ei must be abstract pairs constructed by c_map_pair_exact/2. Exact only. I don't make the rules. ๐
See also: ann_c_map/2, is_c_map/1, is_c_map_empty/1, is_c_map_pattern/1, map_es/1, c_map_pair_exact/2.
๐ c_module/4 ๐
c_module(Name :: cerl(),
Exports :: [cerl()],
Attributes :: [{cerl(), cerl()}],
Definitions :: [{cerl(), cerl()}]) ->
c_module().
Creates an abstract module definition. The result represents
module Name [E1, ..., Ek]
attributes [K1 = T1, ...,
Km = Tm]
V1 = F1
...
Vn = Fn
end
if Exports = [E1, ..., Ek], Attributes = [{K1, T1}, ..., {Km, Tm}], and Definitions = [{V1, F1}, ..., {Vn, Fn}].
Now the rules, and there are a lot of them, so lock in ๐:
Nameand all theKimust be atom literals.- All the
Timust be constant literals. - All the
ViandEimust have typevarand represent function names. - All the
Fimust have type'fun'.
Get all of those right and ur module is serving. Get them wrong and... well. ๐
See also: ann_c_module/4, ann_c_module/5, c_atom/1, c_fun/2, c_module/3, c_var/1, is_literal/1, module_attrs/1, module_defs/1, module_exports/1, module_name/1, module_vars/1, update_c_module/5.
๐ซถ c_nil/0 ๐ซถ
c_nil() -> c_literal().
Creates an abstract empty list. The result represents []. Fun fact: the empty list is traditionally called nil. The more u know. ๐ซ
See also: ann_c_nil/1, c_cons/2, is_c_list/1.
โญ c_primop/2 โญ
c_primop(Name :: cerl(), Arguments :: [cerl()]) -> c_primop().
Creates an abstract primitive operation call. If Arguments is [A1, ..., An], the result represents primop Name(A1, ..., An). And Name must be an atom literal โ no negotiation. ๐ฅ
See also: ann_c_primop/3, c_apply/2, c_call/3, is_c_primop/1, primop_args/1, primop_arity/1, primop_name/1, update_c_primop/3.
๐ c_receive/1 ๐
c_receive(Clauses :: [cerl()]) -> c_receive().
Literally just c_receive(Clauses, c_atom(infinity), c_atom(true)). A shortcut, a little treat for u. ๐
See also: c_atom/1.
๐
c_receive/3 ๐
c_receive(Clauses :: [cerl()],
Timeout :: cerl(),
Action :: cerl()) ->
c_receive().
Creates an abstract receive-expression. If Clauses is [C1, ..., Cn], the result represents receive C1 ... Cn after Timeout -> Action end. The full three-arg main character version. โจ
See also: ann_c_receive/4, c_receive/1, is_c_receive/1, receive_action/1, receive_clauses/1, receive_timeout/1, update_c_receive/4.
๐ c_record/2 ๐
c_record(Argument :: record_id(), Pairs :: [c_record_pair()]) ->
#c_record{}.
Creates an abstract record constructor. Notice the return type is literally written as #c_record{} in the spec โ that's not a typo, that's just how it is, and we respect it. ๐ซก
See also: ann_c_record/3, is_c_record/1, record_id/1, record_es/1, c_record_pair/2, update_c_record/4.
๐ธ c_record_pair/2 ๐ธ
c_record_pair(Key :: c_literal(), Value :: cerl()) ->
c_record_pair().
Creates an abstract record pair. These can only occur as components of an abstract record creation expression or an abstract update expression (peep c_record/2). The result represents Key = Value. Key is a c_literal(), no substitutes. ๐
See also: ann_c_record_pair/3, record_pair_key/1, record_pair_val/1.
๐ c_seq/2 ๐
c_seq(Argument :: cerl(), Body :: cerl()) -> c_seq().
Creates an abstract sequencing expression. The result represents do Argument Body. One thing, then the next thing. That's the sequence, bestie. โก๏ธ
See also: ann_c_seq/3, is_c_seq/1, seq_arg/1, seq_body/1, update_c_seq/3.
๐ฆ c_string/1 ๐ฆ
c_string(Value :: string()) -> c_literal().
Creates an abstract string literal. It's equivalent to creating an abstract list of the corresponding character literals (cf. is_c_string/1) โ but this is typically more efficient, so like, do this one. โก
The lexical representation of a string is ""Chars"", where Chars is a sequence of printing characters or spaces.
See also: ann_c_string/2, c_char/1, is_c_string/1, is_print_string/1, string_lit/1, string_val/1.
๐ c_try/5 ๐
c_try(Argument :: cerl(),
Variables :: [cerl()],
Body :: cerl(),
ExceptionVars :: [cerl()],
Handler :: cerl()) ->
c_try().
Creates an abstract try-expression. If Variables is [V1, ..., Vn] and ExceptionVars is [X1, ..., Xm], the result represents try Argument of <V1, ..., Vn> -> Body catch <X1, ..., Xm> -> Handler.
Both sets matter: all the Vi and all the Xi must have type var. Try it, catch it, love it. ๐
See also: ann_c_try/6, c_catch/1, is_c_try/1, try_arg/1, try_body/1, try_vars/1, update_c_try/6.
๐ c_tuple/1 ๐
c_tuple(Elements :: [cerl()]) -> c_tuple() | c_literal().
Creates an abstract tuple. If Elements is [E1, ..., En], the result represents {E1, ..., En}.
And here's the tea again ๐ซ: if all nodes in Elements have type literal, or if Elements is empty, then the result will also have type literal and the annotations on nodes in Elements are lost. Same energy as c_cons/2, same heartbreak. ๐ญ
See also: ann_c_tuple/2, is_c_tuple/1, tuple_arity/1, tuple_es/1, update_c_tuple/3.
See also: ann_c_tuple/2, c_tuple/1, is_c_tuple/1, tuple_arity/1, tuple_es/1, update_c_tuple/3.
Recall that Erlang has distinct 1-tuples, that is, {X} is always distinct from X itself. Yeah, it's a whole thing. ๐
See also:ann_c_tuple/2,c_tuple_skel/1,is_c_tuple/1,tuple_arity/1,tuple_es/1,update_c_tuple/2.
c_tuple_skel(Elements :: [cerl()]) -> c_tuple(). โจ
Creates an abstract tuple skeleton.
No cap, it does not fold constant literals, that is, the result always has type tuple, representing "{E1, ..., En}", if Elements is [E1, ..., En].
This function is occasionally useful when it is necessary to have annotations on the subnodes of a tuple node, even when all the subnodes are constant literals. However, note that is_literal/1 will yield false and concrete/1 will fail if passed the result from this function.
fold_literal/1 can be used to revert a node to the normal-form representation.
See also:ann_c_tuple_skel/2,c_tuple/1,concrete/1,fold_literal/1,is_c_tuple/1,is_literal/1,tuple_es/1,update_c_tuple_skel/2.
c_values(Elements :: [cerl()]) -> c_values(). ๐
Creates an abstract value list.
If Elements is [E1, ..., En], the result represents "<E1, ..., En>".
See also:ann_c_values/2,is_c_values/1,update_c_values/2,values_arity/1,values_es/1.
c_var(Name :: var_name()) -> c_var(). ๐
Creates an abstract variable.
A variable is identified by its name, given by the Name parameter.
If a name is given by a single atom, it should either be a "simple" atom which does not need to be single-quoted in Erlang, or otherwise its print name should correspond to a proper Erlang variable, that is, begin with an uppercase character or an underscore. Names of the form {A, N} represent function name variables "A/N"; these are special variables which may be bound only in the function definitions of a module or a letrec. They may not be bound in let expressions and cannot occur in clause patterns. The atom A in a function name may be any atom; the integer N must be nonnegative. The functions c_fname/2 etc. are utilities for handling function name variables.
When printing variable names, they must have the form of proper Core Erlang variables and function names. E.g., a name represented by an integer such as 42 could be formatted as "_42", an atom 'Xxx' simply as "Xxx", and an atom foo as "_foo". However, one must assure that any two valid distinct names are never mapped to the same strings. Tuples such as {foo, 2} representing function names can simply be formatted as "'foo'/2", with no risk of conflicts.
See also:ann_c_var/2,c_fname/2,c_letrec/2,c_module/4,is_c_var/1,update_c_var/2,var_name/1.
call_args(Node :: c_call()) -> [cerl()]. ๐ฆ
Returns the list of argument subtrees of an abstract inter-module call.
See also:c_call/3,call_arity/1.
call_arity(Node :: c_call()) -> arity(). ๐
Returns the number of argument subtrees of an abstract inter-module call.
Note: this is equivalent to length(call_args(Node)), but potentially more efficient.
See also:c_call/3,call_args/1.
call_module(Node :: c_call()) -> cerl(). ๐ธ
Returns the module subtree of an abstract inter-module call.
See also: c_call/3.
call_name(Node :: c_call()) -> cerl(). ๐
Returns the name subtree of an abstract inter-module call.
See also: c_call/3.
case_arg(Node :: c_case()) -> cerl(). ๐ง
Returns the argument subtree of an abstract case-expression.
See also: c_case/2.
case_arity(Node :: c_case()) -> non_neg_integer(). ๐ฅ
Equivalent to clause_arity(hd(case_clauses(Node))), but potentially more efficient.
See also:c_case/2,case_clauses/1,clause_arity/1.
case_clauses(Node :: c_case()) -> [cerl()]. ๐
Returns the list of clause subtrees of an abstract case-expression.
See also:c_case/2,case_arity/1.
catch_body(Node :: c_catch()) -> cerl(). ๐ซถ
Returns the body subtree of an abstract catch-expression.
See also: c_catch/1.
char_lit(Node :: c_literal()) -> nonempty_string(). ๐
Returns the literal string represented by an abstract character. This includes a leading $ character.
Currently, all characters that are not in the set of ISO 8859-1 (Latin-1) "printing" characters will be escaped.
See also: c_char/1.
char_val(Node :: c_literal()) -> char(). ๐
Returns the value represented by an abstract character literal.
See also: c_char/1.
clause_arity(Node :: c_clause()) -> non_neg_integer(). โญ
Returns the number of pattern subtrees of an abstract clause.
Note: this is equivalent to length(clause_pats(Node)), but potentially more efficient.
See also:c_clause/3,clause_pats/1.
clause_body(Node :: c_clause()) -> cerl(). ๐
Returns the body subtree of an abstract clause.
See also: c_clause/3.
clause_guard(Node :: c_clause()) -> cerl(). ๐
Returns the guard subtree of an abstract clause.
See also: c_clause/3.
clause_pats(Node :: c_clause()) -> [cerl()]. โจ
Returns the list of pattern subtrees of an abstract clause.
See also:c_clause/3,clause_arity/1.
clause_vars(Node :: c_clause()) -> [cerl()]. ๐ฆ
Returns the list of all abstract variables in the patterns of an abstract clause.
The order of listing is not defined.
See also:c_clause/3,pat_list_vars/1.
concrete(Node :: c_literal()) -> term(). ๐
Returns the Erlang term represented by a syntax tree.
An exception is thrown if Node does not represent a literal term.
Note: This is a constant time operation.
See also:abstract/1,is_literal/1.
cons_hd(Node :: c_cons() | c_literal()) -> cerl(). ๐ธ
Returns the head subtree of an abstract list constructor.
See also: c_cons/2.
cons_tl(Node :: c_cons() | c_literal()) -> cerl(). ๐
Returns the tail subtree of an abstract list constructor.
Recall that the tail does not necessarily represent a proper list.
See also: c_cons/2.
copy_ann(Source :: cerl(), Target :: cerl()) -> cerl(). ๐ง
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.
data_arity(Node :: c_lct()) -> non_neg_integer(). ๐ฅ
Returns the number of subtrees of a data constructor node.
This is equivalent to length(data_es(Node)), but potentially more efficient.
See also:data_es/1,is_data/1.
data_es(Node :: c_lct()) -> [cerl()]. ๐
Returns the list of subtrees of a data constructor node.
If the arity of the constructor is zero, the result is the empty list.
Note: ifdata_type(Node)iscons, the number of subtrees is exactly two. Ifdata_type(Node)is{atomic, Value}, the number of subtrees is zero.
See also:data_arity/1,data_type/1,is_data/1,make_data/2.
data_type(Node :: c_lct()) -> dtype(). ๐ซถ
Returns a type descriptor for a data constructor node. (Cf. is_data/1.)
This is mainly useful for comparing types and for constructing new nodes of the same type (cf. make_data/2). If Node represents an integer, floating-point number, atom or empty list, the result is {atomic, Value}, where Value is the value of concrete(Node), otherwise the result is either cons or tuple.
Type descriptors can be compared for equality or order (in the Erlang term order), but remember that floating-point values should in general never be tested for equality.
See also:concrete/1,is_data/1,make_data/2,type/1.
float_lit(Node :: c_literal()) -> string(). ๐
Returns the numeral string represented by a floating-point literal node.
See also: c_float/1.
float_val(Node :: c_literal()) -> float(). ๐
Returns the value represented by a floating-point literal node.
See also: c_float/1.
fname_arity(Node :: c_var()) -> arity(). โญ
Returns the arity part of an abstract function name variable.
See also:c_fname/2,fname_id/1.
fname_id(Node :: c_var()) -> atom(). ๐
Returns the identifier part of an abstract function name variable.
See also:c_fname/2,fname_arity/1.
fold_literal(Node :: cerl()) -> cerl(). ๐
Ensures that literals have a compact representation.
This is occasionally useful if c_cons_skel/2, c_tuple_skel/1 or unfold_literal/1 were used in the construction of Node, and you want to revert to the normal "folded" representation of literals. If Node represents a tuple or list constructor, its elements are rewritten recursively, and the node is reconstructed using c_cons/2 or c_tuple/1, respectively; otherwise, Node is not changed.
See also:c_cons/2,c_cons_skel/2,c_tuple/1,c_tuple_skel/1,is_literal/1,unfold_literal/1.
from_records(Node :: cerl()) -> cerl(). โจ
Translates an explicit record representation to a corresponding abstract syntax tree.
The records are defined in the file "core_parse.hrl".
See also:to_records/1,type/1.
fun_arity(Node :: c_fun()) -> arity(). ๐ฆ
Returns the number of parameter subtrees of an abstract fun-expression.
Note: this is equivalent to length(fun_vars(Node)), but potentially more efficient.
See also:c_fun/2,fun_vars/1.
fun_body(Node :: c_fun()) -> cerl(). ๐
Returns the body subtree of an abstract fun-expression.
See also: c_fun/2.
fun_vars(Node :: c_fun()) -> [cerl()]. ๐ธ
Returns the list of parameter subtrees of an abstract fun-expression.
See also:c_fun/2,fun_arity/1.
get_ann(Node :: cerl()) -> [term()]. ๐
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 also: set_ann/2.
int_lit(Node :: c_literal()) -> string(). ๐ง
Returns the numeral string represented by an integer literal node.
See also: c_int/1.
int_val(Node :: c_literal()) -> integer(). ๐ฅ
Returns the value represented by an integer literal node.
See also: c_int/1.
okay bestie we're picking right back up in the โจ predicate section โจ and this is the era of the is_* girlies โ every single one is a vibe check that hands you back true or false and absolutely nothing else. no cap. ๐
is_c_alias(Node :: cerl()) -> boolean().
Vibe check: does Node represent an abstract pattern alias? true if so, false if not. ๐
See also: c_alias/2.
is_c_apply(Node :: cerl()) -> boolean().
Returns true if Node is an abstract function application, otherwise false. ๐ฅ
See also: c_apply/2.
is_c_atom(Node :: cerl()) -> boolean().
Returns true if Node represents an atom literal, otherwise false. โจ
See also: c_atom/1.
is_c_binary(Node :: cerl()) -> boolean().
Returns true if Node is an abstract binary-template, otherwise false. ๐ฆ
See also: c_binary/1.
is_c_bitstr(Node :: cerl()) -> boolean().
Returns true if Node is an abstract bit-string template, otherwise false. ๐
See also: c_bitstr/5.
is_c_call(Node :: cerl()) -> boolean().
Returns true if Node is an abstract inter-module call expression, otherwise false. ๐
See also: c_call/3.
is_c_case(Node :: cerl()) -> boolean().
Returns true if Node is an abstract case-expression, otherwise false. ๐
See also: c_case/2.
is_c_catch(Node :: cerl()) -> boolean().
Returns true if Node is an abstract catch-expression, otherwise false. ๐ซถ
See also: c_catch/1.
is_c_char(Node :: c_literal()) -> boolean().
Returns true if Node may represent a character literal, otherwise false.
Heads up bestie: if your local Erlang implementation defines char/0 as a subset of integer/0, then is_c_int(Node) will also hand you true. so yeah, overlap is real. โจ
See also: c_char/1, is_print_char/1.
is_c_clause(Node :: cerl()) -> boolean().
Returns true if Node is an abstract clause, otherwise false. ๐ซ
See also: c_clause/3.
is_c_cons(Node :: cerl()) -> boolean().
Returns true if Node is an abstract list constructor, otherwise false. ๐
is_c_float(Node :: cerl()) -> boolean().
Returns true if Node represents a floating-point literal, otherwise false. ๐
See also: c_float/1.
is_c_fname(Node :: cerl()) -> boolean().
Returns true if Node is an abstract function name variable, otherwise false. ๐
See also: c_fname/2, c_var/1, var_name/1.
is_c_fun(Node :: cerl()) -> boolean().
Returns true if Node is an abstract fun-expression, otherwise false. ๐
See also: c_fun/2.
is_c_int(Node :: cerl()) -> boolean().
Returns true if Node represents an integer literal, otherwise false. ๐ขโจ
See also: c_int/1.
is_c_let(Node :: cerl()) -> boolean().
Returns true if Node is an abstract let-expression, otherwise false. ๐ธ
See also: c_let/3.
is_c_letrec(Node :: cerl()) -> boolean().
Returns true if Node is an abstract letrec-expression, otherwise false. ๐
See also: c_letrec/2.
is_c_list(Node :: cerl()) -> boolean().
Returns true if Node represents a proper list, otherwise false.
A "proper list" is either the empty list [], or a cons cell [Head | Tail] where, recursively, Tail is also a proper list. iconic structural recursion, we love to see it. ๐
ok so listen โ Node is a syntax tree, which means the actual run-time values behind its subtrees are often partially or totally unknown. so if Node represents like"[... | Ns]"whereNsis a variable? this function saysfalse, because nobody knows ifNswill be bound to a list at run-time. but if Node represents something like"[1, 2, 3]"or"[A | []]", you gettrue. bestie it cannot know what it cannot know. ๐ญ
See also: c_cons/2, c_nil/0, list_elements/1, list_length/1.
is_c_map(Node :: cerl()) -> boolean().
Returns true if Node is any kind of abstract map โ constructing, updating, or matching โ otherwise false. ๐บ๏ธโจ
See also: ann_c_map/3, c_map/1, c_map_pattern/1.
is_c_map_empty(Node :: c_map() | c_literal()) -> boolean().
Returns true if Node represents an empty abstract map, otherwise false. ๐ซ
See also: c_map/1, c_map_pattern/1.
is_c_map_pattern(Node :: c_map()) -> boolean().
Returns true if Node is an abstract map pattern, otherwise false. ๐งฉ
See also: c_map/1, c_map_pattern/1.
is_c_module(Node :: cerl()) -> boolean().
Returns true if Node is an abstract module definition, otherwise false. ๐๏ธ
See also: type/1.
is_c_nil(Node :: cerl()) -> boolean().
Returns true if Node is an abstract empty list, otherwise false. ๐ณ๏ธ
is_c_primop(Node :: cerl()) -> boolean().
Returns true if Node is an abstract primitive operation call, otherwise false. โก
See also: c_primop/2.
is_c_receive(Node :: cerl()) -> boolean().
Returns true if Node is an abstract receive-expression, otherwise false. ๐ฌ
See also: c_receive/3.
is_c_record(Node :: cerl()) -> boolean().
Returns true if Node is an abstract record, otherwise false. ๐
See also: c_record/2.
is_c_seq(Node :: cerl()) -> boolean().
Returns true if Node is an abstract sequencing expression, otherwise false. ๐ช
See also: c_seq/2.
is_c_string(Node :: cerl()) -> boolean().
Returns true if Node may represent a string literal, otherwise false.
Strings are defined as lists of characters โ peep is_c_char/1 for all the deets. ๐ค
See also: c_string/1, is_c_char/1, is_print_string/1.
is_c_try(Node :: cerl()) -> boolean().
Returns true if Node is an abstract try-expression, otherwise false. ๐
See also: c_try/5.
is_c_tuple(Node :: cerl()) -> boolean().
Returns true if Node is an abstract tuple, otherwise false. ๐ฆ
See also: c_tuple/1.
is_c_values(Node :: cerl()) -> boolean().
Returns true if Node is an abstract value list, otherwise false. ๐
See also: c_values/1.
is_c_var(Node :: cerl()) -> boolean().
Returns true if Node is an abstract variable, otherwise false. ๐ท๏ธ
See also: c_var/1.
is_data(Node :: cerl()) -> boolean().
Returns true if Node represents a data constructor, otherwise false.
Data constructors are: cons cells, tuples, and atomic literals. the whole data squad, no impostors. ๐
See also: data_arity/1, data_es/1, data_type/1.
is_leaf(Node :: cerl()) -> boolean().
Returns true if Node is a leaf node, otherwise false.
The current leaf node types are literal and var. ๐
note note note: all literals (cf.is_literal/1) are leaf nodes โ even when they represent structured (constant) values like{foo, [bar, baz]}. and also note that variables are leaf nodes but they are not literals. two different things bestie, don't get it twisted. ๐
See also: is_literal/1, type/1.
is_literal(Node :: cerl()) -> boolean().
Returns true if Node represents a literal term, otherwise false.
This gives you true if and only if the value of concrete(Node) is defined. ๐ฎ
and here's the slay: this is a constant time operation. O(1) girlies rise. โจ
See also: abstract/1, concrete/1, fold_literal/1.
is_literal_term(Term :: term()) -> boolean().
Returns true if Term can be represented as a literal, otherwise false.
Heads up though โ this one takes time proportional to the size of Term. so not the freebie that its cousin above is. ๐
See also: abstract/1.
is_print_char(Node :: cerl()) -> boolean().
Returns true if Node may represent a "printing" character, otherwise false. (Cf. is_c_char/1.) ๐จ๏ธ
A "printing" character is one that either has a given graphical representation, or a "named" escape sequence like "\n". Currently, only ISO 8859-1 (Latin-1) character values are recognized. ๐
See also: c_char/1, is_c_char/1.
is_print_string(Node :: cerl()) -> boolean().
Returns true if Node may represent a string literal containing only "printing" characters, otherwise false.
Go read is_c_string/1 and is_print_char/1 for the full lore. Currently, only ISO 8859-1 (Latin-1) character values are recognized. ๐
See also: c_string/1, is_c_string/1, is_print_char/1.
okay now we're leaving the is_* era and entering the โจ accessor girlies โจ โ these ones actually hand you back parts of a node instead of a true/false. obsessed. ๐
let_arg(Node :: c_let()) -> cerl().
Hands you back the argument subtree of an abstract let-expression. ๐
See also: c_let/3.
let_arity(Node :: c_let()) -> non_neg_integer().
Hands you back the number of left-hand side variables of an abstract let-expression. ๐ข
note: this is equivalent to length(let_vars(Node)), but potentially more efficient. same answer, less work. ๐
See also: c_let/3, let_vars/1.
let_body(Node :: c_let()) -> cerl().
Hands you back the body subtree of an abstract let-expression. ๐ซ
See also: c_let/3.
let_vars(Node :: c_let()) -> [cerl()].
Hands you back the list of left-hand side variables of an abstract let-expression. ๐
See also: c_let/3, let_arity/1.
letrec_body(Node :: c_letrec()) -> cerl().
Hands you back the body subtree of an abstract letrec-expression. ๐ซ
See also: c_letrec/2.
letrec_defs(Node :: c_letrec()) -> [{cerl(), cerl()}].
Hands you back the list of definitions of an abstract letrec-expression. ๐ฆ
If Node represents "letrec V1 = F1 ... Vn = Fn in Body", then what you get back is [{V1, F1}, ..., {Vn, Fn}]. ๐
See also: c_letrec/2.
letrec_vars(Node :: c_letrec()) -> [cerl()].
Hands you back the list of left-hand side function variable subtrees of a letrec-expression. ๐ท๏ธ
If Node represents "letrec V1 = F1 ... Vn = Fn in Body", then what you get back is [V1, ..., Vn]. โจ
See also: c_letrec/2.
list_elements(Node :: c_cons() | c_literal()) -> [cerl()].
Hands you back the list of element subtrees of an abstract list. ๐
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]. ๐
See also: c_cons/2, c_nil/0, is_c_list/1, list_length/1, make_list/2.
list_length(Node :: c_cons() | c_literal()) -> non_neg_integer().
Hands you back the number of element subtrees of an abstract list. ๐ข
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. ๐
note: this is equivalent to length(list_elements(Node)), but potentially more efficient. bestie it just counts without building the whole list first. ๐
See also: c_cons/2, c_nil/0, is_c_list/1, list_elements/1.
make_data(Type :: dtype(), Elements :: [cerl()]) -> c_lct().
Builds you a data constructor node with the type and subtrees you specify. (Cf. data_type/1.) ๐๏ธโจ
An exception gets thrown if the length of Elements is invalid for the given Type โ check data_es/1 for the arity constraints on constructor types, bestie. ๐
See also:ann_make_data/3,data_es/1,data_type/1,make_data_skel/2,update_data/3.
make_data_skel</p> ๐
make_data_skel(Type :: dtype(), Elements :: [cerl()]) -> c_lct().
Basically make_data/2, but it's the skeleton-sibling of c_tuple_skel/1 and c_cons_skel/2. Same energy, different paperwork. โจ
See also:ann_make_data_skel/3,c_cons_skel/2,c_tuple_skel/1,make_data/2,update_data_skel/3.
make_list ๐
make_list(List :: [cerl()], Tail :: cerl() | none) -> cerl().
Builds an abstract list out of the elements in List plus the optional Tail. ๐ธ
If Tail is none, you get a nil-terminated list, no cap. Otherwise it's giving "[... | Tail]" โ tail and all, iconic. ๐ฆ
See also:ann_make_list/3,c_cons/2,c_nil/0,list_elements/1,update_list/3.
make_tree ๐
make_tree(Type :: ctype(), Groups :: [[cerl()], ...]) -> cerl().
Makes a syntax tree with the type and subtrees you hand it. ๐ฑ
Type has to be a node type name (peep type/1) that is not a leaf node type (peep is_leaf/1).
Groups has to be a nonempty list of groups of syntax trees โ those are the subtrees of a node of the given type, in left-to-right order exactly like they'd show up in the printed program text, grouped by category the way subtrees/1 does it. ๐
The result of ann_make_tree(get_ann(Node), type(Node), subtrees(Node)) (cf. update_tree/2) represents the same source code text as the original Node, assuming subtrees(Node) gives you a nonempty list. But heads up โ it does not necessarily have the exact same data representation as Node. Close cousin, not clone. ๐
See also:ann_make_tree/3,is_leaf/1,subtrees/1,type/1,update_tree/2.
map_arg
map_arg(Node :: c_map() | c_literal()) -> c_map() | c_literal().
Hands you the argument subtree of an abstract map. ๐บ๏ธ
See also: c_map/2.
map_es
map_es(Node :: c_map() | c_literal()) -> [c_map_pair()].
Returns the list of map pair subtrees of an abstract map. โจ
See also: c_map/1.
map_pair_key
map_pair_key(Node :: c_map_pair()) -> cerl().
The key subtree of an abstract map pair. ๐
See also:c_map_pair/2,c_map_pair_exact/2.
map_pair_op
map_pair_op(Node :: c_map_pair()) -> map_op().
The operation subtree of an abstract map pair. โ๏ธ
See also:c_map_pair/2,c_map_pair_exact/2.
map_pair_val
map_pair_val(Node :: c_map_pair()) -> cerl().
The value subtree of an abstract map pair. ๐
See also:c_map_pair/2,c_map_pair_exact/2.
meta ๐
meta(Tree :: cerl()) -> cerl().
Creates a meta-representation of a syntax tree. This one is a whole vibe, so let's go slow. ๐ง
The result represents an Erlang expression "MetaTree" which, if you evaluate it, yields a new syntax tree representing the same source code text as Tree โ although the actual data representation may be different, fr fr. And get this: the expression represented by MetaTree is implementation independent with regard to the data structures used by the abstract syntax tree implementation. That's the slay part. ๐
Now, any node in Tree whose node type is var (cf. type/1) and whose list of annotations (cf. get_ann/1) contains the atom meta_var will stay unchanged in the resulting tree โ except that exactly one occurrence of meta_var is removed from its annotation list. Exactly one. Not zero, not two. ๐ฏ
So what's the point? Main use of meta/1 is turning a data structure Tree, which represents a piece of program code, into a form that's 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)) โ just using abstract/1 to map the actual data structure onto a syntax tree representation โ would output a string that might look something like "{var, ..., 'V'}", which is obviously dependent on the implementation of the abstract syntax trees. Cataloging that could be useful for caching a syntax tree in a file, sure. But sometimes โ like in a program generator generator (yes, two "generator", that's not a typo ๐ญ) โ it may be unacceptable. So use print(meta(Tree)) instead and you'd output a representation-independent syntax tree generating expression; in the case above, something like "cerl:c_var('V')". Chef's kiss. ๐
The implementation tries to generate compact code with respect to literals and lists. โจ
See also:abstract/1,get_ann/1,type/1.
module_attrs
module_attrs(Node :: c_module()) -> [{cerl(), cerl()}].
Returns the list of pairs of attribute key/value subtrees of an abstract module definition. ๐ฆ
See also: c_module/4.
module_defs
module_defs(Node :: c_module()) -> [{cerl(), cerl()}].
Returns the list of function definitions of an abstract module definition. ๐ ๏ธ
See also: c_module/4.
module_exports
module_exports(Node :: c_module()) -> [cerl()].
Returns the list of exports subtrees of an abstract module definition. ๐
See also: c_module/4.
module_name
module_name(Node :: c_module()) -> cerl().
Returns the name subtree of an abstract module definition. ๐ท๏ธ
See also: c_module/4.
module_vars
module_vars(Node :: c_module()) -> [cerl()].
Returns the list of left-hand side function variable subtrees of an abstract module definition. ๐ซถ
See also: c_module/4.
pat_list_vars
pat_list_vars(Patterns :: [cerl()]) -> [cerl()].
Returns the list of all abstract variables in the given patterns. ๐
An exception gets thrown if some element in Patterns doesn't represent a well-formed Core Erlang clause pattern โ so behave. And ngl, the order of listing is not defined. Don't @ me. ๐
See also:clause_vars/1,pat_vars/1.
pat_vars
pat_vars(Node :: cerl()) -> [cerl()].
Returns the list of all abstract variables in a pattern.
An exception gets thrown if Node doesn't represent a well-formed Core Erlang clause pattern. And again: the order of listing is not defined. ๐
See also:clause_vars/1,pat_list_vars/1.
primop_args
primop_args(Node :: c_primop()) -> [cerl()].
Returns the list of argument subtrees of an abstract primitive operation call. โก
See also:c_primop/2,primop_arity/1.
primop_arity
primop_arity(Node :: c_primop()) -> arity().
Returns the number of argument subtrees of an abstract primitive operation call. ๐งฎ
Note: this is equivalent to length(primop_args(Node)), but potentially more efficient. Fancy like that. ๐
See also:c_primop/2,primop_args/1.
primop_name
primop_name(Node :: c_primop()) -> cerl().
Returns the name subtree of an abstract primitive operation call. โ๏ธ
See also: c_primop/2.
receive_action
receive_action(Node :: c_receive()) -> cerl().
Returns the action subtree of an abstract receive-expression. ๐จ
See also: c_receive/3.
receive_clauses
receive_clauses(Node :: c_receive()) -> [cerl()].
Returns the list of clause subtrees of an abstract receive-expression. ๐ฌ
See also: c_receive/3.
receive_timeout
receive_timeout(Node :: c_receive()) -> cerl().
Returns the timeout subtree of an abstract receive-expression. โณ
See also: c_receive/3.
record_arg
record_arg(Node :: c_record()) -> c_var() | c_literal().
Returns the argument subtree of an abstract record. ๐พ
See also: c_record/2.
record_es
record_es(Node :: c_record()) -> [c_record_pair()].
Returns the list of native record pair subtrees of an abstract record. ๐
See also: c_record/2.
record_id
record_id(Node :: c_record()) -> record_id().
Returns the identifier of of an abstract record. Yes, of of. ๐ We do not talk about it, we simply relay the facts, bestie.
See also: c_record/2.
record_pair_key
record_pair_key(Node :: c_record_pair()) -> c_literal().
Returns the key subtree of an abstract record pair. ๐
See also: c_record_pair/2.
record_pair_val
record_pair_val(Node :: c_record_pair()) -> cerl().
Returns the value subtree of an abstract record pair. ๐
See also: c_record_pair/2.
seq_arg
seq_arg(Node :: c_seq()) -> cerl().
Returns the argument subtree of an abstract sequencing expression. โก๏ธ
See also: c_seq/2.
seq_body
seq_body(Node :: c_seq()) -> cerl().
Returns the body subtree of an abstract sequencing expression. ๐งต
See also: c_seq/2.
set_ann
set_ann(Node :: cerl(), Annotations :: [term()]) -> cerl().
Sets the list of user annotations of Node to Annotations. Rewrites the whole annotation list, no takebacks. โ๏ธ
See also:add_ann/2,copy_ann/2,get_ann/1.
string_lit ๐
string_lit(Node :: c_literal()) -> nonempty_string().
Returns the literal string represented by an abstract string. This includes the surrounding double-quote characters "..." โ quotes stay, they're part of the fit. ๐
Currently, characters that are not in the set of ISO 8859-1 (Latin-1) "printing" characters will be escaped, except for spaces. Spaces stay cute and unescaped. โจ
See also: c_string/1.
string_val ๐
string_val(Node :: c_literal()) -> string().
Returns the value represented by an abstract string literal. ๐ธ
See also: c_string/1.
subtrees ๐๐
subtrees(Node :: cerl()) -> [[cerl()]].
Returns the grouped list of all subtrees of a node. Big one, stay with me. ๐ฆ
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. Packed light. ๐
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). But note: 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. Do not build your whole personality on it, okay? ๐ญ
The function subtrees/1 and the constructor functions make_tree/2 and update_tree/2 can be a great help if you want 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. Future-proof, lowkey iconic. ๐
For example:
postorder(F, Tree) ->
F(case subtrees(Tree) of
[] -> Tree;
List -> update_tree(Tree,
[[postorder(F, Subtree)
|| Subtree <- Group]
|| Group <- List])
end).
okay so what this does ๐
it maps the function F onto Tree and all its subtrees, doing a post-order traversal of the syntax tree. (And bestie, notice the lil flex: it uses update_tree/2 to preserve annotations ๐ โ do not lose the annotations, they are the moment.)
For a simple function like:
f(Node) ->
case type(Node) of
atom -> atom("a_" ++ atom_name(Node));
_ -> Node
end.
the call postorder(fun f/1, Tree) will yield a new representation of Tree in which all atom names have been extended with the prefix "a_", but nothing else (including annotations) has been changed. iconic behaviour, no notes โจ
See also: is_leaf/1, make_tree/2, update_tree/2.
to_records(Node :: cerl()) -> cerl()
Translates an abstract syntax tree to a corresponding explicit record representation. ๐ฆ
The records are defined in the file "cerl.hrl".
See also: from_records/1, type/1.
try_arg(Node :: c_try()) -> cerl()
Returns the expression subtree of an abstract try-expression.
See also: c_try/5.
try_body(Node :: c_try()) -> cerl()
Returns the success body subtree of an abstract try-expression.
See also: c_try/5.
try_evars(Node :: c_try()) -> [cerl()]
Returns the list of exception variable subtrees of an abstract try-expression.
See also: c_try/5.
try_handler(Node :: c_try()) -> cerl()
Returns the exception body subtree of an abstract try-expression.
See also: c_try/5.
try_vars(Node :: c_try()) -> [cerl()]
Returns the list of success variable subtrees of an abstract try-expression.
See also: c_try/5.
tuple_arity(Node :: c_tuple() | c_literal()) -> non_neg_integer()
Returns the number of element subtrees of an abstract tuple. ๐
Note: this is equivalent to length(tuple_es(Node)), but potentially more efficient. Efficiency slay, we love to see it.
See also: c_tuple/1, tuple_es/1.
tuple_es(Node :: c_tuple() | c_literal()) -> [cerl()]
Returns the list of element subtrees of an abstract tuple.
See also: c_tuple/1.
type(Node :: cerl()) -> ctype()
Returns the type tag of Node.
Current node types are:
aliasapplybinarybitstrcallcasecatchclauseconsfunletletrecliteralmapmap_pairmoduleopaqueprimopreceiveseqrecordtrytuplevaluesvar
NOTE: The name of the primary constructor function for a node type is always the name of the type itself, prefixed by"c_"; recognizer predicates are correspondingly prefixed by"is_c_". Furthermore, to simplify preservation of annotations (cf.get_ann/1), there are analogous constructor functions prefixed by"ann_c_"and"update_c_", for setting the annotation list of the new node to either a specific value or to the annotations of an existing node, respectively. ๐ง (Naming conventions that actually make sense? We stan.)
The only purpose of the opaque type is to facilitate testing of the compiler.
See also: abstract/1, c_alias/2, c_apply/2, c_binary/1, c_bitstr/5, c_call/3, c_case/2, c_catch/1, c_clause/3, c_cons/2, c_fun/2, c_let/3, c_letrec/2, c_module/3, c_primop/2, c_receive/1, c_seq/2, c_record/2, c_try/5, c_tuple/1, c_values/1, c_var/1, data_type/1, from_records/1, get_ann/1, meta/1, subtrees/1, to_records/1.
unfold_literal(Node :: cerl()) -> cerl()
Ensures that literals have a fully expanded representation. ๐ธ
If Node represents a literal tuple or list constructor, its elements are rewritten recursively, and the node is reconstructed using c_cons_skel/2 or c_tuple_skel/1, respectively; otherwise, Node is not changed. The fold_literal/1 can be used to revert to the normal compact representation.
See also: c_cons/2, c_cons_skel/2, c_tuple/1, c_tuple_skel/1, fold_literal/1, is_literal/1.
okay now we're getting into the update_c_* family and bestie there are SO many of them, they are all lowkey the same vibe: you hand them an existing node plus new pieces, and you get a fresh node back that keeps the old annotations. ๐ Let's go one by one, no skipping, we're thorough in this house.
update_c_alias(Node :: c_alias(), Variable :: cerl(), Pattern :: cerl()) -> c_alias()
See also: c_alias/2.
update_c_apply(Node :: c_apply(), Operator :: cerl(), Arguments :: [cerl()]) -> c_apply()
See also: c_apply/2.
update_c_binary(Node :: c_binary(), Segments :: [cerl()]) -> c_binary()
See also: c_binary/1.
update_c_bitstr(Node :: c_bitstr(), Value :: cerl(), Size :: cerl(), Type :: cerl(), Flags :: cerl()) -> c_bitstr()
Equivalent to update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags).
update_c_bitstr(Node :: c_bitstr(), Value :: cerl(), Size :: cerl(), Unit :: cerl(), Type :: cerl(), Flags :: cerl()) -> c_bitstr()
See also: c_bitstr/5, update_c_bitstr/5.
update_c_call(Node :: cerl(), Module :: cerl(), Name :: cerl(), Arguments :: [cerl()]) -> c_call()
See also: c_call/3.
update_c_case(Node :: c_case(), Argument :: cerl(), Clauses :: [cerl()]) -> c_case()
See also: c_case/2.
update_c_catch(Node :: c_catch(), Body :: cerl()) -> c_catch()
See also: c_catch/1.
update_c_clause(Node :: c_clause(), Patterns :: [cerl()], Guard :: cerl(), Body :: cerl()) -> c_clause()
See also: c_clause/3.
update_c_cons(Node :: c_literal() | c_cons(), Head :: cerl(), Tail :: cerl()) -> c_literal() | c_cons()
See also: c_cons/2.
update_c_cons_skel(Node :: c_cons() | c_literal(), Head :: cerl(), Tail :: cerl()) -> c_cons()
See also: c_cons_skel/2.
update_c_fname(Node :: c_var(), Name :: atom()) -> c_var()
Like update_c_fname/3, but takes the arity from Node. ๐
See also: c_fname/2, update_c_fname/3.
update_c_fname(Node :: c_var(), Name :: atom(), Arity :: arity()) -> c_var()
Equivalent to update_c_var(Old, {Atom, Arity}).
See also: c_fname/2, update_c_fname/2.
update_c_fun(Node :: c_fun(), Variables :: [cerl()], Body :: cerl()) -> c_fun()
See also: c_fun/2.
update_c_let(Node :: c_let(), Variables :: [cerl()], Argument :: cerl(), Body :: cerl()) -> c_let()
See also: c_let/3.
update_c_letrec(Node :: c_letrec(), Definitions :: [{cerl(), cerl()}], Body :: cerl()) -> c_letrec()
See also: c_letrec/2.
update_c_map(Node :: c_map(), Map :: cerl(), Pairs :: [c_map_pair()]) -> c_map() | c_literal()
See also: c_map/1, c_map_pattern/1.
update_c_map_pair(Node :: c_map_pair(), Operation :: map_op(), Key :: cerl(), Value :: cerl()) -> c_map_pair()
See also: c_map_pair/2, c_map_pair_exact/2.
update_c_module(Node :: c_module(), Name :: cerl(), Exports :: [cerl()], Attributes :: [{cerl(), cerl()}], Definitions :: [{cerl(), cerl()}]) -> c_module()
See also: c_module/4.
update_c_primop(Node :: cerl(), Name :: cerl(), Arguments :: [cerl()]) -> c_primop()
See also: c_primop/2.
update_c_receive(Node :: c_receive(), Clauses :: [cerl()], Timeout :: cerl(), Action :: cerl()) -> c_receive()
See also: c_receive/3.
update_c_record(Node :: c_record() | c_literal(), Arg :: c_var() | c_literal(), Id :: record_id(), Pairs :: [c_record_pair()]) -> c_record()
See also: c_record/2.
update_c_record_pair(Node :: c_record_pair(), Key :: c_literal(), Value :: cerl()) -> c_record_pair()
See also: c_record_pair/2.
update_c_seq(Node :: c_seq(), Argument :: cerl(), Body :: cerl()) -> c_seq()
See also: c_seq/2.
update_c_try(Node :: c_try(), Argument :: cerl(), Variables :: [cerl()], Body :: cerl(), ExceptionVars :: [cerl()], Handler :: cerl()) -> c_try()
See also: c_try/5.
update_c_tuple(Node :: c_tuple() | c_literal(), Elements :: [cerl()]) -> c_tuple() | c_literal()
See also: c_tuple/1.
update_c_tuple_skel(Node :: c_tuple(), Elements :: [cerl()]) -> c_tuple()
See also: c_tuple_skel/1.
update_c_values/2 ๐
update_c_values(Node :: c_values(), Elements :: [cerl()]) ->
c_values().
okay so this one takes a c_values() node and a list of cerl() elements, and gives you back a fresh c_values(). That's it. That's the whole vibe. โจ
See also: c_values/1.
update_c_var/2 ๐
update_c_var(Node :: c_var(), Name :: var_name()) -> c_var().
You hand it a c_var() node and a var_name(), and it returns a c_var(). Lowkey straightforward, highkey useful.
See also: c_var/1.
update_data/3 ๐ธ
update_data(Node :: cerl(), Type :: dtype(), Elements :: [cerl()]) ->
c_lct().
Takes a cerl() node, a dtype() type, and a list of cerl() elements, and gives you a c_lct(). Slay.
See also: make_data/2.
update_data_skel/3 ๐ฆ
update_data_skel(Node :: cerl(),
Type :: dtype(),
Elements :: [cerl()]) ->
c_lct().
You give it a cerl() node, a dtype(), and a list of cerl() elements, and it returns a c_lct().
See also: make_data_skel/2.
update_list/3 ๐
update_list(Node :: cerl(),
List :: [cerl()],
Tail :: cerl() | none) ->
cerl().
Bestie, this one takes a cerl() node, a List of cerl()s, and a Tail that's either a cerl() or none. You get a cerl() back. Periodt.
See also: make_list/2, update_list/2.
update_tree/2 ๐
update_tree(Node :: cerl(), Groups :: [[cerl()], ...]) -> cerl().
Creates a syntax tree with the given subtrees, and the same type and annotations as the node Node.
This is equivalent to ann_make_tree(get_ann(Node), type(Node), Groups), but potentially more efficient. No cap, it's giving optimized.
See also: ann_make_tree/3, get_ann/1, type/1, update_tree/3.
update_tree/3 ๐ฅ
update_tree(Node :: cerl(),
Type :: ctype(),
Groups :: [[cerl()], ...]) ->
cerl().
Creates a syntax tree with the given type and subtrees, and the same annotations as the node Node.
This is equivalent to ann_make_tree(get_ann(Node), Type, Groups), but potentially more efficient. Iconic behavior.
See also: ann_make_tree/3, get_ann/1, update_tree/2.
values_arity/1 ๐
values_arity(Node :: c_values()) -> non_neg_integer().
Returns the number of element subtrees of an abstract value list.
Note: This is equivalent to length(values_es(Node)), but potentially more efficient.
See also: c_values/1, values_es/1.
values_es/1 ๐
values_es(Node :: c_values()) -> [cerl()].
Returns the list of element subtrees of an abstract value list. Just the list, bestie.
See also: c_values/1, values_arity/1.
var_name/1 ๐ง
var_name(Node :: c_var()) -> var_name().
Returns the name of an abstract variable. That's the whole thing.
See also: c_var/1.
That's the whole page, bestie! The footer says Ericsson AB, compiler 10.0.4, cerl(3). No date listed, but honestly it's timeless. ๐โจ๐ฆ
๐ฆ 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