]> git.saurik.com Git - bison.git/blobdiff - src/parse-gram.y
style: use %code for local function declarations in our parser
[bison.git] / src / parse-gram.y
index b897d6915fbd59b24a24d0bd2b52048d736bbf0b..62cee18886ce8bce3d7f02e7533fbd0b882d1b3c 100644 (file)
@@ -1,7 +1,6 @@
 %{/* Bison Grammar Parser                             -*- C -*-
 
 %{/* Bison Grammar Parser                             -*- C -*-
 
-   Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 2002-2013 Free Software Foundation, Inc.
 
    This file is part of Bison, the GNU Compiler Compiler.
 
 
    This file is part of Bison, the GNU Compiler Compiler.
 
 #include <config.h>
 #include "system.h"
 
 #include <config.h>
 #include "system.h"
 
+#include "c-ctype.h"
 #include "complain.h"
 #include "conflicts.h"
 #include "files.h"
 #include "getargs.h"
 #include "gram.h"
 #include "complain.h"
 #include "conflicts.h"
 #include "files.h"
 #include "getargs.h"
 #include "gram.h"
-#include "muscle-tab.h"
 #include "named-ref.h"
 #include "quotearg.h"
 #include "reader.h"
 #include "symlist.h"
 #include "named-ref.h"
 #include "quotearg.h"
 #include "reader.h"
 #include "symlist.h"
+#include "symtab.h"
 #include "scan-gram.h"
 #include "scan-code.h"
 #include "scan-gram.h"
 #include "scan-code.h"
-
-#define YYLLOC_DEFAULT(Current, Rhs, N)  (Current) = lloc_default (Rhs, N)
-static YYLTYPE lloc_default (YYLTYPE const *, int);
-
-#define YY_LOCATION_PRINT(File, Loc) \
-         location_print (File, Loc)
-
-static void version_check (location const *loc, char const *version);
-
-/* Request detailed syntax error messages, and pass them to GRAM_ERROR.
-   FIXME: depends on the undocumented availability of YYLLOC.  */
-#undef  yyerror
-#define yyerror(Msg) \
-       gram_error (&yylloc, Msg)
-static void gram_error (location const *, char const *);
-
-static char const *char_name (char);
+#include "xmemdup0.h"
 %}
 
 %code
 %}
 
 %code
@@ -57,24 +41,44 @@ static char const *char_name (char);
   static int current_prec = 0;
   static location current_lhs_location;
   static named_ref *current_lhs_named_ref;
   static int current_prec = 0;
   static location current_lhs_location;
   static named_ref *current_lhs_named_ref;
-  static symbol *current_lhs;
+  static symbol *current_lhs_symbol;
   static symbol_class current_class = unknown_sym;
   static uniqstr current_type = NULL;
 
   static symbol_class current_class = unknown_sym;
   static uniqstr current_type = NULL;
 
+  /** Set the new current left-hand side symbol, possibly common
+   * to several right-hand side parts of rule.
+   */
+  static void current_lhs (symbol *sym, location loc, named_ref *ref);
+
+  #define YYLLOC_DEFAULT(Current, Rhs, N)         \
+    (Current) = lloc_default (Rhs, N)
+  static YYLTYPE lloc_default (YYLTYPE const *, int);
+
+  #define YY_LOCATION_PRINT(File, Loc)            \
+    location_print (Loc, File)
+
+  static void version_check (location const *loc, char const *version);
+
+  static void gram_error (location const *, char const *);
+
+  /* A string that describes a char (e.g., 'a' -> "'a'").  */
+  static char const *char_name (char);
+
   #define YYTYPE_INT16 int_fast16_t
   #define YYTYPE_INT8 int_fast8_t
   #define YYTYPE_UINT16 uint_fast16_t
   #define YYTYPE_UINT8 uint_fast8_t
 }
 
   #define YYTYPE_INT16 int_fast16_t
   #define YYTYPE_INT8 int_fast8_t
   #define YYTYPE_UINT16 uint_fast16_t
   #define YYTYPE_UINT8 uint_fast8_t
 }
 
-%debug
-%verbose
+%define api.prefix {gram_}
+%define api.pure full
+%define locations
+%define parse.error verbose
+%define parse.lac full
+%define parse.trace
 %defines
 %defines
-%locations
-%pure-parser
-%define parse.error "verbose"
-%name-prefix="gram_"
 %expect 0
 %expect 0
+%verbose
 
 %initial-action
 {
 
 %initial-action
 {
@@ -84,23 +88,9 @@ static char const *char_name (char);
   boundary_set (&@$.end, current_file, 1, 1);
 }
 
   boundary_set (&@$.end, current_file, 1, 1);
 }
 
-%union
-{
-  assoc assoc;
-  char *code;
-  char const *chars;
-  int integer;
-  named_ref *named_ref;
-  symbol *symbol;
-  symbol_list *list;
-  uniqstr uniqstr;
-  unsigned char character;
-};
-
 /* Define the tokens together with their human representation.  */
 %token GRAM_EOF 0 "end of file"
 %token STRING     "string"
 /* Define the tokens together with their human representation.  */
 %token GRAM_EOF 0 "end of file"
 %token STRING     "string"
-%token INT        "integer"
 
 %token PERCENT_TOKEN       "%token"
 %token PERCENT_NTERM       "%nterm"
 
 %token PERCENT_TOKEN       "%token"
 %token PERCENT_NTERM       "%nterm"
@@ -118,7 +108,6 @@ static char const *char_name (char);
 %token PERCENT_DPREC         "%dprec"
 %token PERCENT_MERGE         "%merge"
 
 %token PERCENT_DPREC         "%dprec"
 %token PERCENT_MERGE         "%merge"
 
-
 /*----------------------.
 | Global Declarations.  |
 `----------------------*/
 /*----------------------.
 | Global Declarations.  |
 `----------------------*/
@@ -151,6 +140,7 @@ static char const *char_name (char);
 ;
 
 %token BRACED_CODE     "{...}"
 ;
 
 %token BRACED_CODE     "{...}"
+%token BRACED_PREDICATE "%?{...}"
 %token BRACKETED_ID    "[identifier]"
 %token CHAR            "char"
 %token EPILOGUE        "epilogue"
 %token BRACKETED_ID    "[identifier]"
 %token CHAR            "char"
 %token EPILOGUE        "epilogue"
@@ -165,34 +155,48 @@ static char const *char_name (char);
 %token TAG_ANY         "<*>"
 %token TAG_NONE        "<>"
 
 %token TAG_ANY         "<*>"
 %token TAG_NONE        "<>"
 
+%union {unsigned char character;}
 %type <character> CHAR
 %type <character> CHAR
-%printer { fputs (char_name ($$), stderr); } CHAR
+%printer { fputs (char_name ($$), yyo); } CHAR
 
 /* braceless is not to be used for rule or symbol actions, as it
    calls code_props_plain_init.  */
 
 /* braceless is not to be used for rule or symbol actions, as it
    calls code_props_plain_init.  */
-%type <chars> STRING "%{...%}" EPILOGUE braceless content.opt
-%type <code> "{...}"
-%printer { fputs (quotearg_style (c_quoting_style, $$), stderr); }
-        STRING
-%printer { fprintf (stderr, "{\n%s\n}", $$); }
-        braceless content.opt "{...}" "%{...%}" EPILOGUE
-
-%type <uniqstr> BRACKETED_ID ID ID_COLON PERCENT_FLAG TAG variable
-%printer { fputs ($$, stderr); } <uniqstr>
-%printer { fprintf (stderr, "[%s]", $$); } BRACKETED_ID
-%printer { fprintf (stderr, "%s:", $$); } ID_COLON
-%printer { fprintf (stderr, "%%%s", $$); } PERCENT_FLAG
-%printer { fprintf (stderr, "<%s>", $$); } TAG
-
-%type <integer> INT
-%printer { fprintf (stderr, "%d", $$); } <integer>
-
+%union
+{
+  char *code;
+  char const *chars;
+};
+%type <chars> STRING "%{...%}" EPILOGUE braceless
+%type <code> "{...}" "%?{...}"
+%printer { fputs (quotearg_style (c_quoting_style, $$), yyo); }
+         STRING
+%printer { fprintf (yyo, "{\n%s\n}", $$); }
+         braceless "{...}" "%{...%}" EPILOGUE
+
+%union {uniqstr uniqstr;}
+%type <uniqstr> BRACKETED_ID ID ID_COLON PERCENT_FLAG TAG tag variable
+%printer { fputs ($$, yyo); } <uniqstr>
+%printer { fprintf (yyo, "[%s]", $$); } BRACKETED_ID
+%printer { fprintf (yyo, "%s:", $$); } ID_COLON
+%printer { fprintf (yyo, "%%%s", $$); } PERCENT_FLAG
+%printer { fprintf (yyo, "<%s>", $$); } TAG tag
+
+%union {int integer;};
+%token <integer> INT "integer"
+%printer { fprintf (yyo, "%d", $$); } <integer>
+
+%union {symbol *symbol;}
 %type <symbol> id id_colon string_as_id symbol symbol.prec
 %type <symbol> id id_colon string_as_id symbol symbol.prec
-%printer { fprintf (stderr, "%s", $$->tag); } <symbol>
-%printer { fprintf (stderr, "%s:", $$->tag); } id_colon
+%printer { fprintf (yyo, "%s", $$->tag); } <symbol>
+%printer { fprintf (yyo, "%s:", $$->tag); } id_colon
 
 
+%union {assoc assoc;};
 %type <assoc> precedence_declarator
 %type <assoc> precedence_declarator
+
+%union {symbol_list *list;}
 %type <list>  symbols.1 symbols.prec generic_symlist generic_symlist_item
 %type <list>  symbols.1 symbols.prec generic_symlist generic_symlist_item
+
+%union {named_ref *named_ref;}
 %type <named_ref> named_ref.opt
 
 /*---------.
 %type <named_ref> named_ref.opt
 
 /*---------.
@@ -200,8 +204,6 @@ static char const *char_name (char);
 `---------*/
 %code requires
 {
 `---------*/
 %code requires
 {
-# ifndef PARAM_TYPE
-#  define PARAM_TYPE
   typedef enum
   {
     param_none   = 0,
   typedef enum
   {
     param_none   = 0,
@@ -209,7 +211,6 @@ static char const *char_name (char);
     param_parse  = 1 << 1,
     param_both   = param_lex | param_parse
   } param_type;
     param_parse  = 1 << 1,
     param_both   = param_lex | param_parse
   } param_type;
-# endif
 };
 %code
 {
 };
 %code
 {
@@ -222,20 +223,17 @@ static char const *char_name (char);
   static void add_param (param_type type, char *decl, location loc);
   static param_type current_param = param_none;
 };
   static void add_param (param_type type, char *decl, location loc);
   static param_type current_param = param_none;
 };
-%union
-{
-  param_type param;
-}
+%union {param_type param;}
 %token <param> PERCENT_PARAM "%param";
 %printer
 {
   switch ($$)
     {
 #define CASE(In, Out)                                           \
 %token <param> PERCENT_PARAM "%param";
 %printer
 {
   switch ($$)
     {
 #define CASE(In, Out)                                           \
-      case param_ ## In: fputs ("%" #Out, stderr); break
-      CASE(lex,   lex-param);
-      CASE(parse, parse-param);
-      CASE(both,  param);
+      case param_ ## In: fputs ("%" #Out, yyo); break
+      CASE (lex,   lex-param);
+      CASE (parse, parse-param);
+      CASE (both,  param);
 #undef CASE
       case param_none: aver (false); break;
     }
 #undef CASE
       case param_none: aver (false); break;
     }
@@ -252,12 +250,12 @@ input:
 ;
 
 
 ;
 
 
-       /*------------------------------------.
-       | Declarations: before the first %%.  |
-       `------------------------------------*/
+        /*------------------------------------.
+        | Declarations: before the first %%.  |
+        `------------------------------------*/
 
 prologue_declarations:
 
 prologue_declarations:
-  /* Nothing */
+  %empty
 | prologue_declarations prologue_declaration
 ;
 
 | prologue_declarations prologue_declaration
 ;
 
@@ -277,9 +275,9 @@ prologue_declaration:
     {
       muscle_percent_define_ensure ($1, @1, true);
     }
     {
       muscle_percent_define_ensure ($1, @1, true);
     }
-| "%define" variable content.opt
+| "%define" variable value
     {
     {
-      muscle_percent_define_insert ($2, @2, $3,
+      muscle_percent_define_insert ($2, @2, $3.kind, $3.chars,
                                     MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
     }
 | "%defines"                       { defines_flag = true; }
                                     MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
     }
 | "%defines"                       { defines_flag = true; }
@@ -290,13 +288,13 @@ prologue_declaration:
     }
 | "%error-verbose"
     {
     }
 | "%error-verbose"
     {
-      muscle_percent_define_insert ("parse.error", @1, "verbose",
+      muscle_percent_define_insert ("parse.error", @1, muscle_keyword,
+                                    "verbose",
                                     MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
     }
 | "%expect" INT                    { expected_sr_conflicts = $2; }
                                     MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
     }
 | "%expect" INT                    { expected_sr_conflicts = $2; }
-| "%expect-rr" INT                { expected_rr_conflicts = $2; }
+| "%expect-rr" INT                 { expected_rr_conflicts = $2; }
 | "%file-prefix" STRING            { spec_file_prefix = $2; }
 | "%file-prefix" STRING            { spec_file_prefix = $2; }
-| "%file-prefix" "=" STRING        { spec_file_prefix = $3; } /* deprecated */
 | "%glr-parser"
     {
       nondeterministic_parser = true;
 | "%glr-parser"
     {
       nondeterministic_parser = true;
@@ -311,13 +309,11 @@ prologue_declaration:
       muscle_code_grow ("initial_action", action.code, @2);
       code_scanner_last_string_free ();
     }
       muscle_code_grow ("initial_action", action.code, @2);
       code_scanner_last_string_free ();
     }
-| "%language" STRING           { language_argmatch ($2, grammar_prio, @1); }
+| "%language" STRING            { language_argmatch ($2, grammar_prio, @1); }
 | "%name-prefix" STRING         { spec_name_prefix = $2; }
 | "%name-prefix" STRING         { spec_name_prefix = $2; }
-| "%name-prefix" "=" STRING     { spec_name_prefix = $3; } /* deprecated */
 | "%no-lines"                   { no_lines_flag = true; }
 | "%no-lines"                   { no_lines_flag = true; }
-| "%nondeterministic-parser"   { nondeterministic_parser = true; }
+| "%nondeterministic-parser"    { nondeterministic_parser = true; }
 | "%output" STRING              { spec_outfile = $2; }
 | "%output" STRING              { spec_outfile = $2; }
-| "%output" "=" STRING          { spec_outfile = $3; }  /* deprecated */
 | "%param" { current_param = $1; } params { current_param = param_none; }
 | "%require" STRING             { version_check (&@2, $2); }
 | "%skeleton" STRING
 | "%param" { current_param = $1; } params { current_param = param_none; }
 | "%require" STRING             { version_check (&@2, $2); }
 | "%skeleton" STRING
@@ -335,7 +331,7 @@ prologue_declaration:
             xmalloc (dir_length + 1 + strlen (skeleton_user) + 1);
           if (dir_length > 0)
             {
             xmalloc (dir_length + 1 + strlen (skeleton_user) + 1);
           if (dir_length > 0)
             {
-              strncpy (skeleton_build, current_file, dir_length);
+              memcpy (skeleton_build, current_file, dir_length);
               skeleton_build[dir_length++] = '/';
             }
           strcpy (skeleton_build + dir_length, skeleton_user);
               skeleton_build[dir_length++] = '/';
             }
           strcpy (skeleton_build + dir_length, skeleton_user);
@@ -367,19 +363,17 @@ grammar_declaration:
     {
       grammar_start_symbol_set ($2, @2);
     }
     {
       grammar_start_symbol_set ($2, @2);
     }
-| "%destructor" "{...}" generic_symlist
-    {
-      symbol_list *list;
-      for (list = $3; list; list = list->next)
-       symbol_list_destructor_set (list, $2, @2);
-      symbol_list_free ($3);
-    }
-| "%printer" "{...}" generic_symlist
+| code_props_type "{...}" generic_symlist
     {
     {
-      symbol_list *list;
-      for (list = $3; list; list = list->next)
-       symbol_list_printer_set (list, $2, @2);
-      symbol_list_free ($3);
+      code_props code;
+      code_props_symbol_action_init (&code, $2, @2);
+      code_props_translate_code (&code);
+      {
+        symbol_list *list;
+        for (list = $3; list; list = list->next)
+          symbol_list_code_props_set (list, $1, &code);
+        symbol_list_free ($3);
+      }
     }
 | "%default-prec"
     {
     }
 | "%default-prec"
     {
@@ -403,6 +397,13 @@ grammar_declaration:
     }
 ;
 
     }
 ;
 
+%type <code_type> code_props_type;
+%union {code_props_type code_type;};
+%printer { fprintf (yyo, "%s", code_props_type_string ($$)); } <code_type>;
+code_props_type:
+  "%destructor"  { $$ = destructor; }
+| "%printer"     { $$ = printer; }
+;
 
 /*---------.
 | %union.  |
 
 /*---------.
 | %union.  |
@@ -411,15 +412,15 @@ grammar_declaration:
 %token PERCENT_UNION "%union";
 
 union_name:
 %token PERCENT_UNION "%union";
 
 union_name:
-  /* Nothing. */ {}
-| ID             { muscle_code_grow ("union_name", $1, @1); }
+  %empty {}
+| ID     { muscle_code_grow ("union_name", $1, @1); }
 ;
 
 grammar_declaration:
   "%union" union_name braceless
     {
       union_seen = true;
 ;
 
 grammar_declaration:
   "%union" union_name braceless
     {
       union_seen = true;
-      muscle_code_grow ("stype", $3, @3);
+      muscle_code_grow ("union_members", $3, @3);
       code_scanner_last_string_free ();
     }
 ;
       code_scanner_last_string_free ();
     }
 ;
@@ -443,7 +444,7 @@ symbol_declaration:
       symbol_list *list;
       tag_seen = true;
       for (list = $3; list; list = list->next)
       symbol_list *list;
       tag_seen = true;
       for (list = $3; list; list = list->next)
-       symbol_type_set (list->content.sym, $2, @2);
+        symbol_type_set (list->content.sym, $2, @2);
       symbol_list_free ($3);
     }
 ;
       symbol_list_free ($3);
     }
 ;
@@ -454,10 +455,10 @@ precedence_declaration:
       symbol_list *list;
       ++current_prec;
       for (list = $3; list; list = list->next)
       symbol_list *list;
       ++current_prec;
       for (list = $3; list; list = list->next)
-       {
-         symbol_type_set (list->content.sym, current_type, @2);
-         symbol_precedence_set (list->content.sym, current_prec, $1, @1);
-       }
+        {
+          symbol_type_set (list->content.sym, current_type, @2);
+          symbol_precedence_set (list->content.sym, current_prec, $1, @1);
+        }
       symbol_list_free ($3);
       current_type = NULL;
     }
       symbol_list_free ($3);
       current_type = NULL;
     }
@@ -471,8 +472,8 @@ precedence_declarator:
 ;
 
 tag.opt:
 ;
 
 tag.opt:
-  /* Nothing. */ { current_type = NULL; }
-| TAG            { current_type = $1; tag_seen = true; }
+  %empty { current_type = NULL; }
+| TAG    { current_type = $1; tag_seen = true; }
 ;
 
 /* Just like symbols.1 but accept INT for the sake of POSIX.  */
 ;
 
 /* Just like symbols.1 but accept INT for the sake of POSIX.  */
@@ -480,46 +481,59 @@ symbols.prec:
   symbol.prec
     { $$ = symbol_list_sym_new ($1, @1); }
 | symbols.prec symbol.prec
   symbol.prec
     { $$ = symbol_list_sym_new ($1, @1); }
 | symbols.prec symbol.prec
-    { $$ = symbol_list_prepend ($1, symbol_list_sym_new ($2, @2)); }
+    { $$ = symbol_list_append ($1, symbol_list_sym_new ($2, @2)); }
 ;
 
 symbol.prec:
 ;
 
 symbol.prec:
-    symbol { $$ = $1; }
-  | symbol INT { $$ = $1; symbol_user_token_number_set ($1, $2, @2); }
-  ;
+  symbol
+    {
+      $$ = $1;
+      symbol_class_set ($1, token_sym, @1, false);
+    }
+| symbol INT
+    {
+      $$ = $1;
+      symbol_user_token_number_set ($1, $2, @2);
+      symbol_class_set ($1, token_sym, @1, false);
+    }
+;
 
 /* One or more symbols to be %typed. */
 symbols.1:
   symbol
     { $$ = symbol_list_sym_new ($1, @1); }
 | symbols.1 symbol
 
 /* One or more symbols to be %typed. */
 symbols.1:
   symbol
     { $$ = symbol_list_sym_new ($1, @1); }
 | symbols.1 symbol
-    { $$ = symbol_list_prepend ($1, symbol_list_sym_new ($2, @2)); }
+    { $$ = symbol_list_append ($1, symbol_list_sym_new ($2, @2)); }
 ;
 
 generic_symlist:
   generic_symlist_item { $$ = $1; }
 ;
 
 generic_symlist:
   generic_symlist_item { $$ = $1; }
-| generic_symlist generic_symlist_item { $$ = symbol_list_prepend ($1, $2); }
+| generic_symlist generic_symlist_item { $$ = symbol_list_append ($1, $2); }
 ;
 
 generic_symlist_item:
   symbol    { $$ = symbol_list_sym_new ($1, @1); }
 ;
 
 generic_symlist_item:
   symbol    { $$ = symbol_list_sym_new ($1, @1); }
-| TAG       { $$ = symbol_list_type_new ($1, @1); }
-| "<*>"     { $$ = symbol_list_default_tagged_new (@1); }
-| "<>"      { $$ = symbol_list_default_tagless_new (@1); }
+| tag       { $$ = symbol_list_type_new ($1, @1); }
+;
+
+tag:
+  TAG
+| "<*>" { $$ = uniqstr_new ("*"); }
+| "<>"  { $$ = uniqstr_new (""); }
 ;
 
 /* One token definition.  */
 symbol_def:
   TAG
 ;
 
 /* One token definition.  */
 symbol_def:
   TAG
-     {
-       current_type = $1;
-       tag_seen = true;
-     }
+    {
+      current_type = $1;
+      tag_seen = true;
+    }
 | id
 | id
-     {
-       symbol_class_set ($1, current_class, @1, true);
-       symbol_type_set ($1, current_type, @1);
-     }
+    {
+      symbol_class_set ($1, current_class, @1, true);
+      symbol_type_set ($1, current_type, @1);
+    }
 | id INT
     {
       symbol_class_set ($1, current_class, @1, true);
 | id INT
     {
       symbol_class_set ($1, current_class, @1, true);
@@ -548,9 +562,9 @@ symbol_defs.1:
 ;
 
 
 ;
 
 
-       /*------------------------------------------.
-       | The grammar section: between the two %%.  |
-       `------------------------------------------*/
+        /*------------------------------------------.
+        | The grammar section: between the two %%.  |
+        `------------------------------------------*/
 
 grammar:
   rules_or_grammar_declaration
 
 grammar:
   rules_or_grammar_declaration
@@ -569,8 +583,11 @@ rules_or_grammar_declaration:
 ;
 
 rules:
 ;
 
 rules:
-  id_colon named_ref.opt { current_lhs = $1; current_lhs_location = @1;
-    current_lhs_named_ref = $2; } rhses.1
+  id_colon named_ref.opt { current_lhs ($1, @1, $2); } rhses.1
+  {
+    /* Free the current lhs. */
+    current_lhs (0, @1, 0);
+  }
 ;
 
 rhses.1:
 ;
 
 rhses.1:
@@ -579,14 +596,19 @@ rhses.1:
 | rhses.1 ";"
 ;
 
 | rhses.1 ";"
 ;
 
+%token PERCENT_EMPTY "%empty";
 rhs:
 rhs:
-  /* Nothing.  */
-    { grammar_current_rule_begin (current_lhs, current_lhs_location,
-                                 current_lhs_named_ref); }
+  %empty
+    { grammar_current_rule_begin (current_lhs_symbol, current_lhs_location,
+                                  current_lhs_named_ref); }
 | rhs symbol named_ref.opt
     { grammar_current_rule_symbol_append ($2, @2, $3); }
 | rhs "{...}" named_ref.opt
 | rhs symbol named_ref.opt
     { grammar_current_rule_symbol_append ($2, @2, $3); }
 | rhs "{...}" named_ref.opt
-    { grammar_current_rule_action_append ($2, @2, $3); }
+    { grammar_current_rule_action_append ($2, @2, $3, false); }
+| rhs "%?{...}"
+    { grammar_current_rule_action_append ($2, @2, NULL, true); }
+| rhs "%empty"
+    { grammar_current_rule_empty_set (@2); }
 | rhs "%prec" symbol
     { grammar_current_rule_prec_set ($3, @3); }
 | rhs "%dprec" INT
 | rhs "%prec" symbol
     { grammar_current_rule_prec_set ($3, @3); }
 | rhs "%dprec" INT
@@ -596,28 +618,47 @@ rhs:
 ;
 
 named_ref.opt:
 ;
 
 named_ref.opt:
-  /* Nothing. */ { $$ = 0; }
-|
-  BRACKETED_ID   { $$ = named_ref_new($1, @1); }
+  %empty         { $$ = 0; }
+| BRACKETED_ID   { $$ = named_ref_new($1, @1); }
 ;
 
 ;
 
-
-/*---------------------------.
-| variable and content.opt.  |
-`---------------------------*/
+/*---------------------.
+| variable and value.  |
+`---------------------*/
 
 /* The STRING form of variable is deprecated and is not M4-friendly.
 
 /* The STRING form of variable is deprecated and is not M4-friendly.
-   For example, M4 fails for `%define "[" "value"'.  */
+   For example, M4 fails for '%define "[" "value"'.  */
 variable:
   ID
 | STRING { $$ = uniqstr_new ($1); }
 ;
 
 /* Some content or empty by default. */
 variable:
   ID
 | STRING { $$ = uniqstr_new ($1); }
 ;
 
 /* Some content or empty by default. */
-content.opt:
-  /* Nothing. */   { $$ = ""; }
-| ID { $$ = $1; }
-| STRING
+%code requires {#include "muscle-tab.h"};
+%union
+{
+  struct
+  {
+    char const *chars;
+    muscle_kind kind;
+  } value;
+};
+%type <value> value;
+%printer
+{
+  switch ($$.kind)
+    {
+    case muscle_code:    fprintf (yyo,  "{%s}",  $$.chars); break;
+    case muscle_keyword: fprintf (yyo,   "%s",   $$.chars); break;
+    case muscle_string:  fprintf (yyo, "\"%s\"", $$.chars); break;
+    }
+} <value>;
+
+value:
+  %empty    { $$.kind = muscle_keyword; $$.chars = ""; }
+| ID        { $$.kind = muscle_keyword; $$.chars = $1; }
+| STRING    { $$.kind = muscle_string;  $$.chars = $1; }
+| braceless { $$.kind = muscle_code;    $$.chars = $1; }
 ;
 
 
 ;
 
 
@@ -629,7 +670,7 @@ braceless:
   "{...}"
     {
       code_props plain_code;
   "{...}"
     {
       code_props plain_code;
-      $1[strlen ($1) - 1] = '\n';
+      $1[strlen ($1) - 1] = '\0';
       code_props_plain_init (&plain_code, $1+1, @1);
       code_props_translate_code (&plain_code);
       gram_scanner_last_string_free ();
       code_props_plain_init (&plain_code, $1+1, @1);
       code_props_translate_code (&plain_code);
       gram_scanner_last_string_free ();
@@ -676,7 +717,7 @@ string_as_id:
 ;
 
 epilogue.opt:
 ;
 
 epilogue.opt:
-  /* Nothing.  */
+  %empty
 | "%%" EPILOGUE
     {
       code_props plain_code;
 | "%%" EPILOGUE
     {
       code_props plain_code;
@@ -690,7 +731,6 @@ epilogue.opt:
 
 %%
 
 
 %%
 
-
 /* Return the location of the left-hand side of a rule whose
    right-hand side is RHS[1] ... RHS[N].  Ignore empty nonterminals in
    the right-hand side, and return an empty location equal to the end
 /* Return the location of the left-hand side of a rule whose
    right-hand side is RHS[1] ... RHS[N].  Ignore empty nonterminals in
    the right-hand side, and return an empty location equal to the end
@@ -707,14 +747,14 @@ lloc_default (YYLTYPE const *rhs, int n)
   loc.start = rhs[n].end;
   loc.end = rhs[n].end;
 
   loc.start = rhs[n].end;
   loc.end = rhs[n].end;
 
-  /* Ignore empty nonterminals the start of the the right-hand side.
+  /* Ignore empty nonterminals the start of the right-hand side.
      Do not bother to ignore them at the end of the right-hand side,
      since empty nonterminals have the same end as their predecessors.  */
   for (i = 1; i <= n; i++)
     if (! equal_boundaries (rhs[i].start, rhs[i].end))
       {
      Do not bother to ignore them at the end of the right-hand side,
      since empty nonterminals have the same end as their predecessors.  */
   for (i = 1; i <= n; i++)
     if (! equal_boundaries (rhs[i].start, rhs[i].end))
       {
-       loc.start = rhs[i].start;
-       break;
+        loc.start = rhs[i].start;
+        break;
       }
 
   return loc;
       }
 
   return loc;
@@ -742,28 +782,20 @@ add_param (param_type type, char *decl, location loc)
 
     /* Strip the surrounding '{' and '}', and any blanks just inside
        the braces.  */
 
     /* Strip the surrounding '{' and '}', and any blanks just inside
        the braces.  */
-    while (*--p == ' ' || *p == '\t')
-      continue;
+    --p;
+  while (c_isspace ((unsigned char) *p))
+      --p;
     p[1] = '\0';
     p[1] = '\0';
-    while (*++decl == ' ' || *decl == '\t')
-      continue;
+    ++decl;
+  while (c_isspace ((unsigned char) *decl))
+      ++decl;
   }
 
   if (! name_start)
   }
 
   if (! name_start)
-    complain_at (loc, _("missing identifier in parameter declaration"));
+    complain (&loc, complaint, _("missing identifier in parameter declaration"));
   else
     {
   else
     {
-      char *name;
-      size_t name_len;
-
-      for (name_len = 1;
-          memchr (alphanum, name_start[name_len], sizeof alphanum);
-          name_len++)
-       continue;
-
-      name = xmalloc (name_len + 1);
-      memcpy (name, name_start, name_len);
-      name[name_len] = '\0';
+      char *name = xmemdup0 (name_start, strspn (name_start, alphanum));
       if (type & param_lex)
         muscle_pair_list_grow ("lex_param", decl, name);
       if (type & param_parse)
       if (type & param_lex)
         muscle_pair_list_grow ("lex_param", decl, name);
       if (type & param_parse)
@@ -780,16 +812,16 @@ version_check (location const *loc, char const *version)
 {
   if (strverscmp (version, PACKAGE_VERSION) > 0)
     {
 {
   if (strverscmp (version, PACKAGE_VERSION) > 0)
     {
-      complain_at (*loc, "require bison %s, but have %s",
-                  version, PACKAGE_VERSION);
-      exit (63);
+      complain (loc, complaint, "require bison %s, but have %s",
+                version, PACKAGE_VERSION);
+      exit (EX_MISMATCH);
     }
 }
 
 static void
 gram_error (location const *loc, char const *msg)
 {
     }
 }
 
 static void
 gram_error (location const *loc, char const *msg)
 {
-  complain_at (*loc, "%s", msg);
+  complain (loc, complaint, "%s", msg);
 }
 
 char const *
 }
 
 char const *
@@ -810,3 +842,18 @@ char_name (char c)
       return quotearg_style (escape_quoting_style, buf);
     }
 }
       return quotearg_style (escape_quoting_style, buf);
     }
 }
+
+static
+void
+current_lhs (symbol *sym, location loc, named_ref *ref)
+{
+  current_lhs_symbol = sym;
+  current_lhs_location = loc;
+  /* In order to simplify memory management, named references for lhs
+     are always assigned by deep copy into the current symbol_list
+     node.  This is because a single named-ref in the grammar may
+     result in several uses when the user factors lhs between several
+     rules using "|".  Therefore free the parser's original copy.  */
+  free (current_lhs_named_ref);
+  current_lhs_named_ref = ref;
+}