我正在使用bison / flex在c ++中开发一个解析器,用于表达用户可以在gui中键入字段的表达式。我希望能够在用户输入时向用户提供有关允许令牌(基本上是自动完成)的反馈。 '%error-verbose'生成的信息就足够了,但它只能作为字符串使用。有没有办法在处理解析错误时能够以编程方式访问意外令牌和预期令牌列表?
答案 0 :(得分:4)
令牌本身位于变量yychar
中。那部分很容易。
查找可能性列表比较棘手。
从概念上讲,您可以将当前输入重新分析,但不包括错误的令牌;保存解析器状态;然后依次尝试每个其他可能的令牌,看看是否产生错误。
您需要重新分析的原因是LALR
解析器在遇到语法错误之前可能会执行错误的缩减。 (但它们从不执行错误的转换。)为了发现解析器状态的有效前瞻,这些约简必须撤消,并且没有这样做的机制。一般来说,减少会丢失信息,因此理论上甚至不可能。
如果启用LAC
(qv),为了获得精确错误,您需要执行此操作,error-verbose
解析器通过对每个令牌执行探索性解析(无还原操作)来避免缩减问题这可能会触发不正确的减少。如果此解析失败,则解析器状态可用于构造选项列表;如果成功,则通过减少操作重做。
不幸的是,bison没有提供“复制解析器状态”的API;你可以很容易地对一个人进行逆向工程,但那将非常脆弱。因此,如果您想在不访问生成的解析器内部的情况下尝试此操作,您实际上必须多次重新分析输入,每个可能的前瞻标记一次。
您可以使用规范的LR解析器,该解析器具有在任何缩减之前检测到错误的属性。完整的LR解析表可能是巨大的,但如果你的语法很简单,这可能不是问题。但是,您仍然没有干净的方法来保存解析器状态,因此除非您对其进行反向工程,否则您仍需要为每个成功的前瞻标记重新分析。 (或者它们足以构造有效的错误消息.Bison的详细错误设置最多只会输出五种可能性,并且有充分的理由。)
可能最简单的解决方案是解析bison错误消息,该消息具有简单的固定格式。如果你这样做,我会建议你的令牌名称简单易于解析,并在你的yyerror
处理程序中替换人类可读的文本。
启用LAC
肯定会降低解析速度。通常,所有精确的错误检测和报告修改都会降低解析器的速度,有时甚至会显着降低;这包括保留位置信息(虽然这对于调试输出也很有用,所以在实践中可能还是必要的。)
我总是给出的建议,因为它在实践中对我有用,就是构建两个解析器:一个针对无错误代码进行优化,并且不会尝试做任何事情而不是拒绝输入第一个错误,另一个(可能慢得多)可以处理错误检测和恢复的一个。然后将错误的输入解析两次,一次使用快速解析器,然后再使用慢速解析器;正确的输入只需要解析一次,只能使用快速解析器。这使得项目构建速度很快,并且通常不会减慢初始write-“compile”-edit循环的速度,只要快速解析器实际上很快。保持两个解析器同步可能很烦人,但大多数情况下,错误恢复解析器只需要一些额外的方法,这些方法可以转换为无操作,然后在快速解析器中进行优化。使用此策略,您可以使用快速解析器执行“合法前瞻”生成,并且可能会变得足够快。
一如既往,YMMV。祝你好运。
答案 1 :(得分:1)
我最终定制了bison用来让我访问我想要的信息的骨架。这是一个黑客行为,因为@rici在他的回答中说,野牛不会让公众访问我感兴趣的信息。我修改了error
函数以yytoken
和yystate
为额外参数,传递给yysyntax_error_
的相同变量。然后,我使用yysyntax_error_
生成的相同算法生成其“详细”消息,以生成预期令牌列表并将其传递回驱动程序。这很麻烦,但是对于我的简单语法,它实现了我想要的目标。