7.抽象格式 | 7. The Abstract Format
本节描述Erlang程序的解析树的标准表示形式,如Erlang条款。这种表示形式被称为抽象格式。处理这些分析树的compile:forms/1,2函数是以下模块中的函数:
epp(3)
erl_eval(3)
erl_lint(3)
erl_parse(3)
erl_pp(3)
io(3)
函数也用作解析转换的输入和输出,请参见compile(3)模块。
我们使用该函数Rep来表示从Erlang源结构C到其抽象格式表示的映射R,并写入R = Rep(C)。
LINE本节中的单词表示一个整数,并表示构建发生的源文件中的行号。LINE同一构造中的几个实例可以表示不同的线条。
由于运算符本身并非术语,因此当下面提及运算符时,运算符的表示应视为具有与运算符相同字符的打印名称的原子。
7.1模块声明和表单
模块声明由一系列形式组成,它们是函数声明或属性。
- 如果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,LINE,export,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}。
- 如果F是属性
-import(Mod,[Fun_1/A_1, ..., Fun_k/A_k]),则Rep(F)={attribute,LINE,import,{Mod,[{Fun_1,A_1}, ..., {Fun_k,A_k}]}}。
- 如果F是属性
-module(Mod),则Rep(F)={attribute,LINE,module,Mod}。
- 如果F是属性
-file(File,Line),则Rep(F)={attribute,LINE,file,{File,Line}}。
- 如果F是一个函数声明
Name Fc_1 ; ... ; Name Fc_k,其中每个Fc_i是一个具有相同长度的模式序列的函数子句Arity,则Rep(F)={function,LINE,Name,Arity,[Rep(Fc_1), ...,Rep(Fc_k)]}。
- 如果F是一个函数说明
-Spec Name Ft_1; ...; Ft_k,其中Spec是原子spec或原子callback,并且每个Ft_i都是可能受约束的函数类型,并且参数序列长度相同Arity,则Rep(F)={attribute,Line,Spec,{{Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}。
- 如果F是一个函数说明
-spec Mod:Name Ft_1; ...; Ft_k,其中每个Ft_i参数序列的长度都相同Arity,那么Rep(F)={attribute,Line,spec,{{Mod,Name,Arity},[Rep(Ft_1), ..., Rep(Ft_k)]}}。
- 如果F是一个记录声明
-record(Name,{V_1, ..., V_k}),其中每个V_i记录字段,则Rep(F)={attribute,LINE,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,LINE,Type,{Name,Rep(T),[Rep(V_1), ..., Rep(V_k)]}}。
- 如果F是一个wild属性
-A(T),则{attribute,LINE,A,T}记录声明中的Rep(F)= .Record FieldsEach字段可以包含可选的显式默认初始化表达式和可选类型。
- 如果V是
A,则Rep(V)={record_field,LINE,Rep(A)}。
- 如果V是
A = E,E那么表达式是Rep(V)={record_field,LINE,Rep(A),Rep(E)}。
- 如果V是
A :: T,哪里T是一个类型,那么Rep(V)={typed_record_field,{record_field,LINE,Rep(A)},Rep(T)}。
- 如果V是
A = E :: T,其中E是表达式并且T是类型,则Rep(V)={typed_record_field,{record_field,LINE,Rep(A),Rep(E)},Rep(T)}。分析错误和文件结束的表示除了表单的表示之外,表示模块声明的列表(由epp(3)和中的函数返回erl_parse(3))可以包含以下内容:
- 元组,
{error,E}和{warning,W}表示语法错误的形式和警告。
{eof,LOCATION},表示在完整表单解析之前遇到的流结束。该字LOCATION表示一个整数,并表示源文件中最后一行的编号。7.2原子文字有五种原子文字,它们在模式,表达和警卫中以相同的方式表示:
- 如果L是原子文字,则Rep(L)=
{atom,LINE,L}。
- 如果L是字符文字,则Rep(L)=
{char,LINE,L}。
- 如果L是浮点数字,则Rep(L)=
{float,LINE,L}。
- 如果L是整数文字,则Rep(L)=
{integer,LINE,L}。
- 如果L是由字符组成的字符串
C_1,...C_k,则Rep(L)={string,LINE,[C_1, ..., C_k]}。
请注意,负整数和浮点文字不会像这样发生; 它们被解析为一元否定运算符的应用程序。
7.3模式
如果Ps是一系列模式P_1, ..., P_k,则Rep(Ps)= [Rep(P_1), ..., Rep(P_k)]。这些序列作为函数或乐趣的参数列表出现。
个别模式表现如下:
- 如果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,LINE,[{bin_element,LINE,Rep(P_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,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,LINE,Rep(P_1),Rep(P_2)}。
- 如果P是一个cons模式
[P_h | P_t],那么Rep(P)={cons,LINE,Rep(P_h),Rep(P_t)}。
- 如果P是一个映射模式
#{A_1, ..., A_k},其中每个A_i都是一个关联P_i_1 := P_i_2,那么Rep(P)={map,LINE,[Rep(A_1), ..., Rep(A_k)]}。对于Rep(A),请参阅下文。
- 如果P是零模式
[],则Rep(P)={nil,LINE}。
- 如果P是一个运算符模式
P_1 Op P_2,其中Op是二元运算符(这是一个++应用于文字字符串或字符列表的事件,或者是在编译时可以评估为某个数字的表达式),则Rep(P )={op,LINE,Op,Rep(P_1),Rep(P_2)}。
- 如果P是一个运算符模式
Op P_0,其中Op是一元运算符(这是发生在编译时可以评估为数字的表达式),则Rep(P)={op,LINE,Op,Rep(P_0)}。
- 如果P是一个加括号的模式
( P_0 ),那么Rep(P)=Rep(P_0),也就是括号内的模式不能与它们的身体区分开来。
- 如果P是记录字段索引模式
#Name.Field,则其中Field是原子,则Rep(P)={record_index,LINE,Name,Rep(Field)}。
- 如果P是一个记录模式
#Name{Field_1=P_1, ..., Field_k=P_k},其中每个Field_i都是一个原子,或者_Rep(P)={record,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(P_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(P_k)}]}。
- 如果P是一个元组模式
{P_1, ..., P_k},那么Rep(P)={tuple,LINE,[Rep(P_1), ..., Rep(P_k)]}。
- 如果P是普遍模式
_,则Rep(P)={var,LINE,'_'}。
- 如果P是一个可变模式
V,那么Rep(P)={var,LINE,A},其中A是一个打印名与原始字符相同的原子V。
请注意,每个模式都与某个表达式具有相同的源形式,并以与相应表达式相同的方式表示。
7.4表达式
Body 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,LINE,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,LINE,[{bin_element,LINE,Rep(E_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,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,LINE,Rep(B)}。
- 如果E是一个case表达式
case E_0 of Cc_1 ; ... ; Cc_k end,whereE_0是一个表达式并且每个Cc_i都是一个case子句,那么Rep(E)={'case',LINE,Rep(E_0),[Rep(Cc_1), ..., Rep(Cc_k)]}。
- 如果E是捕获表达式
catch E_0,则Rep(E)={'catch',LINE,Rep(E_0)}。
- 如果E是cons骨架
[E_h | E_t],则Rep(E)={cons,LINE,Rep(E_h),Rep(E_t)}。
- 如果E是一个有趣的表达式
fun Name/Arity,那么Rep(E)={'fun',LINE,{function,Name,Arity}}。
- 如果E是一个有趣的表达式
fun Module:Name/Arity,那么Rep(E)={'fun',LINE,{function,Rep(Module),Rep(Name),Rep(Arity)}}。(在Erlang / OTP R15之前:Rep(E)={'fun',LINE,{function,Module,Name,Arity}}。)
- 如果E是一个有趣的表达式
fun Fc_1 ; ... ; Fc_k end,其中每个Fc_i都是一个函数子句,那么Rep(E)={'fun',LINE,{clauses,[Rep(Fc_1), ..., Rep(Fc_k)]}}。
- 如果E是一个有趣的表达式
fun Name Fc_1 ; ... ; Name Fc_k end,其中Name是一个变量,每个Fc_i都是一个函数子句,则Rep(E)={named_fun,LINE,Name,[Rep(Fc_1), ..., Rep(Fc_k)]}。
- 如果E是函数调用
E_0(E_1, ..., E_k),则Rep(E)={call,LINE,Rep(E_0),[Rep(E_1), ..., Rep(E_k)]}。
- 如果E是函数调用
E_m:E_0(E_1, ..., E_k),则Rep(E)={call,LINE,{remote,LINE,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',LINE,[Rep(Ic_1), ..., Rep(Ic_k)]}。
- 如果E是列表理解
[E_0 || Q_1, ..., Q_k],其中每个Q_i是限定词,则Rep(E)={lc,LINE,Rep(E_0),[Rep(Q_1), ..., Rep(Q_k)]}。对于Rep(Q),请参阅下文。
- 如果E是一个地图创建
#{A_1, ..., A_k},其中每个A_i都是一个关联,E_i_1 => E_i_2或者E_i_1 := E_i_2Rep(E)={map,LINE,[Rep(A_1), ..., Rep(A_k)]}。对于Rep(A),请参阅下文。
- 如果E是地图更新
E_0#{A_1, ..., A_k},其中每个A_i都是关联,E_i_1 => E_i_2或者E_i_1 := E_i_2Rep(E)={map,LINE,Rep(E_0),[Rep(A_1), ..., Rep(A_k)]}。对于Rep(A),请参阅下文。
- 如果E是匹配运算符表达式
P = E_0,P则模式在哪里,则Rep(E)={match,LINE,Rep(P),Rep(E_0)}。
- 如果E为零,
[]则Rep(E)={nil,LINE}。
- 如果E是运算符表达式
E_1 Op E_2,其中Op是除匹配运算符以外的二元运算符=,则Rep(E)={op,LINE,Op,Rep(E_1),Rep(E_2)}。
- 如果E是一个操作符表达
Op E_0,其中Op是一元运算符,然后代表(E)={op,LINE,Op,Rep(E_0)}。
- 如果E是一个加括号的表达式
( E_0 ),那么Rep(E)=Rep(E_0),也就是括号表达式不能与它们的身体区分开来。
- 如果E是一个接收表达式
receive Cc_1 ; ... ; Cc_k end,其中每个Cc_i都是一个case子句,则Rep(E)={'receive',LINE,[Rep(Cc_1), ..., Rep(Cc_k)]}。
- 如果E是一个接收表达式
receive Cc_1 ; ... ; Cc_k after E_0 -> B_t end,其中每个Cc_i都是一个case子句,E_0是一个表达式,并且B_t是一个正文,那么Rep(E)={'receive',LINE,[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,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}。
- 如果E是一个记录字段访问
E_0#Name.Field,其中Field是原子,则Rep(E)={record_field,LINE,Rep(E_0),Name,Rep(Field)}。
- 如果E是记录字段索引
#Name.Field,则其中Field是原子,则Rep(E)={record_index,LINE,Name,Rep(Field)}。
- 如果E是记录更新
E_0#Name{Field_1=E_1, ..., Field_k=E_k},其中每个Field_i是原子,则Rep(E)={record,LINE,Rep(E_0),Name,[{record_field,LINE,Rep(Field_1),Rep(E_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(E_k)}]}。
- 如果E是一个元组骨架
{E_1, ..., E_k},则Rep(E)={tuple,LINE,[Rep(E_1), ..., Rep(E_k)]}。
- 如果E是一个try表达式
try B catch Tc_1 ; ... ; Tc_k end,那么它B是一个正文,每个Tc_i都是一个catch子句,那么Rep(E)={'try',LINE,Rep(B),[],[Rep(Tc_1), ..., Rep(Tc_k)],[]}。
- 如果E是try表达式
try B of Cc_1 ; ... ; Cc_k catch Tc_1 ; ... ; Tc_n end,whereB是一个body,每个Cc_i都是一个case子句,并且每个Tc_j都是一个catch子句,那么Rep(E)={'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],[]}。
- 如果E是一个尝试表达式
try B after A end,其中B和A是身体,则Rep(E)={'try',LINE,Rep(B),[],[],Rep(A)}。
- 如果E是一个尝试表达式
try B of Cc_1 ; ... ; Cc_k after A end,其中B和A是一个实体,并且每个Cc_i都是一个case子句,那么Rep(E)={'try',LINE,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%28E%29={'try',LINE,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,whereB和A是一个body,每个Cc_icase都是一个case子句,并且每个Tc_j都是一个catch子句,那么Rep(E)={'try',LINE,Rep(B),[Rep(Cc_1), ..., Rep(Cc_k)],[Rep(Tc_1), ..., Rep(Tc_n)],Rep(A)}。
- 如果E是一个变量
V,那么Rep(E)={var,LINE,A},其中A是一个打印名与由相同字符组成的原子V。
限定符
限定符Q是下列之一:
- 如果Q是一个过滤器
E,E那么表达式是Rep(Q)=Rep(E)。
- 如果Q是一个生成器
P <- E,P那么模式E是表达式,那么Rep(Q)={generate,LINE,Rep(P),Rep(E)}。
- 如果Q是位串生成器
P <= E,其中P是模式并且E是表达式,则Rep(Q)={b_generate,LINE,Rep(P),Rep(E)}.Bitstring元素类型说明符类型说明符列表位串字符串的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}.AssociationsAn关联A是以下之一:
- 如果A是一个关联
K => V,那么Rep(A)={map_field_assoc,LINE,Rep(K),Rep(V)}。
- 如果A是一个关联
K := V,则Rep(A)={map_field_exact,LINE,Rep(K),Rep(V)}.7.5子句有函数子句,if子句,case子句和catch子句.C子句是下列之一:
- 如果C是一个case子句
P -> B,其中P是一个模式并且B是一个正文,那么Rep(C)={clause,LINE,[Rep(P)],[],Rep(B)}。
- 如果C是一个case子句
P when Gs -> B,其中P是一个模式,Gs是一个保护序列,并且B是一个正文,那么Rep(C)={clause,LINE,[Rep(P)],Rep(Gs),Rep(B)}。
- 如果C是一个catch子句
P -> B,哪里P是一个模式并且B是一个正文,那么Rep(C)={clause,LINE,[Rep({throw,P,_})],[],Rep(B)}。
- 如果C是一个catch子句
X : P -> B,其中X是原子字面量或变量模式,P是一个模式,并且B是一个正文,那么Rep(C)={clause,LINE,[Rep({X,P,_})],[],Rep(B)}。
- 如果C是一个catch子句
P when Gs -> B,P模式在哪里,Gs是一个保护序列,并且B是一个正文,那么Rep(C)={clause,LINE,[Rep({throw,P,_})],Rep(Gs),Rep(B)}。
- 如果C是一个catch子句
X : P when Gs -> B,其中X是原子字面量或变量模式,P是一个模式,Gs是一个保护序列,并且B是一个正文,则Rep(C)={clause,LINE,[Rep({X,P,_})],Rep(Gs),Rep(B)}。
- 如果C是一个函数子句
( Ps ) -> B,其中Ps是一个模式序列并且B是一个正文,那么Rep(C)={clause,LINE,Rep(Ps),[],Rep(B)}。
- 如果C是一个函数子句
( Ps ) when Gs -> B,其中Ps是一个模式序列,Gs是一个保护序列并且B是一个正文,那么Rep(C)={clause,LINE,Rep(Ps),Rep(Gs),Rep(B)}。
- 如果C是if子句
Gs -> B,其中Gs是一个保护序列并且B是一个正文,那么Rep(C)={clause,LINE,[],Rep(Gs),Rep(B)}。
7.6近卫
守卫序列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,LINE,[{bin_element,LINE,Rep(Gt_1),Rep(Size_1),Rep(TSL_1)}, ..., {bin_element,LINE,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,LINE,Rep(Gt_h),Rep(Gt_t)}。
- 如果Gt是一个函数调用
A(Gt_1, ..., Gt_k),其中A是原子,则Rep(Gt)={call,LINE,Rep(A),[Rep(Gt_1), ..., Rep(Gt_k)]}。
- 如果Gt是函数调用
A_m:A(Gt_1, ..., Gt_k),A_m那么原子在哪里erlang,A是原子还是操作符,则Rep(Gt)={call,LINE,{remote,LINE,Rep(A_m),Rep(A)},[Rep(Gt_1), ..., Rep(Gt_k)]}。
- 如果Gt是一个地图创建
#{A_1, ..., A_k},其中每个A_i都是一个关联,Gt_i_1 => Gt_i_2或者Gt_i_1 := Gt_i_2Rep(Gt)={map,LINE,[Rep(A_1), ..., Rep(A_k)]}。对于Rep(A),请参阅上文。
- 如果Gt是地图更新
Gt_0#{A_1, ..., A_k},其中每个A_i都是关联,Gt_i_1 => Gt_i_2或者Gt_i_1 := Gt_i_2Rep(Gt)={map,LINE,Rep(Gt_0),[Rep(A_1), ..., Rep(A_k)]}。对于Rep(A),请参阅上文。
- 如果Gt为零,
[]则Rep(Gt)={nil,LINE}。
- 如果Gt是运营商防护测试,那么除了匹配运算符,
Gt_1 Op Gt_2其中Op是二元运算符=,那么Rep(Gt)={op,LINE,Op,Rep(Gt_1),Rep(Gt_2)}。
- 如果G t为操作者后卫测试
Op Gt_0,其中Op是一元运算符,然后代表(GT)={op,LINE,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,LINE,Name,[{record_field,LINE,Rep(Field_1),Rep(Gt_1)}, ..., {record_field,LINE,Rep(Field_k),Rep(Gt_k)}]}。
- 如果Gt是一个记录字段访问
Gt_0#Name.Field,那么它Field是一个原子,那么Rep(Gt)={record_field,LINE,Rep(Gt_0),Name,Rep(Field)}。
- 如果Gt是记录字段索引
#Name.Field,则其中Field是原子,则Rep(Gt)={record_index,LINE,Name,Rep(Field)}。
- 如果Gt是一个元组骨架
{Gt_1, ..., Gt_k},则Rep(Gt)={tuple,LINE,[Rep(Gt_1), ..., Rep(Gt_k)]}。
- 如果Gt是一个可变模式
V,那么Rep(Gt)={var,LINE,A},其中A是一个打印名称由与其相同的字符组成的原子V。
请注意,每个警卫测试都与某个表达式具有相同的源表单,并且以与相应表达式相同的方式表示。
7.7种
- 如果T是一个注释类型
A :: T_0,其中A是一个变量,那么Rep(T)={ann_type,LINE,[Rep(A),Rep(T_0)]}。
- 如果T是原子或整数字面量L,则Rep(T)= Rep(L)。
- 如果T是一个比特串类型
<<_:M,_:_*N>>,其中M和N单身整数类型,则Rep(T)={type,LINE,binary,[Rep(M),Rep(N)]}。
- 如果T是空列表类型
[],则Rep(T)={type,Line,nil,[]}。
- 如果T是一个有趣的类型
fun(),那么Rep(T)={type,LINE,'fun',[]}。
- 如果T是一个有趣的类型
fun((...) -> T_0),那么Rep(T)={type,LINE,'fun',[{type,LINE,any},Rep(T_0)]}。
- 如果T是一个有趣的类型
fun(Ft),其中Ft是一个函数类型,那么Rep(T)=Rep(Ft)。对于Rep(Ft),请参见下文。
- 如果T是整数范围类型
L .. H,其中L和H单身整数类型,则Rep(T)={type,LINE,range,[Rep(L),Rep(H)]}。
- 如果T是地图类型
map(),则Rep(T)={type,LINE,map,any}。
- 如果T是地图类型
#{A_1, ..., A_k},其中每个A_i都是关联类型,则Rep(T)={type,LINE,map,[Rep(A_1), ..., Rep(A_k)]}。对于Rep(A),请参阅下文。
- 如果T是一个运算符类型
T_1 Op T_2,那么其中Op是二元运算符(这是发生在编译时可以计算为整数的表达式),则Rep(T)={op,LINE,Op,Rep(T_1),Rep(T_2)}。
- 如果T是一个运算符类型
Op T_0,其中Op是一元运算符(这是发生在编译时可评估为整数的表达式),则Rep(T)={op,LINE,Op,Rep(T_0)}。
- 如果T是
( T_0 ),那么Rep(T)=Rep(T_0),也就是说,括号中的类型不能与它们的身体区分开来。
- 如果T是预定义(或内置)类型
N(T_1, ..., T_k),则Rep(T)={type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}。
- 如果T是记录类型
#Name{F_1, ..., F_k},其中每个F_i记录字段类型,则Rep(T)={type,LINE,record,[Rep(Name),Rep(F_1), ..., Rep(F_k)]}。对于Rep(F),请参见下文。
- 如果T是远程类型
M:N(T_1, ..., T_k),则Rep(T)={remote_type,LINE,[Rep(M),Rep(N),[Rep(T_1), ..., Rep(T_k)]]}。
- 如果T是一个元组类型
tuple(),则Rep(T)={type,LINE,tuple,any}。
- 如果T是一个元组类型
{T_1, ..., T_k},则Rep(T)={type,LINE,tuple,[Rep(T_1), ..., Rep(T_k)]}。
- 如果T是一个类型联合
T_1 | ... | T_k,那么Rep(T)={type,LINE,union,[Rep(T_1), ..., Rep(T_k)]}。
- 如果T是一个类型变量
V,那么Rep(T)={var,LINE,A},其中A是一个打印名称由与其相同的字符组成的原子V。一个类型变量是除了下划线(_)以外的任何变量。
- 如果T是用户定义的类型
N(T_1, ..., T_k),则Rep(T)={user_type,LINE,N,[Rep(T_1), ..., Rep(T_k)]}.Function TypesA函数类型Ft是以下之一:
- 如果Ft是一个约束函数类型
Ft_1 when Fc,其中Ft_1是一个函数类型并且Fc是一个函数约束,那么Rep(T)={type,LINE,bounded_fun,[Rep(Ft_1),Rep(Fc)]}。对于Rep(Fc),见下文。
- 如果Ft是一个函数类型
(T_1, ..., T_n) -> T_0,其中每个T_i类型都是一个类型,则Rep(Ft)={type,LINE,'fun',[{type,LINE,product,[Rep(T_1), ..., Rep(T_n)]},Rep(T_0)]}.Function ConstraintsA函数约束Fc是一个非空的约束序列C_1, ..., C_k,Rep(Fc)=[Rep(C_1), ..., Rep(C_k)]。
- 如果C是一个约束
V :: T,其中V是一个类型变量并且T是一个类型,那么Rep(C)={type,LINE,constraint,[{atom,LINE,is_subtype},[Rep(V),Rep(T)]]}。
关联类型
- 如果A是关联类型
K => V,其中K和V类型是,则Rep(A)={type,LINE,map_field_assoc,[Rep(K),Rep(V)]}。
- 如果A是关联类型
K := V,其中K和V类型是,则Rep(A)={type,LINE,map_field_exact,[Rep(K),Rep(V)]}。
记录字段类型
- 如果F是记录字段类型
Name :: Type,其中Type是类型,则Rep(F)={type,LINE,field_type,[Rep(Name),Rep(Type)]}。
7.8预处理后的摘要格式
debug_info可以为编译器指定编译选项,以便将抽象代码存储在abstract_codeBeam文件的块中(用于调试目的)。
从Erlang/OTP R9C开始,该abstract_code块包含{raw_abstract_v1,AbstractCode},AbstractCode本节中描述的抽象代码在哪里。
在R9C之前的OTP版本中,经过一些更多处理后的抽象代码存储在Beam文件中。元组的第一个元素可能是abstract_v1(在OTP R7B中)或abstract_v2(在OTP R8B中)。
本文档系腾讯云开发者社区成员共同维护,如有问题请联系 cloudcommunity@tencent.com

