Skip to content

Commit

Permalink
d: add internationalisation support
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
adelavais authored and akimd committed Jan 6, 2021
1 parent e51e898 commit 594cae5
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 15 deletions.
22 changes: 17 additions & 5 deletions data/skeletons/d.m4
Original file line number Diff line number Diff line change
Expand Up @@ -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. ##
Expand Down Expand Up @@ -252,8 +257,17 @@ m4_define([b4_declare_symbol_enum],
final void toString(W)(W sink) const
if (isOutputRange!(W, char))
{
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:
Expand All @@ -280,9 +294,7 @@ m4_define([b4_declare_symbol_enum],
{
put(sink, "end of input");
return;
}
put(sink, yystr);
}]])[
}
}
]])
Expand Down
59 changes: 50 additions & 9 deletions data/skeletons/lalr1.d
Original file line number Diff line number Diff line change
Expand Up @@ -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 <tt>]m4_bpatsubst(b4_file_name, [^"\(.*\)"$], [\1])[</tt>.
Expand Down Expand Up @@ -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(
Expand Down
60 changes: 60 additions & 0 deletions doc/bison.texi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 18 additions & 1 deletion tests/calc.at
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,23 @@ m4_define([_AT_DATA_CALC_Y(d)],
};
%printer { fprintf (yyo, "%d", $$); } <ival>;

%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 <ival> NUM "number"
Expand All @@ -665,7 +682,7 @@ m4_define([_AT_DATA_CALC_Y(d)],
STAR "*"
SLASH "/"
POW "^"
EOL "\n"
EOL "'\\n'"
LPAR "("
RPAR ")"
NOT "!"
Expand Down

0 comments on commit 594cae5

Please sign in to comment.