【问题标题】:Simple recursive descent in PyParsingPyParsing 中的简单递归下降
【发布时间】:2018-10-08 20:16:56
【问题描述】:

我尝试将 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))...)的简单递归,我缺少什么?

【问题讨论】:

    标签: python pyparsing recursive-descent


    【解决方案1】:

    哇,我猜 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 的 pyparsing 存储库中有手动定义此方法的示例 (fourFn.py),或者使用 infixNotation 帮助程序 (simpleArith.py) 的更新方法。同样,这让 pyparsing 增加了比标记化更多的价值。

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

    -- 保罗

    【讨论】:

      【解决方案2】:

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

      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', ')']
      

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

      【讨论】:

        【解决方案3】:

        语法如下:

        expr :: expr op expr
        

        很难处理,因为递归只是不断地向左跳。

        一个普通的算术语法应该是这样的:

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

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

        【讨论】:

        【解决方案4】:

        使用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']]]]
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多