7.抽象格式 | 7. The Abstract Format
7抽象格式
本节描述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
是CA
TCH子句,则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_i
case都是一个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}
.A
ssociationsA
n关联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_code
Beam文件的块中(用于调试目的)。
从Erlang/OTP R9C开始,该abstract_code
块包含{raw_abstract_v1,AbstractCode}
,AbstractCode
本节中描述的抽象代码在哪里。
在R9C之前的OTP版本中,经过一些更多处理后的抽象代码存储在Beam文件中。元组的第一个元素可能是abstract_v1
(在OTP R7B中)或abstract_v2
(在OTP R8B中)。