PyParsing 简单语言表达式

2 投票
2 回答
1351 浏览
提问于 2025-04-15 14:04

我正在尝试写一个可以解析代码的程序。我已经成功解析了 foo(spam)spam+eggs,但是 foo(spam+eggs) 这个就不行了(好像是递归下降?我对编译器的术语有点生疏)。

我有以下代码:

from pyparsing_py3 import *

myVal = Word(alphas+nums+'_')    
myFunction = myVal + '(' + delimitedList( myVal ) + ')'

myExpr = Forward()
mySubExpr = ( \
    myVal \
    | (Suppress('(') + Group(myExpr) + Suppress(')')) \
    | myFunction \
    )
myExpr << Group( mySubExpr + ZeroOrMore( oneOf('+ - / * =') + mySubExpr ) )


# SHOULD return: [blah, [foo, +, bar]]
# but actually returns: [blah]
print(myExpr.parseString('blah(foo+bar)'))

2 个回答

4

我发现一个好习惯,就是在使用'<<'这个操作符和Forward时,总是把右边的部分用括号括起来。也就是说:

myExpr << mySubExpr + ZeroOrMore( oneOf('+ - / * =') + mySubExpr )

这样写比:

myExpr << ( mySubExpr + ZeroOrMore( oneOf('+ - / * =') + mySubExpr ) )

要好。这是因为我不幸地选择了'<<'作为“插入”操作符,用来把表达式放进Forward里。虽然在这个特定的例子中,括号其实是多余的,但在下面这个例子里:

integer = Word(nums)
myExpr << mySubExpr + ZeroOrMore( oneOf('+ - / * =') + mySubExpr ) | integer

我们就能明白我为什么说这是个不幸的选择。如果我把它简化成“A << B | C”,我们很容易看到操作的优先级导致计算是按照“(A << B) | C”来进行的,因为'<<'的优先级比'|'高。结果是,Forward A 只会把表达式 B 插入进去。而“| C”部分虽然会执行,但结果是“A | C”,这会创建一个 MatchFirst 对象,但因为没有把它赋值给任何变量,所以这个对象会被立刻丢弃。解决这个问题的方法是把语句用括号括起来,变成“A << (B | C)”。在只使用'+'操作的表达式中,实际上不需要括号,因为'+'的优先级比'<<'高。但这只是运气好,后面如果有人添加了一个使用'|'的替代表达式而没有意识到优先级的问题,就会造成麻烦。所以我建议大家都采用“A << (表达式)”这种写法,以避免混淆。

(总有一天我会写出pyparsing 2.0——这将让我打破与现有代码的兼容性——并把这个改成使用'<<='操作符,这样就解决了所有优先级的问题,因为'<<='的优先级比pyparsing使用的其他操作符都低。)

4

这里有几个问题:delimitedList 需要的是一个用逗号分隔的 myVal 列表,也就是标识符,所以它当然无法匹配 'foo+bar'(这不是一个用逗号分隔的 myVal 列表!)。解决了这个问题后,又发现了另一个问题——myVal 和 myFunction 的开头是一样的,所以它们在 mySubExpr 中的顺序很重要。再解决这个问题,又发现了一个新问题——这里有两个层级的嵌套,而不是一个。这个版本看起来还不错……:

myVal = Word(alphas+nums+'_')    

myExpr = Forward()
mySubExpr = (
    (Suppress('(') + Group(myExpr) + Suppress(')'))
    | myVal + Suppress('(') + Group(delimitedList(myExpr)) + Suppress(')')
    | myVal
    )
myExpr << mySubExpr + ZeroOrMore( oneOf('+ - / * =') + mySubExpr ) 

print(myExpr.parseString('blah(foo+bar)'))

它输出了 ['blah', ['foo', '+', 'bar']],这是我们想要的结果。我还去掉了一些多余的反斜杠,因为在括号内逻辑行的延续本来就会发生;这些反斜杠并没有什么实际影响,但确实影响了可读性。

撰写回答