平台中内嵌了 IronPython,需求希望产品在运行或设计时,高级或设计人员可察看表达式各个部分执行的结果,有助于排错,或察看运行结果是否是想要的。
思路:复杂表达式可分解为多个较简单的表达式,较简单的表达又可分解为简单表达式,直到不能分解为止。因此,可以把分解的结果以树结构形式显示:较简单表达式是复杂表达的子节点,简单表达式又是较简单表达式的子节点,。。。。
为了实现上面的需求,用到了 IronPython中的语法分析类 PythonWalker,先从PythonWalke继承进行表达式分解处理,处理比较简单,override 三个方法,这三个方法中调用同一个处理方法实现,这里只是演示,很简单的处理,用缩进空格来表示级别,更复杂的要靠自己了。
处理类代码如下:
private class AtomWalker : PythonWalker { // 原始表达式 private string _orignalExpr; public List<string> AtomExprs; public AtomWalker(string expression) { this._orignalExpr = expression; AtomExprs = new List<string>(); } private int _level = 0; private void AddAtom(Expression node) { int startIndex = node.Start.Index; int endIndex = node.End.Index; string atomStr = _orignalExpr.Substring(startIndex, (endIndex - startIndex)); _level = 0; for (int i = AtomExprs.Count - 1; i >= 0; i--) { string prevAtom = AtomExprs[i]; if (prevAtom.Contains(atomStr)) { for (int j = 0; j < prevAtom.Length; j++) { if (prevAtom[j] == ' ') { _level++; } else { break; } } _level += 2; break; } } for (int i = 0; i < _level; i++) { atomStr = " " + atomStr; } AtomExprs.Add(atomStr); } public override bool Walk(BinaryExpression node) { AddAtom(node); return base.Walk(node); } public override bool Walk(CallExpression node) { AddAtom(node); return base.Walk(node); } public override bool Walk(ConditionalExpression node) { AddAtom(node); return base.Walk(node); } }
然后调用:
public string[] ParserExpression(string expression) { SourceUnit unit = DefaultContext.Default.LanguageContext.CreateSnippet(expression, SourceCodeKind.Expression); CompilerContext context = new CompilerContext(unit, new PythonCompilerOptions(true), ErrorSink.Default); Parser parser = Parser.CreateParser(context, new PythonOptions()); PythonAst ast = parser.ParseTopExpression(); AtomWalker walker = new AtomWalker(expression); ast.Walk(walker); return walker.AtomExprs.ToArray(); }
测试示例:
第一个表达式:
context.c.max(1,cont.min(45,23)).sub(23) + min(2,max(3, unique('ActiveObject.TC001'))) + IIF(2<1,1,2) + (3 if 3>2 else 2 if 4>3 else 5)
输出结果:
context.c.max(1,cont.min(45,23)).sub(23) + min(2,max(3, unique('ActiveObject.TC001'))) + IIF(2<1,1,2)
context.c.max(1,cont.min(45,23)).sub(23) + min(2,max(3, unique('ActiveObject.TC001')))
context.c.max(1,cont.min(45,23)).sub(23)
context.c.max(1,cont.min(45,23))
cont.min(45,23)
min(2,max(3, unique('ActiveObject.TC001')))
max(3, unique('ActiveObject.TC001'))
unique('ActiveObject.TC001')
IIF(2<1,1,2)
2<1
3 if 3>2 else 2 if 4>3 else 5
3>2
2 if 4>3 else 5
4>3
第二个表达式:
'MMDD'.rjust(ActiveObject.MQ005+4, 'Y').ljust(ActiveObject.MQ005+4+ActiveObject.MQ006, '9') if ActiveObject.MQ004 == 1 else ('MM'.rjust(ActiveObject.MQ005+2, 'Y').ljust(ActiveObject.MQ005+2+ActiveObject.MQ006, '9') if ActiveObject.MQ004 == 2 else ''.ljust(ActiveObject.MQ006, '9'))
输出结果:
ActiveObject.MQ004 == 1
'MMDD'.rjust(ActiveObject.MQ005+4, 'Y').ljust(ActiveObject.MQ005+4+ActiveObject.MQ006, '9')
'MMDD'.rjust(ActiveObject.MQ005+4, 'Y')
ActiveObject.MQ005+4
ActiveObject.MQ005+4+ActiveObject.MQ006
ActiveObject.MQ005+4
'MM'.rjust(ActiveObject.MQ005+2, 'Y').ljust(ActiveObject.MQ005+2+ActiveObject.MQ006, '9') if ActiveObject.MQ004 == 2 else ''.ljust(ActiveObject.MQ006, '9')
ActiveObject.MQ004 == 2
'MM'.rjust(ActiveObject.MQ005+2, 'Y').ljust(ActiveObject.MQ005+2+ActiveObject.MQ006, '9')
'MM'.rjust(ActiveObject.MQ005+2, 'Y')
ActiveObject.MQ005+2
ActiveObject.MQ005+2+ActiveObject.MQ006
ActiveObject.MQ005+2
''.ljust(ActiveObject.MQ006, '9')
后续设计思想:用树图形式显示编写的表达式的分解结果,当点击任意节点时,可察看此节点表达式的运行情况(返回值,或错误详细信息);
也可一次性从上下到运行所有节点的表达式,而后输出每个节点的运行情况。