2011-05-17 56 views
4

我正在使用CUP和JFlex來驗證表達式語法。我有基本的功能工作:我可以告訴表達式是否有效。使用Java CUP解析樹生成

下一步是執行簡單的算術運算,如「加1」。例如,如果我的表達式是「1 + a」,結果應該是「2 + a」。我需要訪問解析樹來做到這一點,因爲只需標識數字項就不會這樣做:將「(1 + a)* b」加1的結果應該是「(1 + a)* b + 1」 ,而不是「(2 + a)* b」。

有沒有人有一個生成分析樹的CUP示例?我想我可以從那裏拿走它。

作爲額外的好處,是否有一種方法可以使用JFlex獲取表達式中所有令牌的列表?看起來像一個典型的用例,但我無法弄清楚如何去做。

編輯:實測值上堆棧溢出一個有前途的線索:CUP的 Create abstract tree problem from parser

討論和AST:

http://pages.cs.wisc.edu/~fischer/cs536.s08/lectures/Lecture16.4up.pdf

具體而言,本段:

象徵解析器返回的內容與語法的開始0相關聯符號幷包含整個源程序的AST

這沒有幫助。如何遍歷給定的樹Symbol實例,如果Symbol類沒有任何導航指針給它的子元素?換句話說,它看起來或行爲不像樹節點:

package java_cup.runtime; 
/** 
* Defines the Symbol class, which is used to represent all terminals 
* and nonterminals while parsing. The lexer should pass CUP Symbols 
* and CUP returns a Symbol. 
* 
* @version last updated: 7/3/96 
* @author Frank Flannery 
*/ 

/* **************************************************************** 
    Class Symbol 
    what the parser expects to receive from the lexer. 
    the token is identified as follows: 
    sym: the symbol type 
    parse_state: the parse state. 
    value: is the lexical value of type Object 
    left : is the left position in the original input file 
    right: is the right position in the original input file 
******************************************************************/ 

public class Symbol { 

/******************************* 
    Constructor for l,r values 
*******************************/ 

    public Symbol(int id, int l, int r, Object o) { 
    this(id); 
    left = l; 
    right = r; 
    value = o; 
    } 

/******************************* 
    Constructor for no l,r values 
********************************/ 

    public Symbol(int id, Object o) { 
    this(id, -1, -1, o); 
    } 

/***************************** 
    Constructor for no value 
    ***************************/ 

    public Symbol(int id, int l, int r) { 
    this(id, l, r, null); 
    } 

/*********************************** 
    Constructor for no value or l,r 
***********************************/ 

    public Symbol(int sym_num) { 
    this(sym_num, -1); 
    left = -1; 
    right = -1; 
    value = null; 
    } 

/*********************************** 
    Constructor to give a start state 
***********************************/ 
    Symbol(int sym_num, int state) 
    { 
     sym = sym_num; 
     parse_state = state; 
    } 

/*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ 

    /** The symbol number of the terminal or non terminal being represented */ 
    public int sym; 

    /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ 

    /** The parse state to be recorded on the parse stack with this symbol. 
    * This field is for the convenience of the parser and shouldn't be 
    * modified except by the parser. 
    */ 
    public int parse_state; 
    /** This allows us to catch some errors caused by scanners recycling 
    * symbols. For the use of the parser only. [CSA, 23-Jul-1999] */ 
    boolean used_by_parser = false; 

/******************************* 
    The data passed to parser 
*******************************/ 

    public int left, right; 
    public Object value; 

    /***************************** 
    Printing this token out. (Override for pretty-print). 
    ****************************/ 
    public String toString() { return "#"+sym; } 
} 

回答

6

好吧,我明白了。但不幸的是,我無法按照原樣發佈我的所有代碼。無論如何,我會嘗試提出解決方案,如果有問題不明確,請提問。

JFlex使用它自己的Symbol類。看看這裏: JFlex.jar/java_cup.runtime/Symbol.class

你會看到增加了幾個構造函數:

public Symbol(int id, Symbol left, Symbol right, Object o){ 
    this(id,left.left,right.right,o); 
} 
public Symbol(int id, Symbol left, Symbol right){ 
    this(id,left.left,right.right); 
} 

這裏的關鍵是Object o,這是符號的值。

定義您自己的類來表示AST樹節點,另一個表示詞法分析器標記。當然,你可以使用同一個類,但是我發現使用不同的類來區分這兩個類更加明顯。 JFlex和CUP都將生成Java代碼,並且很容易讓您的令牌和節點混淆。

然後,在你parser.flex,在詞法規則部分,要爲每個令牌做這樣的事情:

{float_lit}  { return symbol(sym.NUMBER, createToken(yytext(), yycolumn)); } 

這樣做對所有的令牌。您的createToken可能是這樣的:

%{ 
    private LexerToken createToken(String val, int start) { 
     LexerToken tk = new LexerToken(val, start); 
     addToken(tk); 
     return tk; 
    } 
}% 

現在讓我們繼續閱讀parser.cup。聲明所有終端類型爲LexerToken,並且所有非終端類型爲Node。你想閱讀CUP手冊,但爲了快速複習,終端將被詞法分析器識別(例如數字,變量,操作符),非終端將成爲語法的一部分(例如表達式,因素,術語...... )。

最後,這一切都彙集在語法定義中。請看下面的例子:

factor ::= factor:f TIMES:times term:t 
       {: RESULT = new Node(times.val, f, t, times.start); :} 
       | 
       factor:f DIVIDE:div term:t 
       {: RESULT = new Node(div.val, f, t, div.start); :} 
       | 
       term:t 
       {: RESULT = t; :} 
       ; 

語法factor:f意味着你的別名係數的值是f,你可以參考它在下面的部分{: ... :}。請記住,我們的終端的值爲LexerToken,而我們的非終端的值爲Node s。

你在表達內可能有如下定義:

term ::= LPAREN expr:e RPAREN 
     {: RESULT = new Node(e.val, e.start); :} 
     | 
     NUMBER:n 
     {: RESULT = new Node(n.val, n.start); :} 
     ; 

當您成功生成解析器代碼,你會在你的parser.java看到建立節點之間的父子關係的一部分:

case 16: // term ::= UFUN LPAREN expr RPAREN 
    { 
     Node RESULT =null; 
    int ufleft = ((java_cup.runtime.Symbol)CUP$parser$stack.elementAt(CUP$parser$top-3)).left; 
    int ufright = ((java_cup.runtime.Symbol)CUP$parser$stack.elementAt(CUP$parser$top-3)).right; 
    LexerToken uf = (LexerToken)((java_cup.runtime.Symbol) CUP$parser$stack.elementAt(CUP$parser$top-3)).value; 
    int eleft = ((java_cup.runtime.Symbol)CUP$parser$stack.elementAt(CUP$parser$top-1)).left; 
    int eright = ((java_cup.runtime.Symbol)CUP$parser$stack.elementAt(CUP$parser$top-1)).right; 
    Node e = (Node)((java_cup.runtime.Symbol) CUP$parser$stack.elementAt(CUP$parser$top-1)).value; 
    RESULT = new Node(uf.val, e, null, uf.start); 
     CUP$parser$result = parser.getSymbolFactory().newSymbol("term",0, ((java_cup.runtime.Symbol)CUP$parser$stack.elementAt(CUP$parser$top-3)), ((java_cup.runtime.Symbol)CUP$parser$stack.peek()), RESULT); 
    } 
    return CUP$parser$result; 

我很抱歉,我不能發佈完整的代碼示例,但希望這將節省別人的試驗和錯誤幾個小時。沒有完整的代碼也很好,因爲它不會使所有這些CS作業任務無用。

作爲生活的證明,這裏是我的示例AST的漂亮打印。

輸入表達式:

T21 + 1A/log(max(F1004036, min(a1, a2))) * MIN(1B, 434) -LOG(xyz) - -3.5+10 -.1 + .3 * (1) 

得到的AST:

|--[+] 
    |--[-] 
    | |--[+] 
    | | |--[-] 
    | | | |--[-] 
    | | | | |--[+] 
    | | | | | |--[T21] 
    | | | | | |--[*] 
    | | | | |  |--[/] 
    | | | | |  | |--[1A] 
    | | | | |  | |--[LOG] 
    | | | | |  |  |--[MAX] 
    | | | | |  |  |--[F1004036] 
    | | | | |  |  |--[MIN] 
    | | | | |  |   |--[A1] 
    | | | | |  |   |--[A2] 
    | | | | |  |--[MIN] 
    | | | | |  |--[1B] 
    | | | | |  |--[434] 
    | | | | |--[LOG] 
    | | | |  |--[XYZ] 
    | | | |--[-] 
    | | |  |--[3.5] 
    | | |--[10] 
    | |--[.1] 
    |--[*] 
     |--[.3] 
     |--[1]