基于新有限自动机引擎的Rolex开发

在开发一个基于新有限自动机引擎的Rolex版本时,遇到了一个瓶颈:需要一本书来完成实现。尽管如此,还是需要Rolex支持Unicode。之前使用的GPLEX工具在输入输出方面存在一些问题,其输入规范类似于LEX文档,输出可以跨越多个文件,并且提供了类似于LEX的编程接口,包括一个名为yylex()的方法。

为了解决这个问题,创建了一个基于GPLEX 1.2引擎的拆解和重构版本的Rolex临时版本。这个程序被修改为接受Rolex词法分析器规范,并生成一个实现IEnumerable<Token>的词法分析器类。

限制

这个临时版本只会生成C#语言的词法分析器。其他.NET语言的支持将在Rolex未来的版本中恢复。

版权

对于那些不熟悉GPLEX(Garden Points Lexer)的人,它是昆士兰科技大学的版权作品,版权所有(c) 2009。源代码中包含了完整的版权信息。GPLEX不是写的,只是对其进行了修改。

使用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 /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会将所有依赖代码作为词法分析器输出文件的一部分生成。如果正在生成多个词法分析器,只需要一份共享代码,所以只需为第二个词法分析器及以后的词法分析器指定noShared。

词法分析器规范主要是为了由其他工具(如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节。

规则可能会有些模糊。如果是这样,规范中的第一个规则就是匹配的规则,从而产生一组按文档顺序优先的规则。

正则表达式语言支持基本的非回溯构造和常见的字符类,以及[:IsLetter:]/[:IsLetterOrDigit:]/[:IsWhiteSpace:]/etc.,这些映射到它们的.NET对应物。确保转义单引号,因为它们在Rolex中用于分隔表达式。

Rolex通过一个简单的API公开生成的词法分析器。每个词法分析器都是一个类,每个符号都有一个整数常量在这个类上。当构造词法分析器时,它接受一个IEnumerator<char>,这通常是一个字符串,但可以是一个char[]数组或实现的某种自定义源。它也可以是一个TextReader或Stream。

可以使用静态Open()方法创建词法分析器,该方法接受一个文件名,并自动管理打开和关闭文件。这是从文件读取的推荐方式。词法分析器类支持foreach枚举,其信息使用Token结构返回。Token结构同时返回行、列、位置、符号和捕获信息,分别在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包含在项目中后,可以像这样使用它:

C# 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一样。只需要担心枚举,让词法分析器处理其他一切。

Rolex可以作为Visual Studio自定义工具与Visual Studio集成,通过安装RolexDevStudio.vsix。简单地导航到Rolex词法分析器规范文件,然后转到Properties|Custom Tool并输入Rolex,生成过程就会开始。

可以通过使用@options指令在词法分析器规范中设置或覆盖命令行选项,该指令接受一个逗号分隔的值列表,格式为属性格式,不包含<和>。这对于指定替代类文件很有用。注意,在词法分析器规范中指定替代输出文件选项会使自定义工具表现不佳。最好不要指定它,让自定义工具做它的事情。@options指令必须出现在任何规则之前。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485