【问题标题】:Pyparsing - grammar with recursionPyparsing - 递归语法
【发布时间】:2019-09-25 13:08:53
【问题描述】:

我正在尝试创建将解析以下表达式的语法:

  1. func()

  2. func(a)

  3. func(a) + func(b)

  4. func(func(a) + func()) + func(b)

我为 (1) 和 (2) 实现了它,但是一旦我将 rvalue << (identifier | function_call) 扩展为 operation,它就会停止工作,原因是:

Exception raised:Expected W:(ABCD...), found ')'  (at char 5), (line:1, col:6)
Exception raised:maximum recursion depth exceeded

谁能解释一下为什么?据我了解,表达式rvalue << (identifier | function_call | operation) function_call 应该在operation 之前匹配,并且不应该发生递归。

代码:

from pyparsing import Forward, Optional, Word, Literal, alphanums, delimitedList

rvalue = Forward()

operation = rvalue + Literal('+') + rvalue
identifier = Word(alphanums + '_')('identifier')
function_args = delimitedList(rvalue)('function_args')

function_name = identifier('function_name')
function_call = (
    (function_name + Literal("(") + Optional(function_args) + Literal(")"))
)('function_call')

rvalue << (identifier | function_call | operation)
function_call.setDebug()


def test_function_call_no_args():
    bdict = function_call.parseString("func()", parseAll=True).asDict()
    assert bdict['function_name'] == 'func'
    assert 'function_args' not in bdict


def test_function_call_one_arg():
    bdict = function_call.parseString("func(arg)", parseAll=True).asDict()
    assert bdict['function_name'] == 'func'
    assert 'function_args' in bdict


def test_function_call_many_args():
    bdict = function_call.parseString("func(arg1, arg2)", parseAll=True).asDict()
    assert bdict['function_name'] == 'func'
    assert 'function_args' in bdict

【问题讨论】:

  • func(a + b) 这样的东西也有效吗?
  • 为你的小解析器写一个 BNF 并寻找左递归。请注意,“|”表示“MatchFirst”,而不是“MatchLongest”,因此在函数之前解析标识符通常是一个问题(您将匹配标识符,然后您将处于“(”并且解析器将“嗯?”)。提示:这是左递归的:rvalue ::= rvalue '+' rvalue.
  • 这很困难,因为 function_callidentifier 开头,而 pyparsing 不做回溯,所以 pyparsing 无法区分 identifierfunction_call - 我们永远无法使用 identifier | function_callfunction_call | identifier 之类的替代,因为 pyparsing 甚至永远不会尝试解析右侧。
  • @Aran-Fey 是的,好地方,func(a + b) 也是有效的。

标签: python pyparsing


【解决方案1】:

据我所知,表达式rvalue &lt;&lt; (identifier | function_call | operation) function_call 应该在操作之前匹配并且不应该发生递归。

如果前面的备选方案之一成功,则不会发生递归。但是如果两者都失败了,operation 会被尝试并且你会得到无限递归。

例如,在test_function_call_no_args 中,您尝试使用function_call 规则解析func()。这会将func 解析为函数的名称,并将( 解析为参数列表的开头。然后它会尝试解析Optional(function_args),然后它会尝试解析delimitedList(rvalue)。现在这将尝试解析rvalue,并且由于) 与前两个选项不匹配,它将尝试最后一个选项,这将导致无限递归。

当规则是递归的时,您必须始终在达到递归之前消耗输入 - 必须在不消耗输入的情况下达到递归。因此,将递归放在最后是不够的 - 实际上必须在它之前成功调用另一个非可选规则(也与空字符串不匹配)。

PS:rvalue 实际上永远无法匹配函数调用,因为函数调用以标识符开头,而您首先匹配 identifier

【讨论】:

  • "如果前面的选择之一成功,则递归不会发生。但如果两者都失败,则尝试操作并获得无限递归。"如果没有operation 代码通过单元测试,这意味着替代方案成功,所以我想知道这行中operation 到底发生了什么变化。
  • @gbajson "没有操作代码通过单元测试,说明替代成功" 不,不是这个意思。当rvalue() 中匹配时,前两个备选方案将失败。如果没有递归规则调用,一切都会好起来的:两种选择都将失败,所以整个规则都失败了,因此解析器知道没有参数并且它继续前进。但是,一旦您将递归调用添加为第三种选择,它就不再正确地失败,而是进入无限递归,这就是事情中断的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多