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,即小數點在開頭的情形:

相關文章
相關標籤/搜索