咱們只須要再添加一些特點,就能夠獲得一個可用的四則運算計算器。在這一版的修改中 ,咱們將使得程序能夠接收括號、負值,而且還能夠經過$符號來引用上一次計算的結果。
對詞法描述文件的修改以下所示,咱們只添加下面3行:java
TOKEN : { < OPEN_PAR : "(" > } TOKEN : { < CLOSE_PAR : ")" > } TOKEN : { < PREVIOUS : "$" > }
咱們沒有必要專門爲負號建立一個token,由於咱們已經定義了MINUS這個token了。
對於語法描述部分的修改,則都是體如今了Primary當中,在Primary當中有4中可能的值:一個數值(跟以前的例子同樣)、一個$符號、帶有括號的表達式、一個負號而後跟着前3個的任一種。BNF符號表達式以下:測試
Primary --> NUMBER | PREVIOUS | OPEN_PAR Expression CLOSE_PAR | MINUS Primary
這個BNF生產式中有兩個遞歸。最後一種選擇是直接遞歸,倒數第二個選擇是間接遞歸,由於Expression最終仍是依賴於Primary。在BNF生產式中使用遞歸是沒有任何問題的,固然也有一些限制,咱們將在後面談論到。
考慮下面的表達式:翻譯
- - 22
那麼Primary就是下圖中的方塊部分:
3d
在語法分析器執行到上面的輸入時,對於每個方塊,都會調用一次Primary。類似的,對於下面的輸入:code
12 * ( 42 + 19 )
咱們把Primary框出來,可獲得以下所示:
orm
相互嵌套的框框,其實就表示了相互遞歸調用的Primary方法。
下面是JavaCC中Primary的生產式:blog
double Primary() throws NumberFormatException : { Token t ; double d ; } { t=<NUMBER> { return Double.parseDouble( t.image ) ; } | <PREVIOUS> { return previousValue ; } | <OPEN_PAR> d=Expression() <CLOSE_PAR> { return d ; } | <MINUS> d=Primary() { return -d ; } }
根據上面的修改,獲得完整的calculator3.jj文件以下:遞歸
/* calculator0.jj An interactive calculator. */ options { STATIC = false ; } PARSER_BEGIN(Calculator) import java.io.PrintStream ; class Calculator { public static void main( String[] args ) throws ParseException, TokenMgrError, NumberFormatException { Calculator parser = new Calculator( System.in ) ; parser.Start( System.out ) ; } double previousValue = 0.0 ; } PARSER_END(Calculator) SKIP : { " " } TOKEN : { < EOL : "\n" | "\r" | "\r\n" > } TOKEN : { < PLUS : "+" > } TOKEN : { < MINUS : "-" > } TOKEN : { < TIMES : "*" > } TOKEN : { < DIVIDE : "/" > } TOKEN : { < OPEN_PAR : "(" > } TOKEN : { < CLOSE_PAR : ")" > } TOKEN : { < PREVIOUS : "$" > } TOKEN : { < NUMBER : <DIGITS> | <DIGITS> "." <DIGITS> | <DIGITS> "." | "."<DIGITS> > } TOKEN : { < #DIGITS : (["0"-"9"])+ > } void Start(PrintStream printStream) throws NumberFormatException : {} { ( previousValue = Expression() <EOL> { printStream.println( previousValue ) ; } )* <EOF> } double Expression() throws NumberFormatException : { double i ; double value ; } { value = Term() ( <PLUS> i = Term() { value += i ; } | <MINUS> i = Term() { value -= i ; } )* { return value ; } } double Term() throws NumberFormatException : { double i ; double value ; } { value = Primary() ( <TIMES> i = Primary() { value *= i ; } | <DIVIDE> i = Primary() { value /= i ; } )* { return value ; } } double Primary() throws NumberFormatException : { Token t ; double d ; } { t=<NUMBER> { return Double.parseDouble( t.image ) ; } | <PREVIOUS> { return previousValue ; } | <OPEN_PAR> d=Expression() <CLOSE_PAR> { return d ; } | <MINUS> d=Primary() { return -d ; } }
計算(1+2)*-3,以下所示,正確計算獲得結果-9:
token
計算器示例到此結束。官方文檔中其實還有一個「文本處理」的例子,時間有限就不翻譯了。能夠直接去閱讀英文原版,其實仍是比較容易懂的。上面的示例經過由淺入深的引導,讓咱們大體知道JavaCC能幹什麼以及是怎麼工做的。若是想要使用JavaCC作更多的事情,建議仍是把最後一個例子的英文原版看完看懂,並多加練習。文檔