PyParsing中的简单递归下降

时间:2009-08-28 04:38:14

标签: python pyparsing recursive-descent

我已经尝试了this code并将其转换为我正在编写的用于编程语言处理的项目,但我遇到了一个简化版本的问题:

op = oneOf( '+ - / *')
lparen, rparen = Literal('('), Literal(')')

expr = Forward()
expr << ( Word(nums) | ( expr + op + expr ) | ( lparen + expr + rparen) )

我已经玩过这个简单设置的许多不同修改。通常,尝试类似:

print(expr.parseString('1+2'))

将返回['1']。虽然我被深深的递归所吸引,但是:

print(expr.parseString('(1+2)'))

关于简单递归而我无法解释的是我无法解析任意算术表达式,例如1+(2 * 3-(4*(5+6)-(7))...

4 个答案:

答案 0 :(得分:25)

哇,我猜pyparsing真的在地图上!感谢Alex和John踩到这个问题。你的回答都是你的标记。但是,让我添加一两条评论:

  1. 如果我们抑制左右括号符号,并使用Group对带括号的表达式进行分组,则pyparsing将是一个更接近AST的结构化结果。

    from pyparsing import Literal,Word,ZeroOrMore,Forward,nums,oneOf,Group
    
    def Syntax():
        op = oneOf('+ -')
        lpar  = Literal( '(' ).suppress()
        rpar  = Literal( ')' ).suppress()
        num = Word(nums)
        expr = Forward()
        atom = num | Group(lpar + expr + rpar)
        expr << atom + ZeroOrMore(op + atom)
        return expr
    
    if __name__ == "__main__":
        expr = Syntax()
        def test(s):
            results = expr.parseString(s)
            print s,'->', results
    
        test( "(9 + 3)" )
        test( "(9 + 3) * (4 / 5)" )
    

    ,并提供:

    (9 + 3) -> [['9', '+', '3']]
    (9 + 3) * (4 / 5) -> [['9', '+', '3'], '*', ['4', '/', '5']]
    

    否则,pyparsing只是标记化,你必须遍历解析的标记列表才能找到嵌套的表达式。

  2. 由于op被定义为oneOf(“+ - * /”),因此没有优先级的操作。在https://github.com/pyparsing/pyparsing/tree/master/examples手动方式定义此(4Fn.py)的pyparsing repo上有一些示例,或者使用infixNotation帮助器(simpleArith.py)的更新方法。同样,这使得pyparsing增加的价值不仅仅是标记化。

  3. 对于OP,请查看这些示例,我认为它们将帮助您推进项目。

    - 保罗

答案 1 :(得分:9)

这或多或少是你想要的......?

from pyparsing import Literal,Word,ZeroOrMore,Forward,nums,oneOf

def Syntax():
    op = oneOf( '+ - / *')
    lpar  = Literal( '(' )
    rpar  = Literal( ')' )
    num = Word(nums)

    expr = Forward()
    atom = num | ( lpar + expr + rpar )
    expr << atom + ZeroOrMore( op + expr )
    return expr


if __name__ == "__main__":

    expr = Syntax()

    def test(s):
        results = expr.parseString( s )
        print s,'->', results

    test( "(9 + 3)" )
    test( "(9 + 3) * (4 / 5)" )

发射

(9 + 3) -> ['(', '9', '+', '3', ')']
(9 + 3) * (4 / 5) -> ['(', '9', '+', '3', ')', '*', '(', '4', '/', '5', ')']

?这通过将“原子”(数字或带括号的表达式)与“表达式”(一个或多个“原子”与中间的运算符)分开来“锚定”递归。

答案 2 :(得分:4)

语法如:

expr :: expr op expr

很难处理,因为递归只是潜入左侧。

正常的算术语法看起来像:

expr :: mulxp | mulxp '+' expr
mulxp :: atom | atom '*' expr
atom :: Word(nums) | '(' + expr + ')'

基本上,你永远不会得到S :: S;任何时候非终结符号出现在语法的一行的左侧和右侧,中间必须有一些文字供解析器使用。

答案 3 :(得分:0)

使用operatorPrecedence构建表达式。它将构建正确的表达式,并在其处理运算符优先级:

num = Word(nums)
plusop = oneOf( '+ -')
multop = oneOf('/ *')
expr = operatorPrecedence(num,
                          [(multop, 2, opAssoc.LEFT),(plusop, 2, opAssoc.LEFT)])

示例:

>> print parsetime.expr.parseString("1+(2 * 3-(4*(5+6)-(7)))")
[['1', '+', [['2', '*', '3'], '-', [['4', '*', ['5', '+', '6']], '-', '7']]]]