【问题标题】:How can I create an abstract syntax tree considering '|'? (Ply / Yacc)考虑到“|”,如何创建抽象语法树? (Ply / Yacc)
【发布时间】:2016-08-04 13:22:50
【问题描述】:

考虑以下语法:

expr : expr '+' term | expr '-' term | term
term : term '*' factor | term '/' factor | factor
factor : '(' expr ')' | identifier | number

这是我使用 ply 的代码:

from ply import lex, yacc

tokens = [
    "identifier",
    "number",
    "plus",
    "minus",
    "mult",
    "div"
]

t_ignore = r" \t"
t_identifier = r"^[a-zA-Z]+$"
t_number = r"[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?"
t_plus = r"\+"
t_minus = r"-"
t_mult = r"\*"
t_div = r"/"

def p_stmt(p):
    """stmt : expr"""
    p[0] = ("stmt", p[1])

def p_expr(p):
    """expr : expr plus term 
            | expr minus term 
            | term"""
    p[0] = ("expr", p[1], p[2]) # Problem here <<<

def p_term(p):
    """term : term mult factor 
            | term div factor 
            | factor"""

def p_factor(p):
    """factor : '(' expr ')' 
              | identifier 
              | number"""


if __name__ == "__main__":
    lex.lex()
    yacc.yacc()
    data = "32 + 10"
    result = yacc.parse(data)
    print(result)

如果我无法访问运算符,我应该如何使用表达式构建 AST?我可以将 p_expr_plus 之类的函数分开,但在这种情况下,我会消除运算符优先级。 docs 没有太大帮助,因为我是初学者,无法解决这个问题。我在is this这个主题上找到的最好的材料,但它没有考虑运算符优先级的复杂性。

编辑:我无法访问 p2 或 p[3],因为我得到一个 IndexError(它仅与术语匹配)。在我链接的 PDF 中,他们明确地将运算符放在元组中,例如:('+', p1, p2),因此,考虑到优先级证明了我的问题(我无法将函数,表达式就是表达式,应该有办法考虑管道和访问任何运算符)。

【问题讨论】:

  • 我不明白为什么你会因为优先级而感到“无法分离功能”。优先级没有问题。你不使用优先级,真的;语法是明确的,运算符优先级是语法中固有的。在两个不同的动作函数之间划分一个非终结符不会改变语法,并且会产生更简单的动作。

标签: python abstract-syntax-tree yacc ply


【解决方案1】:

据我所知,在p[0] = ("expr", p[1], p[2]) 中,p[1] 是左手表达式,p[2] 是运算符,p[3](你没有使用)是右手术语。

只需使用p[2] 来确定运算符,添加p[3],因为你会需要它,你应该很好。

此外,您必须验证 p 有多少项,因为如果最后一条规则 | term""" 匹配,p 将只有两项而不是四项。

看看来自GardenSnake example:的sn-p

def p_comparison(p):
    """comparison : comparison PLUS comparison
                  | comparison MINUS comparison
                  | comparison MULT comparison
                  | comparison DIV comparison
                  | comparison LT comparison
                  | comparison EQ comparison
                  | comparison GT comparison
                  | PLUS comparison
                  | MINUS comparison
                  | power"""
    if len(p) == 4:
        p[0] = binary_ops[p[2]]((p[1], p[3]))
    elif len(p) == 3:
        p[0] = unary_ops[p[1]](p[2])
    else:
        p[0] = p[1]

【讨论】:

  • 问题是当我使用 p[3] 时,我得到一个列表索引超出范围。不考虑运营商。在我链接的 PDF 中,他们明确地“保存”了运算符:('+', p[1], p[2])。这样做的问题是它可以是任何运算符,我需要考虑优先级。
  • 哦,对了。一定是因为最后一行,| term""":当这最后一条规则匹配时,p 将只有两项而不是四项。
  • 这很奇怪,因为 "32 + 10" 应该匹配 "expr plus term",因为 expr 和 term 最终都是一个数字。
  • 最初,是的,但请注意,该规则是递归的,因此 expr 将再次匹配“32”,它匹配最后一条规则。我建议在函数的开头放置断点或日志,以便您更好地理解我的意思。
  • 哦,我现在看到了你的例子......我看到了递归的结束(很明显,不知何故我忘记了那里的基本原理)。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多