檢視原始碼 leex (parsetools v2.6)
Erlang 的詞法分析器產生器
一個基於正規表達式的 Erlang 詞法分析器產生器,類似於 lex
或 flex
。
注意
當
leex
模組被引入時,它被認為是實驗性的。
預設 Leex 選項
(主機作業系統)環境變數 ERL_COMPILER_OPTIONS
可以用來提供預設的 Leex 選項。它的值必須是有效的 Erlang 項。如果該值是一個列表,則直接使用。如果它不是一個列表,則將其放入一個列表中。
該列表會附加到任何傳遞給 file/2
的選項中。
可以使用 compile:env_compiler_options/0
來檢索該列表。
輸入檔案格式
以 %
開頭的 Erlang 風格註解在掃描器檔案中是允許的。定義檔案具有以下格式:
<Header>
Definitions.
<Macro Definitions>
Rules.
<Token Rules>
Erlang code.
<Erlang code>
Definitions.
、Rules.
和 Erlang code
標題是強制性的,並且必須從原始碼行的開頭開始。<Header>
、<Macro Definitions>
和 <Erlang code>
區段可以為空,但至少必須有一條規則。
巨集定義具有以下格式:
NAME = VALUE
而且在 =
周圍必須有空格。巨集可以透過寫入 {NAME}
來在規則的正規表達式中使用。
注意
當巨集在表達式中展開時,巨集呼叫會被替換為巨集值,而不會有任何形式的引用或括在括號中。
規則具有以下格式:
<Regexp> : <Erlang code>.
<Regexp>
必須出現在行的開頭,且不包含任何空格;使用 \t
和 \s
在正規表達式中包含 TAB 和 SPACE 字元。如果 <Regexp>
匹配,則會評估對應的 <Erlang code>
以產生一個 token。在 Erlang 程式碼中,可以使用以下預定義的變數:
TokenChars
- 匹配的 token 中的字元列表。TokenLen
- 匹配的 token 中的字元數。TokenLine
- token 出現的行號。TokenCol
- token 出現的欄號(token 中第一個字元的欄位)。TokenLoc
- token 位置。展開為{TokenLine,TokenCol}
(即使error_location
設定為line
)。
程式碼必須回傳:
{token,Token}
- 回傳Token
給呼叫者。{end_token,Token}
- 回傳Token
且是 tokens 呼叫中的最後一個 token。skip_token
- 完全略過此 token。{error,ErrString}
- token 中的錯誤,ErrString
是描述錯誤的字串。
也可以使用以下回傳將字元推回輸入字元中:
{token,Token,PushBackList}
{end_token,Token,PushBackList}
{skip_token,PushBackList}
這些與正常的回傳具有相同的含義,但是 PushBackList
中的字元將會前置到輸入字元中,並掃描下一個 token。請注意,推回換行符號將表示行號不再正確。
注意
推回字元會導致掃描器進入迴圈的意想不到的可能性!
以下範例會匹配一個簡單的 Erlang 整數或浮點數,並回傳一個可以傳送到 Erlang 解析器的 token:
D = [0-9]
{D}+ :
{token,{integer,TokenLine,list_to_integer(TokenChars)}}.
{D}+\.{D}+((E|e)(\+|\-)?{D}+)? :
{token,{float,TokenLine,list_to_float(TokenChars)}}.
Erlang code.
區段中的 Erlang 程式碼會直接寫入到輸出檔案中的模組宣告和預定義匯出宣告之後,因此可以新增額外的匯出、定義匯入和其他屬性,這些屬性在整個檔案中都是可見的。
正規表達式
此處允許的正規表達式是 egrep
和 AWK 程式語言中的一組子集,如同 A. V. Aho、B. W. Kernighan 和 P. J. Weinberger 所著的 The AWK Programming Language 一書中所定義。它們由以下字元組成:
c
- 匹配非元字元 c。\c
- 匹配跳脫序列或字面字元 c。.
- 匹配任何字元。^
- 匹配字串的開頭。$
- 匹配字串的結尾。[abc...]
- 字元類別,匹配任何字元abc...
。字元範圍由一對以-
分隔的字元指定。[^abc...]
- 反向字元類別,匹配任何除了abc...
之外的字元。r1 | r2
- 交替。它匹配r1
或r2
。r1r2
- 串接。它匹配r1
然後r2
。r+
- 匹配一個或多個r
。r*
- 匹配零個或多個r
。r?
- 匹配零個或一個r
。(r)
- 分組。它匹配r
。
允許的跳脫序列與 Erlang 字串的相同:
\b
- 退格鍵。\f
- 換頁符號。\n
- 換行符號(換行)。\r
- 歸位符號。\t
- Tab。\e
- 跳脫符號。\v
- 垂直 Tab。\s
- 空格。\d
- 刪除。\ddd
- 八進位值ddd
。\xhh
- 十六進位值hh
。\x{h...}
- 十六進位值h...
。\c
- 任何其他字元,例如\\
代表反斜線,\"
代表"
。
以下範例定義了一些簡化的 Erlang 資料類型版本:
Atoms [a-z][0-9a-zA-Z_]*
Variables [A-Z_][0-9a-zA-Z_]*
Floats (\+|-)?[0-9]+\.[0-9]+((E|e)(\+|-)?[0-9]+)?
注意
目前版本的
leex
中未實作使用^
和$
定錨正規表達式,並會產生剖析錯誤。
摘要
類型
從所有 I/O 模組回傳的標準 error_info/0
結構。ErrorDescriptor
可以由 format_error/1
格式化。
產生的掃描器匯出
掃描 String
並回傳其中所有的 token 或一個 error
元組。
這是一個可重入的呼叫,嘗試從 Chars
中掃描單個 token。
這是一個可重入的呼叫,嘗試從 Chars
中掃描 token。
類型
-type error_info() :: {erl_anno:line() | none, module(), ErrorDescriptor :: term()}.
從所有 I/O 模組回傳的標準 error_info/0
結構。ErrorDescriptor
可以由 format_error/1
格式化。
-type errors() :: [{file:filename(), [error_info()]}].
-type ok_ret() :: {ok, Scannerfile :: file:filename()} | {ok, Scannerfile :: file:filename(), warnings()}.
-type warnings() :: [{file:filename(), [error_info()]}].
產生的掃描器匯出
-spec string(String) -> StringRet when String :: string(), StringRet :: {ok, Tokens, EndLoc} | ErrorInfo, Tokens :: [Token], Token :: term(), ErrorInfo :: {error, error_info(), erl_anno:location()}, EndLoc :: erl_anno:location().
等同於 string(String, 1)
。
-spec string(String, StartLoc) -> StringRet when String :: string(), StringRet :: {ok, Tokens, EndLoc} | ErrorInfo, Tokens :: [Token], Token :: term(), ErrorInfo :: {error, error_info(), erl_anno:location()}, StartLoc :: erl_anno:location(), EndLoc :: erl_anno:location().
掃描 String
並回傳其中所有的 token 或一個 error
元組。
StartLoc
和 EndLoc
要不是 erl_anno:line()
就是 erl_anno:location()
,取決於 error_location
選項。
注意
如果
String
中的字元並未全部被消耗,則會發生錯誤。
-spec token(Cont, Chars) -> {more, Cont1} | {done, TokenRet, RestChars} when Cont :: [] | Cont1, Cont1 :: tuple(), Chars :: string() | eof, RestChars :: string() | eof, TokenRet :: {ok, Token, EndLoc} | {eof, EndLoc} | ErrorInfo, ErrorInfo :: {error, error_info(), erl_anno:location()}, Token :: term(), EndLoc :: erl_anno:location().
-spec token(Cont, Chars, StartLoc) -> {more, Cont1} | {done, TokenRet, RestChars} when Cont :: [] | Cont1, Cont1 :: tuple(), Chars :: string() | eof, RestChars :: string() | eof, TokenRet :: {ok, Token, EndLoc} | {eof, EndLoc} | ErrorInfo, ErrorInfo :: {error, error_info(), erl_anno:location()}, Token :: term(), StartLoc :: erl_anno:location(), EndLoc :: erl_anno:location().
這是一個可重入的呼叫,嘗試從 Chars
中掃描單個 token。
如果 Chars
中有足夠的字符可以掃描出一個符記 (token) 或偵測到錯誤,則會回傳 {done,...}
。否則會回傳 {cont,Cont}
,其中 Cont
會在下次呼叫 token()
時使用,並帶入更多字符以嘗試掃描符記。這個過程會持續到成功掃描出符記為止。Cont
的初始值為 []
。
此函式並非設計為由應用程式直接呼叫,而是透過 I/O 系統使用,通常在應用程式中可以透過以下方式呼叫:
io:request(InFile, {get_until,unicode,Prompt,Module,token,[Loc]})
-> TokenRet
-spec tokens(Cont, Chars) -> {more, Cont1} | {done, TokensRet, RestChars} when Cont :: [] | Cont1, Cont1 :: tuple(), Chars :: string() | eof, RestChars :: string() | eof, TokensRet :: {ok, Tokens, EndLoc} | {eof, EndLoc} | ErrorInfo, Tokens :: [Token], Token :: term(), ErrorInfo :: {error, error_info(), erl_anno:location()}, EndLoc :: erl_anno:location().
-spec tokens(Cont, Chars, StartLoc) -> {more, Cont1} | {done, TokensRet, RestChars} when Cont :: [] | Cont1, Cont1 :: tuple(), Chars :: string() | eof, RestChars :: string() | eof, TokensRet :: {ok, Tokens, EndLoc} | {eof, EndLoc} | ErrorInfo, Tokens :: [Token], Token :: term(), ErrorInfo :: {error, error_info(), erl_anno:location()}, StartLoc :: erl_anno:location(), EndLoc :: erl_anno:location().
這是一個可重入的呼叫,嘗試從 Chars
中掃描 token。
如果 Chars
中有足夠的字符可以掃描出多個符記或偵測到錯誤,則會回傳 {done,...}
。否則會回傳 {cont,Cont}
,其中 Cont
會在下次呼叫 tokens()
時使用,並帶入更多字符以嘗試掃描符記。這個過程會持續到成功掃描出所有符記為止。Cont
的初始值為 []
。
此函式與 token
的不同之處在於,它會持續掃描符記,直到掃描到包含 {end_token,Token}
的符記(請參閱下一節)。接著它會回傳所有符記。這通常用於掃描像 Erlang 這種有明確結束符記(例如 '.'
)的文法。如果沒有找到結束符記,則會掃描並回傳整個檔案。如果發生錯誤,則會跳過所有符記,直到下一個結束符記(包含結束符記)。
此函式並非設計為由應用程式直接呼叫,而是透過 I/O 系統使用,通常在應用程式中可以透過以下方式呼叫:
io:request(InFile, {get_until,unicode,Prompt,Module,tokens,[Loc]})
-> TokensRet
函式
-spec file(FileName) -> leex_ret() when FileName :: file:filename().
等同於 file(File, [])
。
-spec file(FileName, Options) -> leex_ret() when FileName :: file:filename(), Options :: Option | [Option], Option :: {dfa_graph, boolean()} | {includefile, Includefile :: file:filename()} | {report_errors, boolean()} | {report_warnings, boolean()} | {report, boolean()} | {return_errors, boolean()} | {return_warnings, boolean()} | {return, boolean()} | {scannerfile, Scannerfile :: file:filename()} | {verbose, boolean()} | {warnings_as_errors, boolean()} | {deterministic, boolean()} | {error_location, line | column} | {tab_size, pos_integer()} | dfa_graph | report_errors | report_warnings | report | return_errors | return_warnings | return | verbose | warnings_as_errors.
從輸入檔案中的定義產生一個詞法分析器。
輸入檔案的副檔名為 .xrl
。如果未指定副檔名,則會將其添加到檔名中。產生的模組名稱為不帶 .xrl
副檔名的 Xrl 檔案名稱。
目前的選項如下:
dfa_graph
- 產生一個.dot
檔案,其中包含 DFA 的描述,其格式可使用 Graphviz (www.graphviz.com
) 檢視。{includefile,Includefile}
- 使用特定的或自訂的序言檔案,而不是預設的lib/parsetools/include/leexinc.hrl
(否則會包含此檔案)。{report_errors, boolean()}
- 啟用後,會印出發生的錯誤。預設值為true
。{report_warnings, boolean()}
- 啟用後,會印出發生的警告。預設值為true
。{report, boolean()}
- 此為report_errors
和report_warnings
的簡寫形式。{return_errors, boolean()}
- 如果設定此標誌,當發生錯誤時會回傳{error, Errors, Warnings}
。預設值為false
。{return_warnings, boolean()}
- 如果設定此標誌,則成功回傳的元組會加入包含Warnings
的額外欄位。預設值為false
。{return, boolean()}
- 此為return_errors
和return_warnings
的簡寫形式。{scannerfile, Scannerfile}
-Scannerfile
為將包含產生的 Erlang 掃描器程式碼的檔案名稱。預設值 (""
) 是將.erl
副檔名加到去除.xrl
副檔名的FileName
上。{verbose, boolean()}
- 輸出從解析輸入檔案和產生內部表格的資訊。{warnings_as_errors, boolean()}
- 將警告視為錯誤。{deterministic, boolean()}
- 使產生的-file()
屬性僅包含檔案路徑的基本名稱。{error_location, line | column}
- 如果設定為column
,錯誤位置將會是{Line,Column}
元組,而不僅僅是Line
。此外,string/2
、token/3
和tokens/3
函式中的StartLoc
和EndLoc
也會是{Line,Column}
元組,而不僅僅是Line
。預設值為line
。請注意,即使error_location
設定為line
,您也可以獨立使用TokenLoc
來取得符記位置。Unicode 字元會被計算為佔用多少位元組來表示的欄位數。
{tab_size, pos_integer()}
- 設定\t
字元的寬度(僅在error_location
設定為column
時相關)。預設值為8
。
任何布林選項都可以透過宣告選項名稱來設定為 true
。例如,verbose
等同於 {verbose, true}
。
Leex 將會把 .hrl
副檔名加入 Includefile
名稱中,並將 .erl
副檔名加入 Scannerfile
名稱中,除非副檔名已經存在。
-spec format_error(ErrorDescriptor) -> io_lib:chars() when ErrorDescriptor :: term().
回傳以英文描述的錯誤原因字串 ErrorDescriptor
,此字串是由 leex:file/1,2
在正規表達式中發生錯誤時回傳的。