PlyYacc错误时如何获得语法生成?

问题描述

yacc.py文件中,我定义了语法输出以及错误,如下所示:

def p_error(p):
  if p:
    print("Error when trying to read the symbol '%s' (Token type: %s)" % (p.value,p.type))
  else:
    print("Syntax error at EOF")
  exit()

除了此错误消息外,我还想打印出错误时读取的生产内容,例如:

print("Error in production: ifstat -> IF LPAREN expression RPAREN statement elsestat")

我该怎么做?

解决方法

真的,不能。您尤其不能像Ply生成的那样使用自下而上的解析器,但是即使使用自上而下的解析器,“当时读取的产品”也不是一个很好定义的概念。

例如,考虑错误代码:

if (x < y return 42;

,其中的错误是缺少括号。至少,这就是我描述错误的方式。但是,圆括号不是唯一可以跟随0的东西。例如,正确的程序可能包含以下任何内容:

if (x < y) return 42;
if (x < y + 10) return 42;
if (x < y && give_up_early) return 42;

还有更多

那么,当解析器看到令牌return时,它试图完成哪种生产?显然,它仍在尝试完成expression(实际上可能具有不同表达式类型的层次结构,或者可能依赖于优先级声明是单个非终结符,或者是两者的某种组合。)但是并不能真正将错误识别为缺少右括号。

在自上而下的解析器中,有可能沿解析器堆栈向上移动以获取包含顺序中部分完成的产品的列表。 (至少,如果解析器维护自己的堆栈,这是可能的。如果它是递归下降式解析器,则检查堆栈会更加复杂。)

但是在自下而上的解析器中,解析器的状态更为复杂。自下而上的解析器比自上而下的解析器更灵活,这恰恰是因为它们实际上可以同时考虑多个产品 。因此,通常实际上并没有一个单一的部分生产。解析器将通过逐步消除所有不起作用的可能性来决定要查看的产品。

这种描述听起来像自下而上的解析器正在做很多工作,这是令人误解的。解析器生成器已经完成了该工作,该生成器会编译一个简单的状态转换表以指导解析。实际上,这意味着解析器知道如何在解析的每个时刻处理每个可能正确的令牌。因此,例如,当它在)之后看到if (x < y时,它立即知道必须完成expression的生成并继续执行if语句的其余部分。

Bison-yacc的C实现-具有可选功能,允许它在遇到错误时列出可能的正确标记。这并不像听起来那么简单,正确地实现它会在解析时间上产生明显的开销,但是有时它很有用。 (不过,这通常没什么用,因为可能的标记列表可能会很长。在我以错误为例的情况下,该列表将包括每个单独的算术运算符,以及可以开始的那些标记一个后缀运算符。当bison扩展错误处理程序到达第六个可能的标记时,它将停止尝试,这意味着如果分析在表达式中间,它将很少生成扩展错误消息。这样的功能。

Ply像野牛一样,确实通过error伪令牌实现错误恢复。错误恢复算法最适合于具有明确的重新同步点的语言,例如在带有确定语句终止符的语言中(不同于类似C的语言,在C语言中,许多语句不以;结尾)。但是您可以使用error产生式强制分析器将其堆栈弹出到某些包含产生式的输出,以产生更好的错误消息。根据我的经验,需要进行大量实验才能正确实施此策略。

简而言之,很难产生有意义的错误消息。我的建议是首先着重于让解析器处理正确的输入。