在开发一个基于新有限自动机引擎的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
以下是一些更重要的选项:
词法分析器规范主要是为了由其他工具(如Parsley)生成,但如果不怕正则表达式,也可以手工使用。它是基于行的格式,每一行都类似于下面的例子:
ident<id=1>= '[A-Z_a-z][0-9A-Z_a-z]*'
或者更一般地:
identifier [ "<" attributes ">" ] "=" ( "'" regexp "'" | "\"" literal "\"" )
每个属性都是一个标识符,后面可选地跟着=和一个JSON风格的字符串、布尔值、整数或其他值。如果没有指定值,它就是true,这意味着任何没有明确指定值的属性都被设置为true。注意字面量表达式是用双引号括起来的,而正则表达式是用单引号括起来的。
有几个可用的属性,如下所示:
规则可能会有些模糊。如果是这样,规范中的第一个规则就是匹配的规则,从而产生一组按文档顺序优先的规则。
正则表达式语言支持基本的非回溯构造和常见的字符类,以及[: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指令必须出现在任何规则之前。