我正在尝试使用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)
为什么我的解析器将内部表达式的仿函数解释为独立标识符?
答案 0 :(得分:11)
很好地发现identifier
在expression
的定义中掩盖了arg
。以下是解析器的其他一些提示:
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
为你编写,你可以进一步压制parens,因为就像分隔逗号一样,它们在解析过程中完成它们的工作,但是通过对你的标记进行分组,它们不再是必需的:
lparen = Literal("(").suppress()
rparen = Literal(")").suppress()
我认为'h()'是一个有效的函数调用,只是没有args。您可以使用Optional
expression << functor + Group(lparen + Optional(args) + rparen)
现在你可以解析“f(g(x),y,h())”。
欢迎来到pyparsing!
答案 1 :(得分:4)
arg
的定义应该与左边的另一个项目一起排列,因此优先匹配:
arg = expression | identifier | integer
答案 2 :(得分:0)
保罗的帖子帮了很多忙。仅供其他人参考,同样可以用来定义for loops
如下(此处简化伪解析器,以显示结构):
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_