javacc运行这个例子

来源:百度文库 编辑:神马文学网 时间:2024/10/04 16:25:12
1.2.5         运行这个例子
再看看Adder类的main方法:
static void main( String[] args )
throws ParseException, TokenMgrError {
Adder parser = new Adder( System.in ) ;
parser.Start() ;
}
首先请注意main方法可能会抛出Throwable类的两个子类,这样写不是好的风格,我们确实应该捕捉这些异常,但是,这样做可以让第一个例子变得简短。
第一条语句创建了一个新的对象parser,其构造方法是自动生成的,带一个参数InputStream,第一个参数还可以是Reader。构造方法会生成类SimpleCharacterStream的一个实例,还会生成词法分析类AdderTokenManager的一个对象。这样,词法分析器通过SimpleCharacterStream对象从System.in读取到字符串,然后再传递到语法分析器。
第二条语句调用Start方法。对每个BNF产生式,JavaCC都会在解析类中生成相应的方法。这个方法试图找出与输入描述相匹配的输入序列。在这个例子中,语法分析器调用Start方法来找出符合输入描述的一系列标记符,该输入描述如下:
( )*
准备好合适的输入文件后,我们就可以运行这个程序了,执行如下命令:
D:\home\JavaCC-Book\adder>java Adder 程序执行后,会出现以下三种情况之一:
1. 发现词法错误。在本例中,仅当输入中出现未预料的字符时才会出现该错误。输入为以下字符串可以产生一个词法错误:
“123 - 456\n”
程序会抛出一个TokenMgrError异常,该异常类的成员变量message如下:Exception in thread ”main” TokenMgrError: Lexical error at line 1,
column 5.Encountered: ”-” (45), after : ””
2. 发现语法错误。当输入不符合Start的规范时会出现该错误。当输入为“123 ++ 456\n”、 “123 456\n”或者“\n”时都会抛出一个ParseException异常,对第一个,该异常的message变量为:
Exception inthread ”main” ParseException: Encountered ”+” at
line 1, column6.
Was expecting:
...
3. 输入字符串符合Start的规范。这种情况下,程序正常执行,没有异常抛出。
目前这个语法分析器仅仅能检验输入字串是否合法,在接下来的部分,我们会作些修改使它更有用处。
1.2.6        生成的代码
为了能理解JavaCC生成语法分析器的工作机理,看看生成的代码是有益的。
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类型作为其传入参数,试图从词法分析器中得到该类型的标记符;如果得不到,会抛出异常。该表达式
(jj_ntk == -1) ? jj_ntk() : jj_ntk
计算下一个未读的标记符。最后一行会尝试获得0类型的标记符,在JavaCC中,0类型代表EOF标记符。
1.2.7         语法分析器的增强
JavaCC按照BNF产生式生成的方法Start只能简单的检查输入是否符合规范,我们可以通过在生成的方法中加入Java代码来增强BNF产生式。JavaCC提供了一个框架,需要我们来填充。
我们对规范文件作些小的改动得到adder1.jj。在BNF产生式的类Start中添加一些声明和java代码。添加或者修改的部分用粗体表示:
int Start() throws NumberFormatException :
{
Token t ;
int i ;
int value ;
}{
t =
{ i = Integer.parseInt( t.image ) ; }
{ value = i ; } (

t =
{ i = Integer.parseInt( t.image ) ; }
{ value += i ; }
)*

{ return value ; }
}
首先,BNF产生式所生成的函数代码的返回值,从void变成了int,我们声明该方法可能抛出NumberFormatException异常。我们声明了三个变量,Token类型的t,类型Token是一个由JavaCC生成的代表标记符的类;Token类的image变量,代表解析到的字符串。当一个标记符符合BNF产生式时,我们可以记录这个Token对象并用如下的行来引用它:
t =
在BNF产生式的花括号内,我们可以添加任何所需的Java代码,这些代码会一字不落的复制到生成的方法中。
由于生成的方法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);
}
本例还可以有一个小的改进,这两行
t =
{ i = Integer.parseInt( t.image ) ; }
出现了两次,虽然在本例中不会有什么明显的差异,毕竟只是两行代码,但这类重复的代码可能导致以后维护的困难。所以我们把这两行重构到一个BNF产生式中,称为Primary。同上,最新的修改用粗体标记。
int Start() throws NumberFormatException :
{
int i ;
int value ;
}{
value = Primary()
(

i = Primary()
{ value += i ; }
)*

{ return value ; }
}
int Primary() throws NumberFormatException :
{
Token t ;
}{
t=
{ 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”);
}
在后面还可以看到,可以在BNF产生式中传参数。
http://laitysoft.blog.sohu.com/40344497.html