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

CodeDom 的表达式解析器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (47投票s)

2006 年 6 月 8 日

CPOL

4分钟阅读

viewsIcon

145644

downloadIcon

1141

厌倦了编写冗长、复杂的 CodeDom 表达式?这个解析器是为你准备的!

引言

如果你曾经尝试过长时间地使用 CodeDom,你就会知道它很冗长,有时也很容易让人困惑。对我来说,使用 CodeDom 最困难的部分是编写表达式。我发现自己试图限制在我的表达式中发生的事情,这样我就可以更容易地编写 CodeDom。

在看到了 Pascal Ganaye 的文章(重新审视表达式计算器(100% 托管 .NET 中的 Eval 函数)[^])之后,我发现我可以使用该代码并创建一个 CodeDom 表达式解析器。本文介绍了我的结果。

投入使用

我将此解析器投入使用,并在 .NET 3.0 中创建了一个可嵌入的规则库。它将 CodeDom 语句存储在应用程序配置中。在这里查看:DmRules - 用于在 .NET 3.0 中运行规则的辅助库[^]。

特点

该解析器使用类似 C# 的语法,可以读取以下所有内容

  • 局部变量
  • 基本类型:stringdoubleintlong 等。
  • 运算符:+、-、*、/、%、>、>=、<、<=、==、!=、!、&、&&、|、||、一元 -
  • 字段
  • 属性
  • 方法
  • 索引
  • 使用 (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 上公开了两个属性:FieldsRecognizedTypes。默认情况下,任何在 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 方法来自动化测试
© . All rights reserved.