檢視原始碼 抽象格式

本節描述 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 是一個映射推導式 #{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 是一個映射創建 #{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 是一個映射更新 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 是一個 record 創建 #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 是一個 record 字段訪問 E_0#Name.Field,其中 Field 是一個原子,則 Rep(E) = {record_field,ANNO,Rep(E_0),Name,Rep(Field)}
  • 如果 E 是一個 record 字段索引 #Name.Field,其中 Field 是一個原子,則 Rep(E) = {record_index,ANNO,Name,Rep(Field)}
  • 如果 E 是一個 record 更新 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 是一個映射產生器 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_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 中)。