上次的表达式没有计算功能,这次继续,让它先算加法.
先看BNF产生式代码: Adder.jj
- 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; }
- }
先看这个方法,首先行1语句的返回值由void变成int了,因为我们要返回和.行2和行6的大括号之间原来是声明变量用的,是java语法,因为java语句中不能直接调用<NUMBER>,所以得用个变量t.i ; 中间临时变量 i,得到t的具体数可以被java识别的据. value是累加后的和.在"{...}"书写java代码,并且顺序相关.8行中的t.image得到token的字符串表现形式.然后解析成int,最后通过 value += i 进行累加.最后返回累加的和,这段代码比较容易理解,可看起来还是杂乱,我们可以用"函数"的概念进行重构.
- 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 ) ; }
- }
这里用了两个BNF产生式,可读性加强了.
主程序的要对int类型的返回值进行相应改变.
- public static void main( String[] args )throws ParseException, TokenMgrError, NumberFormatException {
- Adder adder = new Adder( System.in ) ;
- int value = adder.start() ;
- System.out.println(value);
- }
然后进行编译,运行测试.注意,先把原来生成的文件全删除.程序还是比较好理解的,下面加入减法.
- PARSER_BEGIN(Adder)
- public class Adder{
- public static void main(String[] args) throws ParseException,TokenMgrError,NumberFormatException {
- Adder adder = new Adder(System.in);
- int value = adder.start();
- System.out.println(value);
- }
- }
- PARSER_END(Adder)
- SKIP:{" "|"/t"|"/n"|"/r"|"/r/n"}
- TOKEN:{
- <PLUS:"+">
- |<MINUS:"-">
- |<NUMBER:(["0"-"9"])+>
- }
- int start() throws NumberFormatException :
- {
- int value;
- }
- {
- {value = primary();}
- (
- <PLUS>
- {value += primary();}
- |<MINUS>
- {value -= primary();}
- )*
- <EOF>
- {return value;}
- }
- int primary() throws NumberFormatException :
- {
- Token t;
- }
- {
- t = <NUMBER>
- {return Integer.parseInt(t.image);}
- }
加法和减法都有了,可现在有个问题,每次只有按ctrl + c退出程序才可以得到结果,能不能每次回车换行时候即时输出呢?
首先需要把输出流定义到System.out上:
- PARSER_BEGIN(Adder)
- import java.io.PrintStream ;
- public class Adder{
- public static void main(String[] args) throws ParseException,TokenMgrError,NumberFormatException {
- Adder adder = new Adder(System.in);
- adder.start(System.out);
- }
- }
- PARSER_END(Adder)
因为要识别回车换行,所以Token需要识别回车换行:
- SKIP:{" "|"/t"}
- TOKEN:{
- <PLUS:"+">
- |<MINUS:"-">
- |<NUMBER:(["0"-"9"])+>
- |<EOL:"/n"|"/r"|"/r/n">
- }
start BNF产生式要这样改变:
- void start(PrintStream printStream) throws NumberFormatException :
- {
- int value;
- }
- {
- (
- value = expression()
- <EOL>
- { printStream.println( value ) ; }
- )*
- <EOF>
- }
- int expression() throws NumberFormatException :
- {
- int value ;
- }
- {
- value = primary()
- (
- <PLUS>
- { value += primary() ; }
- |<MINUS>
- { value -= primary() ; }
- )*
- { return value ; }
- }
用BNF标志表示:
start → (expression EOL) * EOF
expression → primary (PLUS primary)*
primary → NUMBER
接下来加入乘除法,由于除法会产生小数,所以先把int换成double,而浮点数的Token 可以这样定义:
TOKEN : { < NUMBER : <DIGITS> | <DIGITS> "." <DIGITS> | <DIGITS> "." | "."<DIGITS> > }
TOKEN : { < #DIGITS : (["0"-"9"])+ > }
其中"#"符号,代表这样一种情况:它后面所跟的名字并不代表一种Token类型,它对于词法分析器只代表一个位置,仅此而已.
其它各返回值都需要把int换成double.
Token中加入乘除符号:
TOKEN : { < TIMES : "*" > }
TOKEN : { < DIVIDE : "/" > }
由于乘除法的优先级高于加减法,所以需要这样处理:
expression → term (PLUS term | MINUS term)*
term → primary (TIMES primary | DIVIDE primary)*
完整代码:Adder.jj
- PARSER_BEGIN(Adder)
- import java.io.PrintStream ;
- public class Adder{
- public static void main(String[] args) throws ParseException,TokenMgrError,NumberFormatException {
- Adder adder = new Adder(System.in);
- adder.start(System.out);
- }
- }
- PARSER_END(Adder)
- SKIP:{" "|"/t"}
- TOKEN:{
- <PLUS:"+">
- |<MINUS:"-">
- | <TIMES:"*">
- |<DIVIDE:"/">
- |<NUMBER:<DIGITS> | <DIGITS> "." <DIGITS> | <DIGITS> "." | "."<DIGITS> >
- |<#DIGITS : (["0"-"9"])+ >
- |<EOL:"/n"|"/r"|"/r/n">
- }
- void start(PrintStream printStream) throws NumberFormatException :
- {
- double value;
- }
- {
- (
- value = expression()
- <EOL>
- { printStream.println( value ) ; }
- )*
- <EOF>
- }
- double expression() throws NumberFormatException :
- {
- double value ;
- }
- {
- value =term()
- (
- <PLUS>
- { value += term() ; }
- |<MINUS>
- { value -= term() ; }
- )*
- { return value ; }
- }
- double term() throws NumberFormatException :
- {
- double value ;
- }
- {
- value = primary()
- (
- <TIMES>
- { value *= primary() ; }
- |<DIVIDE>
- { value /= primary() ; }
- )*
- { return value ; }
- }
- double primary() throws NumberFormatException :
- {
- Token t;
- }
- {
- t = <NUMBER>
- {return Double.parseDouble(t.image);}
- }
编译运行一下,这回象样多了,呵呵.再加点猛料,各种括号:
增加Token符号:
|<OPEN_PAR: "(" >
|<CLOSE_PAR: ")" >
|<OPEN_BET : "[">
|<CLOSE_BET : "]">
|<OPEN_BCE : "{">
|<CLOSE_BCE : "}">
BNF标志:
primary → NUMBER
| OPEN_PAR expression CLOSE_PAR
| OPEN_BET expression CLOSE_BET
| OPEN_BCE expression CLOSE_BCE
有点像递归函数,不难理解.
- PARSER_BEGIN(Adder)
- import java.io.PrintStream ;
- public class Adder{
- public static void main(String[] args) throws ParseException,TokenMgrError,NumberFormatException {
- Adder adder = new Adder(System.in);
- adder.start(System.out);
- }
- }
- PARSER_END(Adder)
- SKIP:{" "|"/t"}
- TOKEN:{
- <PLUS:"+">
- |<MINUS:"-">
- |<TIMES:"*">
- |<DIVIDE:"/">
- |<NUMBER:<DIGITS> | <DIGITS> "." <DIGITS> | <DIGITS> "." | "."<DIGITS> >
- |<#DIGITS: (["0"-"9"])+ >
- |<OPEN_PAR: "(" >
- |<CLOSE_PAR: ")" >
- |<OPEN_BET : "[">
- |<CLOSE_BET : "]">
- |<OPEN_BCE : "{">
- |<CLOSE_BCE : "}">
- |<EOL:"/n"|"/r"|"/r/n">
- }
- void start(PrintStream printStream) throws NumberFormatException :
- {
- double value;
- }
- {
- (
- value = expression()
- <EOL>
- { printStream.println( value ) ; }
- )*
- <EOF>
- }
- double expression() throws NumberFormatException :
- {
- double value ;
- }
- {
- value =term()
- (
- <PLUS>
- { value += term() ; }
- |<MINUS>
- { value -= term() ; }
- )*
- { return value ; }
- }
- double term() throws NumberFormatException :
- {
- double value ;
- }
- {
- value = primary()
- (
- <TIMES>
- { value *= primary() ; }
- |<DIVIDE>
- { value /= primary() ; }
- )*
- { return value ; }
- }
- double primary() throws NumberFormatException :
- {
- Token t;
- double value;
- }
- {
- t = <NUMBER>
- {return Double.parseDouble(t.image);}
- |<OPEN_PAR> value = expression() <CLOSE_PAR>
- { return value ; }
- |<OPEN_BET>value = expression() <CLOSE_BET>
- { return value ; }
- |<OPEN_BCE>value = expression() <CLOSE_BCE>
- { return value ; }
- }
4则混合运算基本OK了,来吧,测试一下成果.忙了半天了.
javacc Adder.jj
javac *.java
java Adder
控制台输入:
2*{1*[3*(1+1)-2]+1}
看看得到了多少?