查看源代码 抽象格式
本节描述了 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 中)。