8

我目前正在尝试使用 Flex+Bison 编写一个小型编译器,但在如何处理错误方面我有点迷茫,特别是如何使所有内容组合在一起。为了激发讨论,请考虑以下我用于字符串文字的词法分析器片段:

["]          { BEGIN(STRING_LITERAL); init_string_buffer(); }
<STRING_LITERAL>{
    \\\\    { add_char_to_buffer('\\'); }
    \\\"    { add_char_to_buffer('\"'); }
    \\.     { /*Invalid escape. How do I treat this error?*/ }
    ["]     { BEGIN(INITIAL); yylval = get_string_buffer(); return TK_STRING; }
}

如何处理无效转义的情况?现在我只是打印一条错误消息并调用exit,但如果可能的话,我希望能够继续并检测每个文件多个错误。

我的问题:

  • 我用什么函数来打印错误信息?野牛稍后会出现同样的 yyerror 吗?如果词法分析器和解析器有单独的文件,我应该把 yyerror 的定义放在哪里?
  • 我应该从我的操作中返回什么令牌代码?0 表示“文件结尾”?一些特殊的 TK_INVALID_STRING 令牌?
  • 如何确保解析器在出现词汇错误(无效的文字、杂散的标点符号等)后可以继续解析?
4

3 回答 3

10

有很多选择。哪个最好可能是见仁见智的问题。(请注意,SO 不会接受答案是意见而不是事实的问题。)

这在很大程度上取决于您通常如何处理应用程序中的错误消息。但这里有两种可能性:

  1. 直接从词法分析器打印错误消息。告诉错误检测系统编译不成功:您可能使用全局错误计数(yuk,globals!),或yylex作为附加参数传递给的共享数据结构。然后忽略该字符并继续进行词法分析。

  2. 返回类似的东西TK_INVALID_STRING给解析器。解析器需要有适当error的产生式才能适当地处理这个错误并从中恢复,这需要更多的工作,但具有将所有错误处理都放入解析器的优点。但是,在字符串的特殊情况下,您可能希望完成字符串的词法分析,直到结束引号为止;否则,继续解析将是徒劳的。

至于yyerror:没有什么神奇的yyerror。该功能完全由您负责。bison 唯一能做的就是用一组指定的参数调用它。如果您发现它对记录词法分析器中注意到的错误很有用(我认为它可能是),那么继续使用它。您完全负责声明,因此将其定义放在您在词法分析器和解析器yyerror中的任何共享头文件中。#include或者摆弄野牛代码生成选项以获取包含在使用野牛创建的头文件中的定义。什么都容易。一旦你弄清楚了如何声明yyerror,你可以在任何你想要的地方定义它:在词法分析器文件中,在野牛文件中,或者(我的偏好)在一个单独的支持函数库中。

(FWIW,我已经尝试过选项 2,在我看来,这确实需要太多的工作;选项 1 对我来说效果很好。但是口味各不相同,还有 YMMV;我不会在这里为我的选择辩护,但我不不介意承认。)

于 2013-09-16T22:19:00.667 回答
3

如果您将 Bi​​son 与 C++ 输出一起使用,则另一种选择是抛出异常。

.   throw yy::parser::syntax_error("invalid character: " + std::string(yytext, yyleng);

如果您使用的是 Bison 3.6 或更高版本(使用所有目标语言,包括 C),那么您还可以返回YYerror 特殊标记。这类似于 rici 的建议return TK_INVALID_STRING,但随后解析器会抱怨这个未知数TK_INVALID_STRING(因此有两条错误消息:一条来自您对 yyerror 的调用,另一条来自 yyparse 关于未知的 TK_INVALID_STRING)。没有这样的东西YYerror,但您确实正确输入了错误恢复。

换句话说,我会建议在 C 中(如果你yyerror支持可变参数):

yyerror (yylloc, _("syntax error: invalid character: %c"), c);
return YYerror;

这是 Bison 发行版中“bistromathic”示例的摘录(可/usr/local/share/doc/bison/examples在典型发行版中或在SavannahGitHub 上获得)。

于 2013-09-19T07:35:13.350 回答
2

最简单的事情就是有一个最终规则

. return yytext[0];

这涵盖了所有单个特殊字符和所有非法字符。在你的语法中直接使用特殊字符,如“:”、“;”等。然后,如果你得到一个非法字符,解析器的错误处理就会被调用,这给了一些恢复的可能性。如果你在词法分析器中处理它们,你所能做的就是打印一个错误并忽略它们。

它还减少了 lex 文件的大小。

于 2013-09-16T23:49:52.613 回答