轻松创建自己的解析器
轻松快速地在 VB.NET 或 C# 中创建手动解析器

引言
计算机科学中最困难的任务之一是构建解析器和编译器。有很多工具可以帮助完成这项繁琐的任务,其中最著名的是 Flex 和 Yacc,它们都可以在 Linux/UNIX 平台上使用。我在本文中介绍的程序名为 TokenIcer。它类似于 Flex,但 TokenIcer 提供了一个漂亮易用的图形界面,用作规则编辑器,也是测试规则的测试平台。此外,一旦定义了解析规则,TokenIcer 就可以根据您的规则在 C# 或 VB.NET 中创建一个解析器类。
背景
要能够很好地使用 TokenIcer,您应该对正则表达式的工作原理有相当深入的了解。您输入到 TokenIcer 的每个规则都将基于正则表达式。 .NET Regex 库可以解析的所有正则表达式,在 TokenIcer 中也同样有效。
解析器的工作方式,TokenIcer 的工作方式也是如此,您将某种输入字符串馈送到解析器。例如,如果我们使用以下行馈送到解析器
3+2 * (6 + 1)
我们应该期望我们的解析器提供类似这样的输出
{Integer}{Plus}{Integer}{Whitespace}{Asterisk}{Whitespace}{LeftParen}
{Integer}{Whitespace}{Plus}{Whitespace}{Integer}{RightParen}{Newline}
我们如何处理此解析器输出取决于我们具体要实现的目标。也许您正在构建一个语言编译器,或者一个数学解析器。这就是 TokenIcer 的作用。它接受像“3+2 * (6 + 1)
”这样的输入,并将其转换为一系列枚举值。
Using the Code
运行 TokenIcer 时,您将看到一个带有 3 个文本框和一个 TreeView
的屏幕。第一个文本框是我们输入规则的地方。规则只是用引号括起来的正则表达式。紧随规则之后是一个空格(如果您愿意,也可以是多个空格)。空格后面是该规则的标识符。如上面的示例所示,标识符可以是 Integer、Whitespace 或您希望使用的任何名称。可以使用任何有效的 VB.NET 或 C# 标识符。继续在文本框中输入以下规则
"[0-9]+" INTEGER
此规则将正确识别一个整数。中间的文本框是我们的测试平台。您在此处输入的任何内容都将根据其上方的文本框中的规则进行解析。继续在测试框中输入您想要的任何整数。输入数字后,单击窗口左下角的“Test Grammar”按钮。完成此操作后,您将看到第三个文本框,即输出文本框,确实已成功将我们的数字识别为 {INTEGER}
。输出树还显示 INTEGER
,但如果您展开树,您将看到解析器解析的实际值。现在,继续在第一个文本框中输入以下两条规则,紧随第一条规则之后
"[0-9]+\.+[0-9]+" FLOAT
"[ \t]+" WHITESPACE
在您的测试框中,尝试测试以下内容
3.2 15 4.932
继续单击“Test Grammar”。您可能会惊讶地看到一堆“UNDEFINED
”标签,而没有 FLOAT
标签。原因(陷阱 #1)是解析器将使用它遇到的第一个匹配规则来解析您的输入。在制定规则时,您必须先放置“优先级更高”的规则。解析器遇到了“3
”并说“好的 3 是一个整数”。它甚至没有查看 3
后面的内容,以查看是否有小数点。要解决此问题,只需将 FLOAT
行放在 INTEGER
行之上。这样,解析器将首先尝试匹配 FLOAT
规则,如果不存在小数点,则移动到 INTEGER
规则。更改规则文本框中的规则,使其看起来像这样
"[0-9]+\.+[0-9]+" FLOAT
"[0-9]+" INTEGER
"[ \t]+" WHITESPACE
现在,如果您单击“Test Grammar”,输出框和输出树将按预期显示。它现在已正确解析,正如您所期望的那样。
为了总结本节,我现在将向您展示 TokenIcer 如何创建 C# 或 VB.NET 类,您可以将这些类包含在您自己的项目中。既然我们现在有三条规则,并且它们已经经过测试,没有输入,请继续单击“Generate Class...”按钮。执行此操作后,将弹出一个窗口,其中包含一个下拉列表,询问您偏好的语言。您可以选择 C# 或 VB.NET。还有一个用于包含注释的复选框。如果您选中此框,还将生成一些简单的注释来帮助您理解代码。选择好语言后,单击“Generate my Class”,将弹出另一个窗口,其中包含生成的 C# 或 VB.NET 代码。您可以按 <Ctrl>+A 全选,然后按 <Ctrl>+C 将代码复制到您自己的项目中。就是这么简单!
TokenParser
类公开以下属性
InputString
-- 实例化TokenParser
副本后,您必须将此string
属性设置为要解析的string
值。
TokenParser
类公开以下方法
GetToken()
-- 调用此方法以从输入string
中检索下一个令牌。GetToken()
方法返回一个Token
对象。Token
对象包含TokenName
(它是一个enum
,包含您之前在规则中定义的令牌,例如INTEGER
、WHITESPACE
、FLOAT
)以及TokenValue
。TokenValue
将是从解析器检索到的值。例如,INTEGER
可能返回“53
”。Peek()
-- 此方法将返回GetToken()
将返回的下一个令牌。它允许您在不实际从队列中移除任何内容的情况下查看令牌缓冲区。Peek()
的返回值是一个PeekToken
。PeekToken
是一个特殊对象,它包含一个Token
对象和一个索引。通过调用Peek()
并将PeekToken
作为参数传递给Peek()
,Peek()
将返回上一次Peek()
调用后返回的Token
。通过这种方式,您可以向前查看多个令牌。ResetParser()
-- 此方法重置解析器。调用此方法后,您必须再次将InputString
属性设置为string
,然后您可以像平常一样调用GetToken()
或PeekToken()
来解析新的string
。
注释
TokenIcer 支持两种类型的注释。行内注释和整行注释。两种注释都可以通过在注释前加上哈希符号(#
符号)来实现。第一种注释,行内注释,是注释枚举的一种方式。看这个例子
"[a-zA-Z_][a-zA-Z0-9_]*" IDENTIFIER #This is an Identifier
当您创建 C# 或 VB.NET 类时,您可以选择注释规则。如果此选项已启用,那么您在规则行内提供的任何注释都将作为注释添加到生成的枚举中。
第二种注释是整行注释,如下所示
# This line is completely ignored.
以哈希符号开头的任何规则行都将被完全忽略。您可以根据自己的需要使用此功能。
示例解析器规则
这是一个非常简单的 BASIC 解析器的示例
"[Ll][Ee][Tt]" LET
"[Pp][Rr][Ii][Nn][Tt]" PRINT
"[Cc][Ll][Ss]" CLS
"[Rr][Ee][Mm][^\r\n]*" REM
"[Ee][Nn][Dd]" END
"[Gg][Oo][Tt][Oo]" GOTO
"[Ff][Oo][Rr]" FOR
"[Ss][Tt][Ee][Pp]" STEP
"[Nn][Ee][Xx][Tt]" NEXT
"[Tt][Oo]" TO
"\:" COLON
"=" EQUALS
"\".*?\"" STRING
"[a-zA-Z_][a-zA-Z0-9_]*" IDENTIFIER
"[ \t]+" WHITESPACE
"[\r\n]+" NEWLINE
"[0-9]?\.+[0-9]+" FLOAT
"[0-9]+" INTEGER
"'.*" APOSTROPHE
"\(" LPAREN
"\)" RPAREN
"\*" ASTERISK
"\/" SLASH
"\+" PLUS
"\-" MINUS
这些规则可以正确解析以下程序
10 CLS
20 PRINT "Hello" : PRINT " World!"
30 X = 5
40 Y = X * 2 + 3.5
50 PRINT Y
60 FOR Z = 50 TO 1 STEP -1
70 PRINT "Z = " + Z
80 NEXT Z
90 END
请随意扩展解析器规则,尝试创建自己的 BASIC 编译器!
历史
- 2011年7月3日 - 发布 1.0 版本
- 2011年7月5日 - 发布 1.1 版本。TokenIcer 现在可以保存语法和测试输入文件了!
- 2011年7月7日 - 发布 1.2 版本。TokenIcer 现在在使用保留的 VB.NET 或 C# 关键字作为标识符时也能正常工作。此外,还进行了一些代码重构以清理一些内容。
- 2011年7月8日 - 发布 1.3 版本。
GetToken()
引擎现在快了约 95%(感谢 T_uRRiCA_N 的帮助)。此外,规则编辑器文本框现在带有行号,便于阅读。 - 2011年7月9日 - 发布 1.4 版本。在介绍新功能之前,我想为在如此短的时间内发布这么多更新表示歉意。我保证会放慢速度,至少在一周左右的时间内您不会看到新的更新(除非此版本存在什么重大错误)。好了,此版本的新功能包括语法高亮显示,以及一个新的选项菜单,用于禁用或更改颜色,以及禁用行号的功能。此外,此版本现在支持注释,包括行内注释和整行注释。还添加了创建 XML 样式注释的选项,以便您可以使用 SandCastle 或其他程序创建文档。