From 2d8fc07778b686e37c53d61cea051862cb0257e6 Mon Sep 17 00:00:00 2001 From: Valentin Tolmer Date: Thu, 1 Aug 2013 14:53:24 +0200 Subject: [PATCH] conflicts: switch to partial order precedence system Even though it is not yet fully deployed, this commit lays the ground for the partial order precedence system, by changing to a graph-based order and introducing precedence groups (only the default one can be used for now). * src/symtab.h (struct symbol): Removed extra fields * src/symtab.h: New function declarations * src/symtab.c: New functions for precedence and groups, new hash table for groups * src/AnnotationList.c, src/conflicts.c, src/gram.c, src/print-xml.c, * src/symtab.c: Adaptation to the new prec_node structure * tests/existing.at (GAWK LALR): Fix --- src/AnnotationList.c | 4 +- src/conflicts.c | 133 +++++++------ src/gram.c | 4 +- src/print-xml.c | 3 +- src/symtab.c | 431 +++++++++++++++++++++++++++++++++++++++++-- src/symtab.h | 24 ++- tests/existing.at | 8 +- 7 files changed, 528 insertions(+), 79 deletions(-) diff --git a/src/AnnotationList.c b/src/AnnotationList.c index 9f0adb93..b1cfab18 100644 --- a/src/AnnotationList.c +++ b/src/AnnotationList.c @@ -740,7 +740,7 @@ AnnotationList__computeDominantContribution (AnnotationList const *self, if (reduce_precedence && (reduce_precedence < shift_precedence || (reduce_precedence == shift_precedence - && token->content->assoc == right_assoc))) + && token->content->prec_node->assoc == right_assoc))) continue; if (!AnnotationList__stateMakesContribution (self, nitems, ci, lookaheads)) @@ -748,7 +748,7 @@ AnnotationList__computeDominantContribution (AnnotationList const *self, /* This uneliminated reduction contributes, so see if it can cause an error action. */ if (reduce_precedence == shift_precedence - && token->content->assoc == non_assoc) + && token->content->prec_node->assoc == non_assoc) { /* It's not possible to find split-stable domination over shift after a potential %nonassoc. */ diff --git a/src/conflicts.c b/src/conflicts.c index 30a2cabd..b8f1dcc7 100644 --- a/src/conflicts.c +++ b/src/conflicts.c @@ -53,7 +53,8 @@ enum conflict_resolution reduce_resolution, left_resolution, right_resolution, - nonassoc_resolution + nonassoc_resolution, + uncomparable_resolution }; @@ -90,6 +91,7 @@ log_resolution (rule *r, symbol_number token, break; case nonassoc_resolution: + case uncomparable_resolution: obstack_printf (&solved_conflicts_obstack, _(" Conflict between rule %d and token %s" " resolved as an error"), @@ -132,6 +134,12 @@ log_resolution (rule *r, symbol_number token, " (%%nonassoc %s)", symbols[token]->tag); break; + case uncomparable_resolution: + obstack_printf (&solved_conflicts_obstack, + " (%s uncomparable with %s)", + r->prec->symbol->tag, + symbols[token]->tag); + break; } obstack_sgrow (&solved_conflicts_obstack, ".\n"); @@ -161,6 +169,7 @@ log_resolution (rule *r, symbol_number token, xml_escape (symbols[token]->tag)); break; + case uncomparable_resolution: case nonassoc_resolution: obstack_printf (&solved_conflicts_xml_obstack, " tag)); - break; + break; + case uncomparable_resolution: + obstack_printf (&solved_conflicts_xml_obstack, + "%s uncomparable with %s", + xml_escape_n (0, symbols[token]->tag), + xml_escape_n (1, r->prec->symbol->tag)); + break; } obstack_sgrow (&solved_conflicts_xml_obstack, "\n"); @@ -243,7 +258,6 @@ flush_reduce (bitset lookahead_tokens, int token) bitset_reset (lookahead_tokens, token); } - /*------------------------------------------------------------------. | Attempt to resolve shift-reduce conflict for one rule by means of | | precedence declarations. It has already been checked that the | @@ -263,66 +277,73 @@ resolve_sr_conflict (state *s, int ruleno, symbol **errors, int *nerrs) reductions *reds = s->reductions; /* Find the rule to reduce by to get precedence of reduction. */ rule *redrule = reds->rules[ruleno]; - int redprec = redrule->prec->prec; + prec_node *redprecsym = redrule->prec->prec_node; bitset lookahead_tokens = reds->lookahead_tokens[ruleno]; for (i = 0; i < ntokens; i++) if (bitset_test (lookahead_tokens, i) - && bitset_test (lookahead_set, i) - && symbols[i]->content->prec) + && bitset_test (lookahead_set, i)) { - /* Shift-reduce conflict occurs for token number i - and it has a precedence. - The precedence of shifting is that of token i. */ - if (symbols[i]->content->prec < redprec) + if (redprecsym && symbols[i]->content->prec_node) { - register_precedence (redrule->prec->number, i); - log_resolution (redrule, i, reduce_resolution); - flush_shift (s, i); - } - else if (symbols[i]->content->prec > redprec) - { - register_precedence (i, redrule->prec->number); - log_resolution (redrule, i, shift_resolution); - flush_reduce (lookahead_tokens, i); + /* Shift-reduce conflict occurs for token number i + and it has a precedence. + The precedence of shifting is that of token i. */ + if (is_prec_superior (redprecsym, symbols[i]->content->prec_node)) + { + register_precedence (redrule->prec->number, i); + log_resolution (redrule, i, reduce_resolution); + flush_shift (s, i); + } + else if (is_prec_superior (symbols[i]->content->prec_node, + redprecsym)) + { + register_precedence (i, redrule->prec->number); + log_resolution (redrule, i, shift_resolution); + flush_reduce (lookahead_tokens, i); + } + else if (is_prec_equal (redprecsym, symbols[i]->content->prec_node)) + /* Matching precedence levels. + For non-defined associativity, keep both: unexpected + associativity conflict. + For left associativity, keep only the reduction. + For right associativity, keep only the shift. + For nonassociativity, keep neither. */ + + switch (symbols[i]->content->prec_node->assoc) + { + case undef_assoc: + break; + + case precedence_assoc: + break; + + case right_assoc: + register_assoc (i, redrule->prec->number); + log_resolution (redrule, i, right_resolution); + flush_reduce (lookahead_tokens, i); + break; + + case left_assoc: + register_assoc (i, redrule->prec->number); + log_resolution (redrule, i, left_resolution); + flush_shift (s, i); + break; + + case non_assoc: + register_assoc (i, redrule->prec->number); + log_resolution (redrule, i, nonassoc_resolution); + flush_shift (s, i); + flush_reduce (lookahead_tokens, i); + /* Record an explicit error for this token. */ + errors[(*nerrs)++] = symbols[i]; + break; + } + else + log_resolution (redrule, i, uncomparable_resolution); } else - /* Matching precedence levels. - For non-defined associativity, keep both: unexpected - associativity conflict. - For left associativity, keep only the reduction. - For right associativity, keep only the shift. - For nonassociativity, keep neither. */ - - switch (symbols[i]->content->assoc) - { - case undef_assoc: - abort (); - - case precedence_assoc: - break; - - case right_assoc: - register_assoc (i, redrule->prec->number); - log_resolution (redrule, i, right_resolution); - flush_reduce (lookahead_tokens, i); - break; - - case left_assoc: - register_assoc (i, redrule->prec->number); - log_resolution (redrule, i, left_resolution); - flush_shift (s, i); - break; - - case non_assoc: - register_assoc (i, redrule->prec->number); - log_resolution (redrule, i, nonassoc_resolution); - flush_shift (s, i); - flush_reduce (lookahead_tokens, i); - /* Record an explicit error for this token. */ - errors[(*nerrs)++] = symbols[i]; - break; - } + log_resolution (redrule, i, uncomparable_resolution); } } @@ -354,7 +375,7 @@ set_conflicts (state *s, symbol **errors) check for shift-reduce conflict, and try to resolve using precedence. */ for (i = 0; i < reds->num; ++i) - if (reds->rules[i]->prec && reds->rules[i]->prec->prec + if (reds->rules[i]->prec /* && reds->rules[i]->prec->prec */ && !bitset_disjoint_p (reds->lookahead_tokens[i], lookahead_set)) resolve_sr_conflict (s, i, errors, &nerrs); diff --git a/src/gram.c b/src/gram.c index 790ec3a9..66f550cb 100644 --- a/src/gram.c +++ b/src/gram.c @@ -239,7 +239,7 @@ grammar_dump (FILE *out, const char *title) for (i = ntokens; i < nsyms; i++) fprintf (out, "%5d %5d %5d %s\n", i, - symbols[i]->content->prec, symbols[i]->content->assoc, + symbols[i]->content->prec, symbols[i]->content->prec_node->assoc, symbols[i]->tag); fprintf (out, "\n\n"); } @@ -262,7 +262,7 @@ grammar_dump (FILE *out, const char *title) fprintf (out, "%3d (%2d, %2d, %2d, %2u-%2u) %2d ->", i, rule_i->prec ? rule_i->prec->prec : 0, - rule_i->prec ? rule_i->prec->assoc : 0, + rule_i->prec ? rule_i->prec->prec_node->assoc : 0, rule_i->useful, rhs_itemno, rhs_itemno + rhs_count - 1, diff --git a/src/print-xml.c b/src/print-xml.c index 3ddaeba4..60eaf368 100644 --- a/src/print-xml.c +++ b/src/print-xml.c @@ -392,7 +392,8 @@ print_grammar (FILE *out, int level) { char const *tag = symbols[token_translations[i]]->tag; int precedence = symbols[token_translations[i]]->content->prec; - assoc associativity = symbols[token_translations[i]]->content->assoc; + assoc associativity = symbols[token_translations[i]]->content->prec_node + ->assoc; xml_indent (out, level + 2); fprintf (out, "target = to; + res->transitive = transitive; + res->next = NULL; + return res; +} + +/*-------------------------------------. +| Destructor for a simple symbol list. | +`-------------------------------------*/ + +static void +symbol_list_prec_free (symbol_list *l) +{ + if (l) + { + symbol_list_prec_free (l->next); + free (l); + } +} + +/*------------------------------------------------. +| Check if PARENT has a higher priority than SON. | +`------------------------------------------------*/ + +bool +is_prec_superior (prec_node *parent, prec_node *son) +{ + prec_link *l; + for (l = parent->sons; l; l = l->next) + if (l->target == son) + return true; + return false; +} + +/*-----------------------------------------. +| Check if S1 has the same priority as S2. | +`-----------------------------------------*/ + +bool +is_prec_equal (prec_node *s1, prec_node *s2) +{ + prec_link *l; + if (s1 == s2) + return true; + for (l = s1->equals; l; l = l->next) + if (l->target == s2) + return true; + return false; +} + +static inline void +complain_contradicting_prec (location *loc, uniqstr s1, uniqstr s2, char c1, + char c2) +{ + complain (loc, Wprecedence, _("contradicting declaration: %s %c %s is in " + "conflict with the previous declaration: %s %c %s"), s1, c1, s2, + s1, c2, s2); +} + +/*-----------------------------------------------------------------------. +| Compare LINK with TARGET, and return whether they are equal. | +| In case of equality, complain of the duplicate precedence declaration. | +`-----------------------------------------------------------------------*/ + +static inline bool +is_prec_target (prec_node *l, prec_node *target, uniqstr from, char c, + location loc) +{ + if (l == target) + { + complain (&loc, Wprecedence, _("duplicate declaration of the precedence " + "relationship %s %c %s"), from, c, + target->symbol->tag); + return true; + } + return false; +} + +/*-----------------------------------------. +| Add a precedence relationship FROM > TO. | +`-----------------------------------------*/ + +static void +add_prec_link (prec_node *from, prec_node *to, bool transitive, location loc) +{ + if (is_prec_superior (to, from)) + complain_contradicting_prec(&loc, from->symbol->tag, to->symbol->tag, + '>', '<'); + else if (is_prec_equal (from, to)) + complain_contradicting_prec(&loc, from->symbol->tag, to->symbol->tag, + '>', '='); + else + { + if (from->sons) + { + if (is_prec_target (from->sons->target, to, from->symbol->tag, '>', + loc)) + return; + + prec_link *son = from->sons; + for (; son->next; son = son->next) + if (is_prec_target (son->next->target, to, from->symbol->tag, '>', + loc)) + return; + son->next = prec_link_new (to, transitive); + } + else + from->sons = prec_link_new (to, transitive); + } +} + +/*-------------------------------------------------. +| Add a precedence relationship S1 == S2, one way. | +`-------------------------------------------------*/ + +static void +create_prec_equal_link (prec_node *s1, prec_node *s2, bool transitive, + location loc) +{ + if (s1->equals) + { + if (is_prec_target (s1->equals->target, s2, s1->symbol->tag, '=', + loc)) + return; + prec_link *eq = s1->equals; + for (; eq->next; eq = eq->next) + if (is_prec_target (eq->next->target, s2, s1->symbol->tag, '=', + loc)) + return; + eq->next = prec_link_new (s2, transitive); + } + else + s1->equals = prec_link_new (s2, transitive); +} + + +/*---------------------------------------------------. +| Add a precedence relationship S1 == S2, both ways. | +`---------------------------------------------------*/ + +static void +add_prec_equal_link (prec_node *s1, prec_node *s2, bool transitive, + location loc) +{ + if (is_prec_superior (s2, s1)) + complain_contradicting_prec(&loc, s1->symbol->tag, s2->symbol->tag, + '=', '>'); + else if (is_prec_superior (s1, s2)) + complain_contradicting_prec(&loc, s1->symbol->tag, s2->symbol->tag, + '=', '<'); + create_prec_equal_link (s1, s2, transitive, loc); + create_prec_equal_link (s2, s1, transitive, loc); +} + + +/*------------------------------------------------------------------------. +| Add a symbol to the current declaration group, and declare the implicit | +| precedence links. SAME_LINE is true if the symbol was declared in the | +| same statement as the previous one (same precedence level). | +`------------------------------------------------------------------------*/ + +void +add_to_current_group (sym_content *s, bool same_line) +{ + if (!same_line) + for (symbol_list *l = current_prec_declaration; l; l = l->next) + { + sym_content *symb = l->content.sym->content; + if (!current_group->symbol_list) + current_group->symbol_list = symb; + else + { + sym_content *sym = current_group->symbol_list; + while (sym->group_next) + sym = sym->group_next; + sym->group_next = symb; + } + } + + if (current_group->symbol_list) + for (sym_content *sym = current_group->symbol_list; sym; + sym = sym->group_next) + add_prec_link (s->prec_node, sym->prec_node, true, + s->prec_node->prec_location); + + if (!same_line) + { + symbol_list_prec_free (current_prec_declaration); + current_prec_declaration = malloc (sizeof *current_prec_declaration); + current_prec_declaration->content.sym = s->symbol; + current_prec_declaration->next = NULL; + } + else + { + symbol_list *l = current_prec_declaration; + for (; true; l = l->next) + { + add_prec_equal_link (s->prec_node, l->content.sym->content->prec_node, + true, s->prec_node->prec_location); + if (!l->next) + break; + } + l->next = malloc (sizeof *l->next); + l->next->content.sym = s->symbol; + l->next->next = NULL; + } +} + +/*-----------------------------------------. +| Create a new prec_node for the symbol s. | +`-----------------------------------------*/ + +static prec_node * +prec_node_new (symbol * s) +{ + prec_node * res = malloc (sizeof *res); + res->symbol = s; + res->assoc = undef_assoc; + res->sons = NULL; + res-> equals = NULL; + return res; +} + + /*--------------------------. | Create a new sym_content. | `--------------------------*/ @@ -78,12 +329,14 @@ sym_content_new (symbol *s) res->number = NUMBER_UNDEFINED; res->prec = 0; - res->assoc = undef_assoc; res->user_token_number = USER_NUMBER_UNDEFINED; res->class = unknown_sym; res->status = undeclared; + res->group_next = NULL; + res->prec_node = prec_node_new (s); + return res; } @@ -116,6 +369,28 @@ symbol_new (uniqstr tag, location loc) return res; } +void +prec_link_free (prec_link * l) +{ + if (l) + { + prec_link_free (l->next); + free (l); + } +} + +/*--------------------. +| Free one prec_node. | +`--------------------*/ + +static void +prec_node_free (prec_node * n) +{ + prec_link_free (n->sons); + prec_link_free (n->equals); + free (n); +} + /*--------------------. | Free a sym_content. | `--------------------*/ @@ -123,6 +398,7 @@ symbol_new (uniqstr tag, location loc) static void sym_content_free (sym_content *sym) { + prec_node_free (sym->prec_node); free (sym); } @@ -367,14 +643,16 @@ symbol_precedence_set (symbol *sym, int prec, assoc a, location loc) sym_content *s = sym->content; if (a != undef_assoc) { - if (s->prec) + if (s->prec_node->assoc != undef_assoc) symbol_redeclaration (sym, assoc_to_string (a), - s->prec_location, loc); + s->prec_node->prec_location, loc); else { s->prec = prec; - s->assoc = a; - s->prec_location = loc; + s->prec_node->assoc = a; + s->prec_node->prec_location = loc; + add_to_current_group (s, prec == current_prec_level); + current_prec_level = prec; } } @@ -684,6 +962,38 @@ hash_semantic_type_hasher (void const *m, size_t tablesize) return hash_semantic_type (m, tablesize); } +/*-------------------------------------. +| Symbol precedence group hash table. | +`-------------------------------------*/ + +static struct hash_table *group_table = NULL; + +static inline bool +hash_compare_group (const symgroup *m1, const symgroup *m2) +{ + /* Since tags are unique, we can compare the pointers themselves. */ + return UNIQSTR_EQ (m1->tag, m2->tag); +} + +static bool +hash_group_comparator (void const *m1, void const *m2) +{ + return hash_compare_group (m1, m2); +} + +static inline size_t +hash_group (const symgroup *m, size_t tablesize) +{ + /* Since tags are unique, we can hash the pointer itself. */ + return ((uintptr_t) m->tag) % tablesize; +} + +static size_t +hash_group_hasher (void const *m, size_t tablesize) +{ + return hash_group (m, tablesize); +} + /*-------------------------------. | Create the symbol hash table. | `-------------------------------*/ @@ -701,6 +1011,12 @@ symbols_new (void) hash_semantic_type_hasher, hash_semantic_type_comparator, free); + group_table = hash_initialize (HT_INITIAL_CAPACITY, + NULL, + hash_group_hasher, + hash_group_comparator, + free); + set_current_group (DEFAULT_GROUP_NAME, NULL); } @@ -815,9 +1131,11 @@ symbols_free (void) { hash_free (symbol_table); hash_free (semantic_type_table); + hash_free (group_table); free (symbols); free (symbols_sorted); free (semantic_types_sorted); + symbol_list_prec_free (current_prec_declaration); } @@ -1102,8 +1420,8 @@ static inline bool is_assoc_useless (symbol *s) { return s - && s->content->assoc != undef_assoc - && s->content->assoc != precedence_assoc + && s->content->prec_node->assoc != undef_assoc + && s->content->prec_node->assoc != precedence_assoc && !used_assoc[s->content->number]; } @@ -1136,21 +1454,110 @@ print_precedence_warnings (void) { symbol *s = symbols[i]; if (s - && s->content->prec != 0 && !prec_nodes[i]->pred && !prec_nodes[i]->succ) { if (is_assoc_useless (s)) - complain (&s->content->prec_location, Wprecedence, + complain (&s->content->prec_node->prec_location, Wprecedence, _("useless precedence and associativity for %s"), s->tag); - else if (s->content->assoc == precedence_assoc) - complain (&s->content->prec_location, Wprecedence, + else if (s->content->prec_node->assoc == precedence_assoc) + complain (&s->content->prec_node->prec_location, Wprecedence, _("useless precedence for %s"), s->tag); } else if (is_assoc_useless (s)) - complain (&s->content->prec_location, Wprecedence, + complain (&s->content->prec_node->prec_location, Wprecedence, _("useless associativity for %s, use %%precedence"), s->tag); } free (used_assoc); assoc_free (); } + +/*------------------------------------------------. +| Counter to create unique anonymous group names. | +`------------------------------------------------*/ + +static unsigned int anon_group_counter = 0; + +/*-------------------------------------------------. +| Return a new unique name for an anonymous group. | +`-------------------------------------------------*/ + +uniqstr new_anonymous_group_name (void) +{ + char buff[20]; + snprintf (buff, 20, "__anon%u__", anon_group_counter++); + return uniqstr_new (buff); +} + +/*-------------------------------------------. +| Constructor for a symbol precedence group. | +`-------------------------------------------*/ + +symgroup * +symgroup_new (const uniqstr tag, location loc) +{ + symgroup *group = xmalloc (sizeof (*group)); + group->tag = tag; + group->symbol_list = NULL; + group->location = loc; + return group; +} + +/*--------------------------------------------------------------------------. +| Get the symbol precedence group by that name. If not present, a new group | +| is created and inserted in the table, with the location information | +| provided, if any. | +`--------------------------------------------------------------------------*/ + +symgroup * +symgroup_from_uniqstr (const uniqstr key, location *loc) +{ + bool null_loc = loc == NULL; + if (null_loc) + { + loc = malloc (sizeof *loc); + boundary_set (&loc->start, uniqstr_new (""), 1, 1); + boundary_set (&loc->end, uniqstr_new (""), 1, 1); + } + symgroup probe; + symgroup *entry; + + probe.tag = key; + entry = hash_lookup (group_table, &probe); + + if (!entry) + { + /* First insertion in the hash. */ + entry = symgroup_new (key, *loc); + if (!hash_insert (group_table, entry)) + xalloc_die (); + } + if (null_loc) + free (loc); + return entry; +} + +/*--------------------------------------------------------------------------. +| Change the current group to the one designated by the name, and create it | +| if necessary. The location information is used for creation if available. | +`--------------------------------------------------------------------------*/ + +void set_current_group (const uniqstr tag, location *loc) +{ + for (symbol_list *l = current_prec_declaration; l; l = l->next) + { + sym_content *symb = l->content.sym->content; + if (!current_group->symbol_list) + current_group->symbol_list = symb; + else + { + sym_content *sym = current_group->symbol_list; + for (; sym->group_next; sym = sym->group_next) + {} + sym->group_next = symb; + } + } + symbol_list_prec_free (current_prec_declaration); + current_prec_declaration = NULL; + current_group = symgroup_from_uniqstr (tag, loc); +} diff --git a/src/symtab.h b/src/symtab.h index 18e68059..5c042b04 100644 --- a/src/symtab.h +++ b/src/symtab.h @@ -129,9 +129,10 @@ struct sym_content code_props props[CODE_PROPS_SIZE]; symbol_number number; - location prec_location; + + /* Not used anymore, to remove. */ int prec; - assoc assoc; + int user_token_number; symbol_class class; @@ -317,6 +318,18 @@ struct symgroup location location; } ; +/** Get a dummy name for an anonymous group. */ +uniqstr new_anonymous_group_name (void); + +/** Set the current group in the token precedence declaration to a new group + * with this name */ +void set_current_group (const uniqstr name, location *loc); + +/** Get or create the group by that name. The location information is used for + * creation when available. */ +symgroup * +symgroup_from_uniqstr (const uniqstr key, location *loc); + /*----------------------------------. | Graph of precedence relationships | `----------------------------------*/ @@ -348,6 +361,13 @@ enum prec_rel_comparator prec_superior, prec_superior_strict, }; +/** Check if s1 and s2 have the same precedence level. */ +bool is_prec_equal (prec_node * s1, prec_node * s2); + +/** Check if from > to . */ +bool is_prec_superior (prec_node * from, prec_node * to); + + /*-----------------. | Semantic types. | `-----------------*/ diff --git a/tests/existing.at b/tests/existing.at index a1b95ac9..a8832250 100644 --- a/tests/existing.at +++ b/tests/existing.at @@ -484,7 +484,7 @@ dnl - 61 -> 328: reduce -> shift on '*', '/', and '%' NAME [reduce using rule 152 (opt_variable)] '$' [reduce using rule 152 (opt_variable)] -@@ -5379,7 +5379,7 @@ +@@ -5385,7 +5385,7 @@ 156 | . '$' non_post_simp_exp NAME shift, and go to state 9 @@ -493,7 +493,7 @@ dnl - 61 -> 328: reduce -> shift on '*', '/', and '%' NAME [reduce using rule 152 (opt_variable)] '$' [reduce using rule 152 (opt_variable)] -@@ -5399,7 +5399,7 @@ +@@ -5405,7 +5405,7 @@ 156 | . '$' non_post_simp_exp NAME shift, and go to state 9 @@ -502,7 +502,7 @@ dnl - 61 -> 328: reduce -> shift on '*', '/', and '%' NAME [reduce using rule 152 (opt_variable)] '$' [reduce using rule 152 (opt_variable)] -@@ -6214,7 +6214,7 @@ +@@ -6220,7 +6220,7 @@ 156 | . '$' non_post_simp_exp NAME shift, and go to state 9 @@ -511,7 +511,7 @@ dnl - 61 -> 328: reduce -> shift on '*', '/', and '%' NAME [reduce using rule 152 (opt_variable)] '$' [reduce using rule 152 (opt_variable)] -@@ -11099,3 +11099,274 @@ +@@ -11117,3 +11117,274 @@ 45 statement: LEX_FOR '(' opt_exp semi opt_nls exp semi opt_nls opt_exp r_paren opt_nls statement . $default reduce using rule 45 (statement)