Rolex(临时版本 0.4):C# 中的词法分析器生成器





5.00/5 (1投票)
为您的 C# 项目添加快速扫描器/分词器
引言
本月,我曾希望发布一个基于我正在开发的新有限自动机引擎的 Unicode 版本的 Rolex。
然而,该引擎目前停滞不前,直到我拿到完成实现所需的书籍。
话虽如此,我仍然需要 Rolex 启用 Unicode。我一直使用 GPLEX 来实现此目的,但其输入和输出处理方式笨拙且不理想。输入规范看起来像一个 LEX 文档,输出可以跨多个文件,并且具有类似 LEX 的编程接口,包括 yylex()
方法。唉。
我所做的是发布了 Rolex 的一个临时版本,该版本基于 GPLEX 1.2 引擎的解构和一些重新改造版本。该程序经过修改,可以接受 Rolex 词法分析器规范并生成实现 IEnumerable<Token>
的分词器类。
限制
此临时版本将仅在 C# 中生成词法分析器。其他 .NET 语言支持将在 Rolex 的未来版本中恢复。
Copyright
对于不熟悉它的人来说,GPLEX,又名 Garden Points Lexer,版权所有 (c) 2009,昆士兰科技大学。完整的版权包含在源代码中。我没有编写 GPLEX。我修改了它。
概念化这个混乱的局面
Rolex 命令行界面
使用 Rolex 相对简单,但有很多选项,其中大部分你永远不需要。这是使用界面
Rolex: Usage
rolex filename [options]
options:
/namespace name -- create code in this namespace - default none
/class name -- create code under this class - default derived from <output>
/ignoreCase -- create a case-insensitive automaton
/check -- create automaton but do not create output file
/codePage NN -- default codepage NN if no unicode prefix (BOM)
/codePageHelp -- display codepage help
/classes -- use character equivalence classes (on by default if unicode)
/help -- display this usage message
/info -- scanner has header comment (on by default)
/listing -- emit listing even if no errors
/noCompress -- do not compress scanner tables
/noCompressMap -- do not compress character map
/noCompressNext -- do not compress nextstate tables
/noMinimize -- do not minimize the states of the dfsa
/noPersistBuffer -- do not retain input buffer throughout processing
/noShared -- do not generated shared code - useful for
2nd scanner in same project
/noUnicode -- do not generate a unicode enabled scanner
/output path -- send output to filename "path"
/output - -- send output to stdout
/parseOnly -- syntax check only, do not create automaton
/stack -- enable built-in stacking of start states
/squeeze -- sacrifice speed for small size
/summary -- emit statistics to list file
/verbose -- chatter on about progress
/version -- give version information for Rolex
我在下面列出了更重要的选项
filename
(必需) - 指示要从中生成的词法分析器规范文件。这些将在下面描述output
- 指示要生成的输出代码文件,或者-
表示stdout
。class
- 指示生成的词法分析器类的名称。默认情况下,它派生自output
。namespace
- 指示代码所在的命名空间。默认为无命名空间。ignoreCase
- 指示应将整个规范视为不区分大小写noShared
- 指示不应生成共享依赖代码。默认情况下,Rolex 将所有依赖代码作为词法分析器输出文件的一部分生成。如果您要生成多个词法分析器,您只需要一份共享代码,因此只需为第 2个 词法分析器及以后指定noShared
Rolex 词法分析器规范
词法分析器规范主要是由其他工具(如 Parsley)机器生成的。但是,如果您不害怕正则表达式,它也很容易手动使用。
它是一种基于行的格式,每行类似于以下内容
ident<id=1>= '[A-Z_a-z][0-9A-Z_a-z]*'
或更一般地说
identifier [ "<" attributes ">" ] "=" ( "'" regexp "'" | "\"" literal "\"" )
每个属性都是一个标识符,可选地后跟 =
和一个 JSON 风格的字符串、布尔值、整数或其他值。如果未指定值,则为 true
,这意味着任何未明确指定值的属性都设置为 true
。请注意,文字表达式用双引号括起来,而正则表达式用单引号括起来。
有一些可用的属性,如下所示
id
- 表示与此符号关联的非负整数 ID- const- 指示为此符号生成的常量名称
hidden
- 指示此符号应被词法分析器跳过,不予报告blockEnd
- 指示指定符号的多字符结束条件。当遇到时,词法分析器将继续读取直到找到blockEnd
值并将其消耗。这对于具有多字符结束条件的表达式很有用,例如 C 块注释、HTML/SGML 注释和 XML CDATA 部分。
规则可能有些模糊。如果它们模糊,则匹配规范中的第一个规则,从而产生一组按文档顺序优先的规则。
正则表达式语言支持基本的非回溯构造和常见的字符类,以及映射到其 .NET 对应项的 [:IsLetter:]
/[:IsLetterOrDigit:]
/[:IsWhiteSpace:]
/等。请确保转义单引号,因为它们在 Rolex 中用于分隔表达式。
编写这个混乱的程序
生成的代码 API
Rolex 使用一个简单的 API 公开生成的词法分析器。每个词法分析器都是一个类,每个符号都是该类上的一个整数常量。词法分析器在构造时,接受一个 IEnumerator<char>
,它通常是一个 string
,但也可以是一个 char[]
数组或您实现的某些自定义源。它也可以是 TextReader
或 Stream
。Tokenizer
可以使用静态 Open()
方法创建,该方法接受文件名,并根据需要自动管理文件的打开和关闭。这是从文件读取的推荐方式。词法分析器 class
支持对令牌进行 foreach
枚举,令牌的信息通过 Token
struct
返回。同时,Token
struct
分别以 Line
、Column
、Position
、SymbolId
和 Value
返回行、列、位置、符号和捕获信息。请注意,列和位置不是一回事!您的位置本质上是您输入中的索引。如果您将字符串传递给词法分析器,则 Token.Position
,当转换为 int
时,将是您字符串中的绝对索引。另一方面,Column
属性是基于 1 的,像 Line
一样,指示输入设备布局中的位置。这包括计算制表符、换行符和回车符。
让我们将我们所涵盖的内容结合起来,开始进行词法分析。首先,让我们创建一个规范文件,Example.rl
id='[A-Z_a-z][0-9A-Z_a-z]*'
int='0|\-?[1-9][0-9]*'
space<hidden>='[\r\n\t\v\f ]'
lineComment<hidden>= '\/\/[^\n]*'
blockComment<hidden,blockEnd="*/">= "/*"
上面,我们定义了 id
和 int
,其余是隐藏的空白和注释。
现在,我们需要从 Rolex 中提取一些代码
Rolex.exe Example.rl /output ExampleTokenizer.cs /namespace RolexDemo
在我们的项目中包含 ExampleTokenizer.cs 后,我们可以这样使用它
var input = "foo bar/* foobar */ bar 123 baz -345 fubar 1foo *#( 0";
var tokenizer = new ExampleTokenizer(input);
foreach(var tok in tokenizer)
{
Console.WriteLine("{0}: {1} at column {2}", tok.SymbolId, tok.Value,tok.Column);
}
这将在控制台输出以下内容
0: foo at column 1
0: bar at column 4
0: bar at column 20
1: 123 at column 24
0: baz at column 28
1: -345 at column 32
0: fubar at column 37
1: 1 at column 43
0: foo at column 44
-1: * at column 48
-1: # at column 49
-1: ( at column 50
1: 0 at column 52
使其工作的底层算法基于快速 DFA 算法,就像标准 Rolex 一样。您只需要担心枚举,让词法分析器处理其他一切。
Visual Studio 集成
通过安装 RolexDevStudio.vsix,Rolex 可以作为 Visual Studio 自定义工具与 Visual Studio 集成。
只需导航到您的 Rolex 词法分析器规范文件,然后转到属性|自定义工具并键入 Rolex,生成过程就会开始。
您可以使用 @options
指令在词法分析器规范中设置或覆盖命令行选项,该指令采用以逗号分隔的属性格式值列表,不带 <
和 >
。这对于指定备用类文件很有用。请注意,在词法分析器规范中指定备用 output
文件选项将使自定义工具行为异常。最好不要指定它,只需让自定义工具完成其工作。@options
指令必须出现在任何规则之前。
关注点
这段代码大部分是对 GPLEX 的巨大修改,我拆解了 AST 和解析器的一部分并进行了修改,以及代码生成器。我保留了大部分原始功能,包括原始解析器完好无损,如果我以后想让 Rolex 接受 GPLEX 词法分析器规范的话。它目前没有暴露给命令行,但大部分都在那里。这里有一些过时的代码,但这主要是因为我仍在开发这个代码库,而且我可能需要它。在我的重写完成之前,我将保持这个代码库的开放选项。
历史
- 2020 年 1 月 31 日 - 首次提交