]> git.draconx.ca Git - cdecl99.git/commitdiff
Solve Bison conflicts using precedence declarations.
authorNick Bowler <nbowler@draconx.ca>
Sun, 23 Jul 2023 14:55:40 +0000 (10:55 -0400)
committerNick Bowler <nbowler@draconx.ca>
Sun, 23 Jul 2023 15:02:16 +0000 (11:02 -0400)
We can use a precedence declaration to directly inform Bison of how
to resolve the specific conflict at hand, rather than just saying
"it is ok to have N conflicts."

There is no significant change to the generated output but this way
keeps things more straightforward when modifying the parser.

src/parse.y

index fd5776ede27596b488d34a7cf113811113eade9a..ec7ea1b74b5e78f1738a240c84b459f39e051575 100644 (file)
@@ -265,11 +265,9 @@ static struct cdecl *insert_identifier(struct cdecl *decl, char *ident)
 %type <decl>       english english_declaration
 %type <decl>       english_parameter
 
-/*
- * Harmless shift/reduce conflicts in english_parameter.  See comments below
- * for more details.
- */
-%expect 2
+/* Precedence declaration to avoid conflict in english_parameter; see below. */
+%right T_TYPE
+%right T_IDENT
 
 %%
 
@@ -460,7 +458,12 @@ english: T_DECLARE T_IDENT T_AS english_declaration {
        $$ = $2;
 }
 
-storage_func_specs: { $$ = NULL; } | declspec_simple storage_func_specs {
+/*
+ * We use a precedence declaration to prefer shifting an identifier
+ * over reducing this empty rule; see below.
+ */
+storage_func_specs: %prec T_TYPE { $$ = NULL; }
+storage_func_specs: declspec_simple storage_func_specs {
        ALLOC_STRUCT($$, struct cdecl_declspec,
                .type = $1,
                .next = $2);
@@ -539,14 +542,18 @@ null_decl: {
 }
 
 /*
- * There is a small shift/reduce conflict here.  An unadorned identifier
- * as the first thing in the parameter might be a typedef name deep in the
- * first english_declaration (thus empty storage_func_specs and empty
- * english_declarator need to be reduced) or it might be the identifier
- * before the "as" (thus the identifier should be shifted).
+ * There is a shift/reduce conflict here when an identifier appears as the
+ * first token.  The conflict is between shifting T_IDENT, or reducing the
+ * empty production for storage_func_specs (cf. english_declaration).
+ *
+ *   - In either case, if we reduce, we won't match T_IDENT T_AS since the
+ *     stack now has the extra storage_func_specs nonterminal symbol.
+ *   - And if we shift, we won't match english_declaration since it is
+ *     too late to add storage_func_specs to the stack.
  *
- * The typedef name conflict is the only issue, so treating it as a special
- * case makes the shift harmless.
+ * The only valid input affected by the conflict is a simple type names,
+ * possibly followed by qualifiers.  So the conflict is adequately resolved
+ * by shifting, so long as we have a special-case reduction to handle this.
  */
 english_parameter: english_declaration | typedef_name_qual null_decl {
        ALLOC_STRUCT($$, struct cdecl,