又一个语言识别工具(ANTLR)
刚刚看到《动态计算字串表达式值的类》,好像有许多人表示更喜欢解析器形式的求值类。其实我个人倒觉得用反射实现没什么不好,恰恰相反,我觉得这种实现方法很聪明!另外,装配中的脑袋的代码可以稍微修改一下来提高效率,那样再用的话就不会因为反复编译而影响效率了。
言规正传,不知道大家有没有注意我最近在Blog中添加了一个叫ANTLR的连接,它就是一个著名的“编译器的编译器”,不过它实际上是ANother Tool for Language Recognition(ANTLR),它的描述语言可以生成词法分析器、语法分析器与语义分析器,也就是说,我们可以用它来识别加工不同的语言(编译器的编译器)。它同时支持3大类语言的输出:C++, Java, C#(按照生日排序),也就是说,我们可以利用它来用C#生成编译器。ANTLR是免费开源的,而且历史也很久远了,目前的版本是2.7.5,有兴趣的朋友可以在我的Blog的连接中找到网址。
那么,除了用反射轻松的实现计算值之外我们也可以用ANTLR来描述这些数学表达式,下面我附上一个我在看到《动态计算字串表达式值的类》后写的一个语法文件,感兴趣的朋友可以去下载ANTLR来试一试下面的语法哦!:
文件Calc.g
header { using CharBuffer = antlr.CharBuffer; }
options { language = "CSharp"; namespace = "Cavingdeep.Test.Calc"; }
/******************************\ Calc Parser \******************************/ class CalcParser extends Parser; options { exportVocab = Calc; buildAST = true; }
expr : sumExpr EOF ;
sumExpr : mulExpr ( ( SUM^ | NEG^ ) mulExpr )* ;
mulExpr : powExpr ( ( MUL^ | DIV^ ) powExpr )* ;
powExpr : negExpr ( POW^ negExpr )* ;
negExpr : ( NEG^ )* a:atom! { if (#negExpr != null) { AST lastNegNode = #negExpr; while (lastNegNode.getNumberOfChildren() > 0) { lastNegNode = lastNegNode.getFirstChild(); } lastNegNode.setFirstChild(#a); } else { ## = #a; } } ;
atom : NUMBER | LCURLY! sumExpr RCURLY! ;
/***************************\ Calc Lexer \***************************/ class CalcLexer extends Lexer; options { caseSensitive = false; exportVocab = Calc; }
WS : ( ' ' | '\t' | '\n' | '\r' )+ { $setType(Token.SKIP); } ;
NUMBER : ( ( '0'..'9' )+ '.' ( '0'..'9' )+ ) => ( '0'..'9' )+ '.' ( '0'..'9' )+ | '0'..'9' ;
SUM : '+' ;
NEG : '-' ;
MUL : '*' ;
DIV : '/' ;
POW : '^' ;
LCURLY : '(' ;
RCURLY : ')' ;
/***************************\ Calc Tree Parser \***************************/ class CalcTreeParser extends TreeParser; options { exportVocab = Calc; }
{ public static void Main(string[] args) { CalcLexer lexer = new CalcLexer(new CharBuffer(Console.In)); CalcParser parser = new CalcParser(lexer); parser.expr(); AST ast = parser.getAST(); CalcTreeParser treeParser = new CalcTreeParser(); double r = treeParser.eval(ast); Console.WriteLine("结果是:" + r); } }
eval returns [double v = 0] : v=expr ;
protected expr returns [double v = 0] { double izq = 0; double der = 0; } : i:NUMBER { v = Convert.ToDouble(i.getText()); } | #( SUM izq=expr der=expr ) { v = izq + der; } | #( DIV izq=expr der=expr ) { v = izq / der; } | ( #( NEG expr expr ) ) => #( NEG izq=expr der=expr ) { v = izq - der; } | #( MUL izq=expr der=expr ) { v = izq * der; } | #( POW izq=expr der=expr ) { v = Math.Pow(izq, der); } | #( NEG izq=expr ) { v = -(izq);} ;