12

我正在尝试使用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)

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

4

3 回答 3

14

很好地发现在您的定义中identifier掩盖了. 以下是解析器的其他一些技巧:expressionarg

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)

由于您的 args 正在Group为您编辑,因此您可以进一步抑制括号,因为就像分隔逗号一样,它们在解析过程中完成了它们的工作,但是通过对您的标记进行分组,它们不再是必需的:

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

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

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

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

欢迎来到 pyparsing!

于 2012-04-17T09:19:46.323 回答
4

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

arg = expression | identifier | integer
于 2012-04-16T05:28:11.097 回答
1

保罗的回答很有帮助。对于后人,同样可以用来定义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_
于 2013-07-24T22:06:32.597 回答