2012-2-7閱讀2279 評論0java
/***********************************************************/ >我使用的測試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代碼的時候 不是靜態的 /***********************************************************/