查看源码 leex (parsetools v2.6)

Erlang 的词法分析器生成器

一个基于正则表达式的 Erlang 词法分析器生成器,类似于 lexflex

注意

当引入 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> 来生成标记。在 Erlang 代码中,可以使用以下预定义的变量

  • TokenChars - 匹配的标记中的字符列表。

  • TokenLen - 匹配的标记中的字符数。

  • TokenLine - 标记出现的行号。

  • TokenCol - 标记出现的列号(包含在标记中的第一个字符的列)。

  • TokenLoc - 标记位置。扩展为 {TokenLine,TokenCol}(即使 error_location 设置为 line)。

代码必须返回

  • {token,Token} - 将 Token 返回给调用者。

  • {end_token,Token} - 返回 Token,并且是标记调用中的最后一个标记。

  • skip_token - 完全跳过此标记。

  • {error,ErrString} - 标记中的错误,ErrString 是描述该错误的字符串。

也可以使用以下返回值将字符推回输入字符中

  • {token,Token,PushBackList}
  • {end_token,Token,PushBackList}
  • {skip_token,PushBackList}

这些与正常返回具有相同的含义,但是 PushBackList 中的字符将添加到输入字符的前面,并扫描下一个标记。请注意,推回换行符将意味着行号不再正确。

注意

推回字符会给你带来意想不到的可能性,导致扫描器循环!

以下示例将匹配一个简单的 Erlang 整数或浮点数,并返回一个可以发送到 Erlang 解析器的标记

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 - 选择。它匹配 r1r2

  • r1r2 - 连接。它匹配 r1,然后匹配 r2

  • r+ - 匹配一个或多个 r

  • r* - 匹配零个或多个 r

  • r? - 匹配零个或一个 r

  • (r) - 分组。它匹配 r

允许的转义序列与 Erlang 字符串的转义序列相同

  • \b - 退格。

  • \f - 换页。

  • \n - 换行符(行馈送)。

  • \r - 回车符。

  • \t - 制表符。

  • \e - 转义符。

  • \v - 垂直制表符。

  • \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 并返回其中的所有标记或 error 元组。

这是一个可重入调用,用于尝试从 Chars 扫描单个标记。

这是一个可重入调用,用于尝试从 Chars 扫描标记。

函数

从输入文件中的定义生成词法分析器。

返回由 leex:file/1,2 返回的错误原因 ErrorDescriptor 的英文描述性字符串,当正则表达式中存在错误时。

类型

链接到此类型

error_info()

查看源码 (未导出)
-type error_info() :: {erl_anno:line() | none, module(), ErrorDescriptor :: term()}.

从所有 I/O 模块返回的标准 error_info/0 结构。ErrorDescriptor 可由 format_error/1 格式化。

链接到此类型

error_ret()

查看源码 (未导出)
-type error_ret() :: error | {error, Errors :: errors(), Warnings :: warnings()}.
-type errors() :: [{file:filename(), [error_info()]}].
链接到此类型

leex_ret()

查看源码 (未导出)
-type leex_ret() :: ok_ret() | error_ret().
-type ok_ret() ::
          {ok, Scannerfile :: file:filename()} | {ok, Scannerfile :: file:filename(), warnings()}.
链接到此类型

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)

链接到此函数

string(String, StartLoc)

查看源码
-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 并返回其中的所有标记或 error 元组。

StartLocEndLocerl_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().

等效于 token(Cont, Chars, 1)

链接到此函数

token(Cont, Chars, StartLoc)

查看源码
-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 扫描单个标记。

如果 Chars 中有足够的字符来扫描一个 token 或检测到一个错误,则将返回 {done,...}。否则,将返回 {cont,Cont},其中 Cont 用于下次调用 token() 时,以便使用更多字符来尝试扫描 token。此过程将持续到扫描到 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().

等效于 tokens(Cont, Chars, 1)

链接到此函数

tokens(Cont, Chars, StartLoc)

查看源码
-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 扫描标记。

如果 Chars 中有足够的字符来扫描 token 或检测到错误,则将返回 {done,...}。否则,将返回 {cont,Cont},其中 Cont 用于下次调用 tokens() 时,以便使用更多字符来尝试扫描 token。此过程将持续到所有 token 都被扫描完毕为止。Cont 的初始值为 []

此函数与 token 的不同之处在于,它将继续扫描 token,直到扫描到 {end_token,Token} 为止(请参阅下一节)。然后,它将返回所有 token。这通常用于扫描像 Erlang 这样的语法,其中存在显式的结束 token,'.'。如果未找到结束 token,则将扫描并返回整个文件。如果发生错误,则将跳过所有 token,直到下一个结束 token(包括下一个结束 token)。

它不是设计为由应用程序直接调用的,而是通过 I/O 系统使用的,通常可以在应用程序中通过以下方式调用:

io:request(InFile, {get_until,unicode,Prompt,Module,tokens,[Loc]})
  -> TokensRet

函数

-spec file(FileName) -> leex_ret() when FileName :: file:filename().

等效于 file(File, [])

链接到此函数

file(FileName, Options)

查看源代码 (自 OTP R16B02 起)
-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_errorsreport_warnings 的简短形式。

  • {return_errors, boolean()} - 如果设置此标志,则当存在错误时,将返回 {error, Errors, Warnings}。默认值为 false

  • {return_warnings, boolean()} - 如果设置此标志,则在成功时返回的元组中添加一个包含 Warnings 的额外字段。默认值为 false

  • {return, boolean()} - 这是 return_errorsreturn_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/2token/3tokens/3 函数中的 StartLocEndLoc 将是 {Line,Column} 元组,而不仅仅是 Line。默认值为 line。请注意,即使 error_location 设置为 line,您也可以独立地使用 TokenLoc 来获取 token 位置。

    Unicode 字符的列数与它们用于表示的字节数相同。

  • {tab_size, pos_integer()} - 设置 \t 字符的宽度(仅当 error_location 设置为 column 时才相关)。默认值为 8

任何布尔选项都可以通过声明选项的名称设置为 true。例如,verbose 等效于 {verbose, true}

Leex 将扩展名 .hrl 添加到 Includefile 名称,并将扩展名 .erl 添加到 Scannerfile 名称,除非扩展名已存在。

链接到此函数

format_error(ErrorDescriptor)

查看源码
-spec format_error(ErrorDescriptor) -> io_lib:chars() when ErrorDescriptor :: term().

返回由 leex:file/1,2 返回的错误原因 ErrorDescriptor 的英文描述性字符串,当正则表达式中存在错误时。