From 594cae57ca63fc7b3f18dad3d6472e043c626df0 Mon Sep 17 00:00:00 2001 From: Adela Vais Date: Sat, 28 Nov 2020 21:09:39 +0200 Subject: [PATCH] d: add internationalisation support The D parser implements this feature similarly to the C parser, by using Gettext. Functions gettext() and dgettext() are imported using extern(C). The internationalisation uses yysymbol_name to report the name of the SymbolKinds. * data/skeletons/d.m4 (SymbolKind.toString.yytranslatable, SymbolKind.toString.yysymbol_name: New), data/skeletons/lalr1.d: Here. * doc/bison.texi: Document it. * tests/calc.at: Test it. --- data/skeletons/d.m4 | 22 ++++++++++++---- data/skeletons/lalr1.d | 59 ++++++++++++++++++++++++++++++++++------- doc/bison.texi | 60 ++++++++++++++++++++++++++++++++++++++++++ tests/calc.at | 19 ++++++++++++- 4 files changed, 145 insertions(+), 15 deletions(-) diff --git a/data/skeletons/d.m4 b/data/skeletons/d.m4 index 331c06a8..5320c483 100644 --- a/data/skeletons/d.m4 +++ b/data/skeletons/d.m4 @@ -192,7 +192,12 @@ b4_symbol_foreach([b4_token_enum])dnl } ]) - +# b4_symbol_translate(STRING) +# --------------------------- +# Used by "bison" in the array of symbol names to mark those that +# require translation. +m4_define([b4_symbol_translate], +[[_($1)]]) ## -------------- ## ## Symbol kinds. ## @@ -252,8 +257,17 @@ m4_define([b4_declare_symbol_enum], final void toString(W)(W sink) const if (isOutputRange!(W, char)) { - string yystr = yytname_[yycode_]; + immutable string[] yy_sname = @{ + ]b4_symbol_names[ + @};]b4_has_translations_if([[ + /* YYTRANSLATABLE[SYMBOL-NUM] -- Whether YY_SNAME[SYMBOL-NUM] is + internationalizable. */ + immutable ]b4_int_type_for([b4_translatable])[[] yytranslatable = @{ + ]b4_translatable[ + @}; + put(sink, yy_sname[yycode_]);]], [[ + string yystr = yytname_[yycode_]; if (yystr[0] == '"') { strip_quotes: @@ -280,9 +294,7 @@ m4_define([b4_declare_symbol_enum], { put(sink, "end of input"); return; - } - - put(sink, yystr); + }]])[ } } ]]) diff --git a/data/skeletons/lalr1.d b/data/skeletons/lalr1.d index fd0038d6..ede7d8c7 100644 --- a/data/skeletons/lalr1.d +++ b/data/skeletons/lalr1.d @@ -40,6 +40,29 @@ version(D_Version2) { ]b4_user_post_prologue[ ]b4_percent_code_get([[imports]])[ import std.format; +import std.conv; + +/** + * Handle error message internationalisation. + */ +static if (!is(typeof(YY_))) { + version(YYENABLE_NLS) + { + version(ENABLE_NLS) + { + extern(C) char* dgettext(const char*, const char*); + string YY_(const char* s) + { + return to!string(dgettext("bison-runtime", s)); + } + } + } + static if (!is(typeof(YY_))) + { + pragma(inline, true) + string YY_(string msg) { return msg; } + } +} /** * A Bison parser, automatically generated from ]m4_bpatsubst(b4_file_name, [^"\(.*\)"$], [\1])[. @@ -680,20 +703,38 @@ m4_popdef([b4_at_dollar])])dnl immutable int argmax = 5; SymbolKind[] yyarg = new SymbolKind[argmax]; int yycount = yysyntaxErrorArguments(yyctx, yyarg, argmax); - string res = "syntax error, unexpected "; - res ~= format!"%s"(yyarg[0]); - if (yycount < argmax + 1) + string res, yyformat; + import std.string; + switch (yycount) { - for (int yyi = 1; yyi < yycount; yyi++) - { - res ~= yyi == 1 ? ", expecting " : " or "; - res ~= format!"%s"(SymbolKind(yyarg[yyi])); - } + case 1: + yyformat = YY_("syntax error, unexpected %s"); + res = format(yyformat, yyarg[0]); + break; + case 2: + yyformat = YY_("syntax error, unexpected %s, expecting %s"); + res = format(yyformat, yyarg[0], yyarg[1]); + break; + case 3: + yyformat = YY_("syntax error, unexpected %s, expecting %s or %s"); + res = format(yyformat, yyarg[0], yyarg[1], yyarg[2]); + break; + case 4: + yyformat = YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + res = format(yyformat, yyarg[0], yyarg[1], yyarg[2], yyarg[3]); + break; + case 5: + yyformat = YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); + res = format(yyformat, yyarg[0], yyarg[1], yyarg[2], yyarg[3], yyarg[4]); + break; + default: + res = YY_("syntax error"); + break; } yyerror(]b4_locations_if([yyctx.getLocation(), ])[res); }]], [[simple]], [[ - yyerror(]b4_locations_if([yyctx.getLocation(), ])["syntax error");]])[ + yyerror(]b4_locations_if([yyctx.getLocation(), ])[YY_("syntax error"));]])[ } ]b4_parse_error_bmatch( diff --git a/doc/bison.texi b/doc/bison.texi index 0a55f343..c749079f 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -13953,6 +13953,66 @@ or nonzero, full tracing. Identify the Bison version and skeleton used to generate this parser. @end deftypecv +The internationalization in D is very simmilar to the one in C. The D +parser uses @code{dgettext} for translating Bison messages. + +To enable internationalisation, compile using +@code{-version ENABLE_NLS -version YYENABLE_NLS} and import +@code{bindtextdomain} and @code{textdomain} from C: + +@example +extern(C) char* bindtextdomain(const char* domainname, const char* dirname); +extern(C) char* textdomain(const char* domainname); +@end example + +The main function should load the translation catalogues, similarly to the +@file{c/bistromathic} example: + +@example +int main() +@{ + import core.stdc.locale; + + // Set up internationalization. + setlocale(LC_ALL, ""); + // Use Bison's standard translation catalogue for error messages + // (the generated messages). + bindtextdomain("bison-runtime", BISON_LOCALEDIR); + // For the translation catalogue of your own project, use the + // name of your project. + bindtextdomain("bison", LOCALEDIR); + textdomain("bison"); + + // usual main content + ... +@} +@end example + +For user messages translations, the user must implement the +@code{string} _(@code{const char*} @var{msg}) function and it is recommended +to use @code{gettext}: + +@example +%code imports @{ + static if (!is(typeof(_))) + @{ + version(ENABLE_NLS) + @{ + extern(C) char* gettext(const char*); + string _(const char* s) + @{ + return to!string(gettext(s)); + @} + @} + @} + static if (!is(typeof(_))) + @{ + pragma(inline, true) + string _(string msg) @{ return msg; @} + @} +@} +@end example + @node D Parser Context Interface @subsection D Parser Context Interface The parser context provides information to build error reports when you diff --git a/tests/calc.at b/tests/calc.at index b1428316..3d319645 100644 --- a/tests/calc.at +++ b/tests/calc.at @@ -654,6 +654,23 @@ m4_define([_AT_DATA_CALC_Y(d)], }; %printer { fprintf (yyo, "%d", $$); } ; +%code { +]AT_TOKEN_TRANSLATE_IF([[ + static string _(string s) + { + switch (s) + { + case "end of input": + return "end of file"; + case "number": + return "nombre"; + default: + return s; + } + } +]])[ +} + /* Bison Declarations */ %token EOF 0 ]AT_TOKEN_TRANSLATE_IF([_("end of file")], ["end of input"])[ %token NUM "number" @@ -665,7 +682,7 @@ m4_define([_AT_DATA_CALC_Y(d)], STAR "*" SLASH "/" POW "^" - EOL "\n" + EOL "'\\n'" LPAR "(" RPAR ")" NOT "!"