查看源代码 抽象格式
本节描述了 Erlang 程序解析树的标准表示形式,使用 Erlang 项表示。此表示形式被称为抽象格式。处理此类解析树的函数有 compile:forms/1,2
以及以下模块中的函数:
这些函数也被用作解析转换的输入和输出,请参阅 compile
模块。
我们使用函数 Rep
来表示从 Erlang 源代码结构 C
到其抽象格式表示 R
的映射,并写作 R = Rep(C)
。
本节中的单词 ANNO
表示一个注解,并且除其他外,表示构造在源文件中出现的行号。有关详细信息,请参阅 erl_anno
。同一构造中 ANNO
的多个实例可以表示不同的注解。
由于运算符本身不是项,因此,当下面提到运算符时,运算符的表示被认为是原子,其打印名称与运算符的字符相同。
模块声明和形式
模块声明由一系列形式组成,这些形式要么是函数声明,要么是属性。
- 如果 D 是一个模块声明,由形式
F_1
, ...,F_k
组成,则 Rep(D) =[Rep(F_1), ..., Rep(F_k)]
。 - 如果 F 是一个属性
-export([Fun_1/A_1, ..., Fun_k/A_k])
,则 Rep(F) ={attribute,ANNO,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}
。 - 如果 F 是一个属性
-import(Mod,[Fun_1/A_1, ..., Fun_k/A_k])
,则 Rep(F) ={attribute,ANNO,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}
。 - 如果 F 是一个属性
-module(Mod)
,则 Rep(F) ={attribute,ANNO,module,Mod}
。 - 如果 F 是一个属性
-file(File,Line)
,则 Rep(F) ={attribute,ANNO,file,{File,Line}}
。 - 如果 F 是一个函数声明
Name Fc_1 ; ... ; Name Fc_k
,其中每个Fc_i
是一个函数子句,其模式序列的长度相同Arity
,则 Rep(F) ={function,ANNO,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}
。 - 如果 F 是一个函数规范
-Spec Name Ft_1; ...; Ft_k
,其中Spec
是原子spec
或原子callback
,并且每个Ft_i
是一个可能受约束的函数类型,其参数序列的长度相同Arity
,则 Rep(F) ={attribute,ANNO,Spec,{{Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}
。 - 如果 F 是一个函数规范
-spec Mod:Name Ft_1; ...; Ft_k
,其中每个Ft_i
是一个可能受约束的函数类型,其参数序列的长度相同Arity
,则 Rep(F) ={attribute,ANNO,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}
。 - 如果 F 是一个记录声明
-record(Name,{V_1, ..., V_k})
,其中每个V_i
是一个记录字段,则 Rep(F) ={attribute,ANNO,record,{Name,[Rep(V_1), ..., Rep(V_k)]}}
。有关 Rep(V),请参见下文。 - 如果 F 是一个类型声明
-Type Name(V_1, ..., V_k) :: T
,其中Type
是原子type
或原子opaque
,每个V_i
是一个类型变量,T
是一个类型,则 Rep(F) ={attribute,ANNO,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}
。 - 如果 F 是一个通配属性
-A(T)
,则 Rep(F) ={attribute,ANNO,A,T}
。
记录字段
记录声明中的每个字段都可以有一个可选的、显式的、默认的初始化表达式以及一个可选的类型。
- 如果 V 是
A
,则 Rep(V) ={record_field,ANNO,Rep(A)}
。 - 如果 V 是
A = E
,其中E
是一个表达式,则 Rep(V) ={record_field,ANNO,Rep(A),Rep(E)}
。 - 如果 V 是
A :: T
,其中T
是一个类型,则 Rep(V) ={typed_record_field,{record_field,ANNO,Rep(A)},Rep(T)}
。 - 如果 V 是
A = E :: T
,其中E
是一个表达式,T
是一个类型,则 Rep(V) ={typed_record_field,{record_field,ANNO,Rep(A),Rep(E)},Rep(T)}
。
解析错误和文件结束的表示
除了形式的表示之外,表示模块声明的列表(由 epp
和 erl_parse
中的函数返回)可以包含以下内容:
- 元组
{error,E}
和{warning,W}
,表示语法上不正确的形式和警告。 {eof,LOCATION}
,表示在解析完整形式之前遇到的流的结尾。单词LOCATION
表示一个位置,并表示源文件中最后一行的行号,以及可能该行最后一列的列号。有关详细信息,请参阅erl_anno
。
有关这些值的更多详细信息,请参阅 erl_parse
中的 form_info/0
类型。
原子字面量
有五种原子字面量,它们在模式、表达式和保护子句中以相同的方式表示:
- 如果 L 是一个原子字面量,则 Rep(L) =
{atom,ANNO,L}
。 - 如果 L 是一个字符字面量,则 Rep(L) =
{char,ANNO,L}
。 - 如果 L 是一个浮点数字面量,则 Rep(L) =
{float,ANNO,L}
。 - 如果 L 是一个整数数字面量,则 Rep(L) =
{integer,ANNO,L}
。 - 如果 L 是一个字符串字面量,由字符
C_1
, ...,C_k
组成,则 Rep(L) ={string,ANNO,[C_1, ..., C_k]}
。
请注意,负整数和浮点数字面量不会以这种形式出现;它们被解析为一元求反运算符的应用。
模式
如果 Ps 是一个模式序列 P_1, ..., P_k
,则 Rep(Ps) = [Rep(P_1), ..., Rep(P_k)]
。此类序列作为函数或 fun 的参数列表出现。
单个模式的表示方式如下:
- 如果 P 是一个原子字面量
L
,则 Rep(P) = Rep(L)。 - 如果 P 是一个位串模式
<<P_1:Size_1/TSL_1, ..., P_k:Size_k/TSL_k>>
,其中每个Size_i
是一个可以计算为整数的表达式,每个TSL_i
是一个类型说明符列表,则 Rep(P) ={bin,ANNO,[{bin_element,ANNO,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,ANNO,Rep(P_k),Rep(Size_k),Rep(TSL_k)}]}
。有关 Rep(TSL),请参见下文。省略的Size_i
由default
表示。省略的TSL_i
由default
表示。 - 如果 P 是一个复合模式
P_1 = P_2
,则 Rep(P) ={match,ANNO,Rep(P_1),Rep(P_2)}
。 如果 P 是一个 cons 模式
[P_h | P_t]
,则 Rep(P) ={cons,ANNO,Rep(P_h),Rep(P_t)}
。- 如果 P 是一个映射模式
#{A_1, ..., A_k}
,其中每个A_i
是一个关联P_i_1 := P_i_2
,则 Rep(P) ={map,ANNO,[Rep(A_1), ..., Rep(A_k)]}
。有关 Rep(A),请参见下文。 - 如果 P 是一个 nil 模式
[]
,则 Rep(P) ={nil,ANNO}
。 - 如果 P 是一个运算符模式
P_1 Op P_2
,其中Op
是一个二元运算符(这要么是应用于文字字符串或字符列表的++
的出现,要么是可以编译时计算为数字的表达式的出现),则 Rep(P) ={op,ANNO,Op,Rep(P_1),Rep(P_2)}
。 - 如果 P 是一个运算符模式
Op P_0
,其中Op
是一个一元运算符(这是一个可以编译时计算为数字的表达式的出现),则 Rep(P) ={op,ANNO,Op,Rep(P_0)}
。 - 如果 P 是一个带括号的模式
( P_0 )
,则 Rep(P) =Rep(P_0)
,也就是说,带括号的模式无法与其主体区分开来。 - 如果 P 是一个记录字段索引模式
#Name.Field
,其中Field
是一个原子,则 Rep(P) ={record_index,ANNO,Name,Rep(Field)}
。 - 如果 P 是一个记录模式
#Name{Field_1=P_1, ..., Field_k=P_k}
,其中每个Field_i
是一个原子或_
,则 Rep(P) ={record,ANNO,Name,[{record_field,ANNO,Rep(Field_1),Rep(P_1)}, ..., {record_field,ANNO,Rep(Field_k),Rep(P_k)}]}
。 - 如果 P 是一个元组模式
{P_1, ..., P_k}
,则 Rep(P) ={tuple,ANNO,[Rep(P_1), ..., Rep(P_k)]}
。 - 如果 P 是一个通用模式
_
,则 Rep(P) ={var,ANNO,'_'}
。 - 如果 P 是一个变量模式
V
,则 Rep(P) ={var,ANNO,A}
,其中 A 是一个原子,其打印名称与V
的字符相同。
请注意,每个模式都与某些表达式具有相同的源形式,并且以与相应表达式相同的方式表示。
表达式
代码块 B 是一个非空表达式序列 E_1, ..., E_k
,且 Rep(B) = [Rep(E_1), ..., Rep(E_k)]
。
表达式 E 是以下类型之一
- 如果 E 是一个原子字面量
L
,则 Rep(E) = Rep(L)。 - 如果 E 是一个位串推导式
<<E_0 || Q_1, ..., Q_k>>
,其中每个Q_i
都是一个限定符,则 Rep(E) ={bc,ANNO,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}
。关于 Rep(Q),请参见下文。 - 如果 E 是一个位串构造器
<<E_1:Size_1/TSL_1, ..., E_k:Size_k/TSL_k>>
,其中每个Size_i
都是一个表达式,每个TSL_i
都是一个类型指定符列表,则 Rep(E) ={bin,ANNO,[{bin_element,ANNO,Rep(E_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,ANNO,Rep(E_k),Rep(Size_k),Rep(TSL_k)}]}
。关于 Rep(TSL),请参见下文。省略的Size_i
用default
表示。省略的TSL_i
用default
表示。 - 如果 E 是一个代码块表达式
begin B end
,其中B
是一个代码块,则 Rep(E) ={block,ANNO,Rep(B)}
。 - 如果 E 是一个 case 表达式
case E_0 of Cc_1 ; ... ; Cc_k end
,其中E_0
是一个表达式,每个Cc_i
都是一个 case 子句,则 Rep(E) ={'case',ANNO,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}
。 - 如果 E 是一个 catch 表达式
catch E_0
,则 Rep(E) ={'catch',ANNO,Rep(E_0)}
。 如果 E 是一个 cons 骨架
[E_h | E_t]
,则 Rep(E) ={cons,ANNO,Rep(E_h),Rep(E_t)}
。- 如果 E 是一个 fun 表达式
fun Name/Arity
,则 Rep(E) ={'fun',ANNO,{function,Name,Arity}}
。 - 如果 E 是一个 fun 表达式
fun Module:Name/Arity
,则 Rep(E) ={'fun',ANNO,{function,Rep(Module),Rep(Name),Rep(Arity)}}
。 - 如果 E 是一个 fun 表达式
fun Fc_1 ; ... ; Fc_k end
,其中每个Fc_i
都是一个函数子句,则 Rep(E) ={'fun',ANNO,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}
。 - 如果 E 是一个 fun 表达式
fun Name Fc_1 ; ... ; Name Fc_k end
,其中Name
是一个变量,每个Fc_i
都是一个函数子句,则 Rep(E) ={named_fun,ANNO,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}
。 - 如果 E 是一个函数调用
E_0(E_1, ..., E_k)
,则 Rep(E) ={call,ANNO,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}
。 - 如果 E 是一个函数调用
E_m:E_0(E_1, ..., E_k)
,则 Rep(E) ={call,ANNO,{remote,ANNO,Rep(E_m),Rep(E_0)},[Rep(E_1), ..., Rep(E_k)]}
。 - 如果 E 是一个 if 表达式
if Ic_1 ; ... ; Ic_k end
,其中每个Ic_i
都是一个 if 子句,则 Rep(E) ={'if',ANNO,[Rep(Ic_1), ..., Rep(Ic_k)]}
。 - 如果 E 是一个列表推导式
[E_0 || Q_1, ..., Q_k]
,其中每个Q_i
都是一个限定符,则 Rep(E) ={lc,ANNO,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}
。关于 Rep(Q),请参见下文。 - 如果 E 是一个 map 推导式
#{E_0 || Q_1, ..., Q_k}
,其中E_0
是一个关联K => V
,每个Q_i
都是一个限定符,则 Rep(E) ={mc,ANNO,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}
。关于 Rep(E_0) 和 Rep(Q),请参见下文。 - 如果 E 是一个 map 创建
#{A_1, ..., A_k}
,其中每个A_i
都是一个关联E_i_1 => E_i_2
,则 Rep(E) ={map,ANNO,[Rep(A_1), ..., Rep(A_k)]}
。关于 Rep(A),请参见下文。 - 如果 E 是一个 map 更新
E_0#{A_1, ..., A_k}
,其中每个A_i
都是一个关联E_i_1 => E_i_2
或E_i_1 := E_i_2
,则 Rep(E) ={map,ANNO,Rep(E_0),[Rep(A_1), ..., Rep(A_k)]}
。关于 Rep(A),请参见下文。 - 如果 E 是一个匹配操作符表达式
P = E_0
,其中P
是一个模式,则 Rep(E) ={match,ANNO,Rep(P),Rep(E_0)}
。 - 如果 E 是一个条件匹配操作符表达式
P ?= E_0
,其中P
是一个模式,则 Rep(E) ={maybe_match,ANNO,Rep(P),Rep(E_0)}
。 - 如果 E 是一个 maybe 表达式
maybe B end
,其中B
是一个代码块,则 Rep(E) ={'maybe',ANNO,Rep(B)}
。 - 如果 E 是一个 maybe 表达式
maybe B else Ec_1 ; ... ; Ec_k end
,其中B
是一个代码块,每个Ec_i
都是一个 else 子句,则 Rep(E) ={'maybe',ANNO,Rep(B),{'else',ANNO,[Rep(Ec_1), ..., Rep(Ec_k)]}}
。 - 如果 E 是 nil,
[]
,则 Rep(E) ={nil,ANNO}
。 - 如果 E 是一个操作符表达式
E_1 Op E_2
,其中Op
是一个二元操作符,而不是匹配操作符=
,则 Rep(E) ={op,ANNO,Op,Rep(E_1),Rep(E_2)}
。 - 如果 E 是一个操作符表达式
Op E_0
,其中Op
是一个一元操作符,则 Rep(E) ={op,ANNO,Op,Rep(E_0)}
。 - 如果 E 是一个带括号的表达式
( E_0 )
,则 Rep(E) =Rep(E_0)
,也就是说,带括号的表达式与其主体无法区分。 - 如果 E 是一个 receive 表达式
receive Cc_1 ; ... ; Cc_k end
,其中每个Cc_i
都是一个 case 子句,则 Rep(E) ={'receive',ANNO,[Rep(Cc_1), ..., Rep(Cc_k)]}
。 - 如果 E 是一个 receive 表达式
receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end
,其中每个Cc_i
都是一个 case 子句,E_0
是一个表达式,B_t
是一个代码块,则 Rep(E) ={'receive',ANNO,[Rep(Cc_1), ..., Rep(Cc_k)],Rep(E_0),Rep(B_t)}
。 - 如果 E 是一个记录创建
#Name{Field_1=E_1, ..., Field_k=E_k}
,其中每个Field_i
都是一个原子或_
,则 Rep(E) ={record,ANNO,Name,[{record_field,ANNO,Rep(Field_1),Rep(E_1)}, ..., {record_field,ANNO,Rep(Field_k),Rep(E_k)}]}
。 - 如果 E 是一个记录字段访问
E_0#Name.Field
,其中Field
是一个原子,则 Rep(E) ={record_field,ANNO,Rep(E_0),Name,Rep(Field)}
。 - 如果 E 是一个记录字段索引
#Name.Field
,其中Field
是一个原子,则 Rep(E) ={record_index,ANNO,Name,Rep(Field)}
。 - 如果 E 是一个记录更新
E_0#Name{Field_1=E_1, ..., Field_k=E_k}
,其中每个Field_i
都是一个原子,则 Rep(E) ={record,ANNO,Rep(E_0),Name,[{record_field,ANNO,Rep(Field_1),Rep(E_1)}, ..., {record_field,ANNO,Rep(Field_k),Rep(E_k)}]}
。 - 如果 E 是一个元组骨架
{E_1, ..., E_k}
,则 Rep(E) ={tuple,ANNO,[Rep(E_1), ..., Rep(E_k)]}
。 - 如果 E 是一个 try 表达式
try B catch Tc_1 ; ... ; Tc_k end
,其中B
是一个代码块,每个Tc_i
都是一个 catch 子句,则 Rep(E) ={'try',ANNO,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}
。 - 如果 E 是一个 try 表达式
try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end
,其中B
是一个代码块,每个Cc_i
都是一个 case 子句,每个Tc_j
都是一个 catch 子句,则 Rep(E) ={'try',ANNO,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],[]}
。 - 如果 E 是一个 try 表达式
try B after A end
,其中B
和A
都是代码块,则 Rep(E) ={'try',ANNO,Rep(B),[],[],Rep(A)}
。 - 如果 E 是一个 try 表达式
try B of Cc_1 ; ... ; Cc_k after A end
,其中B
和A
都是代码块,每个Cc_i
都是一个 case 子句,则 Rep(E) ={'try',ANNO,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[],Rep(A)}
。 - 如果 E 是一个 try 表达式
try B catch Tc_1 ; ... ; Tc_k after A end
,其中B
和A
都是代码块,每个Tc_i
都是一个 catch 子句,则 Rep(E) ={'try',ANNO,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],Rep(A)}
。 - 如果 E 是一个 try 表达式
try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n after A end
,其中B
和A
都是代码块,每个Cc_i
都是一个 case 子句,每个Tc_j
都是一个 catch 子句,则 Rep(E) ={'try',ANNO,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],Rep(A)}
。 - 如果 E 是一个变量
V
,则 Rep(E) ={var,ANNO,A}
,其中A
是一个原子,其打印名称与V
的字符相同。
限定符
限定符 Q 是以下类型之一
- 如果 Q 是一个过滤器
E
,其中E
是一个表达式,则 Rep(Q) =Rep(E)
。 - 如果 Q 是一个列表生成器
P <- E
,其中P
是一个模式,E
是一个表达式,则 Rep(Q) ={generate,ANNO,Rep(P),Rep(E)}
。 - 如果 Q 是一个位串生成器
P <= E
,其中P
是一个模式,E
是一个表达式,则 Rep(Q) ={b_generate,ANNO,Rep(P),Rep(E)}
。 - 如果 Q 是一个 map 生成器
P <- E
,其中P
是一个关联模式P_1 := P_2
,E
是一个表达式,则 Rep(Q) ={m_generate,ANNO,Rep(P),Rep(E)}
。关于 Rep(P),请参见下文。
位串元素类型指定符
位串元素的类型指定符列表 TSL 是一个类型指定符序列 TS_1 - ... - TS_k
,且 Rep(TSL) = [Rep(TS_1), ..., Rep(TS_k)]
。
- 如果 TS 是一个类型指定符
A
,其中A
是一个原子,则 Rep(TS) =A
。 - 如果 TS 是一个类型指定符
A:Value
,其中A
是一个原子,Value
是一个整数,则 Rep(TS) ={A,Value}
。
关联
关联 A 是以下类型之一
- 如果 A 是一个关联
K => V
,则 Rep(A) ={map_field_assoc,ANNO,Rep(K),Rep(V)}
。 - 如果 A 是一个关联
K := V
,则 Rep(A) ={map_field_exact,ANNO,Rep(K),Rep(V)}
。
子句
有函数子句、if 子句、case 子句和 catch 子句。
子句 C 是以下类型之一
- 如果 C 是一个 case 子句
P -> B
,其中P
是一个模式,B
是一个代码块,则 Rep(C) ={clause,ANNO,[Rep(P)],[],Rep(B)}
。 - 如果 C 是一个 case 子句
P when Gs -> B
,其中P
是一个模式,Gs
是一个保护序列,B
是一个代码块,则 Rep(C) ={clause,ANNO,[Rep(P)],Rep(Gs),Rep(B)}
。 - 如果 C 是一个 catch 子句
P -> B
,其中P
是一个模式,B
是一个主体,那么 Rep(C) ={clause,ANNO,[Rep({throw,P,_})],[],Rep(B)}
,也就是说,带有显式异常类throw
且带有或不带有显式堆栈跟踪变量_
的 catch 子句,无法与不带有显式异常类且不带有显式堆栈跟踪变量的 catch 子句区分开来。 - 如果 C 是一个 catch 子句
X : P -> B
,其中X
是一个原子字面量或变量模式,P
是一个模式,B
是一个主体,那么 Rep(C) ={clause,ANNO,[Rep({X,P,_})],[],Rep(B)}
,也就是说,带有显式异常类且带有显式堆栈跟踪变量_
的 catch 子句,无法与带有显式异常类但不带有显式堆栈跟踪变量的 catch 子句区分开来。 - 如果 C 是一个 catch 子句
X : P : S -> B
,其中X
是一个原子字面量或变量模式,P
是一个模式,S
是一个变量,B
是一个主体,那么 Rep(C) ={clause,ANNO,[Rep({X,P,S})],[],Rep(B)}
。 - 如果 C 是一个 catch 子句
P when Gs -> B
,其中P
是一个模式,Gs
是一个卫语句序列,B
是一个主体,那么 Rep(C) ={clause,ANNO,[Rep({throw,P,_})],Rep(Gs),Rep(B)}
,也就是说,带有显式异常类throw
且带有或不带有显式堆栈跟踪变量_
的 catch 子句,无法与不带有显式异常类且不带有显式堆栈跟踪变量的 catch 子句区分开来。 - 如果 C 是一个 catch 子句
X : P when Gs -> B
,其中X
是一个原子字面量或变量模式,P
是一个模式,Gs
是一个卫语句序列,B
是一个主体,那么 Rep(C) ={clause,ANNO,[Rep({X,P,_})],Rep(Gs),Rep(B)}
,也就是说,带有显式异常类且带有显式堆栈跟踪变量_
的 catch 子句,无法与带有显式异常类但不带有显式堆栈跟踪变量的 catch 子句区分开来。 - 如果 C 是一个 catch 子句
X : P : S when Gs -> B
,其中X
是一个原子字面量或变量模式,P
是一个模式,Gs
是一个卫语句序列,S
是一个变量,B
是一个主体,那么 Rep(C) ={clause,ANNO,[Rep({X,P,S})],Rep(Gs),Rep(B)}
。 - 如果 C 是一个函数子句
( Ps ) -> B
,其中Ps
是一个模式序列,B
是一个主体,那么 Rep(C) ={clause,ANNO,Rep(Ps),[],Rep(B)}
。 - 如果 C 是一个函数子句
( Ps ) when Gs -> B
,其中Ps
是一个模式序列,Gs
是一个卫语句序列,B
是一个主体,那么 Rep(C) ={clause,ANNO,Rep(Ps),Rep(Gs),Rep(B)}
。 - 如果 C 是一个 if 子句
Gs -> B
,其中Gs
是一个卫语句序列,B
是一个主体,那么 Rep(C) ={clause,ANNO,[],Rep(Gs),Rep(B)}
。
卫语句
一个卫语句序列 Gs 是一个卫语句序列 G_1; ...; G_k
,并且 Rep(Gs) = [Rep(G_1), ..., Rep(G_k)]
。如果卫语句序列为空,那么 Rep(Gs) = []
。
一个卫语句 G 是一个非空的卫语句测试序列 Gt_1, ..., Gt_k
,并且 Rep(G) = [Rep(Gt_1), ..., Rep(Gt_k)]
。
一个卫语句测试 Gt 是以下之一
- 如果 Gt 是一个原子字面量
L
,那么 Rep(Gt) = Rep(L)。 - 如果 Gt 是一个位串构造器
<<Gt_1:Size_1/TSL_1, ..., Gt_k:Size_k/TSL_k>>
,其中每个Size_i
是一个卫语句测试,每个TSL_i
是一个类型指定列表,那么 Rep(Gt) ={bin,ANNO,[{bin_element,ANNO,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,ANNO,Rep(Gt_k),Rep(Size_k),Rep(TSL_k)}]}
。对于 Rep(TSL),请参见上文。省略的Size_i
由default
表示。省略的TSL_i
由default
表示。 如果 Gt 是一个 cons 骨架
[Gt_h | Gt_t]
,那么 Rep(Gt) ={cons,ANNO,Rep(Gt_h),Rep(Gt_t)}
。- 如果 Gt 是一个函数调用
A(Gt_1, ..., Gt_k)
,其中A
是一个原子,那么 Rep(Gt) ={call,ANNO,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}
。 - 如果 Gt 是一个函数调用
A_m:A(Gt_1, ..., Gt_k)
,其中A_m
是原子erlang
,A
是一个原子或运算符,那么 Rep(Gt) ={call,ANNO,{remote,ANNO,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}
。 - 如果 Gt 是一个 map 创建
#{A_1, ..., A_k}
,其中每个A_i
是一个关联Gt_i_1 => Gt_i_2
,那么 Rep(Gt) ={map,ANNO,[Rep(A_1), ..., Rep(A_k)]}
。对于 Rep(A),请参见上文。 - 如果 Gt 是一个 map 更新
Gt_0#{A_1, ..., A_k}
,其中每个A_i
是一个关联Gt_i_1 => Gt_i_2
或Gt_i_1 := Gt_i_2
,那么 Rep(Gt) ={map,ANNO,Rep(Gt_0),[Rep(A_1), ..., Rep(A_k)]}
。对于 Rep(A),请参见上文。 - 如果 Gt 为 nil,
[]
,那么 Rep(Gt) ={nil,ANNO}
。 - 如果 Gt 是一个运算符卫语句测试
Gt_1 Op Gt_2
,其中Op
是一个二元运算符,而不是匹配运算符=
,那么 Rep(Gt) ={op,ANNO,Op,Rep(Gt_1),Rep(Gt_2)}
。 - 如果 Gt 是一个运算符卫语句测试
Op Gt_0
,其中Op
是一个一元运算符,那么 Rep(Gt) ={op,ANNO,Op,Rep(Gt_0)}
。 - 如果 Gt 是一个带括号的卫语句测试
( Gt_0 )
,那么 Rep(Gt) =Rep(Gt_0)
,也就是说,带括号的卫语句测试无法与它们的主体区分开来。 - 如果 Gt 是一个记录创建
#Name{Field_1=Gt_1, ..., Field_k=Gt_k}
,其中每个Field_i
是一个原子或_
,那么 Rep(Gt) ={record,ANNO,Name,[{record_field,ANNO,Rep(Field_1),Rep(Gt_1)}, ..., {record_field,ANNO,Rep(Field_k),Rep(Gt_k)}]}
。 - 如果 Gt 是一个记录字段访问
Gt_0#Name.Field
,其中Field
是一个原子,那么 Rep(Gt) ={record_field,ANNO,Rep(Gt_0),Name,Rep(Field)}
。 - 如果 Gt 是一个记录字段索引
#Name.Field
,其中Field
是一个原子,那么 Rep(Gt) ={record_index,ANNO,Name,Rep(Field)}
。 - 如果 Gt 是一个元组骨架
{Gt_1, ..., Gt_k}
,那么 Rep(Gt) ={tuple,ANNO,[Rep(Gt_1), ..., Rep(Gt_k)]}
。 - 如果 Gt 是一个变量模式
V
,那么 Rep(Gt) ={var,ANNO,A}
,其中 A 是一个原子,其打印名称与V
的字符相同。
请注意,每个卫语句测试都与某个表达式具有相同的源形式,并且以与相应表达式相同的方式表示。
类型
- 如果 T 是一个带注释的类型
A :: T_0
,其中A
是一个变量,那么 Rep(T) ={ann_type,ANNO,[Rep(A),Rep(T_0)]}
。 - 如果 T 是一个原子、一个字符或一个整数字面量 L,那么 Rep(T) = Rep(L)。
- 如果 T 是一个位串类型
<<_:M,_:_*N>>
,其中M
和N
是单例整数类型,那么 Rep(T) ={type,ANNO,binary,[Rep(M),Rep(N)]}
。 - 如果 T 是空列表类型
[]
,那么 Rep(T) ={type,ANNO,nil,[]}
,也就是说,空列表类型[]
无法与预定义类型nil/0
区分开来。 - 如果 T 是一个 fun 类型
fun()
,那么 Rep(T) ={type,ANNO,'fun',[]}
。 - 如果 T 是一个 fun 类型
fun((...) -> T_0)
,那么 Rep(T) ={type,ANNO,'fun',[{type,ANNO,any},Rep(T_0)]}
。 - 如果 T 是一个 fun 类型
fun(Ft)
,其中Ft
是一个函数类型,那么 Rep(T) =Rep(Ft)
。对于 Rep(Ft),请参见下文。 - 如果 T 是一个整数范围类型
L .. H
,其中L
和H
是单例整数类型,那么 Rep(T) ={type,ANNO,range,[Rep(L),Rep(H)]}
。 - 如果 T 是一个 map 类型
map/0
,那么 Rep(T) ={type,ANNO,map,any}
。 - 如果 T 是一个 map 类型
#{A_1, ..., A_k}
,其中每个A_i
是一个关联类型,那么 Rep(T) ={type,ANNO,map,[Rep(A_1), ..., Rep(A_k)]}
。对于 Rep(A),请参见下文。 - 如果 T 是一个运算符类型
T_1 Op T_2
,其中Op
是一个二元运算符(这是可以在编译时计算为整数的表达式的出现),那么 Rep(T) ={op,ANNO,Op,Rep(T_1),Rep(T_2)}
。 - 如果 T 是一个运算符类型
Op T_0
,其中Op
是一个一元运算符(这是可以在编译时计算为整数的表达式的出现),那么 Rep(T) ={op,ANNO,Op,Rep(T_0)}
。 - 如果 T 是
( T_0 )
,那么 Rep(T) =Rep(T_0)
,也就是说,带括号的类型无法与它们的主体区分开来。 - 如果 T 是一个预定义(或内置)类型
N(T_1, ..., T_k)
,那么 Rep(T) ={type,ANNO,N,[Rep(T_1), ..., Rep(T_k)]}
。 - 如果 T 是一个记录类型
#Name{F_1, ..., F_k}
,其中每个F_i
是一个记录字段类型,那么 Rep(T) ={type,ANNO,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}
。对于 Rep(F),请参见下文。 - 如果 T 是一个远程类型
M:N(T_1, ..., T_k)
,那么 Rep(T) ={remote_type,ANNO,[Rep(M),Rep(N),[Rep(T_1), ..., Rep(T_k)]]}
。 - 如果 T 是一个元组类型
tuple/0
,那么 Rep(T) ={type,ANNO,tuple,any}
。 - 如果 T 是一个元组类型
{T_1, ..., T_k}
,那么 Rep(T) ={type,ANNO,tuple,[Rep(T_1), ..., Rep(T_k)]}
。 如果 T 是类型联合
T_1 | ... | T_k
,则 Rep(T) ={type,ANNO,union,[Rep(T_1), ..., Rep(T_k)]}
。- 如果 T 是类型变量
V
,则 Rep(T) ={var,ANNO,A}
,其中A
是一个原子,其打印名称与V
的字符相同。类型变量是除下划线 (_
) 之外的任何变量。 - 如果 T 是用户定义的类型
N(T_1, ..., T_k)
,则 Rep(T) ={user_type,ANNO,N,[Rep(T_1), ..., Rep(T_k)]}
。
函数类型
函数类型 Ft 是以下其中一种
- 如果 Ft 是约束函数类型
Ft_1 when Fc
,其中Ft_1
是一个函数类型,Fc
是一个函数约束,则 Rep(T) ={type,ANNO,bounded_fun,[Rep(Ft_1),Rep(Fc)]}
。有关 Rep(Fc) 的信息,请参见下文。 - 如果 Ft 是函数类型
(T_1, ..., T_n) -> T_0
,其中每个T_i
都是一个类型,则 Rep(Ft) ={type,ANNO,'fun',[{type,ANNO,product,[Rep(T_1), ..., Rep(T_n)]},Rep(T_0)]}
。
函数约束
函数约束 Fc 是非空的约束序列 C_1, ..., C_k
,并且 Rep(Fc) = [Rep(C_1), ..., Rep(C_k)]
。
- 如果 C 是约束
V :: T
,其中V
是类型变量,T
是类型,则 Rep(C) ={type,ANNO,constraint,[{atom,ANNO,is_subtype},[Rep(V),Rep(T)]]}
。
关联类型
- 如果 A 是关联类型
K => V
,其中K
和V
是类型,则 Rep(A) ={type,ANNO,map_field_assoc,[Rep(K),Rep(V)]}
。 - 如果 A 是关联类型
K := V
,其中K
和V
是类型,则 Rep(A) ={type,ANNO,map_field_exact,[Rep(K),Rep(V)]}
。
记录字段类型
- 如果 F 是记录字段类型
Name :: Type
,其中Type
是类型,则 Rep(F) ={type,ANNO,field_type,[Rep(Name),Rep(Type)]}
。
预处理后的抽象格式
可以向编译器指定编译选项 debug_info
,以便将抽象代码存储在 Beam 文件中的 abstract_code
块中(用于调试目的)。
从 Erlang/OTP R9C 开始,abstract_code
块包含 {raw_abstract_v1,AbstractCode}
,其中 AbstractCode
是本节中描述的抽象代码。
在 R9C 之前的 OTP 版本中,一些经过更多处理后的抽象代码存储在 Beam 文件中。元组的第一个元素将是 abstract_v1
(在 OTP R7B 中) 或 abstract_v2
(在 OTP R8B 中)。