11.JavaCC官方入门指南-例6

例6:计算器--添加括号、一元运算符和历史记录

1.calculator3.jj

  咱们只须要再添加一些特点,就能够获得一个可用的四则运算计算器。在这一版的修改中 ,咱们将使得程序能够接收括号、负值,而且还能够经过$符号来引用上一次计算的结果。
  对词法描述文件的修改以下所示,咱们只添加下面3行:java

TOKEN : { < OPEN_PAR : "(" > }
TOKEN : { < CLOSE_PAR : ")" > }
TOKEN : { < PREVIOUS : "$" > }

  咱们没有必要专门为负号建立一个token,由于咱们已经定义了MINUS这个token了。
  对于语法描述部分的修改,则都是体如今了Primary当中,在Primary当中有4中可能的值:一个数值(跟以前的例子同样)、一个$符号、带有括号的表达式、一个负号而后跟着前3个的任一种。BNF符号表达式以下:测试

Primary --> NUMBER
        | PREVIOUS
        | OPEN_PAR Expression CLOSE_PAR
        | MINUS Primary

  这个BNF生产式中有两个递归。最后一种选择是直接递归,倒数第二个选择是间接递归,由于Expression最终仍是依赖于Primary。在BNF生产式中使用递归是没有任何问题的,固然也有一些限制,咱们将在后面谈论到。
  考虑下面的表达式:翻译

- - 22

  那么Primary就是下图中的方块部分:
3d

  在语法分析器执行到上面的输入时,对于每个方块,都会调用一次Primary。类似的,对于下面的输入:code

12 * ( 42 + 19 )

  咱们把Primary框出来,可获得以下所示:
orm

  相互嵌套的框框,其实就表示了相互递归调用的Primary方法。
  下面是JavaCC中Primary的生产式:blog

double Primary() throws NumberFormatException :
{
Token t ;
double d ;
}
{
    t=<NUMBER>
    { return Double.parseDouble( t.image ) ; }
|   <PREVIOUS>
    { return previousValue ; }
|   <OPEN_PAR> d=Expression() <CLOSE_PAR>
    { return d ; }
|   <MINUS> d=Primary()
    { return -d ; }
}

2. 测试

  根据上面的修改,获得完整的calculator3.jj文件以下:递归

/* calculator0.jj An interactive calculator. */
options {
    STATIC = false ;
}
PARSER_BEGIN(Calculator)
    import java.io.PrintStream ;
    class Calculator {
        public static void main( String[] args )
            throws ParseException, TokenMgrError, NumberFormatException {
            Calculator parser = new Calculator( System.in ) ;
            parser.Start( System.out ) ;
        }
        double previousValue = 0.0 ;
    }
PARSER_END(Calculator)


SKIP : { " " }
TOKEN : { < EOL : "\n" | "\r" | "\r\n" > }
TOKEN : { < PLUS : "+" > }
TOKEN : { < MINUS : "-" > }
TOKEN : { < TIMES : "*" > }
TOKEN : { < DIVIDE : "/" > }
TOKEN : { < OPEN_PAR : "(" > }
TOKEN : { < CLOSE_PAR : ")" > }
TOKEN : { < PREVIOUS : "$" > }
TOKEN : { < NUMBER : <DIGITS>
                   | <DIGITS> "." <DIGITS>
                   | <DIGITS> "."
                   | "."<DIGITS> >
        }
TOKEN : { < #DIGITS : (["0"-"9"])+ > }


void Start(PrintStream printStream) throws NumberFormatException :
{}
{
    (
        previousValue = Expression()
        <EOL> { printStream.println( previousValue ) ; }
    )*
    <EOF>
}

double Expression() throws NumberFormatException :
{
    double i ;
    double value ;
}
{
    value = Term()
    (
        <PLUS>
        i = Term()
        { value += i ; }
      | <MINUS>
        i = Term()
        { value -= i ; }
    )*
    { return value ; }
}

double Term() throws NumberFormatException :
{
    double i ;
    double value ;
}
{
    value = Primary()
    (
        <TIMES>
        i = Primary()
        { value *= i ; }
      | <DIVIDE>
        i = Primary()
        { value /= i ; }
    )*
    { return value ; }
}

double Primary() throws NumberFormatException :
{
Token t ;
double d ;
}
{
    t=<NUMBER>
    { return Double.parseDouble( t.image ) ; }
|   <PREVIOUS>
    { return previousValue ; }
|   <OPEN_PAR> d=Expression() <CLOSE_PAR>
    { return d ; }
|   <MINUS> d=Primary()
    { return -d ; }
}

计算(1+2)*-3,以下所示,正确计算获得结果-9:
token

  计算器示例到此结束。官方文档中其实还有一个“文本处理”的例子,时间有限就不翻译了。能够直接去阅读英文原版,其实仍是比较容易懂的。上面的示例经过由浅入深的引导,让咱们大体知道JavaCC能干什么以及是怎么工做的。若是想要使用JavaCC作更多的事情,建议仍是把最后一个例子的英文原版看完看懂,并多加练习。文档

相关文章
相关标签/搜索