1

我有一个可以很好地满足我的目的的野牛解析器。它甚至打印本地化的错误消息。但是令牌名称没有被翻译。查看我发现的源代码,我可以将定义YY_用于我自己的 gettext 函数并传递YY_给 gettext 以提供我自己的错误消息翻译。但这不适用于令牌名称。

是否有一些开关或隐藏功能可用于从解析器中提取令牌名称并进行翻译?

到目前为止,我发现yytnamerr可以覆盖哪个来格式化令牌名称。因为它不仅仅是重新格式化名称,我不喜欢触摸这个功能,因为我必须将它与 Bison 的进度同步。另一方面,我还需要一种从解析器中提取标记名称的方法,以便将它们添加到语言定义文件中。

您如何使用 Bison 实现用户友好的错误报告?

4

1 回答 1

0

如果您指定%token-table,则 bison 将生成该yytname表。此表包括所有野牛符号,包括内部符号 ($end$error) $undefined、终结符(命名的单引号字符和双引号字符串)和非终结符,其中还包括为中间规则操作生成的名称。

使用yytname可见,可以很容易地以gettext包可识别的格式提取令牌。例如,您可以在.y文件中添加如下内容:

#ifdef MAKE_TOKEN
int main(void) {
   puts("#include <libintl.h>");
   puts("#include <stdio.h>");
   puts("int main() {");
   for (const char* const* p = yytname; *p; ++p) {
     // See Note 1 below
     printf("  printf(\"%%s: %%s\\n\", \"%s\", gettext (\"%s\"));\n", *p, *p);
   }
   puts("}");
 }
 #endif

然后在您的 Makefile 中添加一个节(对文件名进行适当的替换):

messages.pot: my_parser.c
    $(CC) $(CFLAGS) -DMAKE_TOKEN -o token_lister $<
    ./token_lister > my_parser.tokens.c
    # See Note 2 below
    $(CC) -o my_parser.tokens my_parser.tokens.c
    xgettext -o $@ my_parser.tokens.c

一旦你有了翻译,你仍然需要弄清楚如何使用它们,因为 bison 没有提供一个接口来将翻译的标记名称插入到它生成的错误消息中。可能最简单的方法是通过迭代该数组并用其翻译替换每个标记名称来直接插入yytname翻译(这必须在解析器启动时完成)。这呈现出野牛骨架yytname所宣称的烦恼;const但是,可以使用非常简单的sedorawk调用来删除有问题的const. [注3]

话虽如此,我并不清楚这些自动生成的错误消息是否“用户友好”,除非用户非常熟悉该语言的正式语法。并且熟悉语法的用户可能更喜欢原始标记名称,以便在语法中找到它,而不是巧合地类似于原始概念的非专家翻译。并不是说我特别指责任何人。

你可能会喜欢 Russ Cox 的这篇引人入胜的文章,关于他如何为 Go 实现真正友好的错误消息。


注意事项

  1. "在表示包含 a或 a的标记的情况下,在 C 字符串中直接使用标记名称将不起作用\。特别是,任何关键字标记 ( "and"or "<=") 都会失败,单字符标记'"''\\'. 这些在语法中并不经常出现。如果您在扫描仪中替换国际化关键字,则根本不可能使用 bison 的带引号的字符串功能。

    如果您确实想使用此类令牌,​​则必须为 gettext 生成器输出代码,该代码在令牌名称中转义"\字符。

  2. 实际上,使用几节会更好,但我认为那一节就足以让你继续前进。您可能希望将部分或全部中间结果标记为.INTERMEDIATE. 生成的可执行文件my_parser.tokens可用于验证翻译,但这完全是可选的,因此您可能希望删除该行。另一方面,它确实验证了字符串是可编译的。

  3. 有关示例,请参见 Russ Cox gc(上面提供的链接)。他的 Makefile 修改了 bison 输出以删除constfrom yytname,以便生成的解析器可以用他喜欢的标记名称替换错误消息,因此您可以看到工作中的一般想法。

于 2013-09-01T03:54:16.807 回答