如果您指定%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
但是,可以使用非常简单的sed
orawk
调用来删除有问题的const
. [注3]
话虽如此,我并不清楚这些自动生成的错误消息是否“用户友好”,除非用户非常熟悉该语言的正式语法。并且熟悉语法的用户可能更喜欢原始标记名称,以便在语法中找到它,而不是巧合地类似于原始概念的非专家翻译。并不是说我特别指责任何人。
你可能会喜欢 Russ Cox 的这篇引人入胜的文章,关于他如何为 Go 实现真正友好的错误消息。
注意事项:
"
在表示包含 a或 a的标记的情况下,在 C 字符串中直接使用标记名称将不起作用\
。特别是,任何关键字标记 ( "and"
or "<="
) 都会失败,单字符标记'"'
和'\\'
. 这些在语法中并不经常出现。如果您在扫描仪中替换国际化关键字,则根本不可能使用 bison 的带引号的字符串功能。
如果您确实想使用此类令牌,则必须为 gettext 生成器输出代码,该代码在令牌名称中转义"
和\
字符。
实际上,使用几节会更好,但我认为那一节就足以让你继续前进。您可能希望将部分或全部中间结果标记为.INTERMEDIATE
. 生成的可执行文件my_parser.tokens
可用于验证翻译,但这完全是可选的,因此您可能希望删除该行。另一方面,它确实验证了字符串是可编译的。
有关示例,请参见 Russ Cox gc
(上面提供的链接)。他的 Makefile 修改了 bison 输出以删除const
from yytname
,以便生成的解析器可以用他喜欢的标记名称替换错误消息,因此您可以看到工作中的一般想法。