共享一个从字符串转 Lambda 表达式的类(4)

开始写第四篇,别的不说了。这篇将涉及到如何使用字符串解析结果,生成一个 Lambda 表达式树。东西有点多,我先整理一下思路,在下面说明一下。如果你有问题,在后面的评论上写下来,我看到了会回复你。

在前几篇中,我写了一个字符串解析模块,还有一个根据字符串解析成 Type 对象的类型分析模块。这两个模块,都将为这个 Expression 核心解析类提供辅助,所以如果前两篇文章你没有看到,可以到我博客中先把前几篇文章看一下,理解我的整体思路后,再回过头来看这篇。

我希望从这篇文章开始,看到一些有用的评论,谢谢了!

Expression 核心解析类(ExpressionParserCore)对外发布的只有一个方法 —— ToLambdaExpression ,用于输出分析得到的 Lambda 表达式树。在贴代码之前,我把大致思路说明一下:

首先,需要几个前提条件:

1.  待分析的字符串

2.  Lambda 表达式树包含的委托类型

3.  类型解析可能用到的命名空间和程序集列表

第一个,很好理解,在这里不做阐述。

第二个,是告诉要解析的目标参数和返回值信息。

第三个,类型解析中会用到,这里只是做一下参数传递。具体的代码可以参看第三篇。

其次,由于 Lambda 表达式树的递归特性,所以其解析工作不可能一蹴而就,所以解析工作将其拆分为三步:

1.  ReadFirstExpression

2.  ReadNextExpression

3.  ReadExpression

从字面上可以看出来:ReadFirstExpression 是读取第一个 Expression ,ReadNextExpression 是读取接下来的 Expression ,ReadExpression 是读取 Expression 。

ReadFirstExpression:读取第一个 Expression ,不去管后面是不是还有没有解析的 Expression 。比如:【() => 1 + 2】这个表达式,如果使用该方法则只会读取常量表达式【1】,不会继续读取剩下的常量表达式【2】和加法表达式。一句话总结:读取了一个 Expression 就停止读取。

ReadNextExpression:读取下一个 Expression ,在已读取了表达式的基础上继续解析 Expression 。接着上面的表达式【() => 1 + 2】继续说,读取了表达式【1】后,继续读取常量表达式【2】,再继续读取加法表达式,返回整个表达式。

到了这里,有人会问:为什么会分成读取第一个和读取下一个两个方法?有以下原因吧,我感觉分开好些:

1.  有些表达式不能作为第一个表达式(比如加法表达式),有些不能作为后续的表达式(比如求反表达式)。

2.  就是有些符号不能在开始表达式中(比如右括号),有些不能作为在结束表达式中(比如左括号)。

3.  开始表达式从零构造一个表达式,后续表达式需要一个前置表达式。

还有一些,暂时想到的就这些了。

ReadExpression:读取表达式,可以递归调用实现 Expression 的递归。在发布的方法 ToLambdaExpression 中,调用的就是 ReadExpression ,进行第一次的递归操作。在表达式中如果需要,可能会多次调用,比如表达式【() => 1 + 2】会调用两次:一次是开始调用 ToLambdaExpression 的时候,一次是解析常量表达式【2】的时候。

在此,我把大概的流程用【() => 1 + 2】说明一下吧:

1.  准备前提条件。

2.  将字符串【() => 1 + 2】解析为我们需要的 Token 单元集合。

3.  分析目标委托类型(可以理解为 typeof(Func<int>).GetType() )为 0 个参数,返回值为 int 的委托。

4.  调用 ToLambdaExpression 开始解析 Lambda 表达式树:第一次调用 ReadExpression 方法。

5.  ReadExpression 方法调用 ReadFirstExpression 方法获取一个常量表达式树【1】。

6.  ReadExpression 方法继续调用 ReadNextExpression 方法,并传递参数常量表达式树【1】。

7.  读取到加法表达式树,将常量表达式树【1】作为加法左边的表达式树,尚需要的右边表达式树,调用 ReadExpression 方法读取。

8.  ReadExpression 方法调用 ReadFirstExpression 方法获取一个常量表达式树【2】。

9.  ReadExpression 方法继续调用 ReadNextExpression 方法,并传递参数常量表达式树【2】。

10. 发现已经读取到末尾,ReadNextExpression 方法返回传递进来的参数常量表达式树【2】。

11. ReadExpression 返回读取到的常量表达式【2】,这里将返回到第 7 步。

12. 加法表达式的左右表达式均已具备,构造加法表达式【1 + 2】,并返回到第 6 步。

13. 继续读取发现读取到末尾,返回加法表达式【1 + 2】到第 4 步。

14. 根据委托类型构造 Lambda 表达式树并返回,解析完成。

我先将这三个方法的原型说明一下:

1.  ReadFirstExpression()

2.  ReadNextExpression(int level, ReadResult previousResult, TokenId? wrapStart = null)

3.  ReadExpression(int level = 0)

level         : 这个参数是运算符优先级,优先级越大该数字越大,比如【1 + 2 * 3】这个表达式,会先进行【2 * 3】的组合而不是【1 + 2】的组合。

previousResult: 表示前面分析得到的结果。包含两个属性:表达式和是否读取结束。

wrapStart     : 表示读取下一个表达式的前导 Token 类型。previousResult 中的属性【是否读取结束】和这个参数相关:每种括号的开始和结束对应。

就到这里吧,写太长了,估计多数人没看下去的兴趣。下篇我会把 ReadFirstExpression 方法的逻辑整理一下,顺便贴出来部分代码。如果你感觉我写的东西有用,或者你期待下一篇,请点一下推荐,谢谢了!

原文地址:https://www.cnblogs.com/lenic/p/2563559.html