/***********************************************************/
>我使用的測試jjt,jj文件來自於javacc5.0版本
>dir_hier/javacc-5.0/javacc-5.0/examples/
JJTreeExamples
SimpleExamples
/***********************************************************/
/***********************************************************/
0.javacc(the java Compiler Compiler)概要
>Abstract summary:
javacc一般用於編寫某種語言的書寫規則,如java的語法規則
變量必須以字母或者$開頭或者以數字結尾等,這是一種書寫規范,
書寫規范是這門語言特有的,他必須滿足,那么他是怎么去判斷
書寫的字符竄符合了規則了,那就需要進行驗證,javacc就是
一種用於定義某種特定輸入格式字符竄規則的工具,也是驗證這種
特定格式字符竄的驗證者。定義字符竄輸入格式,以及驗證輸入的
字符竄是javacc的最基本作用。
javacc語法規則描述文件的書寫方式有兩種一種是一種是.jj文件,
另外一種是.jjt文件,兩種文件的書寫方式差異性不大,但是jjt文件
比jj文件表達語法規則更加容易,jj文件的可選參數項和jjt有很大的
不同,這是他們最大的區別。
/***********************************************************/
/***********************************************************/
1.javacc語法規則書寫操作基本代碼
>如何在生產表達式中寫條件結構
(
term = < TERM >
|
term = < STAR >
|
term = < PREFIXTERM >
|
term = < WILDTERM >
|
term = < NUMBER >
)
>生成后的代碼對應到
分析:
因為在javacc中(<A> | <B>)被表示成了在兩個規則中任意選擇一個的意思。
switch(tokenkind){
case TERM:
/** doSomthing...*/
break;
case STAR:
/** doSomthing...*/
break;
...
}
void ifExp():{int k=0;/** java局部變量聲明處*/}{
{/*在{}中寫任意的java代碼並且是以面向對象的方式*/}
(
{
if(k==2){break;}
System.out.println("k="+k);
k++;
}
)*
}
>如何在生產表達式中寫循環結構
->(
{
if(k==2){break;}
System.out.println("k="+k);
k++;
}
->)*
>生成對應的代碼
while(true){
if(k==2){
break;
}
System.out.println("k="+k);
k++;
}
>在生產表達式中調用其他的生產表達式
對應到java中的方法調用
void B():
{
StringBuffer sb = new StringBuffer();
Token t = null;
}{
t = A /** 這里對應生成的java代碼t=jj_consume_token(A);
* 因為等於就是要消費一個記號把消費的這記號的應用給t變量
*/
}
void A():{}{}
>javacc中的詞法狀態介紹
<*>TOKEN:{}
<DEFAULT>TOKEN:{<A:"2001 TO 2002">:DateRange
|
<B:"anything">
}
<DateRange>TOKEN:{}
解析:<*>,<DEFAULT>,<DateRange>是詞法狀態
定義詞法狀態的好處是,如果在生產表達式中
正好有一個字符竄匹配到了2001 TO 2002這個字符竄
那么他就會馬上把下一個要匹配的模式字符竄定義
為DateRange這種詞法狀態中定義的Token
>有了循環有了條件結構有了定義變量的方式
javacc語法文件就相當完美了。
/***********************************************************/
/***********************************************************/
2.JJT語法規則描述文件書寫方法
>jjt概要
jjt能夠很清晰的表達出語法分析的思路,
並且他把每一個生產表達式都作為一個節點來表示,
將這些節點的執行順序有效的組織成了一顆語法分析樹
>.options
>BUILD_NODE_FILES (default: true)
為SimpleNode以及語法中使用的其它節點創建樣本實現。
>MULTI(default: false)
創建多模式解析樹。
此選項默認為False,生成一個單一模式解析樹。
>NODE_DEFAULT_VOID (default: false)
此選項設置為True時,
不在使每個非包裝產生式定義一個節點,取而代之為空。
>NODE_FACTORY (default: false)
用下面的方式使用一個工廠方法創建一個節點:
public static Node jjtCreate(int id)
>NODE_PACKAGE (default: "")
被放進生成節點類里的包。默認為解析器的包。
>NODE_PREFIX (default: "AST") AST意思為抽象節點
在多模式中,前綴用來從節點標志符構造節點類名字。
默認前綴為” AST”
>NODE_SCOPE_HOOK (default: false)
在節點作用域入口和出口處插入調用用戶自定義的解析方法。
在節點的生命開始和結束之前需要調用的方法被成為鈎子。
參見:節點作用域鈎子。
>NODE_USES_PARSER (default: false)
是否將當前解析器對象也出入到節點對象的屬性中
JJTree會使用一個選擇的形式將解析對象傳給構造函數。例如:
public static Node MyNode.jjtCreate(MyParser p, int id);
MyNode(MyParser p, int id);
>STATIC (default: true)
為靜態解析器生成代碼。
選項默認為True。
這必須一致的通過等效的JavaCC選項被使用。
選項的值發布於JavaCC的源碼中。
>VISITOR (default: false)
在節點類中插入jjtAccept()方法,
為語法中使用的每個節點類型產生一個訪問者實現。
>VISITOR_EXCEPTION (default: "")
如果這個選項被設置,它將使用jjtAccept()和visit()方法的形式。
注意:這個選項將會在以后的某個JJTree版本中刪除。如果不影響你請不要使用它。
>JJTREE_OUTPUT_DIRECTORY (default: use value of OUTPUT_DIRECTORY)
默認情況下,在全局OUTPUT_DIRECTORY設置中指定JJTree生成的輸出目錄。
明確的設置這個選項允許用戶從樹文件中分離解析器。
/***********************************************************/
/***********************************************************/
3.語法分析樹節點(Node)
>javacc把每一個生產表達式都看作為一個簡單節點(SimpleNode)在默認
MULTI(default: false)的情況下,javacc已經提供了這個SimpleNode類的簡單實現。
>MULTI(default: true)將為每一個節點都按照[NODE_PREFIX_生產表達式的名字]這樣
一種形式來提供默認的簡單實現類。
>定義節點的方式
明確定義:一個以指定子節點數創建的節點
void AdditiveExpression() #void : {}
{
(
MultiplicativeExpression()
(
( "+" | "-" )MultiplicativeExpression()
)*
)#Add(3)
}
>語法分析樹參考結果:
Add
Integer
Integer
Integer
>#void不會為這個生產表達式生成對應的節點
>#Add表示在循環執行過程中生成的所有節點中的前三個節點作為
以Add為命名的兒子節點。
按照條件定義:
void AdditiveExpression() #void : {}
{
(
MultiplicativeExpression()
(
( "+" | "-" ) MultiplicativeExpression()
)*
) #Add(>3)
}
>#Add(>1)另外一種寫法#Add(jjtree.arity() > 1)
生成的源碼:
jjtree.closeNodeScope(jjtn001, jjtree.nodeArity() > 1);
在執行循環過程中創建的節點個數大於1的話就會將所有的節點
做為Add的子節點然后將Add節點添加到堆棧中,如果條件不滿足
那么創建的所有節點將會保留在棧中默認會在做為上一個節點的
子節點
>語法分析樹參考結果:
Start /** 因為在執行循環過程中創建的節點數沒有達到指定的個數所以是start*/
Integer
Integer
>notes:
( ... ) #N ( a() )
上面表達式邏輯不清,
你必須明確的使用條件式:
( ... ) #N(true) ( a() )
>為產生式的節點指定名稱
void ProductExp() #MyNode:{} {//doAnything}
#MyNode為這個生產表達式所對應的節點的名稱
>特殊應用
void P3():{}
{
P4() ( P5() )+ #ListOfP5s P6()
}
#name=jjtree.closeNodeScope(jjtn001, true);
#name(3)=jjtree.closeNodeScope(jjtn001, 3);
#name(>3)=jjtree.closeNodeScope(jjtn001, jjtree.nodeArity() > 1);
>什么是jjThis
ASTStart Start() : {}
{
Expression() ";"
{ return jjtThis; }
}
分析:
jjThis代表了當前節點的引用地址,
就好像ASTStart Start()這個生產表達式對應到Start這個
節點那么jjtThis就指向了這個對象,
在其內部的實現代碼為:
ASTStart jjtn000 = new ASTStart(JJTSTART);
>什么是NODE_SCOPE_HOOK="true"(默認為true表示有鈎子)
這個名氣叫的很有味,鈎子,正好是在某個節點被創建的
時候調用指定的鈎子方法,在節點添加到棧的時候要調用的方法
鈎子在open的時候可以做一些預備工作,結束可以做一些后續工作
示例代碼:
static final public void AdditiveExpression() throws ParseException {
ASTAdd jjtn001 = new ASTAdd(JJTADD);// 創建節點
boolean jjtc001 = true;
jjtree.openNodeScope(jjtn001); // 這就是鈎子
try {
//do something...
} finally {
if (jjtc001) {
jjtree.closeNodeScope(jjtn001, 3);// 這就是鈎子
}
}
}
/***********************************************************/
4.jjt文件生成的文件解析
SimpleQueryParser.jjt
SimpleQueryParser.jj
JJTSimpleQueryParserState.java
Node.java
ParseException.java
SimpleCharStream.java
SimpleNode.java
SimpleQueryParser.java
SimpleQueryParserConstants.java
SimpleQueryParserTokenManager.java
SimpleQueryParserTreeConstants.java
Token.java
TokenMgrError.java
>jjt文件可以生成.jj文件
>SimpleQueryParser這個名字在很多類名中都以重復的字眼出現,這是通過
Parser_Begin(SimpleQueryParser)...Parser_End(SimpleQueryParser)
指定的。
>JJTSimpleQueryParserState.java用於記錄節點的組織情況
這個類有單獨的說明,在JJTSimpleQueryParserState.java
中,他是一個以堆棧形式設計的類。
>Node.java是一個接口jjt中節點必須實現他,在選項MULTI(default: false)
的時候javacc編譯器已經幫我們簡單的做了實現這個類為SimpleNode.java。
>ParseException.java在語法分析的時候遇到不符合規則的字符時拋出的異常
>SimpleQueryParserConstants.java這個類主要用於關聯定義的Token
>SimpleQueryParserTreeConstants.java主要用於關聯定義的節點
如果沒有給生產表達式命名那么他默認就是用生產表達式的名字做為節點的
名字
>SimpleQueryParserTokenManager.java這是最重要的類他用於生產Token對象
在getNextToken()中體現了這么一點
>Token.java這個對象作為最小的記號單元用於封裝SimpleQueryParserTokenManager
生成的記號
>SimpleQueryParser.java將類綜合起來提供語法分析服務,他是消費Token對象的,
在jj_consume_Token(token_kind)中可以體現這一點,傳入一個記號的類型返回這個
類型的一個token對象
>TokenMgrError.java在生產Token對象的過程中遇到錯誤
>SimpleCharStream.java用於封裝輸入的字符竄
>SimpleQueryParserTokenManager.java/**用於產生Token*/
SimpleQueryParserTokenManager.getNextToken()/** 生產Token的方法*/
/** 消費Token的SimpleQueryParser利用這些Token生成特定的邏輯對象*/
SimpleQueryParser.java
SimpleQueryParser.jj_consume_token(int kind);/** 根據指定的類型消費一個token*/
SimpleQueryParser.jj_ntk()/** 如果當前jj_ntk變量為-1
說明當前token對像的下一個token沒有找到需要接着要到tokenmanage里面
拿一個token出來作為當前token的next*/
/***********************************************************/
/***********************************************************/
5.jj文件寫法解析
>options
>STATIC=false;
意思所有的生產表達式對應到java代碼的時候
不是靜態的
/***********************************************************/
轉載:http://blog.csdn.net/zyb243380456/article/details/7240225