bison-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC] Allow %expect and %expect annotations on rules


From: Paul Hilfinger
Subject: [RFC] Allow %expect and %expect annotations on rules
Date: Tue, 26 Feb 2013 16:28:36 -0800

Especially when writing GLR parsers, I'd like to be able to document
(and check) rules where I'm depending on GLR to resolve conflicts.  The
enclosed patch provides that.  I'd appreciate your comments.

At the moment, the patch allows you to document just SOME of the rules
involved in conflicts, and makes no additional comment on rules where
you don't.  It also still requires one to give an overall %expect or
%expect-rr declaration for total conflicts.  Perhaps if one comments any
one specific rule, all others should be considered as marked with 0
expected conflicts, and the overall declarations should not be
required.  Again, I'd appreciate your thoughts.

Paul Hilfinger

Author: Paul Hilfinger <address@hidden>
Date:   Tue Feb 26 16:03:13 2013 -0800

    Allow %expect and %expect-rr modifiers on individual rules.
    
    This change allows one to document (and check) which rules participate
    in shift/reduce and reduce/reduce conflicts.  This is particularly
    important GLR parsers, where conflicts are a normal occurrence.  For
    example,
    
        %glr-parser
        %expect 1
        %%
    
        ...
    
        argument_list:
          arguments %expect 1
        | arguments ','
        | %empty
        ;
    
        arguments:
          expression
        | argument_list ',' expression
        ;
    
        ...
    
    Looking at the output from -v, one can see that the shift-reduce
    conflict here is due to the fact that the parser does not know whether
    to reduce arguments to argument_list until it sees the token AFTER the
    following ','.  By marking the rule with %expect 1 (because there is a
    conflict in one state), we document the source of the 1 overall shift-
    reduce conflict.
    
    In GLR parsers, we can use %expect-rr in a rule for reduce/reduce
    conflicts.  In this case, we mark each of the conflicting rules.  For
    example,
    
        %glr-parser
        %expect-rr 1
    
        %%
    
        stmt:
          target_list '=' expr ';'
        | expr_list ';'
        ;
    
        target_list:
          target
        | target ',' target_list
        ;
    
        target:
          ID %expect-rr 1
        ;
    
        expr_list:
          expr
        | expr ',' expr_list
        ;
    
        expr:
          ID %expect-rr 1
        | ...
        ;
    
    In a statement such as
    
        x, y = 3, 4;
    
    the parser must reduce x to a target or an expr, but does not know
    which until it sees the '='.  So we notate the two possible reductions
    to indicate that each conflicts in one rule.
    
    This patch contains changes to the parser and documentation, and some
    additional tests.
    
    Changelog:
    
    * doc/bison.texi (Suppressing Conflict Warnings): Document %expect,
    %expect-rr in grammar rules.
    * src/conflicts.c (count_state_rr_conflicts): Adjust comment.
    (rule_has_state_sr_conflicts): New static function.
    (count_rule_sr_conflicts): New static function.
    (rule_nast_state_rr_conflicts): New static function.
    (count_rule_rr_conflicts): New static function.
    (rule_conflicts_print): New static function.
    (conflicts_print): Also use rule_conflicts_print to report on individual
    rules.
    * src/gram.h (struct rule): Add new fields expected_sr_conflicts,
    expected_rr_conflicts.
    * src/reader.c (grammar_midrule_action): Transfer expected_sr_conflicts,
    expected_rr_conflicts to new rule, and turn off in current_rule.
    (grammar_current_rule_expect_sr): New function.
    (grammar_current_rule_expect_rr): New function.
    (packgram): Transfer expected_sr_conflicts, expected_rr_conflicts
    to new rule.
    * src/reader.h (grammar_current_rule_expect_sr): New function.
    (grammar_current_rule_expect_rr): New function.
    * src/symlist.c (symbol_list_sym_new): Initialize expected_sr_conflicts,
    expected_rr_conflicts.
    * src/symlist.h (struct symbol_list): Add new fields expected_sr_conflicts,
    expected_rr_conflicts.
    * tests/conflicts.at: Add tests "%expect in grammar rule not enough",
    "%expect in grammar rule right.", "%expect in grammar rule too much."
    
    * src/parse-gram.c: Regenerate.
    * src/parse-gram.h: Regenerate.

diff --git a/doc/bison.texi b/doc/bison.texi
index 328b88b..0f36c9e 100644
--- a/doc/bison.texi
+++ b/doc/bison.texi
@@ -5070,6 +5070,53 @@ in GLR parsers, using the declaration:
 %expect-rr @var{n}
 @end example
 
+You may wish to be more specific in your
+specification of expected conflicts.  To this end, you can also attach
address@hidden and @code{%expect-rr} modifiers to individual rules.
+The interpretation of these modifiers differs from their use as
+declarations.  When attached to rules, they indicate the number of states
+in which the rule is involved in a conflict.  You will need to consult the
+output resulting from @samp{-v} to determine appropriate numbers to use.
+For example, for the following grammar fragment, the first rule for
address@hidden appears in two states in which the @samp{[} token is a
+lookahead.  Having determined that, you can document this fact with an
address@hidden modifier as follows:
+
address@hidden
+dims:
+  empty_dims
+| '[' expr ']' dims
+;
+
+empty_dims:
+  %empty   %expect 2
+| empty_dims '[' ']'
+;
address@hidden example
+
+Mid-rule actions generate implicit rules that are also subject to conflicts
+(@pxref{Mid-Rule Conflicts,, Conflicts due to Mid-Rule Actions}). To attach
+an @code{%expect} or @code{%expect-rr} annotation to an implicit
+mid-rule action's rule, put it before the action.  For example,
+
address@hidden
+%glr-parser
+%expect-rr 1
+
+%%
+
+clause:
+  "condition" %expect-rr 1 @{ value_mode(); @} '(' exprs ')'
+| "condition" %expect-rr 1 @{ class_mode(); @} '(' types ')'
+;
address@hidden example
+
address@hidden
+Here, the appropriate mid-rule action will not be determined until after 
+the @samp{(} token is shifted.  Thus,
+the two actions will clash with each other, and we should expect one
+reduce/reduce conflict for each.
+
 In general, using @code{%expect} involves these steps:
 
 @itemize @bullet
@@ -5085,8 +5132,17 @@ go back to the beginning.
 
 @item
 Add an @code{%expect} declaration, copying the number @var{n} from the
-number which Bison printed.  With GLR parsers, add an
+number that Bison printed.  With GLR parsers, add an
 @code{%expect-rr} declaration as well.
+
address@hidden
+Optionally, count up the number of states in which one or more
+conflicted reductions for particular rules appear and add these numbers
+to the affected rules as @code{%expect-rr} or @code{%expect} modifiers
+as appropriate.  Rules that are in conflict appear in the output listing
+surrounded by square brackets or, in the case of reduce/reduce conflicts,
+as reductions having the same lookahead symbol as a square-bracketed
+reduction in the same state.
 @end itemize
 
 Now Bison will report an error if you introduce an unexpected conflict,
@@ -5310,7 +5366,14 @@ Start-Symbol}).
 @end deffn
 
 @deffn {Directive} %expect
-Declare the expected number of shift-reduce conflicts
+Declare the expected number of shift-reduce conflicts, either overall or
+for a given rule
+(@pxref{Expect Decl, ,Suppressing Conflict Warnings}).
address@hidden deffn
+
address@hidden {Directive} %expect-rr
+Declare the expected number of reduce-reduce conflicts, either overall or
+for a given rule
 (@pxref{Expect Decl, ,Suppressing Conflict Warnings}).
 @end deffn
 
diff --git a/src/conflicts.c b/src/conflicts.c
index 1840473..3363270 100644
--- a/src/conflicts.c
+++ b/src/conflicts.c
@@ -473,8 +473,8 @@ count_sr_conflicts (void)
 /*----------------------------------------------------------------.
 | Count the number of reduce/reduce conflicts.  If ONE_PER_TOKEN, |
 | count one conflict for each token that has any reduce/reduce    |
-| conflicts.  Otherwise, count one conflict for each pair of      |
-| conflicting reductions.                                         |
+| conflicts.  Otherwise, count one conflict for each reduction    |
+| after the first for a given token.                              |
 `----------------------------------------------------------------*/
 
 static size_t
@@ -511,6 +511,86 @@ count_rr_conflicts (bool one_per_token)
 }
 
 
+/*----------------------------------------------------------------------.
+| For a given rule, count the number of states for which it is involved |
+| in shift/reduce conflicts.                                               |
+`----------------------------------------------------------------------*/
+
+static bool
+rule_has_state_sr_conflicts (rule *r, state *s)
+{
+  int i;
+  int j;
+  transitions *trans = s->transitions;
+  reductions *reds = s->reductions;
+
+  for (i = 0; i < reds->num; i++)
+    if (reds->rules[i] == r)
+      break;
+  if (i >= reds->num)
+    return false;
+
+  FOR_EACH_SHIFT (trans, j)
+    if (bitset_test (reds->lookahead_tokens[i], TRANSITION_SYMBOL (trans, j)))
+      return true;
+
+  return false;
+}
+
+static size_t
+count_rule_sr_conflicts (rule *r)
+{
+  state_number i;
+  size_t res;
+
+  res = 0;
+  for (i = 0; i < nstates; i++)
+    if (conflicts[i] && rule_has_state_sr_conflicts (r, states[i]))
+      res++;
+  return res;
+}
+
+/*-----------------------------------------------------------------.
+| For a given rule, count the number of states in which it is      |
+| involved in reduce/reduce conflicts.                             |
+`-----------------------------------------------------------------*/
+
+static bool
+rule_has_state_rr_conflicts (rule *r, state *s)
+{
+  int i;
+  reductions *reds = s->reductions;
+  size_t res;
+  bitset lookaheads;
+  
+  for (i = 0; i < reds->num; i++)
+    if (reds->rules[i] == r)
+      break;
+  if (i >= reds->num)
+    return 0;
+  lookaheads = reds->lookahead_tokens[i];
+
+  for (i = 0; i < reds->num; i++)
+    if (reds->rules[i] != r &&
+        !bitset_disjoint_p (lookaheads, reds->lookahead_tokens[i]))
+      return true;
+
+  return false;
+}
+
+static size_t
+count_rule_rr_conflicts (rule *r)
+{
+  state_number i;
+  size_t res;
+
+  res = 0;
+  for (i = 0; i < nstates; i++)
+    if (conflicts[i] && rule_has_state_rr_conflicts (r, states[i]))
+       res++;
+  return res;
+}
+
 /*-----------------------------------------------------------.
 | Output the detailed description of states with conflicts.  |
 `-----------------------------------------------------------*/
@@ -556,14 +636,46 @@ conflicts_total_count (void)
   return count_sr_conflicts () + count_rr_conflicts (false);
 }
 
+/*------------------------------.
+| Reporting per-rule conflicts. |
+`------------------------------*/
 
-/*------------------------------------------.
-| Reporting the total number of conflicts.  |
-`------------------------------------------*/
+static void
+rule_conflicts_print (void)
+{
+  rule_number i;
+
+  for (i = 0; i < nrules; i += 1)
+    {
+      rule *r = &rules[i];
+      int expected_sr = r->expected_sr_conflicts;
+      int expected_rr = r->expected_rr_conflicts;
+
+      if (expected_sr != -1 || expected_rr != -1) 
+        {
+          int sr = count_rule_sr_conflicts (r);
+          int rr = count_rule_rr_conflicts (r);
+          if (sr != expected_sr && (sr != 0 || expected_sr != -1))
+            complain (&r->location, complaint, _("\
+shift/reduce conflicts for rule %d: %d found, %d expected"),
+                      r->user_number, sr, expected_sr);
+          if (rr != expected_rr && (rr != 0 || expected_rr != -1))
+            complain (&r->location, complaint, _("\
+reduce/reduce conflicts for rule %d: %d found, %d expected"),
+                      r->user_number, rr, expected_rr);
+        }
+    }
+}
+
+/*---------------------------------.
+| Reporting numbers of conflicts.  |
+`---------------------------------*/
 
 void
 conflicts_print (void)
 {
+  rule_conflicts_print ();
+
   if (! glr_parser && expected_rr_conflicts != -1)
     {
       complain (NULL, Wother, _("%%expect-rr applies only to GLR parsers"));
@@ -617,7 +729,6 @@ conflicts_print (void)
   }
 }
 
-
 void
 conflicts_free (void)
 {
diff --git a/src/gram.h b/src/gram.h
index c1dd9a6..f0e8d20 100644
--- a/src/gram.h
+++ b/src/gram.h
@@ -196,6 +196,11 @@ typedef struct
   bool useful;
   bool is_predicate;
 
+  /* Counts of the numbers of expected conflicts for this rule, or -1 if none
+     given. */
+  int expected_sr_conflicts;
+  int expected_rr_conflicts;
+
   const char *action;
   location action_location;
 } rule;
diff --git a/src/parse-gram.c b/src/parse-gram.c
index 90e0efe..567de1d 100644
--- a/src/parse-gram.c
+++ b/src/parse-gram.c
@@ -1,4 +1,4 @@
-/* A Bison parser, made by GNU Bison 2.7.1116-42fef.  */
+/* A Bison parser, made by GNU Bison 2.7.1119-e39fc.  */
 
 /* Bison implementation for Yacc-like parsers in C
 
@@ -44,7 +44,7 @@
 #define YYBISON 1
 
 /* Bison version.  */
-#define YYBISON_VERSION "2.7.1116-42fef"
+#define YYBISON_VERSION "2.7.1119-e39fc"
 
 /* Skeleton name.  */
 #define YYSKELETON_NAME "yacc.c"
@@ -569,16 +569,16 @@ union yyalloc
 /* YYFINAL -- State number of the termination state.  */
 #define YYFINAL  3
 /* YYLAST -- Last index in YYTABLE.  */
-#define YYLAST   158
+#define YYLAST   161
 
 /* YYNTOKENS -- Number of terminals.  */
 #define YYNTOKENS  58
 /* YYNNTS -- Number of nonterminals.  */
 #define YYNNTS  38
 /* YYNRULES -- Number of rules.  */
-#define YYNRULES  110
+#define YYNRULES  112
 /* YYNSTATES -- Number of states.  */
-#define YYNSTATES  144
+#define YYNSTATES  148
 
 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
    by yylex, with out-of-bounds checking.  */
@@ -639,9 +639,9 @@ static const yytype_uint16 yyrline[] =
      507,   512,   514,   519,   520,   524,   525,   529,   530,   531,
      536,   541,   546,   552,   558,   569,   570,   579,   580,   586,
      587,   588,   595,   595,   603,   604,   605,   610,   613,   615,
-     617,   619,   621,   623,   625,   630,   631,   641,   642,   647,
-     648,   649,   658,   678,   680,   689,   694,   695,   700,   708,
-     709
+     617,   619,   621,   623,   625,   627,   629,   634,   635,   645,
+     646,   651,   652,   653,   662,   682,   684,   693,   698,   699,
+     704,   712,   713
 };
 #endif
 
@@ -690,12 +690,12 @@ static const yytype_uint16 yytoknum[] =
 };
 # endif
 
-#define YYPACT_NINF -99
+#define YYPACT_NINF -87
 
 #define yypact_value_is_default(Yystate) \
-  (!!((Yystate) == (-99)))
+  (!!((Yystate) == (-87)))
 
-#define YYTABLE_NINF -110
+#define YYTABLE_NINF -112
 
 #define yytable_value_is_error(Yytable_value) \
   0
@@ -704,21 +704,21 @@ static const yytype_uint16 yytoknum[] =
      STATE-NUM.  */
 static const yytype_int16 yypact[] =
 {
-     -99,     5,   102,   -99,   -99,   -99,   -48,   -99,   -99,   -99,
-     -99,   -99,   -99,    19,   -99,    27,    52,   -99,    56,    60,
-     -99,    67,   -99,    34,    73,    76,   -99,   -99,   -99,    86,
-      87,    88,     0,   -99,   -99,   -99,    15,   -99,   -99,   -99,
-      46,   -99,   -99,    54,   -99,   -99,    45,     4,     4,     0,
-     -99,    58,   -99,   -99,   -99,    31,   -99,   -99,   -99,   -99,
-     -99,   -99,   -99,   -99,   -99,   -99,   -99,   -99,   -99,   -99,
-     -99,   -99,    49,   -99,    50,     1,   -99,   -99,    61,    62,
-     -99,    58,    32,   -99,     0,   -99,   -99,     4,    84,     4,
-       0,   -99,   -99,   -99,   -99,   -99,   -99,   -99,    72,   -99,
-     -99,   -99,   -99,   -99,    64,   -99,   -99,   -99,   -99,    32,
-     -99,   -99,   -99,     0,   -99,   101,   -99,   114,   -99,   -99,
-     -99,   -99,   -99,   -99,   -99,   -99,   -99,    12,    26,   -99,
-     -99,     0,   138,    66,    61,   -99,   -99,    61,    26,   -99,
-     -99,   -99,   -99,   -99
+     -87,     4,   105,   -87,   -87,   -87,     7,   -87,   -87,   -87,
+     -87,   -87,   -87,    -4,   -87,    27,    61,   -87,    66,    72,
+     -87,    79,   -87,    43,    81,    82,   -87,   -87,   -87,    83,
+      84,    85,     0,   -87,   -87,   -87,    15,   -87,   -87,   -87,
+      44,   -87,   -87,    49,   -87,   -87,    39,    -5,    -5,     0,
+     -87,    54,   -87,   -87,   -87,    31,   -87,   -87,   -87,   -87,
+     -87,   -87,   -87,   -87,   -87,   -87,   -87,   -87,   -87,   -87,
+     -87,   -87,    46,   -87,    50,     1,   -87,   -87,    62,    63,
+     -87,    54,    26,   -87,     0,   -87,   -87,    -5,    36,    -5,
+       0,   -87,   -87,   -87,   -87,   -87,   -87,   -87,    75,   -87,
+     -87,   -87,   -87,   -87,    65,   -87,   -87,   -87,   -87,    26,
+     -87,   -87,   -87,     0,   -87,   102,   -87,   104,   -87,   -87,
+     -87,   -87,   -87,   -87,   -87,   -87,   -87,     5,    52,   -87,
+     -87,     0,   116,    69,   141,   142,    62,   -87,   -87,    62,
+      52,   -87,   -87,   -87,   -87,   -87,   -87,   -87
 };
 
   /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
@@ -731,25 +731,25 @@ static const yytype_uint8 yydefact[] =
        7,     0,    15,     0,     0,     0,    37,    19,    20,     0,
        0,     0,     0,    26,    27,    28,     0,     6,    29,    22,
       42,     4,     5,     0,    33,    32,    55,     0,     0,     0,
-     102,     0,    38,    98,    97,    99,    10,    12,    13,    14,
-      16,    17,    18,    21,    24,    25,   108,   104,   103,   106,
-      34,   107,     0,   105,     0,     0,    77,    79,    95,     0,
+     104,     0,    38,   100,    99,   101,    10,    12,    13,    14,
+      16,    17,    18,    21,    24,    25,   110,   106,   105,   108,
+      34,   109,     0,   107,     0,     0,    77,    79,    97,     0,
       43,     0,     0,    56,     0,    70,    75,    48,    71,    46,
-      49,    61,    39,   101,   100,     8,    81,    80,     0,    78,
-       2,    96,    82,    31,    23,    44,    67,    68,    69,    35,
+      49,    61,    39,   103,   102,     8,    81,    80,     0,    78,
+       2,    98,    82,    31,    23,    44,    67,    68,    69,    35,
       63,    66,    65,    50,    57,    59,    76,    72,    73,    62,
-     110,    87,    30,    64,    58,    60,    74,    83,    84,    87,
-      86,     0,     0,     0,    95,    90,    91,    95,    85,    92,
-      93,    94,    89,    88
+     112,    87,    30,    64,    58,    60,    74,    83,    84,    87,
+      86,     0,     0,     0,     0,     0,    97,    90,    91,    97,
+      85,    92,    93,    94,    95,    96,    89,    88
 };
 
   /* YYPGOTO[NTERM-NUM].  */
 static const yytype_int16 yypgoto[] =
 {
-     -99,   -99,   -99,   -99,   -99,   -99,   141,   -99,   -99,   -99,
-     -99,   -99,   -99,   -99,   -99,   -99,    33,   -99,   -99,    35,
-     -99,    -7,    97,   -99,    74,   -99,   -99,   -99,    18,   -98,
-     -99,   -99,   -13,     6,   -99,   -32,   -73,   -99
+     -87,   -87,   -87,   -87,   -87,   -87,   145,   -87,   -87,   -87,
+     -87,   -87,   -87,   -87,   -87,   -87,    35,   -87,   -87,    40,
+     -87,   -24,   103,   -87,    77,   -87,   -87,   -87,    21,   -86,
+     -87,   -87,   -46,    13,   -87,   -32,   -73,   -87
 };
 
   /* YYDEFGOTO[NTERM-NUM].  */
@@ -766,42 +766,44 @@ static const yytype_int16 yydefgoto[] =
      number is the opposite.  If YYTABLE_NINF, syntax error.  */
 static const yytype_int16 yytable[] =
 {
-      70,  -109,    72,    66,    49,     3,     4,     5,     6,     7,
+      70,  -111,    72,    66,     3,    92,     4,     5,     6,     7,
        8,     9,    10,    11,    12,   118,    72,    91,    13,    14,
        4,     5,     6,     7,     8,     9,    10,    11,    12,    66,
-      53,    26,    13,    14,    93,    66,   142,    32,    92,   143,
-     131,   132,   133,    67,   126,    26,    68,    67,    73,    98,
-      68,    32,   115,    88,    88,    56,    85,    40,   119,    50,
-      57,   129,    73,   130,    58,    51,   134,   135,   105,    67,
-      59,    40,    68,    54,    60,    67,    61,    94,    68,    62,
-     116,   115,   116,   136,   106,   107,   108,    66,   117,    63,
-      64,    65,    80,    88,    82,    88,   137,    83,    50,   139,
-      96,    97,   103,   101,   122,   125,   137,     4,     5,     6,
-       7,     8,     9,    10,    11,    12,   120,    66,   141,    13,
-      14,    15,    16,    17,    18,    19,    20,    21,    22,    23,
-      24,    25,    26,    27,    28,    29,    30,    31,    32,    33,
-      34,    35,   140,    42,   123,    89,   124,   138,     0,    99,
-      36,     0,    37,    38,     0,     0,     0,    39,    40
+      53,    26,    13,    14,    93,   105,    50,    32,    67,    66,
+     117,    68,    51,    67,   126,    26,    68,    85,    73,    98,
+     146,    32,   115,   147,   129,    66,   130,    40,   119,    49,
+      88,    88,    73,   116,    56,   116,   131,   132,   133,    67,
+      57,    40,    68,    54,   134,   135,    58,    94,   106,   107,
+     108,   115,    59,    60,    61,    62,    63,    64,    65,    82,
+      80,    83,   136,   137,    50,    67,   139,    96,    68,   141,
+      88,    97,    88,   103,   101,   122,   125,    66,   139,   138,
+       4,     5,     6,     7,     8,     9,    10,    11,    12,   120,
+     142,   143,    13,    14,    15,    16,    17,    18,    19,    20,
+      21,    22,    23,    24,    25,    26,    27,    28,    29,    30,
+      31,    32,    33,    34,    35,   144,   145,    42,   124,   123,
+     140,    89,    99,    36,     0,    37,    38,     0,     0,     0,
+      39,    40
 };
 
 static const yytype_int16 yycheck[] =
 {
-      32,     0,     1,     3,    52,     0,     5,     6,     7,     8,
+      32,     0,     1,     3,     0,    51,     5,     6,     7,     8,
        9,    10,    11,    12,    13,    88,     1,    49,    17,    18,
        5,     6,     7,     8,     9,    10,    11,    12,    13,     3,
-       3,    30,    17,    18,     3,     3,   134,    36,    51,   137,
-      14,    15,    16,    43,   117,    30,    46,    43,    47,    48,
-      46,    36,    84,    47,    48,     3,    52,    56,    90,    40,
-       4,    49,    47,    51,     4,    46,    40,    41,    81,    43,
-       3,    56,    46,    46,    40,    43,     3,    46,    46,     3,
-      87,   113,    89,    57,    52,    53,    54,     3,     4,     3,
-       3,     3,    46,    87,    40,    89,   128,    52,    40,   131,
-      51,    51,    40,    42,    40,     4,   138,     5,     6,     7,
-       8,     9,    10,    11,    12,    13,    44,     3,    52,    17,
-      18,    19,    20,    21,    22,    23,    24,    25,    26,    27,
-      28,    29,    30,    31,    32,    33,    34,    35,    36,    37,
-      38,    39,     4,     2,   109,    48,   113,   129,    -1,    75,
-      48,    -1,    50,    51,    -1,    -1,    -1,    55,    56
+       3,    30,    17,    18,     3,    81,    40,    36,    43,     3,
+       4,    46,    46,    43,   117,    30,    46,    52,    47,    48,
+     136,    36,    84,   139,    49,     3,    51,    56,    90,    52,
+      47,    48,    47,    87,     3,    89,    14,    15,    16,    43,
+       4,    56,    46,    46,    22,    23,     4,    46,    52,    53,
+      54,   113,     3,    40,     3,     3,     3,     3,     3,    40,
+      46,    52,    40,    41,    40,    43,   128,    51,    46,   131,
+      87,    51,    89,    40,    42,    40,     4,     3,   140,    57,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    44,
+       4,    52,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39,     4,     4,     2,   113,   109,
+     129,    48,    75,    48,    -1,    50,    51,    -1,    -1,    -1,
+      55,    56
 };
 
   /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
@@ -821,8 +823,8 @@ static const yytype_uint8 yystos[] =
       95,    42,    87,    40,    63,    90,    52,    53,    54,    76,
       77,    78,    93,    73,    74,    93,    79,     4,    94,    93,
       44,    84,    40,    77,    74,     4,    94,    85,    86,    49,
-      51,    14,    15,    16,    40,    41,    57,    93,    86,    93,
-       4,    52,    87,    87
+      51,    14,    15,    16,    22,    23,    40,    41,    57,    93,
+      86,    93,     4,    52,     4,     4,    87,    87
 };
 
   /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
@@ -837,9 +839,9 @@ static const yytype_uint8 yyr1[] =
       74,    75,    75,    76,    76,    77,    77,    78,    78,    78,
       79,    79,    79,    79,    79,    80,    80,    81,    81,    82,
       82,    82,    84,    83,    85,    85,    85,    86,    86,    86,
-      86,    86,    86,    86,    86,    87,    87,    88,    88,    89,
-      89,    89,    90,    91,    91,    92,    93,    93,    94,    95,
-      95
+      86,    86,    86,    86,    86,    86,    86,    87,    87,    88,
+      88,    89,    89,    89,    90,    91,    91,    92,    93,    93,
+      94,    95,    95
 };
 
   /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
@@ -854,9 +856,9 @@ static const yytype_uint8 yyr2[] =
        2,     1,     2,     1,     2,     1,     1,     1,     1,     1,
        1,     1,     2,     2,     3,     1,     2,     1,     2,     1,
        2,     2,     0,     4,     1,     3,     2,     0,     3,     3,
-       2,     2,     3,     3,     3,     0,     1,     1,     1,     0,
-       1,     1,     1,     1,     1,     1,     1,     1,     1,     0,
-       2
+       2,     2,     3,     3,     3,     3,     3,     0,     1,     1,
+       1,     0,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     0,     2
 };
 
 
@@ -1014,67 +1016,67 @@ yy_symbol_value_print (FILE *yyoutput, int yytype, 
YYSTYPE const * const yyvalue
     case 3: /* "string"  */
 #line 192 "src/parse-gram.y" /* yacc.c:707  */
       { fputs (quotearg_style (c_quoting_style, ((*yyvaluep).chars)), yyo); }
-#line 1018 "src/parse-gram.c" /* yacc.c:707  */
+#line 1020 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 4: /* "integer"  */
 #line 205 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "%d", ((*yyvaluep).integer)); }
-#line 1024 "src/parse-gram.c" /* yacc.c:707  */
+#line 1026 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 24: /* "%<flag>"  */
 #line 201 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "%%%s", ((*yyvaluep).uniqstr)); }
-#line 1030 "src/parse-gram.c" /* yacc.c:707  */
+#line 1032 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 40: /* "{...}"  */
 #line 194 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "{\n%s\n}", ((*yyvaluep).code)); }
-#line 1036 "src/parse-gram.c" /* yacc.c:707  */
+#line 1038 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 42: /* "[identifier]"  */
 #line 199 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "[%s]", ((*yyvaluep).uniqstr)); }
-#line 1042 "src/parse-gram.c" /* yacc.c:707  */
+#line 1044 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 43: /* "char"  */
 #line 186 "src/parse-gram.y" /* yacc.c:707  */
       { fputs (char_name (((*yyvaluep).character)), yyo); }
-#line 1048 "src/parse-gram.c" /* yacc.c:707  */
+#line 1050 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 44: /* "epilogue"  */
 #line 194 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "{\n%s\n}", ((*yyvaluep).chars)); }
-#line 1054 "src/parse-gram.c" /* yacc.c:707  */
+#line 1056 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 46: /* "identifier"  */
 #line 198 "src/parse-gram.y" /* yacc.c:707  */
       { fputs (((*yyvaluep).uniqstr), yyo); }
-#line 1060 "src/parse-gram.c" /* yacc.c:707  */
+#line 1062 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 47: /* "identifier:"  */
 #line 200 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "%s:", ((*yyvaluep).uniqstr)); }
-#line 1066 "src/parse-gram.c" /* yacc.c:707  */
+#line 1068 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 50: /* "%{...%}"  */
 #line 194 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "{\n%s\n}", ((*yyvaluep).chars)); }
-#line 1072 "src/parse-gram.c" /* yacc.c:707  */
+#line 1074 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 52: /* "<tag>"  */
 #line 202 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "<%s>", ((*yyvaluep).uniqstr)); }
-#line 1078 "src/parse-gram.c" /* yacc.c:707  */
+#line 1080 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 55: /* "%param"  */
@@ -1091,67 +1093,67 @@ yy_symbol_value_print (FILE *yyoutput, int yytype, 
YYSTYPE const * const yyvalue
       case param_none: aver (false); break;
     }
 }
-#line 1095 "src/parse-gram.c" /* yacc.c:707  */
+#line 1097 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 65: /* code_props_type  */
 #line 420 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "%s", code_props_type_string (((*yyvaluep).code_type))); 
}
-#line 1101 "src/parse-gram.c" /* yacc.c:707  */
+#line 1103 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 74: /* symbol.prec  */
 #line 208 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "%s", ((*yyvaluep).symbol)->tag); }
-#line 1107 "src/parse-gram.c" /* yacc.c:707  */
+#line 1109 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 78: /* tag  */
 #line 202 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "<%s>", ((*yyvaluep).uniqstr)); }
-#line 1113 "src/parse-gram.c" /* yacc.c:707  */
+#line 1115 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 88: /* variable  */
 #line 198 "src/parse-gram.y" /* yacc.c:707  */
       { fputs (((*yyvaluep).uniqstr), yyo); }
-#line 1119 "src/parse-gram.c" /* yacc.c:707  */
+#line 1121 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 89: /* content.opt  */
 #line 194 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "{\n%s\n}", ((*yyvaluep).chars)); }
-#line 1125 "src/parse-gram.c" /* yacc.c:707  */
+#line 1127 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 90: /* braceless  */
 #line 194 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "{\n%s\n}", ((*yyvaluep).chars)); }
-#line 1131 "src/parse-gram.c" /* yacc.c:707  */
+#line 1133 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 91: /* id  */
 #line 208 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "%s", ((*yyvaluep).symbol)->tag); }
-#line 1137 "src/parse-gram.c" /* yacc.c:707  */
+#line 1139 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 92: /* id_colon  */
 #line 209 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "%s:", ((*yyvaluep).symbol)->tag); }
-#line 1143 "src/parse-gram.c" /* yacc.c:707  */
+#line 1145 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 93: /* symbol  */
 #line 208 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "%s", ((*yyvaluep).symbol)->tag); }
-#line 1149 "src/parse-gram.c" /* yacc.c:707  */
+#line 1151 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
     case 94: /* string_as_id  */
 #line 208 "src/parse-gram.y" /* yacc.c:707  */
       { fprintf (yyo, "%s", ((*yyvaluep).symbol)->tag); }
-#line 1155 "src/parse-gram.c" /* yacc.c:707  */
+#line 1157 "src/parse-gram.c" /* yacc.c:707  */
         break;
 
       default:
@@ -1873,7 +1875,7 @@ YYLTYPE yylloc = yyloc_default;
   boundary_set (&yylloc.start, current_file, 1, 1);
   boundary_set (&yylloc.end, current_file, 1, 1);
 }
-#line 1877 "src/parse-gram.c" /* yacc.c:1452  */
+#line 1879 "src/parse-gram.c" /* yacc.c:1452  */
   yylsp[0] = yylloc;
   goto yysetstate;
 
@@ -2077,7 +2079,7 @@ yyreduce:
                         plain_code.code, (yylsp[0]));
       code_scanner_last_string_free ();
     }
-#line 2081 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2083 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 7:
@@ -2085,7 +2087,7 @@ yyreduce:
     {
       muscle_percent_define_ensure ((yyvsp[0].uniqstr), (yylsp[0]), true);
     }
-#line 2089 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2091 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 8:
@@ -2094,13 +2096,13 @@ yyreduce:
       muscle_percent_define_insert ((yyvsp[-1].uniqstr), (yylsp[-1]), 
(yyvsp[0].chars),
                                     MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
     }
-#line 2098 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2100 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 9:
 #line 302 "src/parse-gram.y" /* yacc.c:1669  */
     { defines_flag = true; }
-#line 2104 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2106 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 10:
@@ -2109,7 +2111,7 @@ yyreduce:
       defines_flag = true;
       spec_defines_file = xstrdup ((yyvsp[0].chars));
     }
-#line 2113 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2115 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 11:
@@ -2118,25 +2120,25 @@ yyreduce:
       muscle_percent_define_insert ("parse.error", (yylsp[0]), "verbose",
                                     MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE);
     }
-#line 2122 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2124 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 12:
 #line 313 "src/parse-gram.y" /* yacc.c:1669  */
     { expected_sr_conflicts = (yyvsp[0].integer); }
-#line 2128 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2130 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 13:
 #line 314 "src/parse-gram.y" /* yacc.c:1669  */
     { expected_rr_conflicts = (yyvsp[0].integer); }
-#line 2134 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2136 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 14:
 #line 315 "src/parse-gram.y" /* yacc.c:1669  */
     { spec_file_prefix = (yyvsp[0].chars); }
-#line 2140 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2142 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 15:
@@ -2145,7 +2147,7 @@ yyreduce:
       nondeterministic_parser = true;
       glr_parser = true;
     }
-#line 2149 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2151 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 16:
@@ -2158,55 +2160,55 @@ yyreduce:
       muscle_code_grow ("initial_action", action.code, (yylsp[0]));
       code_scanner_last_string_free ();
     }
-#line 2162 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2164 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 17:
 #line 330 "src/parse-gram.y" /* yacc.c:1669  */
     { language_argmatch ((yyvsp[0].chars), grammar_prio, (yylsp[-1])); }
-#line 2168 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2170 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 18:
 #line 331 "src/parse-gram.y" /* yacc.c:1669  */
     { spec_name_prefix = (yyvsp[0].chars); }
-#line 2174 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2176 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 19:
 #line 332 "src/parse-gram.y" /* yacc.c:1669  */
     { no_lines_flag = true; }
-#line 2180 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2182 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 20:
 #line 333 "src/parse-gram.y" /* yacc.c:1669  */
     { nondeterministic_parser = true; }
-#line 2186 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2188 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 21:
 #line 334 "src/parse-gram.y" /* yacc.c:1669  */
     { spec_outfile = (yyvsp[0].chars); }
-#line 2192 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2194 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 22:
 #line 335 "src/parse-gram.y" /* yacc.c:1669  */
     { current_param = (yyvsp[0].param); }
-#line 2198 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2200 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 23:
 #line 335 "src/parse-gram.y" /* yacc.c:1669  */
     { current_param = param_none; }
-#line 2204 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2206 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 24:
 #line 336 "src/parse-gram.y" /* yacc.c:1669  */
     { version_check (&(yylsp[0]), (yyvsp[0].chars)); }
-#line 2210 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2212 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 25:
@@ -2234,37 +2236,37 @@ yyreduce:
         }
       skeleton_arg (skeleton_user, grammar_prio, (yylsp[-1]));
     }
-#line 2238 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2240 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 26:
 #line 361 "src/parse-gram.y" /* yacc.c:1669  */
     { token_table_flag = true; }
-#line 2244 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2246 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 27:
 #line 362 "src/parse-gram.y" /* yacc.c:1669  */
     { report_flag |= report_states; }
-#line 2250 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2252 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 28:
 #line 363 "src/parse-gram.y" /* yacc.c:1669  */
     { yacc_flag = true; }
-#line 2256 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2258 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 30:
 #line 368 "src/parse-gram.y" /* yacc.c:1669  */
     { add_param (current_param, (yyvsp[0].code), (yylsp[0])); }
-#line 2262 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2264 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 31:
 #line 369 "src/parse-gram.y" /* yacc.c:1669  */
     { add_param (current_param, (yyvsp[0].code), (yylsp[0])); }
-#line 2268 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2270 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 34:
@@ -2272,7 +2274,7 @@ yyreduce:
     {
       grammar_start_symbol_set ((yyvsp[0].symbol), (yylsp[0]));
     }
-#line 2276 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2278 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 35:
@@ -2288,7 +2290,7 @@ yyreduce:
         symbol_list_free ((yyvsp[0].list));
       }
     }
-#line 2292 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2294 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 36:
@@ -2296,7 +2298,7 @@ yyreduce:
     {
       default_prec = true;
     }
-#line 2300 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2302 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 37:
@@ -2304,7 +2306,7 @@ yyreduce:
     {
       default_prec = false;
     }
-#line 2308 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2310 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 38:
@@ -2315,7 +2317,7 @@ yyreduce:
       muscle_code_grow ("percent_code()", (yyvsp[0].chars), (yylsp[0]));
       code_scanner_last_string_free ();
     }
-#line 2319 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2321 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 39:
@@ -2324,31 +2326,31 @@ yyreduce:
       muscle_percent_code_grow ((yyvsp[-1].uniqstr), (yylsp[-1]), 
(yyvsp[0].chars), (yylsp[0]));
       code_scanner_last_string_free ();
     }
-#line 2328 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2330 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 40:
 #line 422 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.code_type) = destructor; }
-#line 2334 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2336 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 41:
 #line 423 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.code_type) = printer; }
-#line 2340 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2342 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 42:
 #line 433 "src/parse-gram.y" /* yacc.c:1669  */
     {}
-#line 2346 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2348 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 43:
 #line 434 "src/parse-gram.y" /* yacc.c:1669  */
     { muscle_code_grow ("union_name", (yyvsp[0].uniqstr), (yylsp[0])); }
-#line 2352 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2354 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 44:
@@ -2358,13 +2360,13 @@ yyreduce:
       muscle_code_grow ("union_members", (yyvsp[0].chars), (yylsp[0]));
       code_scanner_last_string_free ();
     }
-#line 2362 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2364 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 45:
 #line 450 "src/parse-gram.y" /* yacc.c:1669  */
     { current_class = nterm_sym; }
-#line 2368 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2370 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 46:
@@ -2373,13 +2375,13 @@ yyreduce:
       current_class = unknown_sym;
       current_type = NULL;
     }
-#line 2377 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2379 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 47:
 #line 455 "src/parse-gram.y" /* yacc.c:1669  */
     { current_class = token_sym; }
-#line 2383 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2385 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 48:
@@ -2388,7 +2390,7 @@ yyreduce:
       current_class = unknown_sym;
       current_type = NULL;
     }
-#line 2392 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2394 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 49:
@@ -2400,7 +2402,7 @@ yyreduce:
         symbol_type_set (list->content.sym, (yyvsp[-1].uniqstr), (yylsp[-1]));
       symbol_list_free ((yyvsp[0].list));
     }
-#line 2404 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2406 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 50:
@@ -2416,115 +2418,115 @@ yyreduce:
       symbol_list_free ((yyvsp[0].list));
       current_type = NULL;
     }
-#line 2420 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2422 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 51:
 #line 486 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.assoc) = left_assoc; }
-#line 2426 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2428 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 52:
 #line 487 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.assoc) = right_assoc; }
-#line 2432 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2434 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 53:
 #line 488 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.assoc) = non_assoc; }
-#line 2438 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2440 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 54:
 #line 489 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.assoc) = precedence_assoc; }
-#line 2444 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2446 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 55:
 #line 493 "src/parse-gram.y" /* yacc.c:1669  */
     { current_type = NULL; }
-#line 2450 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2452 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 56:
 #line 494 "src/parse-gram.y" /* yacc.c:1669  */
     { current_type = (yyvsp[0].uniqstr); tag_seen = true; }
-#line 2456 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2458 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 57:
 #line 500 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.list) = symbol_list_sym_new ((yyvsp[0].symbol), (yylsp[0])); }
-#line 2462 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2464 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 58:
 #line 502 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.list) = symbol_list_append ((yyvsp[-1].list), symbol_list_sym_new 
((yyvsp[0].symbol), (yylsp[0]))); }
-#line 2468 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2470 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 59:
 #line 506 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.symbol) = (yyvsp[0].symbol); }
-#line 2474 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2476 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 60:
 #line 507 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.symbol) = (yyvsp[-1].symbol); symbol_user_token_number_set 
((yyvsp[-1].symbol), (yyvsp[0].integer), (yylsp[0])); }
-#line 2480 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2482 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 61:
 #line 513 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.list) = symbol_list_sym_new ((yyvsp[0].symbol), (yylsp[0])); }
-#line 2486 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2488 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 62:
 #line 515 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.list) = symbol_list_append ((yyvsp[-1].list), symbol_list_sym_new 
((yyvsp[0].symbol), (yylsp[0]))); }
-#line 2492 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2494 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 63:
 #line 519 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.list) = (yyvsp[0].list); }
-#line 2498 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2500 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 64:
 #line 520 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.list) = symbol_list_append ((yyvsp[-1].list), (yyvsp[0].list)); }
-#line 2504 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2506 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 65:
 #line 524 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.list) = symbol_list_sym_new ((yyvsp[0].symbol), (yylsp[0])); }
-#line 2510 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2512 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 66:
 #line 525 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.list) = symbol_list_type_new ((yyvsp[0].uniqstr), (yylsp[0])); }
-#line 2516 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2518 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 68:
 #line 530 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.uniqstr) = uniqstr_new ("*"); }
-#line 2522 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2524 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 69:
 #line 531 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.uniqstr) = uniqstr_new (""); }
-#line 2528 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2530 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 70:
@@ -2533,7 +2535,7 @@ yyreduce:
       current_type = (yyvsp[0].uniqstr);
       tag_seen = true;
     }
-#line 2537 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2539 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 71:
@@ -2542,7 +2544,7 @@ yyreduce:
       symbol_class_set ((yyvsp[0].symbol), current_class, (yylsp[0]), true);
       symbol_type_set ((yyvsp[0].symbol), current_type, (yylsp[0]));
     }
-#line 2546 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2548 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 72:
@@ -2552,7 +2554,7 @@ yyreduce:
       symbol_type_set ((yyvsp[-1].symbol), current_type, (yylsp[-1]));
       symbol_user_token_number_set ((yyvsp[-1].symbol), (yyvsp[0].integer), 
(yylsp[0]));
     }
-#line 2556 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2558 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 73:
@@ -2562,7 +2564,7 @@ yyreduce:
       symbol_type_set ((yyvsp[-1].symbol), current_type, (yylsp[-1]));
       symbol_make_alias ((yyvsp[-1].symbol), (yyvsp[0].symbol), (yyloc));
     }
-#line 2566 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2568 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 74:
@@ -2573,7 +2575,7 @@ yyreduce:
       symbol_user_token_number_set ((yyvsp[-2].symbol), (yyvsp[-1].integer), 
(yylsp[-1]));
       symbol_make_alias ((yyvsp[-2].symbol), (yyvsp[0].symbol), (yyloc));
     }
-#line 2577 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2579 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 81:
@@ -2581,13 +2583,13 @@ yyreduce:
     {
       yyerrok;
     }
-#line 2585 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2587 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 82:
 #line 595 "src/parse-gram.y" /* yacc.c:1669  */
     { current_lhs ((yyvsp[-1].symbol), (yylsp[-1]), (yyvsp[0].named_ref)); }
-#line 2591 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2593 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 83:
@@ -2596,108 +2598,120 @@ yyreduce:
     /* Free the current lhs. */
     current_lhs (0, (yylsp[-3]), 0);
   }
-#line 2600 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2602 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 84:
 #line 603 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_end ((yylsp[0])); }
-#line 2606 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2608 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 85:
 #line 604 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_end ((yylsp[0])); }
-#line 2612 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2614 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 87:
 #line 611 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_begin (current_lhs_symbol, current_lhs_location,
                                   current_lhs_named_ref); }
-#line 2619 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2621 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 88:
 #line 614 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_symbol_append ((yyvsp[-1].symbol), (yylsp[-1]), 
(yyvsp[0].named_ref)); }
-#line 2625 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2627 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 89:
 #line 616 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_action_append ((yyvsp[-1].code), (yylsp[-1]), 
(yyvsp[0].named_ref), false); }
-#line 2631 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2633 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 90:
 #line 618 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_action_append ((yyvsp[0].code), (yylsp[0]), NULL, 
true); }
-#line 2637 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2639 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 91:
 #line 620 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_empty_set ((yylsp[0])); }
-#line 2643 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2645 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 92:
 #line 622 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_prec_set ((yyvsp[0].symbol), (yylsp[0])); }
-#line 2649 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2651 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 93:
 #line 624 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_dprec_set ((yyvsp[0].integer), (yylsp[0])); }
-#line 2655 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2657 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 94:
 #line 626 "src/parse-gram.y" /* yacc.c:1669  */
     { grammar_current_rule_merge_set ((yyvsp[0].uniqstr), (yylsp[0])); }
-#line 2661 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2663 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
   case 95:
+#line 628 "src/parse-gram.y" /* yacc.c:1669  */
+    { grammar_current_rule_expect_sr ((yyvsp[0].integer), (yylsp[0])); }
+#line 2669 "src/parse-gram.c" /* yacc.c:1669  */
+    break;
+
+  case 96:
 #line 630 "src/parse-gram.y" /* yacc.c:1669  */
+    { grammar_current_rule_expect_rr ((yyvsp[0].integer), (yylsp[0])); }
+#line 2675 "src/parse-gram.c" /* yacc.c:1669  */
+    break;
+
+  case 97:
+#line 634 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.named_ref) = 0; }
-#line 2667 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2681 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 96:
-#line 631 "src/parse-gram.y" /* yacc.c:1669  */
+  case 98:
+#line 635 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.named_ref) = named_ref_new((yyvsp[0].uniqstr), (yylsp[0])); }
-#line 2673 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2687 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 98:
-#line 642 "src/parse-gram.y" /* yacc.c:1669  */
+  case 100:
+#line 646 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.uniqstr) = uniqstr_new ((yyvsp[0].chars)); }
-#line 2679 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2693 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 99:
-#line 647 "src/parse-gram.y" /* yacc.c:1669  */
+  case 101:
+#line 651 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.chars) = ""; }
-#line 2685 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2699 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 100:
-#line 648 "src/parse-gram.y" /* yacc.c:1669  */
+  case 102:
+#line 652 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.chars) = (yyvsp[0].uniqstr); }
-#line 2691 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2705 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 101:
-#line 649 "src/parse-gram.y" /* yacc.c:1669  */
+  case 103:
+#line 653 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.chars) = (yyvsp[0].chars); }
-#line 2697 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2711 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 102:
-#line 659 "src/parse-gram.y" /* yacc.c:1669  */
+  case 104:
+#line 663 "src/parse-gram.y" /* yacc.c:1669  */
     {
       code_props plain_code;
       (yyvsp[0].code)[strlen ((yyvsp[0].code)) - 1] = '\n';
@@ -2706,42 +2720,42 @@ yyreduce:
       gram_scanner_last_string_free ();
       (yyval.chars) = plain_code.code;
     }
-#line 2710 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2724 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 103:
-#line 679 "src/parse-gram.y" /* yacc.c:1669  */
+  case 105:
+#line 683 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.symbol) = symbol_from_uniqstr ((yyvsp[0].uniqstr), (yylsp[0])); }
-#line 2716 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2730 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 104:
-#line 681 "src/parse-gram.y" /* yacc.c:1669  */
+  case 106:
+#line 685 "src/parse-gram.y" /* yacc.c:1669  */
     {
       (yyval.symbol) = symbol_get (char_name ((yyvsp[0].character)), 
(yylsp[0]));
       symbol_class_set ((yyval.symbol), token_sym, (yylsp[0]), false);
       symbol_user_token_number_set ((yyval.symbol), (yyvsp[0].character), 
(yylsp[0]));
     }
-#line 2726 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2740 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 105:
-#line 689 "src/parse-gram.y" /* yacc.c:1669  */
+  case 107:
+#line 693 "src/parse-gram.y" /* yacc.c:1669  */
     { (yyval.symbol) = symbol_from_uniqstr ((yyvsp[0].uniqstr), (yylsp[0])); }
-#line 2732 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2746 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 108:
-#line 701 "src/parse-gram.y" /* yacc.c:1669  */
+  case 110:
+#line 705 "src/parse-gram.y" /* yacc.c:1669  */
     {
       (yyval.symbol) = symbol_get (quotearg_style (c_quoting_style, 
(yyvsp[0].chars)), (yylsp[0]));
       symbol_class_set ((yyval.symbol), token_sym, (yylsp[0]), false);
     }
-#line 2741 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2755 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
-  case 110:
-#line 710 "src/parse-gram.y" /* yacc.c:1669  */
+  case 112:
+#line 714 "src/parse-gram.y" /* yacc.c:1669  */
     {
       code_props plain_code;
       code_props_plain_init (&plain_code, (yyvsp[0].chars), (yylsp[0]));
@@ -2750,11 +2764,11 @@ yyreduce:
       muscle_code_grow ("epilogue", plain_code.code, (yylsp[0]));
       code_scanner_last_string_free ();
     }
-#line 2754 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2768 "src/parse-gram.c" /* yacc.c:1669  */
     break;
 
 
-#line 2758 "src/parse-gram.c" /* yacc.c:1669  */
+#line 2772 "src/parse-gram.c" /* yacc.c:1669  */
         default: break;
       }
     if (yychar_backup != yychar)
@@ -3001,7 +3015,7 @@ yyreturn:
 #endif
   return yyresult;
 }
-#line 720 "src/parse-gram.y" /* yacc.c:1929  */
+#line 724 "src/parse-gram.y" /* yacc.c:1929  */
 
 
 /* Return the location of the left-hand side of a rule whose
diff --git a/src/parse-gram.h b/src/parse-gram.h
index 3f78bbf..1b2d2b1 100644
--- a/src/parse-gram.h
+++ b/src/parse-gram.h
@@ -1,4 +1,4 @@
-/* A Bison parser, made by GNU Bison 2.7.1116-42fef.  */
+/* A Bison parser, made by GNU Bison 2.7.1119-e39fc.  */
 
 /* Bison interface for Yacc-like parsers in C
 
diff --git a/src/parse-gram.y b/src/parse-gram.y
index 4e887e8..2c84072 100644
--- a/src/parse-gram.y
+++ b/src/parse-gram.y
@@ -624,6 +624,10 @@ rhs:
     { grammar_current_rule_dprec_set ($3, @3); }
 | rhs "%merge" TAG
     { grammar_current_rule_merge_set ($3, @3); }
+| rhs "%expect" INT
+    { grammar_current_rule_expect_sr ($3, @3); }
+| rhs "%expect-rr" INT
+    { grammar_current_rule_expect_rr ($3, @3); }
 ;
 
 named_ref.opt:
diff --git a/src/reader.c b/src/reader.c
index dbf4e95..ab5fc8a 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -405,6 +405,11 @@ grammar_midrule_action (void)
                                current_rule->action_props.is_predicate);
   code_props_none_init (&current_rule->action_props);
 
+  midrule->expected_sr_conflicts = current_rule->expected_sr_conflicts;
+  midrule->expected_rr_conflicts = current_rule->expected_rr_conflicts;
+  current_rule->expected_sr_conflicts = -1;
+  current_rule->expected_rr_conflicts = -1;
+
   if (previous_rule_end)
     previous_rule_end->next = midrule;
   else
@@ -535,6 +540,26 @@ grammar_current_rule_action_append (const char *action, 
location loc,
                                current_rule, name, is_predicate);
 }
 
+/* Set the expected number of shift-reduce (reduce-reduce) conflicts for
+ * the current rule.  If a midrule is encountered later, the count
+ * is transferred to it and reset in the current rule to -1. */
+
+void
+grammar_current_rule_expect_sr (int count, location loc)
+{
+  current_rule->expected_sr_conflicts = count;
+}
+
+void
+grammar_current_rule_expect_rr (int count, location loc)
+{
+  if (! glr_parser)
+    complain (&loc, Wother, _("%s affects only GLR parsers"),
+              "%expect-rr");
+  else
+    current_rule->expected_rr_conflicts = count;
+}
+
 
 /*---------------------------------------------------------------.
 | Convert the rules into the representation using RRHS, RLHS and |
@@ -573,6 +598,8 @@ packgram (void)
       rules[ruleno].action = p->action_props.code;
       rules[ruleno].action_location = p->action_props.location;
       rules[ruleno].is_predicate = p->action_props.is_predicate;
+      rules[ruleno].expected_sr_conflicts = p->expected_sr_conflicts;
+      rules[ruleno].expected_rr_conflicts = p->expected_rr_conflicts;
 
       /* If the midrule's $$ is set or its $n is used, remove the '$' from the
          symbol name so that it's a user-defined symbol so that the default
diff --git a/src/reader.h b/src/reader.h
index ba6ffe6..c7987f9 100644
--- a/src/reader.h
+++ b/src/reader.h
@@ -52,6 +52,8 @@ void grammar_current_rule_empty_set (location loc);
 void grammar_current_rule_prec_set (symbol *precsym, location loc);
 void grammar_current_rule_dprec_set (int dprec, location loc);
 void grammar_current_rule_merge_set (uniqstr name, location loc);
+void grammar_current_rule_expect_sr (int count, location loc);
+void grammar_current_rule_expect_rr (int count, location loc);
 void grammar_current_rule_symbol_append (symbol *sym, location loc,
                                          named_ref *nref);
 void grammar_current_rule_action_append (const char *action, location loc,
diff --git a/src/symlist.c b/src/symlist.c
index 50915c1..d2cded0 100644
--- a/src/symlist.c
+++ b/src/symlist.c
@@ -50,6 +50,8 @@ symbol_list_sym_new (symbol *sym, location loc)
   res->dprec_location = empty_location;
   res->merger = 0;
   res->merger_declaration_location = empty_location;
+  res->expected_sr_conflicts = -1;
+  res->expected_rr_conflicts = -1;
 
   res->next = NULL;
 
diff --git a/src/symlist.h b/src/symlist.h
index c369f69..be0bc95 100644
--- a/src/symlist.h
+++ b/src/symlist.h
@@ -87,6 +87,11 @@ typedef struct symbol_list
   int merger;
   location merger_declaration_location;
 
+  /* Counts of the number of expected conflicts for this rule, or -1 if none
+     given. */
+  int expected_sr_conflicts;
+  int expected_rr_conflicts;
+
   /* The list.  */
   struct symbol_list *next;
 } symbol_list;
diff --git a/tests/conflicts.at b/tests/conflicts.at
index 07ff178..0ad395f 100644
--- a/tests/conflicts.at
+++ b/tests/conflicts.at
@@ -1140,6 +1140,61 @@ AT_BISON_CHECK([-o input.c input.y], 1, [],
 AT_CLEANUP
 
 
+## ------------------------------------ ##
+## %expect in grammar rule not enough.  ##
+## ------------------------------------ ##
+
+AT_SETUP([%expect in grammar rule not enough])
+
+AT_DATA([input.y],
+[[%token NUM OP
+%expect 1
+%%
+exp: exp OP exp %expect 0 | NUM;
+]])
+
+AT_BISON_CHECK([-o input.c input.y], 1, [],
+[[input.y:4.6-25: error: shift/reduce conflicts for rule 1: 1 found, 0 expected
+]])
+AT_CLEANUP
+
+
+## ------------------------------- ##
+## %expect in grammar rule right.  ##
+## ------------------------------- ##
+
+AT_SETUP([%expect in grammar rule right])
+
+AT_DATA([input.y],
+[[%token NUM OP
+%expect 1
+%%
+exp: exp OP exp %expect 1 | NUM;
+]])
+
+AT_BISON_CHECK([-o input.c input.y])
+AT_CLEANUP
+
+
+## ---------------------------------- ##
+## %expect in grammar rule too much.  ##
+## ---------------------------------- ##
+
+AT_SETUP([%expect in grammar rule too much])
+
+AT_DATA([input.y],
+[[%token NUM OP
+%expect 1
+%%
+exp: exp OP exp | NUM %expect 1;
+]])
+
+AT_BISON_CHECK([-o input.c input.y], 1, [],
+[[input.y:4.19-31: error: shift/reduce conflicts for rule 2: 0 found, 1 
expected
+]])
+AT_CLEANUP
+
+
 ## ------------------------- ##
 ## %prec with user strings.  ##
 ## ------------------------- ##




reply via email to

[Prev in Thread] Current Thread [Next in Thread]