8.JavaCC官方入门指南-例3

例3:计算器—double类型加法

  下面咱们对上个例子的代码进行进一步的修改,使得代码具备简单的四则运算的功能。
  第一步修改,咱们将打印出每一行的值,使得计算器更具交互性。一开始,咱们只是把数字加起来,而后再关注其余运算,好比减法、乘法和除法。java

1.Options和class声明块

  描述文件calculator0.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)

  Calculator类中的previousValue属性,用于存储上一行的计算结果的,咱们将在另外一个版本中使用到该值,到时可使用美圆符号来表示它。import导入语句声明说明了在PARSER_BEGIN和PARSER_END之间可能有import导入声明;这些代码都会被原样复制到生成的语法解析类和token管理类中去。一样还能够有包package的声明,package的声明将会被复制到最后生成的全部java类中去。code

2.词法描述文件

  词法分析器的描述文件在这里将会发生一些变化,首先一行的结束符也被声明为了token,并给这些行结束符命名为EOL,这样一来这个token也能够被传递给语法分析器了。orm

SKIP : { " " }
TOKEN : { < EOL : "\n" | "\r" | "\r\n" > }
TOKEN : { < PLUS : "+" > }

  接下来,咱们要定义合适的token使得容许输入中的数值有小数点。为此咱们修改NUMBER这个token的定义,使得它能够识别decimal类型的数值。当数值中有小数点,它能够有以下的4中类型,咱们分别用竖线分隔开来了,这4中类型分别是:整型,没有小数点、小数点在中间、小数点在末尾和小数点在开头。知足此需求的描述串以下:blog

TOKEN { < NUMBER : (["0"-"9"])+
                 | (["0"-"9"])+ "." (["0"-"9"])+
                 | (["0"-"9"])+ "."
                 | "." (["0"-"9"])+ >
        }

  有时候,一样的规则表达式可能会出现屡次。为了更好的可读性,最好是给这些重复出现的表达式起一个名字。对于那些只在词法描述文件中使用到,但又不是token的规则表达式,咱们建立了一个特殊的标识来表示它:#。所以,对于上面的词法描述,能够替换成以下:token

TOKEN : { < NUMBER : <DIGITS>
                   | <DIGITS> "." <DIGITS>
                   | <DIGITS> "."
                   | "."<DIGITS> >
        }
TOKEN : { < #DIGITS : (["0"-"9"])+ > }

  能够看到,咱们把([”0”-”9”])+这串规则表达式提取了出来,并将其命名为了DIGITS。可是要注意到,DIGITS这个并非token,这意味着在后面生成的Token类中,将不会有DIGITS对应的属性,而在语法分析器中也没法使用DIGITS。ci

3.语法描述文件

  语法分析器的输入由零行或多行组成。每行包含一个表达式。经过使用BNF符号表达式,语法分析器能够写成以下:io

Start -->(Expression EOL) * EOF

  由此咱们能够得出BNF生产式以下:class

void Start() :
{}
{
    (
        Expression()
        <EOL>
    )*
    <EOF>
}

  咱们在上面的BNF生产式中填充上java代码,使得它具有接收入参、记录并打印每一行的计算结果:import

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

  每一个表达式由一个或多个数字组成,这些数字目前用加号隔开。用BNF符号表达式以下:

Expression --> Primary(PLUS Primary)*

  在这里的Primary,咱们暂时用它来表示数值。
  上面的BNF符号表达式用JavaCC表示出来以下所示:

double Expression() throws NumberFormatException : {
    double i ;
    double value ;
}
{
    value = Primary()
    (
        <PLUS>
        i = Primary()
        { value += i ; }
    )*
    { return value ; }
}

  这个跟咱们前面例子中的Start BNF生产式差很少,咱们只是将数值的类型由int修改为了double类型而已。至于Primary(),跟上面例子也很是相似,它用BNF符号表达式来表示:

Primary --> NUMBER

  Primary()对应的JavaCC描述文件其实也差很少,只不过在这里它是对double精度的数值进行的转换计算:

double Primary() throws NumberFormatException :
{
    Token t ;
}
{
    t = <NUMBER>
    { return Double.parseDouble( t.image ) ; }
}

  下面咱们用BNF符号表达式将语法分析器的逻辑表示出来:

Start --> (Expression EOL) * EOF
Expression --> Primary (PLUS Primary)*
Primary --> NUMBER

  至此,咱们就把calculator0.jj描述文件都修改完了,接下来能够跑几个例子进行测试。

4.测试

4.1 calculator0.jj

  通过上面的修改,最后获得的.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 : { < 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 = Primary()
    (
        <PLUS>
        i = Primary()
        { value += i ; }
    )*
    { return value ; }
}

double Primary() throws NumberFormatException :
{
    Token t ;
}
{
    t = <NUMBER>
    { return Double.parseDouble( t.image ) ; }
}

4.2 运行

  咱们首先计算1+2,以下所示:

  接下来计算1+2.,即小数点在末尾的情形:

  接下来计算1 + 2.3,即小数点在中间的情形:

  接下来计算1 + .2,即小数点在开头的情形:

相关文章
相关标签/搜索