先說說我們打算干什么,表達(dá)式是幾乎所有高級(jí)編程語言中,都會(huì)出現(xiàn)的重要組成部分。因此,如何準(zhǔn)確的理解一個(gè)表達(dá)式,可以說是各種不同的語言所共同面臨的問題。表達(dá)式千變?nèi)f化,真正要想正確解釋C/C++那樣的復(fù)雜表達(dá)式,是非常困難的,我們這里只從最簡(jiǎn)單的表達(dá)式做起。
執(zhí)行這個(gè)Main.class,輸入個(gè)表達(dá)式給它試一試,比如: 1+2-3*4/5^6;系統(tǒng)應(yīng)該就能給出正確的答案了:
咱們來一句一句的解釋一下這個(gè)expression.g文件。還是從最簡(jiǎn)單的幾行開始:
class ExpressionLexer extends Lexer; //這是用來聲明一個(gè)詞法分析器,名字叫
//ExpressionLexer
PLUS : ‘+‘ ; //加號(hào)
MINUS : ‘-‘ ; //減號(hào)
MUL : ‘*‘ ; //乘號(hào)
DIV : ‘/‘ ; //除號(hào)
MOD : ‘%‘ ; //求余
POW : ‘^‘ ; //開方
SEMI : ‘;‘ ; //結(jié)束號(hào)
//上面這些都太簡(jiǎn)單了,簡(jiǎn)直就不需要說明
protected DIGIT : ‘0‘..‘9‘ ; //數(shù)字,這是一個(gè)受保護(hù)的單詞,只能被
//詞法分析器內(nèi)部使用
INT : (DIGIT)+ ; //出現(xiàn)了一次以上的數(shù)字的詞,就是整數(shù),
//它通過受保護(hù)的單詞:“數(shù)字”來定義自己。
//如果DIGIT不是被保護(hù)的單詞,則詞法分析器就會(huì)
//無法分辨究竟是數(shù)字還是整數(shù)了
接下來看語法分析器的代碼:
class ExpressionParser extends Parser; //定義一個(gè)語法分析器,名字叫ExpressionParser
options { buildAST=true; } //告訴ANTLR,要幫我生成一個(gè)抽象語法樹,
//留著以后有用
//接下來的部分就非常復(fù)雜了,主要是多出來了兩個(gè)特殊的符號(hào)“!”、“^”
//這兩個(gè)符號(hào),不是EBNF原有的,而是ANTLR為了生成AST而增加的符號(hào)
//“!”,是告訴AST生成程序,不要把自己算進(jìn)去
//“^”,是告訴AST生成程序,把這個(gè)符號(hào),放在一顆樹的根部,或者一顆子樹的根部
//另外,“*”表示出現(xiàn)0次以上,“+”表示出現(xiàn)一次以上,“?”表示出現(xiàn)0或1次
expr : sumExpr SEMI!; //“;”作為結(jié)束符,不放入AST
sumExpr : prodExpr ((PLUS^|MINUS^) prodExpr)* ; //“+”“-”作為計(jì)算符號(hào)
//放在樹的頂部
prodExpr : powExpr ((MUL^|DIV^|MOD^) powExpr)* ; //剩下的就不解釋了,都能明白的
powExpr : atom (POW^ atom)? ;
atom : INT ;
再來看AST計(jì)算器的代碼。這“AST計(jì)算器”是我起的名字,也就是通過對(duì)一個(gè)生成的抽象語法樹,遞歸求值,得到最后的結(jié)果。
{import java.lang.Math;} //ExpressionTreeWalker要用到的
class ExpressionTreeWalker extends TreeParser; //聲明一個(gè)樹計(jì)算器
expr returns [double r] //有一個(gè)方法叫expr
//它的返回值是double類型
{double a,b; r=0; } //嵌入的代碼,后面要用到
: #(PLUS a=expr b=expr) { r=a+b; } //以下就是計(jì)算各種算符,不用多說了
| #(MINUS a=expr b=expr) { r=a-b; }
| #(MUL a=expr b=expr) { r=a*b; }
| #(DIV a=expr b=expr) { r=a/b; }
| #(MOD a=expr b=expr) { r=a%b; }
| #(POW a=expr b=expr) { r=Math.pow(a,b); }
| i:INT { r=(double)Integer.parseInt(i.getText()); }
;