在线文档教程
Erlang 20

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 = EE那么表达式是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,where E_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_0P则模式在哪里,则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,where B是一个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,其中BA是身体,则Rep(E)= {'try',LINE,Rep(B),[],[],Rep(A)}

  • 如果E是一个尝试表达式try B of Cc_1 ; ... ; Cc_k after A end,其中BA是一个实体,并且每个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,在哪里BA是尸体,每一个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,where BA是一个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是一个过滤器EE那么表达式是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那么原子在哪里erlangA是原子还是操作符,则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,其中LH单身整数类型,则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,其中KV类型是,则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中)。