LambdaParser的实现

之前在GoogleCode里创建的项目LambdaParser,好久没管了,前两天进去一看,居然PR值是4,很是奇怪,搜了一下,几乎没任何反向链接,搞不懂PR值为什么会这么高。

 

既然Google这么看重它,那我也不能把它荒废了:)最近.NET4.0已经出来了,Expression已经支持代码块了(以前只支持表达式,区别就是以前不能写多个语句,现在可以了),像if、while、switch等等这些都已经支持了,那么现在的Expression理论上讲已经可以表达任何方法体了。只可惜不知微软出于什么考虑(也许是担心LINQ这边误用吧,这个功能是用来为Dynamic Language Runtime 即DLR服务的,不是LINQ),不能直接将代码块转换成表达式树。如:

Expression<Action<string>> action = (m=>

{

    MessageBox.Show(m.Length);

    MessageBox.Show(m);

});

这种写法以前不行,现在仍然不行。因为这是个代码块。(对表达式树不熟悉的可以看看这篇文章:表达式树基础)

 

那么,想要构造代码块就得用Expression手动慢慢拼了。这是件痛苦的事情。当然,LambdaParser的下一步就是要解决它,有了BlockExpression、SwitchExpression

这些的东西,实现这个功能相信不是难事。如果有人愿意加入进来共同完成这个功能,可以联系我。

 

本文主要想讲讲目前的LambdaParser的实现。

 

解决方案截图:

 

“Demo”是自己用来简单试用的。“Test_Zhucai.LambdaParser”是基于VSTS的单元测试项目,新接触的人看这个比较容易理解这个项目是干什么的。“Zhucai.LambdaParser”是核心模块。

 

下面是主要的几个类:

 

CodeParser类:负责解析代码。从前向后读取代码,通过ReadString方法每次读取一个代码单元。

代码单元:这是个自创的名词,用来表示代码的最小单元,如代码MessageBox.Show(m);对应的代码单元是:

 1 MessageBox

 2 .

 3 Show

 4 (

 5 m

 6 )

 7 ;

那么ReadString方法会成功读取7次,直到第8次返回null结束。

 

ExpressionParserCore<TDelegate>类:这个类负责将代码解析为表达式树。它通过构造函数得到字符串代码,通过唯一的公共方法ToLambdaExpression()输出解析结果Expression。

 

此类内部先通过输入的字符串代码构造一个CodeParser对象,然后通过泛型参数(将要构造出的委托类型)得到代码的参数类型,之后便开始用CodeParser对象遍历分析代码。

 

分析代码的过程:

 1. 首先检查是否有Lambda表达式的前置符号=>,若有,则分析出参数列表。

 2. 通过ReadExpression方法递归读取表达式。

ReadExpression方法是整个模块的核心方法,方法的大致思路如下:

将表达式看成A(+B)*n模式,即先读取表达式的A部分,再用循环读取接下来的n个+B部分。A部分表示表达式的第一个子表达式,+B部分表示一个操作符后面跟一个子表达式,而*n表示可能有0个或多个+B部分。

那么ReadExpression方法的伪代码大致便是这样:

读取第一个子表达式;

while()
{

读取二元/三元操作符;

读取下一个子表达式;

用指定操作符操作两个表达式,得到新表达式;

}

返回最新的表达式;

大致便是这样了,这里略去了很多需要处理的细节问题,如操作符优先级、类型判断等等。但有个问题是不能略过的,那便是子表达式又可能包含多个子表达式。

如果把平行连接的表达式操作看作横向操作,那么多层的子表达式包含便是纵向操作。横向问题是用while循环来解决,纵向问题用递归是最直观的。事实上,由于操作符优先级的问题,这里的横向操作也会有递归调用,或者说,是横向还是纵向变得不那么明确。

于是,读取子表达式的时候,调用ReadExpression来递归读取。

 

最终,ReadExpression方法的函数签名是三个参数:

int priorityLevel:当前操作(或者说方法外部)的优先级。递归情况下使用。当方法内的下个操作优先级低于此值时直接返回。

string wrapStart:当前操作的前置括号。如(或[或{等。递归情况下使用。

out bool isClosedWrap:告诉方法外面是否在里面遇到了与前置括号匹配的结束符号。递归情况下使用。

 

ExpressionParser类:作为此模块向外暴露的接口,其内部调用ExpressionParserCore<TDelegate>类,提供一系列方法来编译执行代码。

 

这便是这个项目目前的大致情况。有时间了便考虑实现在.NET4.0下的代码块功能,那么这个项目也许就会有意义些了。

 

posted @ 2010-05-31 00:55  朱才  阅读(3310)  评论(0编辑  收藏  举报