X-Git-Url: https://git.saurik.com/bison.git/blobdiff_plain/39a06c251ad9c523b1172835e545d2043431873d..6cf3716c3166d6d41cdbeea45d018b1760184421:/src/parse-gram.y?ds=sidebyside

diff --git a/src/parse-gram.y b/src/parse-gram.y
index 642a97cc..c932ba54 100644
--- a/src/parse-gram.y
+++ b/src/parse-gram.y
@@ -1,12 +1,12 @@
-/* Bison Grammar Parser                             -*- C -*-
+%{/* Bison Grammar Parser                             -*- C -*-
 
-   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002-2012 Free Software Foundation, Inc.
 
    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
-   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,
@@ -15,21 +15,9 @@
    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., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307  USA
-*/
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-
-%debug
-%defines
-%locations
-%pure-parser
-%error-verbose
-%defines
-%name-prefix="gram_"
-
-%{
+#include <config.h>
 #include "system.h"
 
 #include "complain.h"
@@ -37,14 +25,23 @@
 #include "files.h"
 #include "getargs.h"
 #include "gram.h"
-#include "muscle_tab.h"
-#include "output.h"
+#include "muscle-tab.h"
+#include "named-ref.h"
+#include "quotearg.h"
 #include "reader.h"
 #include "symlist.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) \
+          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
@@ -52,38 +49,71 @@ static YYLTYPE lloc_default (YYLTYPE const *, int);
         gram_error (&yylloc, Msg)
 static void gram_error (location const *, char const *);
 
-#define YYPRINT(File, Type, Value) \
-	print_token_value (File, Type, &Value)
-static void print_token_value (FILE *, int, YYSTYPE const *);
+static char const *char_name (char);
+%}
 
-static void add_param (char const *, char *, location);
+%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;
+  }
 
-symbol_class current_class = unknown_sym;
-uniqstr current_type = 0;
-symbol *current_lhs;
-location current_lhs_location;
-assoc current_assoc;
-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
+}
+
+%debug
+%verbose
+%defines
+%locations
+%pure-parser
+%define parse.error "verbose"
+%define parse.lac full
+%name-prefix="gram_"
+%expect 0
 
 %initial-action
 {
   /* Bison's grammar can initial empty locations, hence a default
      location is needed. */
-  @$.start.file   = @$.end.file   = current_file;
-  @$.start.line   = @$.end.line   = 1;
-  @$.start.column = @$.end.column = 0;
+  boundary_set (&@$.start, current_file, 1, 1);
+  boundary_set (&@$.end, current_file, 1, 1);
 }
 
-/* Only NUMBERS have a value.  */
 %union
 {
+  assoc assoc;
+  char *code;
+  char const *chars;
+  int integer;
+  named_ref *named_ref;
   symbol *symbol;
   symbol_list *list;
-  int integer;
-  char *chars;
-  assoc assoc;
   uniqstr uniqstr;
+  unsigned char character;
 };
 
 /* Define the tokens together with their human representation.  */
@@ -95,43 +125,42 @@ int current_prec = 0;
 %token PERCENT_NTERM       "%nterm"
 
 %token PERCENT_TYPE        "%type"
-%token PERCENT_DESTRUCTOR  "%destructor {...}"
-%token PERCENT_PRINTER     "%printer {...}"
-
-%token PERCENT_UNION       "%union {...}"
+%token PERCENT_DESTRUCTOR  "%destructor"
+%token PERCENT_PRINTER     "%printer"
 
 %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"
 
-
 /*----------------------.
 | Global Declarations.  |
 `----------------------*/
 
 %token
-  PERCENT_DEBUG           "%debug"
+  PERCENT_CODE            "%code"
   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_FLAG            "%<flag>"
   PERCENT_FILE_PREFIX     "%file-prefix"
   PERCENT_GLR_PARSER      "%glr-parser"
-  PERCENT_INITIAL_ACTION  "%initial-action {...}"
-  PERCENT_LEX_PARAM       "%lex-param {...}"
-  PERCENT_LOCATIONS       "%locations"
+  PERCENT_INITIAL_ACTION  "%initial-action"
+  PERCENT_LANGUAGE        "%language"
   PERCENT_NAME_PREFIX     "%name-prefix"
+  PERCENT_NO_DEFAULT_PREC "%no-default-prec"
   PERCENT_NO_LINES        "%no-lines"
   PERCENT_NONDETERMINISTIC_PARSER
                           "%nondeterministic-parser"
   PERCENT_OUTPUT          "%output"
-  PERCENT_PARSE_PARAM     "%parse-param {...}"
-  PERCENT_PURE_PARSER     "%pure-parser"
+  PERCENT_REQUIRE         "%require"
   PERCENT_SKELETON        "%skeleton"
   PERCENT_START           "%start"
   PERCENT_TOKEN_TABLE     "%token-table"
@@ -139,81 +168,217 @@ int current_prec = 0;
   PERCENT_YACC            "%yacc"
 ;
 
-%token TYPE            "type"
+%token BRACED_CODE     "{...}"
+%token BRACED_PREDICATE "%?{...}"
+%token BRACKETED_ID    "[identifier]"
+%token CHAR            "char"
+%token EPILOGUE        "epilogue"
 %token EQUAL           "="
-%token SEMICOLON       ";"
-%token PIPE            "|"
 %token ID              "identifier"
 %token ID_COLON        "identifier:"
 %token PERCENT_PERCENT "%%"
+%token PIPE            "|"
 %token PROLOGUE        "%{...%}"
-%token EPILOGUE        "epilogue"
-%token BRACED_CODE     "{...}"
-
+%token SEMICOLON       ";"
+%token TAG             "<tag>"
+%token TAG_ANY         "<*>"
+%token TAG_NONE        "<>"
+
+%type <character> CHAR
+%printer { fputs (char_name ($$), stderr); } 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
+%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 <chars> STRING string_content
-	      "%destructor {...}"
-	      "%initial-action {...}"
-	      "%lex-param {...}"
-	      "%parse-param {...}"
-	      "%printer {...}"
-	      "%union {...}"
-	      BRACED_CODE action
-	      PROLOGUE EPILOGUE
-%type <uniqstr> TYPE
 %type <integer> INT
-%type <symbol> ID ID_COLON symbol string_as_id
+%printer { fprintf (stderr, "%d", $$); } <integer>
+
+%type <symbol> id id_colon string_as_id symbol symbol.prec
+%printer { fprintf (stderr, "%s", $$->tag); } <symbol>
+%printer { fprintf (stderr, "%s:", $$->tag); } id_colon
+
 %type <assoc> precedence_declarator
-%type <list>  symbols.1
+%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:
-  declarations "%%" grammar epilogue.opt
+  prologue_declarations "%%" grammar epilogue.opt
 ;
 
 
-	/*------------------------------------.
-	| Declarations: before the first %%.  |
-	`------------------------------------*/
+        /*------------------------------------.
+        | Declarations: before the first %%.  |
+        `------------------------------------*/
 
-declarations:
+prologue_declarations:
   /* Nothing */
-| declarations declaration
+| prologue_declarations prologue_declaration
 ;
 
-declaration:
+prologue_declaration:
   grammar_declaration
-| PROLOGUE                                 { prologue_augment ($1, @1); }
-| "%debug"                                 { debug_flag = true; }
-| "%define" string_content string_content  { muscle_insert ($2, $3); }
-| "%defines"                               { defines_flag = true; }
-| "%error-verbose"                         { error_verbose = true; }
-| "%expect" INT                            { expected_conflicts = $2; }
-| "%file-prefix" "=" string_content        { spec_file_prefix = $3; }
+| "%{...%}"
+    {
+      code_props plain_code;
+      code_props_plain_init (&plain_code, $1, @1);
+      code_props_translate_code (&plain_code);
+      gram_scanner_last_string_free ();
+      muscle_code_grow (union_seen ? "post_prologue" : "pre_prologue",
+                        plain_code.code, @1);
+      code_scanner_last_string_free ();
+    }
+| "%<flag>"
+    {
+      muscle_percent_define_ensure ($1, @1, true);
+    }
+| "%define" variable content.opt
+    {
+      muscle_percent_define_insert ($2, @2, $3,
+                                    MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
+    }
+| "%defines"                       { defines_flag = true; }
+| "%defines" STRING
+    {
+      defines_flag = true;
+      spec_defines_file = xstrdup ($2);
+    }
+| "%error-verbose"
+    {
+      muscle_percent_define_insert ("parse.error", @1, "verbose",
+                                    MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
+    }
+| "%expect" INT                    { expected_sr_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"
-  {
-    nondeterministic_parser = true;
-    glr_parser = true;
-  }
-| "%initial-action {...}"
-  {
-    muscle_code_grow ("initial_action", $1, @1);
-  }
-| "%lex-param {...}"			   { add_param ("lex_param", $1, @1); }
-| "%locations"                             { locations_flag = true; }
-| "%name-prefix" "=" string_content        { spec_name_prefix = $3; }
-| "%no-lines"                              { no_lines_flag = true; }
-| "%nondeterministic-parser" 		   { nondeterministic_parser = true; }
-| "%output" "=" string_content             { spec_outfile = $3; }
-| "%parse-param {...}"			   { add_param ("parse_param", $1, @1); }
-| "%pure-parser"                           { pure_parser = true; }
-| "%skeleton" string_content               { skeleton = $2; }
-| "%token-table"                           { token_table_flag = true; }
-| "%verbose"                               { report_flag = report_states; }
-| "%yacc"                                  { yacc_flag = true; }
+    {
+      nondeterministic_parser = true;
+      glr_parser = true;
+    }
+| "%initial-action" "{...}"
+    {
+      code_props action;
+      code_props_symbol_action_init (&action, $2, @2);
+      code_props_translate_code (&action);
+      gram_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); }
+| "%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; }
+| "%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
+    {
+      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; }
+| "%verbose"                    { report_flag |= report_states; }
+| "%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
@@ -221,35 +386,66 @@ grammar_declaration:
     {
       grammar_start_symbol_set ($2, @2);
     }
-| "%union {...}"
+| "%destructor" "{...}" generic_symlist
     {
-      typed = true;
-      MUSCLE_INSERT_INT ("stype_line", @1.start.line);
-      muscle_insert ("stype", $1);
+      symbol_list *list;
+      for (list = $3; list; list = list->next)
+        symbol_list_code_props_set (list, destructor, @2, $2);
+      symbol_list_free ($3);
     }
-| "%destructor {...}" symbols.1
+| "%printer" "{...}" generic_symlist
     {
       symbol_list *list;
-      for (list = $2; list; list = list->next)
-	symbol_destructor_set (list->sym, $1, @1);
-      symbol_list_free ($2);
+      for (list = $3; list; list = list->next)
+        symbol_list_code_props_set (list, printer, @2, $2);
+      symbol_list_free ($3);
     }
-| "%printer {...}" symbols.1
+| "%default-prec"
     {
-      symbol_list *list;
-      for (list = $2; list; list = list->next)
-	symbol_printer_set (list->sym, $1, list->location);
-      symbol_list_free ($2);
+      default_prec = true;
+    }
+| "%no-default-prec"
+    {
+      default_prec = false;
     }
-| "%default-prec" INT
+| "%code" braceless
+    {
+      /* 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
+    {
+      muscle_percent_code_grow ($2, @2, $3, @3);
+      code_scanner_last_string_free ();
+    }
+;
+
+
+/*---------.
+| %union.  |
+`---------*/
+
+%token PERCENT_UNION "%union";
+
+union_name:
+  /* Nothing. */ {}
+| ID             { muscle_code_grow ("union_name", $1, @1); }
+;
+
+grammar_declaration:
+  "%union" union_name braceless
     {
-      if (0 <= $2 && $2 <= 1)
-	default_prec = $2;
-      else
-	complain_at (@1, _("invalid value for `%default-prec'"));
+      union_seen = true;
+      muscle_code_grow ("stype", $3, @3);
+      code_scanner_last_string_free ();
     }
 ;
 
+
+
+
 symbol_declaration:
   "%nterm" { current_class = nterm_sym; } symbol_defs.1
     {
@@ -261,74 +457,103 @@ symbol_declaration:
       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_type_set (list->sym, $2, @2);
+        symbol_type_set (list->content.sym, $2, @2);
       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_type_set (list->sym, current_type, @2);
-	  symbol_precedence_set (list->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:
-  "%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; }
-| TYPE           { current_type = $1; }
+| 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)); }
 ;
 
-/* One or more nonterminals to be %typed. */
+symbol.prec:
+    symbol { $$ = $1; }
+  | symbol INT { $$ = $1; symbol_user_token_number_set ($1, $2, @2); }
+  ;
 
+/* One or more symbols to be %typed. */
 symbols.1:
-  symbol            { $$ = symbol_list_new ($1, @1); }
-| symbols.1 symbol  { $$ = symbol_list_prepend ($1, $2, @2); }
+  symbol
+    { $$ = symbol_list_sym_new ($1, @1); }
+| symbols.1 symbol
+    { $$ = symbol_list_prepend ($1, symbol_list_sym_new ($2, @2)); }
+;
+
+generic_symlist:
+  generic_symlist_item { $$ = $1; }
+| generic_symlist generic_symlist_item { $$ = symbol_list_prepend ($1, $2); }
+;
+
+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); }
 ;
 
 /* One token definition.  */
 symbol_def:
-  TYPE
+  TAG
      {
        current_type = $1;
+       tag_seen = true;
      }
-| ID
+| id
      {
-       symbol_class_set ($1, current_class, @1);
+       symbol_class_set ($1, current_class, @1, true);
        symbol_type_set ($1, current_type, @1);
      }
-| ID INT
+| id INT
     {
-      symbol_class_set ($1, current_class, @1);
+      symbol_class_set ($1, current_class, @1, true);
       symbol_type_set ($1, current_type, @1);
       symbol_user_token_number_set ($1, $2, @2);
     }
-| ID string_as_id
+| id string_as_id
     {
-      symbol_class_set ($1, current_class, @1);
+      symbol_class_set ($1, current_class, @1, true);
       symbol_type_set ($1, current_type, @1);
       symbol_make_alias ($1, $2, @$);
     }
-| ID INT string_as_id
+| id INT string_as_id
     {
-      symbol_class_set ($1, current_class, @1);
+      symbol_class_set ($1, current_class, @1, true);
       symbol_type_set ($1, current_type, @1);
       symbol_user_token_number_set ($1, $2, @2);
       symbol_make_alias ($1, $3, @$);
@@ -342,9 +567,9 @@ symbol_defs.1:
 ;
 
 
-	/*------------------------------------------.
-	| The grammar section: between the two %%.  |
-	`------------------------------------------*/
+        /*------------------------------------------.
+        | The grammar section: between the two %%.  |
+        `------------------------------------------*/
 
 grammar:
   rules_or_grammar_declaration
@@ -356,10 +581,6 @@ grammar:
 rules_or_grammar_declaration:
   rules
 | grammar_declaration ";"
-    {
-      if (yacc_flag)
-	complain_at (@$, _("POSIX forbids declarations in the grammar"));
-    }
 | error ";"
     {
       yyerrok;
@@ -367,64 +588,126 @@ rules_or_grammar_declaration:
 ;
 
 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:
-  rhs                { grammar_rule_end (@1); }
-| rhses.1 "|" rhs    { grammar_rule_end (@3); }
+  rhs                { grammar_current_rule_end (@1); }
+| rhses.1 "|" rhs    { grammar_current_rule_end (@3); }
 | rhses.1 ";"
 ;
 
 rhs:
   /* Nothing.  */
-    { grammar_rule_begin (current_lhs, current_lhs_location); }
-| rhs symbol
-    { grammar_current_rule_symbol_append ($2, @2); }
-| rhs action
-    { 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 "%merge" TYPE
+| rhs "%merge" TAG
     { grammar_current_rule_merge_set ($3, @3); }
 ;
 
-symbol:
-  ID              { $$ = $1; }
-| string_as_id    { $$ = $1; }
+named_ref.opt:
+  /* Nothing. */ { $$ = 0; }
+|
+  BRACKETED_ID   { $$ = named_ref_new($1, @1); }
 ;
 
-action:
-  BRACED_CODE
-   { $$ = $1; }
+/*---------------------------.
+| 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
+| STRING { $$ = uniqstr_new ($1); }
 ;
 
-/* A string used as an ID: we have to keep the quotes. */
-string_as_id:
-  STRING
+/* Some content or empty by default. */
+content.opt:
+  /* Nothing. */   { $$ = ""; }
+| ID { $$ = $1; }
+| STRING
+;
+
+
+/*------------.
+| braceless.  |
+`------------*/
+
+braceless:
+  "{...}"
     {
-      $$ = symbol_get ($1, @1);
-      symbol_class_set ($$, token_sym, @1);
+      code_props plain_code;
+      $1[strlen ($1) - 1] = '\n';
+      code_props_plain_init (&plain_code, $1+1, @1);
+      code_props_translate_code (&plain_code);
+      gram_scanner_last_string_free ();
+      $$ = plain_code.code;
     }
 ;
 
-/* A string used for its contents.  Strip the quotes. */
-string_content:
-  STRING
+
+/*--------------.
+| Identifiers.  |
+`--------------*/
+
+/* Identifiers are returned as uniqstr values by the scanner.
+   Depending on their use, we may need to make them genuine symbols.  */
+
+id:
+  ID
+    { $$ = symbol_from_uniqstr ($1, @1); }
+| CHAR
     {
-      $$ = $1 + 1;
-      $$[strlen ($$) - 1] = '\0';
-    };
+      $$ = symbol_get (char_name ($1), @1);
+      symbol_class_set ($$, token_sym, @1, false);
+      symbol_user_token_number_set ($$, $1, @1);
+    }
+;
+
+id_colon:
+  ID_COLON { $$ = symbol_from_uniqstr ($1, @1); }
+;
 
 
+symbol:
+  id
+| string_as_id
+;
+
+/* A string used as an ID: quote it.  */
+string_as_id:
+  STRING
+    {
+      $$ = symbol_get (quotearg_style (c_quoting_style, $1), @1);
+      symbol_class_set ($$, token_sym, @1, false);
+    }
+;
+
 epilogue.opt:
   /* Nothing.  */
 | "%%" EPILOGUE
     {
-      muscle_code_grow ("epilogue", $2, @2);
-      scanner_last_string_free ();
+      code_props plain_code;
+      code_props_plain_init (&plain_code, $2, @2);
+      code_props_translate_code (&plain_code);
+      gram_scanner_last_string_free ();
+      muscle_code_grow ("epilogue", plain_code.code, @2);
+      code_scanner_last_string_free ();
     }
 ;
 
@@ -441,107 +724,78 @@ lloc_default (YYLTYPE const *rhs, int n)
 {
   int i;
   YYLTYPE loc;
-  loc.start = loc.end = rhs[n].end;
 
-  /* Ignore empty nonterminals the start of the the right-hand side.
+  /* SGI MIPSpro 7.4.1m miscompiles "loc.start = loc.end = rhs[n].end;".
+     The bug is fixed in 7.4.2m, but play it safe for now.  */
+  loc.start = rhs[n].end;
+  loc.end = rhs[n].end;
+
+  /* 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))
       {
-	loc.start = rhs[i].start;
-	break;
+        loc.start = rhs[i].start;
+        break;
       }
 
   return loc;
 }
 
 
-/* Add a lex-param or a parse-param (depending on TYPE) with
-   declaration DECL and location LOC.  */
-
 static void
-add_param (char const *type, char *decl, location loc)
+add_param (param_type type, char *decl, location loc)
 {
-  static char const alphanum[] =
-    "0123456789"
+  static char const alphanum[26 + 26 + 1 + 10] =
     "abcdefghijklmnopqrstuvwxyz"
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-    "_";
-  char const *alpha = alphanum + 10;
-  char const *name_start = NULL;
-  char *p;
-
-  for (p = decl; *p; p++)
-    if ((p == decl || ! strchr (alphanum, p[-1])) && strchr (alpha, p[0]))
-      name_start = p;
+    "_"
+    "0123456789";
 
-  /* Strip the surrounding '{' and '}'.  */
-  decl++;
-  p[-1] = '\0';
+  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;
+  }
 
   if (! name_start)
     complain_at (loc, _("missing identifier in parameter declaration"));
   else
     {
-      char *name;
-      size_t name_len;
-
-      for (name_len = 1;
-	   name_start[name_len] && strchr (alphanum, name_start[name_len]);
-	   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);
     }
 
-  scanner_last_string_free ();
+  gram_scanner_last_string_free ();
 }
 
-/*----------------------------------------------------.
-| When debugging the parser, display tokens' values.  |
-`----------------------------------------------------*/
 
 static void
-print_token_value (FILE *file, int type, YYSTYPE const *value)
+version_check (location const *loc, char const *version)
 {
-  fputc (' ', file);
-  switch (type)
-    {
-    case ID:
-      fprintf (file, " = %s", value->symbol->tag);
-      break;
-
-    case INT:
-      fprintf (file, " = %d", value->integer);
-      break;
-
-    case STRING:
-      fprintf (file, " = \"%s\"", value->chars);
-      break;
-
-    case TYPE:
-      fprintf (file, " = <%s>", value->uniqstr);
-      break;
-
-    case BRACED_CODE:
-    case PERCENT_DESTRUCTOR:
-    case PERCENT_LEX_PARAM:
-    case PERCENT_PARSE_PARAM:
-    case PERCENT_PRINTER:
-    case PERCENT_UNION:
-    case PROLOGUE:
-    case EPILOGUE:
-      fprintf (file, " = {{ %s }}", value->chars);
-      break;
-
-    default:
-      fprintf (file, "unknown token type");
-      break;
+  if (strverscmp (version, PACKAGE_VERSION) > 0)
+    {
+      complain_at (*loc, "require bison %s, but have %s",
+                   version, PACKAGE_VERSION);
+      exit (EX_MISMATCH);
     }
 }
 
@@ -554,5 +808,18 @@ gram_error (location const *loc, char const *msg)
 char const *
 token_name (int type)
 {
-  return yytname[type];
+  return yytname[YYTRANSLATE (type)];
+}
+
+static char const *
+char_name (char c)
+{
+  if (c == '\'')
+    return "'\\''";
+  else
+    {
+      char buf[4];
+      buf[0] = '\''; buf[1] = c; buf[2] = '\''; buf[3] = '\0';
+      return quotearg_style (escape_quoting_style, buf);
+    }
 }