65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2020年1月31日

MIT

6分钟阅读

viewsIcon

9613

downloadIcon

120

为您的 C# 项目添加快速扫描器/分词器

Rolex usage screen

引言

本月,我曾希望发布一个基于我正在开发的新有限自动机引擎的 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[] 数组或您实现的某些自定义源。它也可以是 TextReaderStreamTokenizer 可以使用静态 Open() 方法创建,该方法接受文件名,并根据需要自动管理文件的打开和关闭。这是从文件读取的推荐方式。词法分析器 class 支持对令牌进行 foreach 枚举,令牌的信息通过 Token struct 返回。同时,Token struct 分别以 LineColumnPositionSymbolIdValue 返回行、列、位置、符号和捕获信息。请注意,列和位置不是一回事!您的位置本质上是您输入中的索引。如果您将字符串传递给词法分析器,则 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="*/">= "/*"

上面,我们定义了 idint,其余是隐藏的空白和注释。

现在,我们需要从 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 日 - 首次提交
© . All rights reserved.