上一个例子中,JavaCC为BNF生产式所生成的方法,好比Start(),这些方法默认只简单的检查输入是否匹配BNF生产式指定的规范。可是咱们也能够用java代码来扩充BNF生产式,使得由生产式生成的方法中也包含java代码。
咱们加下来会对上面例一中的adder.jj代码作一些修改。对于其中的Start这个BNF生产式,咱们加入一些声明和java代码,以下所示: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); }
除此以外,咱们还要作以下的一点小改进,咱们看到,下面的代码出现了两次:orm
{ i = Integer.parseInt( t.image ) ; } { value = i ; }
虽然在这个例子中影响并不大,可是像这种重复出现的代码仍是可能会致使代码维护的问题。所以咱们对这两行代码进行重构,将它重构成另一个BNF生产式,并把这个生产式命名为Primary。blog
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的变量声明和逻辑代码跟生产式融合起来的。token
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”); }
通过上面的修改,最终获得.jj描述文件内容以下,咱们将其保存命名为adder2.jj:input
/* 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 ) ; } }
能够看到,input.txt输入文件中的内容是1+2,运行完程序以后,便可计算出结果:3。
一样的,若是input.txt文件中的内容是1-2,则会报 词法异常 ,以下图所示:
it
若是input.txt文件中的内容是1++2,则会报 语法异常 ,以下图所示:
io