【问题标题】:Parsing nested function calls using pyparsing使用 pyparsing 解析嵌套函数调用
【发布时间】:2012-04-16 05:05:44
【问题描述】:

我正在尝试使用pyparsing 来解析表单中的函数调用:

f(x, y)

这很容易。但是由于它是一个递归下降解析器,它也应该很容易解析:

f(g(x), y)

这是我无法得到的。这是一个简化的示例:

from pyparsing import Forward, Word, alphas, alphanums, nums, ZeroOrMore, Literal

lparen = Literal("(")
rparen = Literal(")")

identifier = Word(alphas, alphanums + "_")
integer  = Word( nums )

functor = identifier

# allow expression to be used recursively
expression = Forward()

arg = identifier | integer | expression
args = arg + ZeroOrMore("," + arg)

expression << functor + lparen + args + rparen

print expression.parseString("f(x, y)")
print expression.parseString("f(g(x), y)")

这是输出:

['f', '(', 'x', ',', 'y', ')']
Traceback (most recent call last):
  File "tmp.py", line 14, in <module>
    print expression.parseString("f(g(x), y)")
  File "/usr/local/lib/python2.6/dist-packages/pyparsing-1.5.6-py2.6.egg/pyparsing.py", line 1032, in parseString
    raise exc
pyparsing.ParseException: Expected ")" (at char 3), (line:1, col:4)

为什么我的解析器将内部表达式的函子解释为独立标识符?

【问题讨论】:

    标签: python parsing pyparsing


    【解决方案1】:

    很好地发现identifier 在您对arg 的定义中掩盖了expression。以下是有关解析器的其他一些提示:

    x + ZeroOrMore(',' + x) 是 pyparsing 解析器中非常常见的模式,因此 pyparsing 包含一个辅助方法 delimitedList,它允许您将该表达式替换为 delimitedList(x)。实际上,delimitedList 做了另一件事 - 它基于分隔符在解析时有用的概念,但在尝试时只是杂乱的标记,它抑制了分隔逗号(或其他分隔符,如果使用可选的 delim 参数给出)之后筛选解析的数据。因此,您可以将 args 重写为 args = delimitedList(arg),您将只获得列表中的 args,无需“跳过”逗号。

    您可以使用Group 类在已解析的标记中创建实际结构。这将为您构建嵌套层次结构,而无需遍历此列表以查找 '(' 和 ')' 来告诉您何时在函数嵌套中下降了一个级别:

     arg = Group(expression) | identifier | integer
     expression << functor + Group(lparen + args + rparen)
    

    由于您的参数是Grouped,您可以进一步抑制括号,因为就像分隔逗号一样,它们在解析期间完成了它们的工作,但是通过对您的标记进行分组,它们不再是必需的:

    lparen = Literal("(").suppress()
    rparen = Literal(")").suppress()
    

    我假设 'h()' 是一个有效的函数调用,只是没有参数。您可以使用 Optional 允许 args 是可选的:

    expression << functor + Group(lparen + Optional(args) + rparen)
    

    现在你可以解析“f(g(x), y, h())”了。

    欢迎使用 pyparsing!

    【讨论】:

    • 感谢所有有用的 cmets!这个例子实际上是改编自 pyparsing 文档;我使用了您在我的实际解析器中描述的大部分技术。 (并且语言实现现在可以在大约 6 小时的工作中使用 --- 在 Python 中使用 pyparsing 进行原型设计非常快。)
    • Suppress("(")和Literal("(").suppress()有什么区别?
    • 没有任何区别。 expr.suppress() 返回 Suppress(expr),如果将字符串作为 Suppress 的初始值设定项传递,则该字符串将提升为 Literal。
    【解决方案2】:

    arg的定义应该与左边以另一个开头的项目排列,所以优先匹配:

    arg = expression | identifier | integer
    

    【讨论】:

      【解决方案3】:

      Paul's answer 帮了大忙。对于后人,同样可以用来定义for loops,如下(这里是简化的伪解析器,展示结构):

      from pyparsing import (
          Forward, Group, Keyword, Literal, OneOrMore)
      
      sep = Literal(';')
      if_ = Keyword('if')
      then_ = Keyword('then')
      elif_ = Keyword('elif')
      end_ = Keyword('end')
      
      if_block = Forward()
      do_block = Forward()
      
      stmt = other | if_block
      stmts = OneOrMore(stmt + sep)
      
      case = Group(guard + then_ + stmts)
      cases = case + OneOrMore(elif_ + case)
      
      if_block << if_ + cases + end_
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-19
        • 2012-03-13
        • 1970-01-01
        • 1970-01-01
        • 2022-11-17
        • 2014-07-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多