一、JavaCC
JavaCC是java的compiler compiler。JavaCC是LL解析器生成器,可處理的語法范圍比較狹窄,但支持無限長的token超前掃描。
安裝過程:
我是從github上down下來的zip壓縮包,然后安裝了下ant, 然后通過ant安裝的javacc
1. 首先下載下來ant的源碼,然后tar -zvxf apache-ant....tag.gz 解壓縮,然后可以在解壓出來的bin目錄中看到ant的可執行文件
2. 從github下載javacc, 進入解壓縮的目錄執行xxxxxx/ant。 然后會在target 目錄中看到javacc.jar 包
3. 這個時候可以通過如下方法將jar包做成一個可執行文件:
首先創建一個shell腳本:
#!/bin/sh MYSELF=`which "$0" 2>/dev/null` [ $? -gt 0 -a -f "$0" ] && MYSELF="./$0" java=java if test -n "$JAVA_HOME"; then java="$JAVA_HOME/bin/java" fi exec "$java" $java_args -cp $MYSELF "$@" exit 1
命名為stub.sh, 然后在jar包的所在目錄執行: cat stub.sh javacc.jar > javacc && chmod +x javacc。 這樣一個可執行文件就有了,不過在解析.jj文件時需要帶一個javacc的參數,像這樣: javacc javacc Adder.jj
二、語法描述文件
1、簡介
JavaCC的語法描述文件是擴展名為.jj的文件,一般情況下,語法描述文件的內容采用如下形式
options { JavaCC的選項 } PARSER_BEGIN(解析器類名) package 包名; import 庫名; public class 解析器類名 { 任意的Java代碼 } PARSER_END(解析器類名) 掃描器的描述 解析器的描述
JavaCC和java一樣將解析器的內容定義在單個類中,因此會在PARSER_BEGIN和PARSER_END之間描述這個類的相關內容。
2、Example
如下代碼是一個解析正整數加法運算並進行計算的解析器的語法描述文件。
options { STATIC = false; } PARSER_BEGIN(Adder) import java.io.*; class Adder { public static void main(String[] args) { for (String arg : args) { try { System.out.println(evaluate(arg)); } catch (ParseException ex) { System.err.println(ex.getMessage()); } } } public static long evaluate(String src) throws ParseException { Reader reader = new StringReader(src); return new Adder(reader).expr(); } } PARSER_END(Adder) SKIP: { <[" ", "\t", "\r", "\n"]> } TOKEN: { <INTEGER: (["0"-"9"])+> } long expr(): { Token x, y; } { x=<INTEGER> "+" y=<INTEGER> <EOF> { return Long.parseLong(x.image) + Long.parseLong(y.image); } }
options塊中將STATIC選項設置為false, 將該選項設置為true的話JavaCC生成的所有成員及方法都將被定義為static,若將STATIC設置為true則所生成的解析器無法在多線程環境下使用,因此該選項總是被設置為false。(STATIC的默認值為true)
從PARSER_BEING(Adder)到PARSER_END(Adder)是解析器類的定義。解析器類中需要定義的成員和方法也寫在這里。為了實現即使只有Adder類也能夠運行,這里定義了main函數。
之后的SKIP和TOKEN部分定義了掃描器。SKIP表示要跳過空格、制表符(tab)和換行符。TOKEN表示掃描整數字符並生成token。
long expr...開始到最后的部分定義了狹義的解析器。這部分解析token序列並執行某些操作。
3、運行JavaCC
要用JavaCC來處理Adder.jj(圖中是demo1.jj),需要使用如下javacc命令
運行如上命令會生成Adder.java和其他輔助類。
要編譯生成的Adder.java,只需要javac命令即可:
這樣就生成了Adder.class文件。Adder類是從命令行參數獲取計算式並進行計算的,因此可以如下這樣從命令行輸入計算式並執行
三、啟動JavaCC生成的解析器
現在解析一下main函數的代碼。 main函數將所有命令行參數的字符串作為計算對象的算式,依次用evaluate方法進行計算。
evaluate方法中生成了Adder類的對象實例 。並讓Adder對象來計算(解析)參數字符串src。
要運行JavaCC生成的解析器類,需要下面2個步驟:
- 生成解析器類的對象實例
- 用生成的對象調用和需要解析的語句同名的方法
第1點: JavaCC4.0生成的解析器中默認定義有如下四種類型的構造函數。
- Parser(InputStream s)
- Parser(InputStream s, String encoding)
- Parser(Reader r)
- Parser(x x x x TokenManager tm)
第1種的構造函數是通過傳入InputStream對象來構造解析的。這個構造函數無法設定輸入字符串的編碼,因此無法處理中文字符等。
而地2種的構造函數除了InputStream對象外,還可以設置輸入字符串的編碼來生成解析器。但如果要解析中文字符串或注釋的話,就必須使用第2種/3種構造函數。
第3種的構造函數用於解析Reader對象所讀入的內容。
第4種是將掃描器作為參數傳入。
解析器生成后,用這個實例調用和需要解析的語法同名的方法。這里調用Adder對象的expr方法,接回開始解析,解析正常結束后會返回語義值。
四、中文的處理
要使JavaCC能夠處理中文首先需要將語法描述文件的options快的UNICODE_INPUT選項設置為true:
options { STATUS = false; DEBUG_PARSER = true; UNICODE_PARSER = true; JDK_VERSION = "1.5"; }
這樣就會先將輸入的字符轉換成UNICODE后再進行處理。UNICODE_INPUT選項為false時只能處理ASCII范圍的字符。
另外還需要使用第2/3種構造方法為輸入的字符串設置適當的編碼。