如果您指定%token-table
,那麼野牛會生成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 [email protected] my_parser.tokens.c
一旦你的翻譯,您仍然需要弄清楚如何使用它們,因爲bison不提供將翻譯的標記名插入到其生成的錯誤消息中的接口。可能最簡單的方法是直接將翻譯插入到yytname
中,方法是遍歷該數組,並使用其翻譯替換每個標記名(這必須在解析器啓動時完成)。這令人討厭yytname
被野牛骨架宣佈爲const
;但是,可以使用非常簡單的sed
或awk
調用來刪除違規的const
。 [注3]
話雖如此,我不清楚這些自動生成的錯誤消息是「用戶友好的」,除非用戶對語言的形式語法非常熟悉。而熟悉語法的用戶可能會更喜歡原始的記號名稱,以便在語法中找到它,而不是非專業翻譯,它們只是巧合地類似於原始概念。並不是說我特別指責任何人。
您可能會喜歡Russ Cox的fascinating essay,瞭解他如何爲Go實現實際友好的錯誤消息。
注意:
在C柱直接用標記名稱將不會在令牌,其表示包括"
或\
的情況下工作。特別是,任何關鍵字標記("and"
或"<="
)都會失敗,單個字符標記'"'
和'\\'
也會失敗。這些在語法中不經常出現;如果您在掃描儀中替換國際化關鍵字,則根本不可能使用野牛的引用字符串功能。
如果您確實想要使用這些標記,您必須輸出gettext生成器的代碼,以便在標記名稱中跳過"
和\
個字符。
實際上,最好是使用幾段,但是我認爲這樣做足以讓你繼續前進。您可能需要將部分或全部中間結果標記爲.INTERMEDIATE
。生成的可執行文件my_parser.tokens
可用於驗證翻譯,但這是完全可選的,因此您可能需要刪除該行。另一方面,它確實驗證了字符串是可編譯的。
例如,參見Russ Cox的gc
(上面提供的鏈接)。他的Makefile會修改野牛輸出以從yytname
中刪除const
,以便生成的解析器可以用他的首選標記名替換錯誤消息,以便您可以看到工作中的一般想法。
感謝您的回答。不幸的是'%token-table'還不夠,因爲我無法自動提取字符串進行翻譯。我已經使用不同的人類可讀令牌名稱爲不同的人類語言提供了人類可讀的語法描述。我想用這些人類可讀的標記名輸出錯誤消息。此外,每個保留字至少存在兩種口味:英語和德語。到目前爲止,這是由掃描儀管理的。所以我不能無條件地只使用一種語言的關鍵字。 – Keinstein
@ keinstein:如果它們僅存在於掃描程序中,那麼您將無法在yytname中找到關鍵字,並且據我所知,yytnamerr的唯一目的是使yytname中的名稱可呈現給最終用戶,因此將它們翻譯將會很好(儘管你當然必須保留並跟蹤API)。 – rici
關鍵字除了到目前爲止還沒有翻譯過的問題之外都是可以呈現的。還有一個問題:gettext不知道'yytname'中的關鍵字。所以它不能提取它們。我如何讓它們自動抽取? – Keinstein