1.2.5 運行這個(gè)例子
再看看Adder類(lèi)的main方法:
| static void main( String[] args ) throws ParseException, TokenMgrError { Adder parser = new Adder( System.in ) ; parser.Start() ; } |
首先請注意main方法可能會(huì )拋出Throwable類(lèi)的兩個(gè)子類(lèi),這樣寫(xiě)不是好的風(fēng)格,我們確實(shí)應該捕捉這些異常,但是,這樣做可以讓第一個(gè)例子變得簡(jiǎn)短。
第一條語(yǔ)句創(chuàng )建了一個(gè)新的對象parser,其構造方法是自動(dòng)生成的,帶一個(gè)參數InputStream,第一個(gè)參數還可以是Reader。構造方法會(huì )生成類(lèi)SimpleCharacterStream的一個(gè)實(shí)例,還會(huì )生成詞法分析類(lèi)AdderTokenManager的一個(gè)對象。這樣,詞法分析器通過(guò)SimpleCharacterStream對象從System.in讀取到字符串,然后再傳遞到語(yǔ)法分析器。
第二條語(yǔ)句調用Start方法。對每個(gè)BNF產(chǎn)生式,JavaCC都會(huì )在解析類(lèi)中生成相應的方法。這個(gè)方法試圖找出與輸入描述相匹配的輸入序列。在這個(gè)例子中,語(yǔ)法分析器調用Start方法來(lái)找出符合輸入描述的一系列標記符,該輸入描述如下:
| <NUMBER> (<PLUS> <NUMBER>)* <EOF> |
準備好合適的輸入文件后,我們就可以運行這個(gè)程序了,執行如下命令:
| D:\home\JavaCC-Book\adder>java Adder <input.txt |
程序執行后,會(huì )出現以下三種情況之一:
1. 發(fā)現詞法錯誤。在本例中,僅當輸入中出現未預料的字符時(shí)才會(huì )出現該錯誤。輸入為以下字符串可以產(chǎn)生一個(gè)詞法錯誤:
| “123 - 456\n” |
程序會(huì )拋出一個(gè)TokenMgrError異常,該異常類(lèi)的成員變量message如下:Exception in thread ”main” TokenMgrError: Lexical error at line 1,
column 5.Encountered: ”-” (45), after : ””
2. 發(fā)現語(yǔ)法錯誤。當輸入不符合Start的規范時(shí)會(huì )出現該錯誤。當輸入為“123 ++ 456\n”、 “123 456\n”或者“\n”時(shí)都會(huì )拋出一個(gè)ParseException異常,對第一個(gè),該異常的message變量為:
Exception inthread ”main” ParseException: Encountered ”+” at
line 1, column6.
Was expecting:
<NUMBER>...
3. 輸入字符串符合Start的規范。這種情況下,程序正常執行,沒(méi)有異常拋出。
目前這個(gè)語(yǔ)法分析器僅僅能檢驗輸入字串是否合法,在接下來(lái)的部分,我們會(huì )作些修改使它更有用處。
1.2.6 生成的代碼
為了能理解JavaCC生成語(yǔ)法分析器的工作機理,看看生成的代碼是有益的。
| final public void Start() throws ParseException { ij_consume_token(NUMBER); label 1: while (true) { ij_consume_token (PLUS); ij_consume_token (NUMBER); switch ((jj_ntk == -1) ? jj_ntk () : jj_ntk) { case PLUS:; break; default: jj_la1[0] = jj_gen; break label 1; } } jj_consume_token(0); } |
jj_consume_token方法使用Token類(lèi)型作為其傳入參數,試圖從詞法分析器中得到該類(lèi)型的標記符;如果得不到,會(huì )拋出異常。該表達式
| (jj_ntk == -1) ? jj_ntk() : jj_ntk |
計算下一個(gè)未讀的標記符。最后一行會(huì )嘗試獲得0類(lèi)型的標記符,在JavaCC中,0類(lèi)型代表EOF標記符。
1.2.7 語(yǔ)法分析器的增強
JavaCC按照BNF產(chǎn)生式生成的方法Start只能簡(jiǎn)單的檢查輸入是否符合規范,我們可以通過(guò)在生成的方法中加入Java代碼來(lái)增強BNF產(chǎn)生式。JavaCC提供了一個(gè)框架,需要我們來(lái)填充。
我們對規范文件作些小的改動(dòng)得到adder1.jj。在BNF產(chǎn)生式的類(lèi)Start中添加一些聲明和java代碼。添加或者修改的部分用粗體表示:
| int Start() throws NumberFormatException : { Token t ; int i ; int value ; }{ t = <NUMBER> { i = Integer.parseInt( t.image ) ; } { value = i ; } ( <PLUS> t = <NUMBER> { i = Integer.parseInt( t.image ) ; } { value += i ; } )* <EOF> { return value ; } } |
首先,BNF產(chǎn)生式所生成的函數代碼的返回值,從void變成了int,我們聲明該方法可能拋出NumberFormatException異常。我們聲明了三個(gè)變量,Token類(lèi)型的t,類(lèi)型Token是一個(gè)由JavaCC生成的代表標記符的類(lèi);Token類(lèi)的image變量,代表解析到的字符串。當一個(gè)標記符符合BNF產(chǎn)生式時(shí),我們可以記錄這個(gè)Token對象并用如下的行來(lái)引用它:
| t = <NUMBER> |
在BNF產(chǎn)生式的花括號內,我們可以添加任何所需的Java代碼,這些代碼會(huì )一字不落的復制到生成的方法中。
由于生成的方法Start有了返回值,我們必須修改main方法,如下
| static void main( String[] args ) throws ParseException, TokenMgrError, NumberFormatException { Adder parser = new Adder( System.in ) ; int val = parser.Start() ; System.out.println(val); } |
本例還可以有一個(gè)小的改進(jìn),這兩行
| t = <NUMBER> { i = Integer.parseInt( t.image ) ; } |
出現了兩次,雖然在本例中不會(huì )有什么明顯的差異,畢竟只是兩行代碼,但這類(lèi)重復的代碼可能導致以后維護的困難。所以我們把這兩行重構到一個(gè)BNF產(chǎn)生式中,稱(chēng)為Primary。同上,最新的修改用粗體標記。
| int Start() throws NumberFormatException : { int i ; int value ; }{ value = Primary() ( <PLUS> i = Primary() { value += i ; } )* <EOF> { return value ; } } int Primary() throws NumberFormatException : { Token t ; }{ t=<NUMBER> { return Integer.parseInt( t.image ) ; } } |
來(lái)看看生成的方法,展示了JavaCC是如何把Java聲明和表達式集成到生成的方法的框架中的。
| final public int Start() throws ParseException, NumberFormatException { int i ; int value ; value = Primary(); label 1: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case PLUS:; break; default: jj_la1[0] = jj_gen; break label 1; } jj_consume_token(PLUS); i = Primary(); value += i ; } jj_consume_token(0); {if (true) return value ;} throw new Error(”Missing return statement in function”); } final public int Primary() throws ParseException, NumberFormatException { Token t ; t = jj_consume_token(NUMBER); {if (true) return Integer.parseInt( t.image ) ;} throw new Error(”Missing return statement in function”); } |
在后面還可以看到,可以在BNF產(chǎn)生式中傳參數。
http://laitysoft.blog.sohu.com/40344497.html
聯(lián)系客服