[PATCH for Dlang support 2/2] d: add the custom error message feature

Adela Vais <[email protected]>
Newsgroups gmane.comp.parsers.bison.patches
Message-ID <[email protected]>
* data/skeletons/lalr1.d: Add the custom error message feature.
* doc/bison.texi: Document it.
* tests/calc.at, tests/local.at: Test it.
---
 data/skeletons/lalr1.d | 38 +++++++++++++++++++++++++-------------
 doc/bison.texi         | 37 +++++++++++++++++++++++++++++++++++++
 tests/calc.at          | 23 +++++++++++++++--------
 tests/local.at         | 41 ++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 117 insertions(+), 22 deletions(-)

diff --git a/data/skeletons/lalr1.d b/data/skeletons/lalr1.d
index 9b7c786c..a9e7e917 100644
--- a/data/skeletons/lalr1.d
+++ b/data/skeletons/lalr1.d
@@ -77,7 +77,15 @@ public interface Lexer
    * @@param loc The location of the element to which the
    *                error message is related]])[
    * @@param s The string for the error message.  */
-   void yyerror (]b4_locations_if([b4_location_type[ loc, ]])[string s);
+  void yyerror (]b4_locations_if([b4_location_type[ loc, ]])[string s);
+]b4_parse_error_bmatch([custom], [[
+  /**
+   * Build and emit a "syntax error" message in a user-defined way.
+   *
+   * @@param ctx  The context of the error.
+   */
+  void syntax_error(]b4_parser_class[.Context ctx);
+]])[
 }
 
 ]b4_locations_if([b4_position_type_if([[
@@ -276,7 +284,7 @@ b4_user_union_members
     return yylexer.yylex ();
   }
 
-  protected final void yyerror (]b4_locations_if(ref [b4_location_type[ loc, ]])[string s) {
+  protected final void yyerror (]b4_locations_if([b4_location_type[ loc, ]])[string s) {
     yylexer.yyerror (]b4_locations_if([loc, ])[s);
   }
 
@@ -555,7 +563,7 @@ m4_popdef([b4_at_dollar])])dnl
           ++yynerrs_;
           if (yychar == TokenKind.]b4_symbol(empty, id)[)
             yytoken = ]b4_symbol(empty, kind)[;
-          yyerror (]b4_locations_if([yylloc, ])[yysyntax_error(new Context(yystack, yytoken]b4_locations_if([[, yylloc]])[)));
+          yysyntax_error(new Context(yystack, yytoken]b4_locations_if([[, yylloc]])[));
         }
 ]b4_locations_if([
         yyerrloc = yylloc;])[
@@ -659,8 +667,11 @@ m4_popdef([b4_at_dollar])])dnl
   }
 
   // Generate an error message.
-  private final string yysyntax_error(Context yyctx)
-  {]b4_parse_error_case([verbose], [[
+  private final void yysyntax_error(Context yyctx)
+  {]b4_parse_error_bmatch(
+[custom], [[
+    yylexer.syntax_error(yyctx);]],
+[detailed\|verbose], [[
     /* There are many possibilities here to consider:
        - Assume YYFAIL is not used.  It's too flawed to consider.
          See
@@ -691,16 +702,16 @@ m4_popdef([b4_at_dollar])])dnl
          list is correct for canonical LR with one exception: it
          will still contain any token that will not be accepted due
          to an error action in a later state.
-      */
+     */
     if (yyctx.getToken() != ]b4_symbol(empty, kind)[)
     {
       // FIXME: This method of building the message is not compatible
       // with internationalization.
       string res = "syntax error, unexpected ";
-      res ~= format!"%s"(yyctx.getToken);
+      res ~= format!"%s"(yyctx.getToken());
       immutable int argmax = 5;
-      SymbolKind[] yyarg = new SymbolKind[argmax];
-      int yycount = yyctx.getExpectedTokens(yyarg, argmax);
+      SymbolKind[] yyarg = new SymbolKind[yyntokens_];
+      int yycount = yyctx.getExpectedTokens(yyarg, yyntokens_);
       if (yycount < argmax)
       {
         for (int yyi = 0; yyi < yycount; yyi++)
@@ -709,9 +720,10 @@ m4_popdef([b4_at_dollar])])dnl
           res ~= format!"%s"(SymbolKind(yyarg[yyi]));
         }
       }
-      return res;
-    }]])[
-    return "syntax error";
+      yyerror(]b4_locations_if([yyctx.getLocation(), ])[res);
+    }]],
+[[simple]], [[
+    yyerror(]b4_locations_if([yyctx.getLocation(), ])["syntax error");]])[
   }
 
   /**
@@ -770,7 +782,7 @@ m4_popdef([b4_at_dollar])])dnl
           if (yycheck_[yyx + yyn] == yyx && yyx != ]b4_symbol(1, kind)[
               && !yyTableValueIsError(yytable_[yyx + yyn]))
             yycount++;
-        if (yycount < yyargn)
+        if (yycount < yyntokens_)
         {
           yycount = 0;
           for (int x = yyxbegin; x < yyxend; ++x)
diff --git a/doc/bison.texi b/doc/bison.texi
index e49828c9..419173cc 100644
--- a/doc/bison.texi
+++ b/doc/bison.texi
@@ -14018,6 +14018,43 @@ They should return new objects for each call, to avoid that all the symbol
 share the same Position boundaries.
 @end deftypemethod
 
+@deftypemethod {Lexer} {void} syntax_error(@code{YYParser.Context} @var{ctx})
+If you invoke @samp{%define parse.error custom} (@pxref{Bison
+Declarations}), then the parser no longer passes syntax error messages to
+@code{yyerror}, rather it delegates that task to the user by calling the
+@code{reportSyntaxError} function.
+
+Whether it uses @code{yyerror} is up to the user.
+
+Here is an example of a reporting function (@pxref{D Parser Context
+Interface}).
+
+@example
+public void syntax_error(YYParser.Context ctx)
+@{
+  stderr.write(ctx.getLocation(), ": syntax error");
+  // Report the expected tokens.
+  @{
+    immutable int TOKENMAX = 5;
+    YYParser.SymbolKind[] arg = new YYParser.SymbolKind[TOKENMAX];
+    int n = ctx.getExpectedTokens(arg, TOKENMAX);
+    if (n < TOKENMAX)
+      for (int i = 0; i < n; ++i)
+        stderr.write((i == 0 ? ": expected " : " or "), arg[i]);
+  @}
+  // Report the unexpected token which triggered the error.
+  @{
+    YYParser.SymbolKind lookahead = ctx.getToken();
+    stderr.writeln(" before ", lookahead);
+  @}
+@}
+@end example
+
+@noindent
+This implementation is inappropriate for internationalization, see
+the ‘c/bistromathic’ example for a better alternative.
+@end deftypemethod
+
 @node Java Parsers
 @section Java Parsers
 
diff --git a/tests/calc.at b/tests/calc.at
index a96fc98a..cd7c8622 100644
--- a/tests/calc.at
+++ b/tests/calc.at
@@ -364,7 +364,7 @@ void location_print (FILE *o, Span s);
 }]])[
 
 /* Bison Declarations */
-%token CALC_EOF 0 ]AT_TOKEN_TRANSLATE_IF([_("end of input")], ["end of input"])[
+%token CALC_EOF 0 ]AT_TOKEN_TRANSLATE_IF([_("end of file")], ["end of input"])[
 %token <]AT_VALUE_UNION_IF([int], [ival])[> NUM   "number"
 %type  <]AT_VALUE_UNION_IF([int], [ival])[> exp
 
@@ -666,20 +666,20 @@ m4_define([_AT_DATA_CALC_Y(d)],
 %printer { fprintf (yyo, "%d", $$); } <ival>;
 
 /* Bison Declarations */
-%token EOF 0 ]AT_TOKEN_TRANSLATE_IF([_("end of input")], ["end of input"])[
+%token EOF 0 ]AT_TOKEN_TRANSLATE_IF([_("end of file")], ["end of input"])[
 %token <ival> NUM   "number"
 %type  <ival> exp
 
-%token PLUS   "+"
+%token EQUAL  "="
        MINUS  "-"
+       PLUS   "+"
        STAR   "*"
        SLASH  "/"
+       POW    "^"
+       EOL    "\n"
        LPAR   "("
        RPAR   ")"
-       EQUAL  "="
-       POW    "^"
        NOT    "!"
-       EOL    "\n"
 
 %nonassoc "="   /* comparison          */
 %left "-" "+"
@@ -736,7 +736,6 @@ power (int base, int exponent)
   return res;
 }
 
-]AT_YYERROR_DEFINE[
 ]AT_CALC_YYLEX
 AT_CALC_MAIN])
 ])# _AT_DATA_CALC_Y(d)
@@ -861,7 +860,7 @@ m4_define([_AT_DATA_CALC_Y(java)],
 }
 
 /* Bison Declarations */
-%token CALC_EOF 0 ]AT_TOKEN_TRANSLATE_IF([_("end of input")], ["end of input"])[
+%token CALC_EOF 0 ]AT_TOKEN_TRANSLATE_IF([_("end of file")], ["end of input"])[
 %token <Integer> NUM "number"
 %type  <Integer> exp
 
@@ -1448,6 +1447,14 @@ AT_CHECK_CALC_LALR1_D([%define parse.error verbose %define api.prefix {calc} %ve
 
 AT_CHECK_CALC_LALR1_D([%debug])
 
+AT_CHECK_CALC_LALR1_D([%define parse.error custom])
+AT_CHECK_CALC_LALR1_D([%define parse.error detailed])
+AT_CHECK_CALC_LALR1_D([%define parse.error simple])
+AT_CHECK_CALC_LALR1_D([%define parse.error verbose])
+AT_CHECK_CALC_LALR1_D([%locations %define parse.error custom])
+AT_CHECK_CALC_LALR1_D([%locations %define parse.error detailed])
+AT_CHECK_CALC_LALR1_D([%locations %define parse.error simple])
+AT_CHECK_CALC_LALR1_D([%locations %define parse.error verbose])
 AT_CHECK_CALC_LALR1_D([%define parse.error verbose %debug %verbose])
 AT_CHECK_CALC_LALR1_D([%define parse.error verbose %debug %define api.token.prefix {TOK_} %verbose])
 AT_CHECK_CALC_LALR1_D([%define parse.error verbose %debug %define api.symbol.prefix {SYMB_} %verbose])
diff --git a/tests/local.at b/tests/local.at
index b18bdf3b..36019602 100644
--- a/tests/local.at
+++ b/tests/local.at
@@ -871,7 +871,46 @@ m4_define([AT_YYERROR_DEFINE(d)],
 public void yyerror (]AT_LOCATION_IF([[YYLocation l, ]])[string m)
 {
   stderr.writeln (]AT_LOCATION_IF([[l, ": ", ]])[m);
-}]])
+}
+]AT_ERROR_CUSTOM_IF([[
+// In the case of D, there are no single quotes around the symbols
+// so they need to be added here
+public string transformToken(]AT_API_PREFIX[Parser.SymbolKind token)
+{
+  string tokenStr;
+  foreach (i; format("%s", token))
+  {
+    if (i == '\"')
+      tokenStr ~= '\'';
+    else
+      tokenStr ~= i;
+  }
+  if (tokenStr.length == 1)
+    return '\'' ~ tokenStr ~ '\'';
+  else
+    return tokenStr;
+}
+
+public void syntax_error(]AT_API_PREFIX[Parser.Context ctx)
+{
+    stderr.write(]AT_LOCATION_IF([[ctx.getLocation(), ": ",]])["syntax error");
+    {
+      ]AT_API_PREFIX[Parser.SymbolKind token = ctx.getToken();
+      stderr.write(" on token @<:@", transformToken(token), "@:>@");
+    }
+    {
+      immutable int argmax = 7;
+      ]AT_API_PREFIX[Parser.SymbolKind[] arg = new ]AT_API_PREFIX[Parser.SymbolKind[argmax];
+      int n = ctx.getExpectedTokens(arg, argmax);
+      if (0 < n) {
+        stderr.write(" (expected:");
+        for (int i = 0; i < n; ++i)
+          stderr.write(" @<:@", transformToken(arg[i]), "@:>@");
+        stderr.writeln(")");
+      }
+    }
+}
+]])[]])
 
 
 m4_define([AT_MAIN_DEFINE(d)],
-- 
2.17.1
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.