CodeDom 的表达式解析器






4.78/5 (47投票s)
厌倦了编写冗长、复杂的 CodeDom 表达式?这个解析器是为你准备的!
引言
如果你曾经尝试过长时间地使用 CodeDom,你就会知道它很冗长,有时也很容易让人困惑。对我来说,使用 CodeDom 最困难的部分是编写表达式。我发现自己试图限制在我的表达式中发生的事情,这样我就可以更容易地编写 CodeDom。
在看到了 Pascal Ganaye 的文章(重新审视表达式计算器(100% 托管 .NET 中的 Eval 函数)[^])之后,我发现我可以使用该代码并创建一个 CodeDom 表达式解析器。本文介绍了我的结果。
投入使用
我将此解析器投入使用,并在 .NET 3.0 中创建了一个可嵌入的规则库。它将 CodeDom 语句存储在应用程序配置中。在这里查看:DmRules - 用于在 .NET 3.0 中运行规则的辅助库[^]。
特点
该解析器使用类似 C# 的语法,可以读取以下所有内容
- 局部变量
- 基本类型:
string
、double
、int
、long
等。 - 运算符:+、-、*、/、%、>、>=、<、<=、==、!=、!、&、&&、|、||、一元 -
- 字段
- 属性
- 方法
- 索引
- 使用 (Type) 进行类型转换
typeof()
- Enums
Using the Code
假设我必须用 CodeDom 编写一个语句,如下所示
xr.HasAttributes && xr.MoveToAttribute("xmi.value")
这对于放入 if
条件来说已经足够简单了。编写 CodeDom 看起来像这样
CodeExpression ce = new CodeBinaryOperatorExpression(
new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("xr"), "HasAttributes"),
CodeBinaryOperatorType.BooleanAnd, new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(
new CodeVariableReferenceExpression("xr"), "MoveToAttribute"),
new CodeExpression[] { new CodePrimitiveExpression("xmi.value") }));
使用这个库,你可以这样写
Parser p = new Parser();
CodeExpression ce = p.ParseExpression(
"xr.HasAttributes && xr.MoveToAttribute(\"xmi.value\")");
现在,我还有其他要求。例如,我需要解析器知道什么是字段,并识别何时进行类型转换。所以,我可能想有以下表达式
(double)Convert.ChangeType(xr.Value, typeof(double))
它在 CodeDom 中的样子
CodeExpression ce1 = new CodeCastExpression(
new CodeTypeReference(typeof(double)),
new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(
new CodeTypeReferenceExpression(typeof(Convert)), "ChangeType"),
new CodeExpression[] { new CodePropertyReferenceExpression(
new CodeVariableReferenceExpression("xr"), "Value"),
new CodeTypeOfExpression(new CodeTypeReference(typeof(double))) }));
这是如何使用解析器编写它的
Parser p = new Parser();
CodeExpression ce = p.ParseExpression(
"(double)Convert.ChangeType(xr.Value, typeof(double))");
ParseExpression
方法被作为实例方法而不是 static
方法完成的原因是我希望 Parser
对象收集一些上下文。在 Parser
上公开了两个属性:Fields
和 RecognizedTypes
。默认情况下,任何在 this
引用之后的内容都被视为属性。如果名称与 Fields
集合中的某个条目匹配,则将其视为一个字段。此外,解析器将检查 RecognizedTypes
以查看标识符是否应被视为变量或类型。这也有助于使用类型转换或 typeof
。
在此代码的最新更新中,我包含了使用 enum
的功能。每个枚举都被视为类型的字段引用,因此解析器需要以不同的方式处理它们。只需将 enum
类型添加到解析器的 Enums
属性即可。
Parser p = new Parser();
p.Enums.Add("MyEnum", new CodeTypeReference("MyEnum"));
CodeExpression ce = p.ParseExpression("this.Foo == MyEnum.Bar");
其他一些例子
以下所有内容都被正确解析
-5 // This one actually parses as (0 - 5)
!stream.EndOfFile // Parses as (stream.EndOfFile == false)
!stream.EndOfFile && this.Count > 5
foo[0]
foo.bar()[0] > 2
(int)foo
s == ""
里面有什么
以下是内部文件的列表
CodeDomExpParser
- Enums.cs
- Parser.cs
- Token.cs
- Tokenizer.cs
CodeDomExpParser.Test
- 这是解析器库的测试工具。- DogFood.cs - 这包含我在 XMI CodeDom 库中必须做的一些更复杂的 CodeDom 语句,以及从注释中收集的一些示例。因此有了 "吃我自己的狗粮" 的说法。
- TestParser.cs - 对解析器抛出一些测试。查看此文件以获取可以解析的示例。
- TestTokenizer.cs - 针对词法分析器运行一些测试。
摘要
我使用了 Pascal 在他的文章中使用的很多想法来进行词法分析器和解析器。有了这些基本的东西,我就基本按照自己的方式走了。这是一个很棒的项目。希望你们中的一些人会觉得它有用。
历史
- 0.1 : 2006-06-08 : 初始版本
- 处理了我需要它做的的大部分表达式
- 更彻底的测试肯定会很好
- 待办事项之一是允许输入现有的 CodeDom 图,以查找类型、字段等
- 0.2 : 2006-06-21
- 添加了模运算符,并创建了 .NET 2.0 版本
- 0.3 : 2006-07-19
- 修复了空字符串未被解析的问题
- 增加了解析枚举的能力
- 0.4 : 2009-04-20
- 修复了注释中提到的两个错误
- 转换为 Visual Studio 2008 解决方案
- 使用 Visual Studio 测试工具代替 NUnit
- DogFood 测试用例现在使用
CompareExpression
方法来自动化测试