例2:整數加法運算--改良版(增強語法分析器)
1.修改
上一個例子中,JavaCC為BNF生產式所生成的方法,比如Start(),這些方法默認只簡單的檢查輸入是否匹配BNF生產式指定的規范。但是我們也可以用java代碼來擴充BNF生產式,使得由生產式生成的方法中也包含java代碼。
我們加下來會對上面例一中的adder.jj代碼做一些修改。對於其中的Start這個BNF生產式,我們加入一些聲明和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生產式的返回類型,這就使得由該BNF生產式生成的方法的返回值類型由void變成了int。另外的改動是,我們聲明了一個可能拋出的異常NumberFormatException。在方法內,聲明了3個變量,其中變量t是Token類型的,Token類是我們編譯.jj文件文件之后生成的類,而Token類中的image屬性則表示匹配到的token的值。在聲明完變量之后,當一個token被BNF生產式匹配到,我們就可以通過t =
現在生成的Start方法將有一個返回值,因此我們對main方法做如下修改:
Public static void main( String[] args ) throws ParseException, TokenMgrError, NumberFormatException {
Adder parser = new Adder( System.in );
int val = parser.Start() ;
System.out.println(val);
}
除此之外,我們還要做如下的一點小改進,我們看到,下面的代碼出現了兩次:
{ i = Integer.parseInt( t.image ) ; }
{ value = i ; }
雖然在這個例子中影響並不大,但是像這種重復出現的代碼還是可能會導致代碼維護的問題。因此我們對這兩行代碼進行重構,將它重構成另外一個BNF生產式,並把這個生產式命名為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 ) ; }
}
下面是生成的代碼,從中我們可以看出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”);
}
2.運行
2.1 adder2.jj
經過上面的修改,最終得到.jj描述文件內容如下,我們將其保存命名為adder2.jj:
/* adder.jj Adding up numbers */
options {
STATIC = false ;
}
PARSER_BEGIN(Adder)
class Adder {
public static void main( String[] args ) throws ParseException, TokenMgrError, NumberFormatException {
Adder parser = new Adder( System.in );
int val = parser.Start();
System.out.println(val);
}
}
PARSER_END(Adder)
SKIP : { " "}
SKIP : { "\n" | "\r" | "\r\n" }
TOKEN : { < PLUS : "+" > }
TOKEN : { < NUMBER : (["0"-"9"])+ > }
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 ) ; }
}
2.2 運行javacc命令
2.3 編譯生成的java文件
2.4 運行程序
可以看到,input.txt輸入文件中的內容是1+2,運行完程序之后,即可計算出結果:3。
同樣的,如果input.txt文件中的內容是1-2,則會報 詞法異常 ,如下圖所示:
如果input.txt文件中的內容是1++2,則會報 語法異常 ,如下圖所示: