2011-05-17 57 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]