我正在尝试从pyparsing中理解Forward()
元素。假设我有这个简单的BNF:
identifier =
"a..z,$,_" < "a..z,$,_,0..9" >
package_name =
identifier
/ ( package_name "." identifier )
我尝试解析一个像java.lang.String
之类的简单包。我得到的结果只是java
或者永远不会从递归中返回。
我试过这样的话:
from pyparsing import alphas,alphanums, Word, Forward, ZeroOrMore, Group, Literal
identifier=Word(alphas+"$_",alphanums+"$_")
dot=Literal(".")
package_name = Forward()
definition = package_name+dot+identifier
package_name << Group(identifier+ZeroOrMore(definition))
package_name.parseString("java.lang.String")
将打印[['java']]
from pyparsing import alphas,alphanums, Word, Forward, ZeroOrMore, Group, Literal
identifier=Word(alphas+"$_",alphanums+"$_")
dot=Literal(".")
package_name = Forward()
definition = identifier^package_name+dot+identifier
package_name << definition
package_name.parseString("java.lang.String")
将达到递归限制
这个Forward
占位符是如何工作的?
答案 0 :(得分:13)
问题不在于Forward
,而在于你的语法,它本身要么过早限制,要么以一种像Pyparsing这样天真的递归下降解析器不可判断的方式递归。
你有这个:
package_name = identifier | (package_name "." identifier )
如果从左到右匹配,这将始终匹配单个标识符,然后停止,而不尝试匹配以下时间段。如果您将订单切换为与identifier
last:
package_name = (package_name "." identifier) | identifier
。 。 。然后它将无限递归,因为为了确定package_name
是否匹配,它要做的第一件事是决定package_name
是否匹配。这是一个left-recursive语法,像Pyparsing这样的简单递归下降解析器无法处理。 Pyparsing不会向前看,看看比赛将如何影响后续比赛。它只是从左到右尝试比赛。
您可以通过更改语法递归的方式,获得Forward
如何工作的简单示例:
identifier = pyp.Word(pyp.alphas+"$_", pyp.alphanums+"$_")
package_name = pyp.Forward()
package_name << ((identifier + '.' + package_name) | identifier)
>>> package_name.parseString("java.lang.String")
[u'java', u'.', u'lang', u'.', u'String'], {})
此处,递归发生在右侧,而不是左侧,因此Pyparsing可以逐渐匹配。
(你使用ZeroOrMore是一个红色的鲱鱼。如果你想要这样的递归语法,你不想使用ZeroOrMore,因为递归定义已经允许你的子表达式多次匹配。正如我在评论中所建议的那样,无论如何都可以更简单地定义这种语法而无需递归。)