Re: [Bison-Announce] Bison 3.5.90 released [beta]

Akim Demaille <[email protected]>
Newsgroups gmane.comp.parsers.bison.bugs
Message-ID <[email protected]>
Hi Frank,

> Le 20 avr. 2020 à 23:02, Frank Heckenbach <[email protected]> a écrit :
> 
> Akim Demaille wrote:

>> So you are in a way "in between": not using the Autotools magic,
>> but willing to use the machinery inside.
>> 
>> I'm saying it's wrong.
> 
> Not saying, I guess!? ;)

Of course, sorry about that :)

I'm installing this, I think it should make things much clearer.
Thanks a lot for the impetus!

Comments most welcome.

commit 3237a3c107339de21d75ff6b3b130cc72b04aaff
Author: Akim Demaille <[email protected]>
Date:   Sun Apr 19 09:13:47 2020 +0200

    examples: bistromathic: demonstrate internationalization
    
    Currently it was only using stubs.  Let's actually translate the
    strings using gettext.
    
    * examples/c/bistromathic/local.mk: Define LOCALEDIR, BISON_LOCALEDIR
    and link with libintl.
    * examples/c/bistromathic/parse.y: Use them.
    Remove useless includes.
    Take ENABLE_NLS into account.
    (error_format_string): New.
    (yyreport_syntax_error): Rewrite to rely on a format string, which is
    more appropriate for internationalization.
    * examples/c/bistromathic/Makefile: We no longer use Flex.
    We need readline and intl.
    
    * doc/bison.texi: Point to bistromathic for a better option for
    internationalization.
    * po/POTFILES.in: Add bistromathic.

diff --git a/doc/bison.texi b/doc/bison.texi
index 3ed36098..141fcb0c 100644
--- a/doc/bison.texi
+++ b/doc/bison.texi
@@ -7514,7 +7514,9 @@ values, which is always less than @code{YYNTOKENS}.
 The name of the symbol whose kind is @var{symbol}, possibly translated.
 @end deftypefun
 
-A custom syntax error function looks as follows.
+A custom syntax error function looks as follows.  This implementation is
+inappropriate for internationalization, see the @file{c/bistromathic}
+example for a better altnative.
 
 @example
 int
@@ -10601,7 +10603,7 @@ Print the version number of Bison and exit.
 Print the name of the directory containing locale-dependent data.
 
 @item --print-datadir
-Print the name of the directory containing skeletons and XSLT.
+Print the name of the directory containing skeletons, CSS and XSLT.
 
 @item -u
 @item --update
@@ -12074,7 +12076,9 @@ values, which is always less than @code{YYNTOKENS}.
 The name of the symbol whose kind is @var{symbol}, possibly translated.
 @end deftypemethod
 
-A custom syntax error function looks as follows.
+A custom syntax error function looks as follows.  This implementation is
+inappropriate for internationalization, see the @file{c/bistromathic}
+example for a better altnative.
 
 @example
 void
@@ -13327,6 +13331,10 @@ public void reportSyntaxError(YYParser.Context ctx) @{
   System.err.println("");
 @}
 @end example
+
+@noindent
+This implementation is inappropriate for internationalization, see the
+@file{c/bistromathic} example for a better altnative.
 @end deftypemethod
 
 @node Java Action Features
diff --git a/examples/c/README.md b/examples/c/README.md
index e1e04d84..47963f13 100644
--- a/examples/c/README.md
+++ b/examples/c/README.md
@@ -57,6 +57,8 @@ This example demonstrates best practices when using Bison.
   push-parser API to feed the parser with the incoming tokens.
 - It features an interactive command line with completion based on the
   parser state, based on `yyexpected_tokens`.
+- It uses Bison's standard catalogue for internationalization of generated
+  messages.
 - It uses a custom syntax error with location, lookahead correction and
   token internationalization.
 - It supports debug traces with semantic values.
diff --git a/examples/c/bistromathic/Makefile b/examples/c/bistromathic/Makefile
index 359d0ac5..c1ac6887 100644
--- a/examples/c/bistromathic/Makefile
+++ b/examples/c/bistromathic/Makefile
@@ -3,20 +3,26 @@
 
 BASE = bistromathic
 BISON = bison
-FLEX = flex
 XSLTPROC = xsltproc
 
+# We need to find the headers and libs for readline and intl.
+# You probably need to customize this for your own environment.
+CPPFLAGS = -I/opt/local/include
+LDFLAGS = -L/opt/local/lib
+
+# Find the translation catalogue for Bison's generated messagess.
+BISON_LOCALEDIR = $(shell $(BISON) $(BISON_FLAGS) --print-localedir)
+CPPFLAGS += -DENABLE_NLS -DBISON_LOCALEDIR='"$(BISON_LOCALEDIR)"'
+
+LIBS = -lreadline -lintl
+
 all: $(BASE)
 
 %.c %.h %.xml %.gv: %.y
 	$(BISON) $(BISONFLAGS) --defines --xml --graph=$*.gv -o $*.c $<
 
-%.c: %.l
-	$(FLEX) $(FLEXFLAGS) -o$*.c $<
-
-scan.o: parse.h
-$(BASE): parse.o scan.o
-	$(CC) $(CFLAGS) -o $@ $^
+$(BASE): parse.o
+	$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
 
 run: $(BASE)
 	@echo "Type bistromathic expressions.  Quit with ctrl-d."
@@ -28,8 +34,7 @@ html: $(BASE).html
 
 CLEANFILES =						\
   $(BASE) *.o						\
-  parse.[ch] parse.output parse.xml parse.html parse.gv	\
-  scan.c
+  parse.[ch] parse.output parse.xml parse.html parse.gv
 
 clean:
 	rm -f $(CLEANFILES)
diff --git a/examples/c/bistromathic/README.md b/examples/c/bistromathic/README.md
index 6b376b71..6b6521de 100644
--- a/examples/c/bistromathic/README.md
+++ b/examples/c/bistromathic/README.md
@@ -6,6 +6,8 @@ This example demonstrates best practices when using Bison.
   push-parser API to feed the parser with the incoming tokens.
 - It features an interactive command line with completion based on the
   parser state, based on `yyexpected_tokens`.
+- It uses Bison's standard catalogue for internationalization of generated
+  messages.
 - It uses a custom syntax error with location, lookahead correction and
   token internationalization.
 - It supports debug traces with semantic values.
diff --git a/examples/c/bistromathic/local.mk b/examples/c/bistromathic/local.mk
index cab026e2..607fdc52 100644
--- a/examples/c/bistromathic/local.mk
+++ b/examples/c/bistromathic/local.mk
@@ -26,8 +26,11 @@ nodist_%C%_bistromathic_SOURCES = %D%/parse.y %D%/parse.h
 %D%/parse.c: $(dependencies)
 
 # Don't use gnulib's system headers.
-%C%_bistromathic_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
-%C%_bistromathic_LDADD = -lm -lreadline
+%C%_bistromathic_CPPFLAGS =			\
+  -DBISON_LOCALEDIR='"$(localdir)"'		\
+  -DLOCALEDIR='"$(localdir)"'			\
+  -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
+%C%_bistromathic_LDADD = -lm -lreadline $(LIBINTL)
 
 dist_bistromathic_DATA = %D%/parse.y %D%/Makefile %D%/README.md
 CLEANFILES += %D%/parse.[ch] %D%/parse.output
diff --git a/examples/c/bistromathic/parse.y b/examples/c/bistromathic/parse.y
index a3b34c38..1342ba60 100644
--- a/examples/c/bistromathic/parse.y
+++ b/examples/c/bistromathic/parse.y
@@ -3,13 +3,23 @@
 %code top {
   #include <ctype.h>  // isdigit
   #include <math.h>   // cos, sin, etc.
-  #include <stddef.h> // ptrdiff_t
   #include <stdio.h>  // printf
-  #include <stdlib.h> // calloc.
+  #include <stdlib.h> // calloc
   #include <string.h> // strcmp
 
   #include <readline/readline.h>
   #include <readline/history.h>
+
+  #if defined ENABLE_NLS && ENABLE_NLS
+  // Unable the translation of Bison's generated messages.
+  # define YYENABLE_NLS 1
+  # include <libintl.h>
+  // Unless specified otherwise, we expect bistromathic's own
+  // catalogue to be installed in the same tree as Bison's catalogue.
+  # ifndef LOCALEDIR
+  #  define LOCALEDIR BISON_LOCALEDIR
+  # endif
+  #endif
 }
 
 %code requires {
@@ -40,8 +50,12 @@
 }
 
 %code {
-#define N_
-#define _
+  #if defined ENABLE_NLS && ENABLE_NLS
+  # define _(Msgid)  gettext (Msgid)
+  #else
+  # define _(Msgid)  (Msgid)
+  #endif
+  #define N_(Msgid) (Msgid)
 
   // Whether to quit.
   int done = 0;
@@ -286,35 +300,69 @@ yylex (const char **line, YYSTYPE *yylval, YYLTYPE *yylloc)
 | Parser.  |
 `---------*/
 
+
+const char *
+error_format_string (int argc)
+{
+  switch (argc)
+    {
+    default: /* Avoid compiler warnings. */
+    case 0: return _("%@: syntax error");
+    case 1: return _("%@: syntax error: unexpected %u");
+    case 2: return _("%@: syntax error: expected %0e before %u");
+    case 3: return _("%@: syntax error: expected %0e or %1e before %u");
+    case 4: return _("%@: syntax error: expected %0e or %1e or %2e before %u");
+    case 5: return _("%@: syntax error: expected %0e or %1e or %2e or %3e before %u");
+    case 6: return _("%@: syntax error: expected %0e or %1e or %2e or %3e or %4e before %u");
+    case 7: return _("%@: syntax error: expected %0e or %1e or %2e or %3e or %4e or %5e before %u");
+    case 8: return _("%@: syntax error: expected %0e or %1e or %2e or %3e or %4e or %5e or %6e before %u");
+    }
+}
+
+
 int
 yyreport_syntax_error (const yypcontext_t *ctx)
 {
-  int res = 0;
-  YY_LOCATION_PRINT (stderr, *yypcontext_location (ctx));
-  fprintf (stderr, ": syntax error");
-  // Report the tokens expected at this point.
-  {
-    enum { TOKENMAX = 10 };
-    yysymbol_kind_t expected[TOKENMAX];
-    int n = yypcontext_expected_tokens (ctx, expected, TOKENMAX);
-    if (n < 0)
-      // Forward errors to yyparse.
-      res = n;
+  enum { ARGS_MAX = 7 };
+  yysymbol_kind_t arg[ARGS_MAX];
+  int argsize = yypcontext_expected_tokens (ctx, arg, ARGS_MAX);
+  if (argsize < 0)
+    return argsize;
+  const char *format = error_format_string (1 + argsize);
+
+  while (*format)
+    // %@: location.
+    if (format[0] == '%' && format[1] == '@')
+      {
+        YY_LOCATION_PRINT (stderr, *yypcontext_location (ctx));
+        format += 2;
+      }
+    // %t: unexpected token.
+    else if (format[0] == '%' && format[1] == 'u')
+      {
+        fputs (yysymbol_name (yypcontext_token (ctx)), stderr);
+        format += 2;
+      }
+    // %0e, %1e...: expected token.
+    else if (format[0] == '%'
+             && isdigit (format[1])
+             && format[2] == 'e'
+             && (format[1] - '0') < argsize)
+      {
+        int i = format[1] - '0';
+        fputs (yysymbol_name (arg[i]), stderr);
+        format += 3;
+      }
     else
-      for (int i = 0; i < n; ++i)
-        fprintf (stderr, "%s %s",
-                 i == 0 ? ": expected" : " or", yysymbol_name (expected[i]));
-  }
-  // Report the unexpected token.
-  {
-    yysymbol_kind_t lookahead = yypcontext_token (ctx);
-    if (lookahead != YYSYMBOL_YYEMPTY)
-      fprintf (stderr, " before %s", yysymbol_name (lookahead));
-  }
-  fprintf (stderr, "\n");
-  return res;
+      {
+        fputc (*format, stderr);
+        ++format;
+      }
+  fputc ('\n', stderr);
+  return 0;
 }
 
+
 // Called by yyparse on error.
 void yyerror (YYLTYPE *loc, char const *msg)
 {
@@ -467,6 +515,18 @@ void init_readline (void)
 
 int main (int argc, char const* argv[])
 {
+#if defined ENABLE_NLS && ENABLE_NLS
+  // Set up internationalization.
+  setlocale (LC_ALL, "");
+  // Use Bison's standard translation catalogue for error messages
+  // (the generated messages).
+  bindtextdomain ("bison-runtime", BISON_LOCALEDIR);
+  // The translation catalogue of bistromathic is actually included in
+  // Bison's.  In your own project, use the name of your project.
+  bindtextdomain ("bison", LOCALEDIR);
+  textdomain ("bison");
+#endif
+
   // Enable parse traces on option -p.
   if (argc == 2 && strcmp (argv[1], "-p") == 0)
     yydebug = 1;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0961eca3..1eca4cdd 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,3 +1,4 @@
+examples/c/bistromathic/parse.y
 src/complain.c
 src/conflicts.c
 src/files.c
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.