自定义表达式引擎
某天,公司里在讨论表达式解析,经讨论他们选择了将表达式动态编译并反射实现。个人兴起,回家自己写了个简单的表达式解析程序,鼓励下自己要好好加油,在此做个记录以防自己以后忘记。
源代码(对部分bug有所更改,2012年4月)
对于一般的表达式的观察发现,表达式分运算符、参数、函数三部分。而运算符其实也是函数。如 +,其实+有两个参数和一个返回值即:object Add(parameter1,parameter2).参数是函数的输入。所以我将表达式定义成了函数对象的递归树。于是定义了如下接口及抽象类。
函数抽象:
2 {
3 /// <summary>
4 /// 如果为Int32.MaxValue表示不限定参数个数
5 /// </summary>
6 public abstract ParameterCount NeedParameterCount
7 {
8 get;
9 }
10
11 public abstract string KeyWord
12 {
13 get;
14 }
15
16 protected List<object> _inArguments = new List<object>();
17
18 public abstract object Excute();
19
20 public abstract void PushArguments(object argument);
21
22 public abstract object PopArgument();
23
24 public virtual IEnumerator<object> GetArgumentItor();
25
26 public virtual void ReplaceArgument(int index, object v);
27
28 public virtual bool IsParamertEnough(out FunctionBase notEnoughFun);
29
30 public virtual FunctionBase Clone();
31
32 public abstract FunctionBase Create();
33
34 internal bool Check();
35
36 }
区分运算符的标识接口(运算符有优先级概念)
1 public interface IOperator
2 {
3 float Level
4 {
5 get;
6 }
7 }
以下列举一个 +和Sin函数的实现。
+号:
1 /// <summary>
2 /// 加法
3 /// </summary>
4 public class Add : FunctionBase, IOperator, IFunFactory
5 {
6 //实现
151
152 }
Sin函数的实现
1 /// <summary>
2 /// 以角度求Sin值
3 /// </summary>
4 public class Sin : FunctionBase, IFunFactory
5 {
6 public override string KeyWord
7 {
8 get
9 {
10 return "Sin";
11 }
12 }
13
14 private List<object> _inArguments = new List<object>();
15
16 public override void PushArguments(object argument)
17 {
18 if (null == argument)
19 {
20 throw new ArgumentNullException();
21 }
22
23 if (_inArguments.Count == 1)
24 {
25 throw new ArgumentNotMatchException("argument is more");
26 }
27 _inArguments.Add(argument);
28 }
29
30 public override object PopArgument()
31 {
32 int last = _inArguments.Count - 1;
33 if (last >= 0)
34 {
35 object o = _inArguments[last];
36 _inArguments.RemoveAt(last);
37 return o;
38 }
39 return null;
40 }
41
42 public override object Excute()
43 {
44 if (_inArguments.Count > 1)
45 {
46 throw new ArgumentNotMatchException("argument is more");
47 }
48
49 if (_inArguments.Count < 1)
50 {
51 throw new ArgumentNotMatchException("argument is not enough");
52 }
53
54 double a1 = 0;
55 object arg0 = _inArguments[0];
56
57 if (arg0 is FunctionBase)
58 {
59 arg0 = ((FunctionBase)arg0).Excute();
60 }
61
62 if (!double.TryParse(arg0.ToString(), out a1))
63 {
64 throw new ArgumentNotMatchException("argument's type is not match");
65 }
66
67 return Math.Sin(a1);
68
69 }
70
71 FunctionBase IFunFactory.Create()
72 {
73 return new Sin();
74 }
75 }
关键部分,ExpCompile类,将表达式String解析成fun对象。
使用ExpExcuteContext可以缓存表达式解析后的对象;并且该类主要是想实现表达式中绑定外部变量的赋值。当然这以被注释,实际已应用。
主要是通过函数名(关键字),得到函数起始部分和结束部分,然后以逗号为分隔取参数。
注意:参数可能是函数执行的结果,如果此种情况要递归分析。继续Compile(ref FunctionBase rootFun,string exp)
原型如下:
1 public class ExpCompile
2 {
3
28 public void Compile(ref FunctionBase rootFun,string exp);
//.....
499 }

浙公网安备 33010602011771号