查看源代码 抽象格式

本节描述了 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)}

解析错误和文件结束的表示

除了形式的表示之外,表示模块声明的列表(由 epperl_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_idefault 表示。省略的 TSL_idefault 表示。
  • 如果 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_idefault 表示。省略的 TSL_idefault 表示。
  • 如果 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_2E_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,其中 BA 都是代码块,则 Rep(E) = {'try',ANNO,Rep(B),[],[],Rep(A)}
  • 如果 E 是一个 try 表达式 try B of Cc_1 ; ... ; Cc_k after A end,其中 BA 都是代码块,每个 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,其中 BA 都是代码块,每个 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,其中 BA 都是代码块,每个 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_2E 是一个表达式,则 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_idefault 表示。省略的 TSL_idefault 表示。
  • 如果 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 是原子 erlangA 是一个原子或运算符,那么 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_2Gt_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>>,其中 MN 是单例整数类型,那么 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,其中 LH 是单例整数类型,那么 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,其中 KV 是类型,则 Rep(A) = {type,ANNO,map_field_assoc,[Rep(K),Rep(V)]}
  • 如果 A 是关联类型 K := V,其中 KV 是类型,则 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 中)。