]> git.saurik.com Git - bison.git/blobdiff - src/parse-gram.y
glr.cc, lalr1.cc: define b4_shared_declarations
[bison.git] / src / parse-gram.y
index 802347fa628ddaa32232c85a1085743d1f7dcbb0..518969948f0586a22d438f41606175240e32e19d 100644 (file)
@@ -1,13 +1,12 @@
 %{/* Bison Grammar Parser                             -*- C -*-
 
 %{/* Bison Grammar Parser                             -*- C -*-
 
-   Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation,
-   Inc.
+   Copyright (C) 2002-2012 Free Software Foundation, Inc.
 
    This file is part of Bison, the GNU Compiler Compiler.
 
 
    This file is part of Bison, the GNU Compiler Compiler.
 
-   This program is free software; you can redistribute it and/or modify
+   This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301  USA
-*/
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 #include "system.h"
 
 
 #include <config.h>
 #include "system.h"
 
-#include <strverscmp.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 "muscle-tab.h"
+#include "named-ref.h"
 #include "quotearg.h"
 #include "reader.h"
 #include "symlist.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"
+#include "xmemdup0.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) \
 
 #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)
+          location_print (File, Loc)
 
 static void version_check (location const *loc, char const *version);
 
 
 static void version_check (location const *loc, char const *version);
 
@@ -50,38 +47,52 @@ static void version_check (location const *loc, char const *version);
    FIXME: depends on the undocumented availability of YYLLOC.  */
 #undef  yyerror
 #define yyerror(Msg) \
    FIXME: depends on the undocumented availability of YYLLOC.  */
 #undef  yyerror
 #define yyerror(Msg) \
-       gram_error (&yylloc, Msg)
+        gram_error (&yylloc, Msg)
 static void gram_error (location const *, char const *);
 
 static char const *char_name (char);
 static void gram_error (location const *, char const *);
 
 static char const *char_name (char);
-
-/** Add a lex-param or a parse-param.
- *
- * \param type  \a lex_param or \a parse_param
- * \param decl  the formal argument
- * \param loc   the location in the source.
- */
-static void add_param (char const *type, char *decl, location loc);
-
-
-static symbol_class current_class = unknown_sym;
-static uniqstr current_type = NULL;
-static symbol *current_lhs;
-static location current_lhs_location;
-static int current_prec = 0;
-
-#define YYTYPE_INT16 int_fast16_t
-#define YYTYPE_INT8 int_fast8_t
-#define YYTYPE_UINT16 uint_fast16_t
-#define YYTYPE_UINT8 uint_fast8_t
 %}
 
 %}
 
+%code
+{
+  static int current_prec = 0;
+  static location current_lhs_location;
+  static named_ref *current_lhs_named_ref;
+  static symbol *current_lhs_symbol;
+  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)
+  {
+    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;
+  }
+
+  #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
 %defines
 %locations
 %pure-parser
 %debug
 %verbose
 %defines
 %locations
 %pure-parser
-%error-verbose
+%define parse.error "verbose"
+%define parse.lac full
 %name-prefix="gram_"
 %expect 0
 
 %name-prefix="gram_"
 %expect 0
 
@@ -95,12 +106,13 @@ static int current_prec = 0;
 
 %union
 {
 
 %union
 {
+  assoc assoc;
+  char *code;
+  char const *chars;
+  int integer;
+  named_ref *named_ref;
   symbol *symbol;
   symbol_list *list;
   symbol *symbol;
   symbol_list *list;
-  int integer;
-  char const *chars;
-  char *code;
-  assoc assoc;
   uniqstr uniqstr;
   unsigned char character;
 };
   uniqstr uniqstr;
   unsigned char character;
 };
@@ -120,43 +132,36 @@ static int current_prec = 0;
 %token PERCENT_LEFT        "%left"
 %token PERCENT_RIGHT       "%right"
 %token PERCENT_NONASSOC    "%nonassoc"
 %token PERCENT_LEFT        "%left"
 %token PERCENT_RIGHT       "%right"
 %token PERCENT_NONASSOC    "%nonassoc"
+%token PERCENT_PRECEDENCE  "%precedence"
 
 %token PERCENT_PREC          "%prec"
 %token PERCENT_DPREC         "%dprec"
 %token PERCENT_MERGE         "%merge"
 
 
 %token PERCENT_PREC          "%prec"
 %token PERCENT_DPREC         "%dprec"
 %token PERCENT_MERGE         "%merge"
 
-
 /*----------------------.
 | Global Declarations.  |
 `----------------------*/
 
 %token
   PERCENT_CODE            "%code"
 /*----------------------.
 | Global Declarations.  |
 `----------------------*/
 
 %token
   PERCENT_CODE            "%code"
-  PERCENT_DEBUG           "%debug"
   PERCENT_DEFAULT_PREC    "%default-prec"
   PERCENT_DEFINE          "%define"
   PERCENT_DEFINES         "%defines"
   PERCENT_ERROR_VERBOSE   "%error-verbose"
   PERCENT_EXPECT          "%expect"
   PERCENT_DEFAULT_PREC    "%default-prec"
   PERCENT_DEFINE          "%define"
   PERCENT_DEFINES         "%defines"
   PERCENT_ERROR_VERBOSE   "%error-verbose"
   PERCENT_EXPECT          "%expect"
-  PERCENT_EXPECT_RR      "%expect-rr"
+  PERCENT_EXPECT_RR       "%expect-rr"
+  PERCENT_FLAG            "%<flag>"
   PERCENT_FILE_PREFIX     "%file-prefix"
   PERCENT_GLR_PARSER      "%glr-parser"
   PERCENT_INITIAL_ACTION  "%initial-action"
   PERCENT_LANGUAGE        "%language"
   PERCENT_FILE_PREFIX     "%file-prefix"
   PERCENT_GLR_PARSER      "%glr-parser"
   PERCENT_INITIAL_ACTION  "%initial-action"
   PERCENT_LANGUAGE        "%language"
-  PERCENT_LEX_PARAM       "%lex-param"
-  PERCENT_LOCATIONS       "%locations"
   PERCENT_NAME_PREFIX     "%name-prefix"
   PERCENT_NO_DEFAULT_PREC "%no-default-prec"
   PERCENT_NO_LINES        "%no-lines"
   PERCENT_NONDETERMINISTIC_PARSER
   PERCENT_NAME_PREFIX     "%name-prefix"
   PERCENT_NO_DEFAULT_PREC "%no-default-prec"
   PERCENT_NO_LINES        "%no-lines"
   PERCENT_NONDETERMINISTIC_PARSER
-                         "%nondeterministic-parser"
+                          "%nondeterministic-parser"
   PERCENT_OUTPUT          "%output"
   PERCENT_OUTPUT          "%output"
-  PERCENT_PARSE_PARAM     "%parse-param"
-  PERCENT_PURE_PARSER     "%pure-parser"
-  PERCENT_PUSH_PARSER     "%push-parser"
-  PERCENT_PUSH_PULL_PARSER
-                          "%push-pull-parser"
-  PERCENT_REQUIRE        "%require"
+  PERCENT_REQUIRE         "%require"
   PERCENT_SKELETON        "%skeleton"
   PERCENT_START           "%start"
   PERCENT_TOKEN_TABLE     "%token-table"
   PERCENT_SKELETON        "%skeleton"
   PERCENT_START           "%start"
   PERCENT_TOKEN_TABLE     "%token-table"
@@ -165,6 +170,8 @@ static int current_prec = 0;
 ;
 
 %token BRACED_CODE     "{...}"
 ;
 
 %token BRACED_CODE     "{...}"
+%token BRACED_PREDICATE "%?{...}"
+%token BRACKETED_ID    "[identifier]"
 %token CHAR            "char"
 %token EPILOGUE        "epilogue"
 %token EQUAL           "="
 %token CHAR            "char"
 %token EPILOGUE        "epilogue"
 %token EQUAL           "="
@@ -174,36 +181,90 @@ static int current_prec = 0;
 %token PIPE            "|"
 %token PROLOGUE        "%{...%}"
 %token SEMICOLON       ";"
 %token PIPE            "|"
 %token PROLOGUE        "%{...%}"
 %token SEMICOLON       ";"
-%token TYPE            "type"
-%token TYPE_TAG_ANY    "<*>"
-%token TYPE_TAG_NONE   "<>"
+%token TAG             "<tag>"
+%token TAG_ANY         "<*>"
+%token TAG_NONE        "<>"
 
 %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.  */
 %type <chars> STRING "%{...%}" EPILOGUE braceless content.opt
 
 /* 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> TYPE ID ID_COLON variable
-%printer { fprintf (stderr, "<%s>", $$); } TYPE
-%printer { fputs ($$, stderr); } ID variable
-%printer { fprintf (stderr, "%s:", $$); } ID_COLON
+%type <code> "{...}" "%?{...}"
+%printer { fputs (quotearg_style (c_quoting_style, $$), yyo); }
+         STRING
+%printer { fprintf (yyo, "{\n%s\n}", $$); }
+         braceless content.opt "{...}" "%{...%}" EPILOGUE
+
+%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
 
 %type <integer> INT
 
 %type <integer> INT
-%printer { fprintf (stderr, "%d", $$); } INT
+%printer { fprintf (yyo, "%d", $$); } <integer>
 
 
-%type <symbol> id id_colon symbol string_as_id
-%printer { fprintf (stderr, "%s", $$->tag); } id symbol string_as_id
-%printer { fprintf (stderr, "%s:", $$->tag); } id_colon
+%type <symbol> id id_colon string_as_id symbol symbol.prec
+%printer { fprintf (yyo, "%s", $$->tag); } <symbol>
+%printer { fprintf (yyo, "%s:", $$->tag); } id_colon
 
 %type <assoc> precedence_declarator
 
 %type <assoc> precedence_declarator
-%type <list>  symbols.1 generic_symlist generic_symlist_item
+%type <list>  symbols.1 symbols.prec generic_symlist generic_symlist_item
+%type <named_ref> named_ref.opt
+
+/*---------.
+| %param.  |
+`---------*/
+%code requires
+{
+# ifndef PARAM_TYPE
+#  define PARAM_TYPE
+  typedef enum
+  {
+    param_none   = 0,
+    param_lex    = 1 << 0,
+    param_parse  = 1 << 1,
+    param_both   = param_lex | param_parse
+  } param_type;
+# endif
+};
+%code
+{
+  /** Add a lex-param and/or a parse-param.
+   *
+   * \param type  where to push this formal argument.
+   * \param decl  the formal argument.  Destroyed.
+   * \param loc   the location in the source.
+   */
+  static void add_param (param_type type, char *decl, location loc);
+  static param_type current_param = param_none;
+};
+%union
+{
+  param_type param;
+}
+%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);
+#undef CASE
+      case param_none: aver (false); break;
+    }
+} <param>;
+
+
+                     /*==========\
+                     | Grammar.  |
+                     \==========*/
 %%
 
 input:
 %%
 
 input:
@@ -211,9 +272,9 @@ input:
 ;
 
 
 ;
 
 
-       /*------------------------------------.
-       | Declarations: before the first %%.  |
-       `------------------------------------*/
+        /*------------------------------------.
+        | Declarations: before the first %%.  |
+        `------------------------------------*/
 
 prologue_declarations:
   /* Nothing */
 
 prologue_declarations:
   /* Nothing */
@@ -232,18 +293,14 @@ prologue_declaration:
                         plain_code.code, @1);
       code_scanner_last_string_free ();
     }
                         plain_code.code, @1);
       code_scanner_last_string_free ();
     }
-| "%debug"                         { debug_flag = true; }
+| "%<flag>"
+    {
+      muscle_percent_define_ensure ($1, @1, true);
+    }
 | "%define" variable content.opt
     {
 | "%define" variable content.opt
     {
-      char const name_prefix[] = "percent_define_";
-      char *name = xmalloc (sizeof name_prefix + strlen ($2));
-      strcpy (name, name_prefix);
-      strcpy (name + sizeof name_prefix - 1, $2);
-      if (muscle_find_const (name))
-        warn_at (@2, _("%s `%s' redefined"), "%define variable", $2);
-      MUSCLE_INSERT_STRING (uniqstr_new (name), $3);
-      free (name);
-      muscle_grow_user_name_list ("user_percent_define_variables", $2, @2);
+      muscle_percent_define_insert ($2, @2, $3,
+                                    MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
     }
 | "%defines"                       { defines_flag = true; }
 | "%defines" STRING
     }
 | "%defines"                       { defines_flag = true; }
 | "%defines" STRING
@@ -251,9 +308,13 @@ prologue_declaration:
       defines_flag = true;
       spec_defines_file = xstrdup ($2);
     }
       defines_flag = true;
       spec_defines_file = xstrdup ($2);
     }
-| "%error-verbose"                 { error_verbose = true; }
+| "%error-verbose"
+    {
+      muscle_percent_define_insert ("parse.error", @1, "verbose",
+                                    MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
+    }
 | "%expect" INT                    { expected_sr_conflicts = $2; }
 | "%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 = $3; } /* deprecated */
 | "%glr-parser"
 | "%file-prefix" STRING            { spec_file_prefix = $2; }
 | "%file-prefix" "=" STRING        { spec_file_prefix = $3; } /* deprecated */
 | "%glr-parser"
@@ -270,27 +331,55 @@ 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, 1, &@1); }
-| "%lex-param" "{...}"         { add_param ("lex_param", $2, @2); }
-| "%locations"                  { locations_flag = true; }
+| "%language" STRING            { language_argmatch ($2, grammar_prio, @1); }
 | "%name-prefix" STRING         { spec_name_prefix = $2; }
 | "%name-prefix" "=" STRING     { spec_name_prefix = $3; } /* deprecated */
 | "%no-lines"                   { no_lines_flag = true; }
 | "%name-prefix" STRING         { spec_name_prefix = $2; }
 | "%name-prefix" "=" STRING     { spec_name_prefix = $3; } /* deprecated */
 | "%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 = $3; }  /* deprecated */
 | "%output" STRING              { spec_outfile = $2; }
 | "%output" "=" STRING          { spec_outfile = $3; }  /* deprecated */
-| "%parse-param" "{...}"       { add_param ("parse_param", $2, @2); }
-| "%pure-parser"                { pure_parser = true; }
-| "%push-parser"                { push_parser = true; pull_parser = false; }
-| "%push-pull-parser"           { push_parser = true; pull_parser = true; }
+| "%param" { current_param = $1; } params { current_param = param_none; }
 | "%require" STRING             { version_check (&@2, $2); }
 | "%require" STRING             { version_check (&@2, $2); }
-| "%skeleton" STRING            { skeleton_arg ($2, 1, &@1); }
+| "%skeleton" STRING
+    {
+      char const *skeleton_user = $2;
+      if (strchr (skeleton_user, '/'))
+        {
+          size_t dir_length = strlen (current_file);
+          char *skeleton_build;
+          while (dir_length && current_file[dir_length - 1] != '/')
+            --dir_length;
+          while (dir_length && current_file[dir_length - 1] == '/')
+            --dir_length;
+          skeleton_build =
+            xmalloc (dir_length + 1 + strlen (skeleton_user) + 1);
+          if (dir_length > 0)
+            {
+              memcpy (skeleton_build, current_file, dir_length);
+              skeleton_build[dir_length++] = '/';
+            }
+          strcpy (skeleton_build + dir_length, skeleton_user);
+          skeleton_user = uniqstr_new (skeleton_build);
+          free (skeleton_build);
+        }
+      skeleton_arg (skeleton_user, grammar_prio, @1);
+    }
 | "%token-table"                { token_table_flag = true; }
 | "%token-table"                { token_table_flag = true; }
-| "%verbose"                    { report_flag = report_states; }
+| "%verbose"                    { report_flag |= report_states; }
 | "%yacc"                       { yacc_flag = true; }
 | /*FIXME: Err?  What is this horror doing here? */ ";"
 ;
 
 | "%yacc"                       { yacc_flag = true; }
 | /*FIXME: Err?  What is this horror doing here? */ ";"
 ;
 
+params:
+   params "{...}"  { add_param (current_param, $2, @2); }
+| "{...}"          { add_param (current_param, $1, @1); }
+;
+
+
+/*----------------------.
+| grammar_declaration.  |
+`----------------------*/
+
 grammar_declaration:
   precedence_declaration
 | symbol_declaration
 grammar_declaration:
   precedence_declaration
 | symbol_declaration
@@ -298,19 +387,17 @@ grammar_declaration:
     {
       grammar_start_symbol_set ($2, @2);
     }
     {
       grammar_start_symbol_set ($2, @2);
     }
-| "%destructor" "{...}" generic_symlist
+| code_props_type "{...}" 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
-    {
-      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"
     {
@@ -322,26 +409,29 @@ grammar_declaration:
     }
 | "%code" braceless
     {
     }
 | "%code" braceless
     {
-      muscle_code_grow ("percent_code", $2, @2);
+      /* Do not invoke muscle_percent_code_grow here since it invokes
+         muscle_user_name_list_grow.  */
+      muscle_code_grow ("percent_code()", $2, @2);
       code_scanner_last_string_free ();
     }
 | "%code" ID braceless
     {
       code_scanner_last_string_free ();
     }
 | "%code" ID braceless
     {
-      char const name_prefix[] = "percent_code_";
-      char *name = xmalloc (sizeof name_prefix + strlen ($2));
-      strcpy (name, name_prefix);
-      strcpy (name + sizeof name_prefix - 1, $2);
-      muscle_code_grow (uniqstr_new (name), $3, @3);
-      free (name);
+      muscle_percent_code_grow ($2, @2, $3, @3);
       code_scanner_last_string_free ();
       code_scanner_last_string_free ();
-      muscle_grow_user_name_list ("user_percent_code_qualifiers", $2, @2);
     }
 ;
 
     }
 ;
 
+%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.  |
+`---------*/
 
 %token PERCENT_UNION "%union";
 
 
 %token PERCENT_UNION "%union";
 
@@ -373,40 +463,54 @@ symbol_declaration:
       current_class = unknown_sym;
       current_type = NULL;
     }
       current_class = unknown_sym;
       current_type = NULL;
     }
-| "%type" TYPE symbols.1
+| "%type" TAG symbols.1
     {
       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);
     }
 ;
 
 precedence_declaration:
       symbol_list_free ($3);
     }
 ;
 
 precedence_declaration:
-  precedence_declarator type.opt symbols.1
+  precedence_declarator tag.opt symbols.prec
     {
       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;
     }
 ;
 
 precedence_declarator:
       symbol_list_free ($3);
       current_type = NULL;
     }
 ;
 
 precedence_declarator:
-  "%left"     { $$ = left_assoc; }
-| "%right"    { $$ = right_assoc; }
-| "%nonassoc" { $$ = non_assoc; }
+  "%left"       { $$ = left_assoc; }
+| "%right"      { $$ = right_assoc; }
+| "%nonassoc"   { $$ = non_assoc; }
+| "%precedence" { $$ = precedence_assoc; }
 ;
 
 ;
 
-type.opt:
+tag.opt:
   /* Nothing. */ { current_type = NULL; }
   /* Nothing. */ { current_type = NULL; }
-| TYPE           { current_type = $1; tag_seen = true; }
+| TAG            { current_type = $1; tag_seen = true; }
+;
+
+/* Just like symbols.1 but accept INT for the sake of POSIX.  */
+symbols.prec:
+  symbol.prec
+    { $$ = symbol_list_sym_new ($1, @1); }
+| symbols.prec symbol.prec
+    { $$ = symbol_list_prepend ($1, symbol_list_sym_new ($2, @2)); }
+;
+
+symbol.prec:
+  symbol     { $$ = $1; }
+| symbol INT { $$ = $1; symbol_user_token_number_set ($1, $2, @2); }
 ;
 
 /* One or more symbols to be %typed. */
 ;
 
 /* One or more symbols to be %typed. */
@@ -423,15 +527,19 @@ generic_symlist:
 ;
 
 generic_symlist_item:
 ;
 
 generic_symlist_item:
-  symbol            { $$ = symbol_list_sym_new ($1, @1); }
-| TYPE              { $$ = symbol_list_type_new ($1, @1); }
-| "<*>"             { $$ = symbol_list_default_tagged_new (@1); }
-| "<>"             { $$ = symbol_list_default_tagless_new (@1); }
+  symbol    { $$ = symbol_list_sym_new ($1, @1); }
+| tag       { $$ = symbol_list_type_new ($1, @1); }
+;
+
+tag:
+  TAG
+| "<*>" { $$ = uniqstr_new ("*"); }
+| "<>"  { $$ = uniqstr_new (""); }
 ;
 
 /* One token definition.  */
 symbol_def:
 ;
 
 /* One token definition.  */
 symbol_def:
-  TYPE
+  TAG
      {
        current_type = $1;
        tag_seen = true;
      {
        current_type = $1;
        tag_seen = true;
@@ -469,9 +577,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
@@ -490,7 +598,11 @@ rules_or_grammar_declaration:
 ;
 
 rules:
 ;
 
 rules:
-  id_colon { current_lhs = $1; current_lhs_location = @1; } 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:
@@ -501,43 +613,50 @@ rhses.1:
 
 rhs:
   /* Nothing.  */
 
 rhs:
   /* Nothing.  */
-    { grammar_current_rule_begin (current_lhs, current_lhs_location); }
-| rhs symbol
-    { grammar_current_rule_symbol_append ($2, @2); }
-| rhs "{...}"
-    { grammar_current_rule_action_append ($2, @2); }
+    { 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
+    { grammar_current_rule_action_append ($2, @2, $3, false); }
+| rhs "%?{...}"
+    { grammar_current_rule_action_append ($2, @2, NULL, true); }
 | rhs "%prec" symbol
     { grammar_current_rule_prec_set ($3, @3); }
 | rhs "%dprec" INT
     { grammar_current_rule_dprec_set ($3, @3); }
 | rhs "%prec" symbol
     { grammar_current_rule_prec_set ($3, @3); }
 | rhs "%dprec" INT
     { grammar_current_rule_dprec_set ($3, @3); }
-| rhs "%merge" TYPE
+| rhs "%merge" TAG
     { grammar_current_rule_merge_set ($3, @3); }
 ;
 
     { grammar_current_rule_merge_set ($3, @3); }
 ;
 
+named_ref.opt:
+  /* Nothing. */ { $$ = 0; }
+|
+  BRACKETED_ID   { $$ = named_ref_new($1, @1); }
+;
 
 
-/*----------------------------*
- | variable and content.opt.  |
- *---------------------------*/
+/*---------------------------.
+| variable and content.opt.  |
+`---------------------------*/
 
 
+/* The STRING form of variable is deprecated and is not M4-friendly.
+   For example, M4 fails for `%define "[" "value"'.  */
 variable:
   ID
 variable:
   ID
-  | STRING { $$ = uniqstr_new ($1); } /* deprecated and not M4-friendly */
-  ;
+| STRING { $$ = uniqstr_new ($1); }
+;
 
 
-/* Some content or "1" by default. */
+/* Some content or empty by default. */
 content.opt:
 content.opt:
-  /* Nothing. */
-    {
-      static char one[] = "1";
-      $$ = one;
-    }
+  /* Nothing. */   { $$ = ""; }
+| ID { $$ = $1; }
 | STRING
 ;
 
 
 | STRING
 ;
 
 
-/*-------------*
- | braceless.  |
- *-------------*/
+/*------------.
+| braceless.  |
+`------------*/
 
 braceless:
   "{...}"
 
 braceless:
   "{...}"
@@ -552,9 +671,9 @@ braceless:
 ;
 
 
 ;
 
 
-/*---------------*
- | Identifiers.  |
- *---------------*/
+/*--------------.
+| Identifiers.  |
+`--------------*/
 
 /* Identifiers are returned as uniqstr values by the scanner.
    Depending on their use, we may need to make them genuine symbols.  */
 
 /* Identifiers are returned as uniqstr values by the scanner.
    Depending on their use, we may need to make them genuine symbols.  */
@@ -621,65 +740,58 @@ 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;
 }
 
 
-/* Add a lex-param or a parse-param (depending on TYPE) with
-   declaration DECL and location LOC.  */
-
 static void
 static void
-add_param (char const *type, char *decl, location loc)
+add_param (param_type type, char *decl, location loc)
 {
   static char const alphanum[26 + 26 + 1 + 10] =
     "abcdefghijklmnopqrstuvwxyz"
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     "_"
     "0123456789";
 {
   static char const alphanum[26 + 26 + 1 + 10] =
     "abcdefghijklmnopqrstuvwxyz"
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     "_"
     "0123456789";
+
   char const *name_start = NULL;
   char const *name_start = NULL;
-  char *p;
-
-  /* Stop on last actual character.  */
-  for (p = decl; p[1]; p++)
-    if ((p == decl
-        || ! memchr (alphanum, p[-1], sizeof alphanum))
-       && memchr (alphanum, p[0], sizeof alphanum - 10))
-      name_start = p;
-
-  /* Strip the surrounding '{' and '}', and any blanks just inside
-     the braces.  */
-  while (*--p == ' ' || *p == '\t')
-    continue;
-  p[1] = '\0';
-  while (*++decl == ' ' || *decl == '\t')
-    continue;
+  {
+    char *p;
+    /* Stop on last actual character.  */
+    for (p = decl; p[1]; p++)
+      if ((p == decl
+           || ! memchr (alphanum, p[-1], sizeof alphanum))
+          && memchr (alphanum, p[0], sizeof alphanum - 10))
+        name_start = p;
+
+    /* Strip the surrounding '{' and '}', and any blanks just inside
+       the braces.  */
+    while (*--p == ' ' || *p == '\t')
+      continue;
+    p[1] = '\0';
+    while (*++decl == ' ' || *decl == '\t')
+      continue;
+  }
 
   if (! name_start)
 
   if (! name_start)
-    complain_at (loc, _("missing identifier in parameter declaration"));
+    complain_at (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';
-      muscle_pair_list_grow (type, decl, name);
+      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)
+        muscle_pair_list_grow ("parse_param", decl, name);
       free (name);
     }
 
       free (name);
     }
 
@@ -692,16 +804,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_at (*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_at (*loc, complaint, "%s", msg);
 }
 
 char const *
 }
 
 char const *