From b9f1d9a47a49c40e8f0a5006b492daba53f25b20 Mon Sep 17 00:00:00 2001 From: Alex Rozenman Date: Sat, 23 May 2009 18:48:03 +0300 Subject: [PATCH] Named symbol references. Discussed in: http://lists.gnu.org/archive/html/bison-patches/2009-01/msg00000.html http://lists.gnu.org/archive/html/bison-patches/2009-02/msg00002.html http://lists.gnu.org/archive/html/bison-patches/2009-03/msg00009.html * src/parse-gram.y: Add new syntax (named_ref.opt). * src/reader.c: Store named refs in symbol lists. * src/reader.h: New argument for symbol_append and action_append functions. * src/scan-code.h: Add new field (named_ref) into code_props data structure. Keeps named ref of midrule actions. * src/scan-code.l: Support for named refs in semantic action code. New function 'parse_named_ref'. * src/scan-gram.l: Support bracketed id. * src/symlist.c: Store named refs in symbol lists. * src/symlist.h: New field in symbol list: named_ref. * src/named-ref.h: New file, a struct for named_ref. * src/named-ref.cp: New file, named_ref_new function. * src/local.mk: Add two new files. * tests/testsuite.at: Include new test group: * tests/named-refs.at: this new file. --- ChangeLog | 21 ++ src/local.mk | 4 +- src/named-ref.c | 41 ++++ src/named-ref.h | 38 ++++ src/parse-gram.y | 29 ++- src/reader.c | 57 +++++- src/reader.h | 10 +- src/scan-code.h | 9 +- src/scan-code.l | 458 ++++++++++++++++++++++++++++++++++++++------ src/scan-gram.l | 102 +++++++++- src/symlist.c | 13 +- src/symlist.h | 7 + tests/named-refs.at | 457 +++++++++++++++++++++++++++++++++++++++++++ tests/testsuite.at | 3 + 14 files changed, 1162 insertions(+), 87 deletions(-) create mode 100644 src/named-ref.c create mode 100644 src/named-ref.h create mode 100644 tests/named-refs.at diff --git a/ChangeLog b/ChangeLog index a8834a1b..2293e62a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2009-06-27 Alex Rozenman + + Implement support for named symbol references. + * src/parse-gram.y: Add new syntax (named_ref.opt). + * src/reader.c: Store named refs in symbol lists. + * src/reader.h: New argument for symbol_append and + action_append functions. + * src/scan-code.h: Add new field (named_ref) into + code_props data structure. Keeps named ref of midrule + actions. + * src/scan-code.l: Support for named refs in semantic + action code. New function 'parse_named_ref'. + * src/scan-gram.l: Support bracketed id. + * src/symlist.c: Store named refs in symbol lists. + * src/symlist.h: New field in symbol list: named_ref. + * src/named-ref.h: New file, a struct for named_ref. + * src/named-ref.cp: New file, named_ref_new function. + * src/local.mk: Add two new files. + * tests/testsuite.at: Include new test group: + * tests/named-refs.at: this new file. + 2009-06-25 Akim Demaille hash: check insertion for memory exhaustion. diff --git a/src/local.mk b/src/local.mk index bdf9d2e3..9ee1824a 100644 --- a/src/local.mk +++ b/src/local.mk @@ -104,7 +104,9 @@ src_bison_SOURCES = \ src/tables.c \ src/tables.h \ src/uniqstr.c \ - src/uniqstr.h + src/uniqstr.h \ + src/named-ref.c \ + src/named-ref.h EXTRA_src_bison_SOURCES = \ src/scan-code.l \ diff --git a/src/named-ref.c b/src/named-ref.c new file mode 100644 index 00000000..4388acba --- /dev/null +++ b/src/named-ref.c @@ -0,0 +1,41 @@ +/* Named symbol references for Bison + + Copyright 2009 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 + it under the terms of the GNU General Public License as published by + 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, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, see . */ + +#include +#include "system.h" + +#include "named-ref.h" + +named_ref * +named_ref_new (uniqstr id, location loc) +{ + named_ref *res = xmalloc (sizeof *res); + + res->id = id; + res->loc = loc; + + return res; +} + +void +named_ref_free (named_ref *r) +{ + free (r); +} + diff --git a/src/named-ref.h b/src/named-ref.h new file mode 100644 index 00000000..9100296f --- /dev/null +++ b/src/named-ref.h @@ -0,0 +1,38 @@ +/* Named symbol references for Bison + + Copyright 2009 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 + it under the terms of the GNU General Public License as published by + 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, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, see . */ + +#ifndef NAMED_REF_H_ +# define NAMED_REF_H_ + +#include "uniqstr.h" +#include "location.h" + +typedef struct named_ref named_ref; + +struct named_ref +{ + uniqstr id; + location loc; +}; + +named_ref *named_ref_new (uniqstr id, location loc); + +void named_ref_free (named_ref *r); + +#endif /* !NAMED_REF_H_ */ diff --git a/src/parse-gram.y b/src/parse-gram.y index 8718a6d3..0fbb8cbd 100644 --- a/src/parse-gram.y +++ b/src/parse-gram.y @@ -30,6 +30,7 @@ #include "quotearg.h" #include "reader.h" #include "symlist.h" +#include "named-ref.h" #include "scan-gram.h" #include "scan-code.h" @@ -63,6 +64,7 @@ static symbol_class current_class = unknown_sym; static uniqstr current_type = NULL; static symbol *current_lhs; static location current_lhs_location; +static named_ref *current_lhs_named_ref; static int current_prec = 0; #define YYTYPE_INT16 int_fast16_t @@ -98,6 +100,7 @@ static int current_prec = 0; assoc assoc; uniqstr uniqstr; unsigned char character; + named_ref *named_ref; }; /* Define the tokens together with their human representation. */ @@ -167,6 +170,7 @@ static int current_prec = 0; %token TAG "" %token TAG_ANY "<*>" %token TAG_NONE "<>" +%token BRACKETED_ID "[id]" %type CHAR %printer { fputs (char_name ($$), stderr); } CHAR @@ -180,7 +184,8 @@ static int current_prec = 0; %printer { fprintf (stderr, "{\n%s\n}", $$); } braceless content.opt "{...}" "%{...%}" EPILOGUE -%type TAG ID ID_COLON PERCENT_FLAG variable +%type TAG ID ID_COLON BRACKETED_ID PERCENT_FLAG variable +%type named_ref.opt %printer { fputs ($$, stderr); } ID variable %printer { fprintf (stderr, "%s:", $$); } ID_COLON %printer { fprintf (stderr, "%%%s", $$); } PERCENT_FLAG @@ -505,7 +510,8 @@ rules_or_grammar_declaration: ; rules: - id_colon { current_lhs = $1; current_lhs_location = @1; } rhses.1 + id_colon named_ref.opt { current_lhs = $1; current_lhs_location = @1; + current_lhs_named_ref = $2; } rhses.1 ; rhses.1: @@ -516,11 +522,12 @@ rhses.1: rhs: /* Nothing. */ - { grammar_current_rule_begin (current_lhs, current_lhs_location); } -| rhs symbol - { grammar_current_rule_symbol_append ($2, @2); } -| rhs "{...}" - { grammar_current_rule_action_append ($2, @2); } + { grammar_current_rule_begin (current_lhs, 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); } | rhs "%prec" symbol { grammar_current_rule_prec_set ($3, @3); } | rhs "%dprec" INT @@ -529,6 +536,14 @@ rhs: { grammar_current_rule_merge_set ($3, @3); } ; +named_ref.opt: + /* Nothing. */ + { $$ = 0; } +| + BRACKETED_ID + { $$ = named_ref_new($1, @1); } +; + /*---------------------------. | variable and content.opt. | diff --git a/src/reader.c b/src/reader.c index f20d3ca0..08113174 100644 --- a/src/reader.c +++ b/src/reader.c @@ -21,6 +21,7 @@ #include #include "system.h" +#include #include #include "complain.h" @@ -169,7 +170,7 @@ free_merger_functions (void) static symbol_list *grammar_end = NULL; /* Append SYM to the grammar. */ -static void +static symbol_list * grammar_symbol_append (symbol *sym, location loc) { symbol_list *p = symbol_list_sym_new (sym, loc); @@ -185,6 +186,8 @@ grammar_symbol_append (symbol *sym, location loc) part of it. */ if (sym) ++nritems; + + return p; } /* The rule currently being defined, and the previous rule. @@ -199,12 +202,29 @@ static symbol_list *previous_rule_end = NULL; `----------------------------------------------*/ void -grammar_current_rule_begin (symbol *lhs, location loc) +grammar_current_rule_begin (symbol *lhs, location loc, + named_ref *lhs_named_ref) { + symbol_list* p; + /* Start a new rule and record its lhs. */ ++nrules; previous_rule_end = grammar_end; - grammar_symbol_append (lhs, loc); + + p = grammar_symbol_append (lhs, loc); + if (lhs_named_ref) + { + if (lhs_named_ref->id == lhs->tag) + { + warn_at (lhs_named_ref->loc, + _("duplicated symbol name for %s ignored"), + quote (lhs->tag)); + named_ref_free (lhs_named_ref); + } + else + p->named_ref = lhs_named_ref; + } + current_rule = grammar_end; /* Mark the rule's lhs as a nonterminal if not already so. */ @@ -328,6 +348,9 @@ grammar_midrule_action (void) symbol *dummy = dummy_symbol_get (dummy_location); symbol_list *midrule = symbol_list_sym_new (dummy, dummy_location); + /* Remember named_ref of previous action */ + named_ref *named_ref = current_rule->action_props.named_ref; + /* Make a new rule, whose body is empty, before the current one, so that the action just read can belong to it. */ ++nrules; @@ -337,7 +360,7 @@ grammar_midrule_action (void) code_props_rule_action_init (&midrule->action_props, current_rule->action_props.code, current_rule->action_props.location, - midrule); + midrule, 0); code_props_none_init (¤t_rule->action_props); if (previous_rule_end) @@ -353,7 +376,7 @@ grammar_midrule_action (void) /* Insert the dummy nonterminal replacing the midrule action into the current rule. Bind it to its dedicated rule. */ - grammar_current_rule_symbol_append (dummy, dummy_location); + grammar_current_rule_symbol_append (dummy, dummy_location, named_ref); grammar_end->midrule = midrule; midrule->midrule_parent_rule = current_rule; midrule->midrule_parent_rhs_index = symbol_list_length (current_rule->next); @@ -402,24 +425,40 @@ grammar_current_rule_merge_set (uniqstr name, location loc) action as a mid-rule action. */ void -grammar_current_rule_symbol_append (symbol *sym, location loc) +grammar_current_rule_symbol_append (symbol *sym, location loc, + named_ref *named_ref) { + symbol_list *p; if (current_rule->action_props.code) grammar_midrule_action (); - grammar_symbol_append (sym, loc); + p = grammar_symbol_append (sym, loc); + + if (named_ref) + { + if (named_ref->id == sym->tag) + { + warn_at (named_ref->loc, + _("duplicated symbol name for %s ignored"), + quote (sym->tag)); + named_ref_free (named_ref); + } + else + p->named_ref = named_ref; + } } /* Attach an ACTION to the current rule. */ void -grammar_current_rule_action_append (const char *action, location loc) +grammar_current_rule_action_append (const char *action, location loc, + named_ref *named_ref) { if (current_rule->action_props.code) grammar_midrule_action (); /* After all symbol declarations have been parsed, packgram invokes code_props_translate_code. */ code_props_rule_action_init (¤t_rule->action_props, action, loc, - current_rule); + current_rule, named_ref); } diff --git a/src/reader.h b/src/reader.h index 2d73ab37..736bd0e7 100644 --- a/src/reader.h +++ b/src/reader.h @@ -23,6 +23,7 @@ # include "location.h" # include "symlist.h" +# include "named-ref.h" # include "parse-gram.h" @@ -42,14 +43,17 @@ char const *token_name (int type); /* From reader.c. */ void grammar_start_symbol_set (symbol *sym, location loc); -void grammar_current_rule_begin (symbol *lhs, location loc); +void grammar_current_rule_begin (symbol *lhs, location loc, + named_ref *lhs_named_ref); void grammar_current_rule_end (location loc); void grammar_midrule_action (void); 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_symbol_append (symbol *sym, location loc); -void grammar_current_rule_action_append (const char *action, location loc); +void grammar_current_rule_symbol_append (symbol *sym, location loc, + named_ref *named_ref); +void grammar_current_rule_action_append (const char *action, location loc, + named_ref *named_ref); void reader (void); void free_merger_functions (void); diff --git a/src/scan-code.h b/src/scan-code.h index a980b866..cf5b6d70 100644 --- a/src/scan-code.h +++ b/src/scan-code.h @@ -21,6 +21,7 @@ # define SCAN_CODE_H_ # include "location.h" +# include "named-ref.h" struct symbol_list; @@ -64,6 +65,9 @@ typedef struct code_props { /** \c NULL iff \c code_props::kind is not \c CODE_PROPS_RULE_ACTION. */ struct symbol_list *rule; + + /* Named reference. */ + named_ref *named_ref; } code_props; /** @@ -76,7 +80,7 @@ void code_props_none_init (code_props *self); /** Equivalent to \c code_props_none_init. */ #define CODE_PROPS_NONE_INIT \ - {CODE_PROPS_NONE, NULL, EMPTY_LOCATION_INIT, false, NULL} + {CODE_PROPS_NONE, NULL, EMPTY_LOCATION_INIT, false, NULL, NULL} /** Initialized by \c CODE_PROPS_NONE_INIT with no further modification. */ extern code_props const code_props_none; @@ -129,7 +133,8 @@ void code_props_symbol_action_init (code_props *self, char const *code, * - The caller frees \c rule. */ void code_props_rule_action_init (code_props *self, char const *code, - location code_loc, struct symbol_list *rule); + location code_loc, struct symbol_list *rule, + named_ref *named_ref); /** * \pre diff --git a/src/scan-code.l b/src/scan-code.l index 88f89902..4c91fd1a 100644 --- a/src/scan-code.l +++ b/src/scan-code.l @@ -48,6 +48,10 @@ YY_DECL; static void handle_action_dollar (symbol_list *rule, char *cp, location dollar_loc); static void handle_action_at (symbol_list *rule, char *cp, location at_loc); + +/* A string to be pushed to obstack after dollar/at has been handled */ +static char *ref_tail_fields; + static location the_location; static location *loc = &the_location; @@ -56,6 +60,7 @@ static char *last_string; /* True if an untyped $$ or $n was seen. */ static bool untyped_var_seen; + %} /* C and C++ comments in code. */ %x SC_COMMENT SC_LINE_COMMENT @@ -75,6 +80,12 @@ tag [^\0\n>]+ white space between the backslash and the newline. */ splice (\\[ \f\t\v]*\n)* +/* C style identifier. Must start with letter. Will be used for + named symbol references. */ +letter [-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_] +id {letter}({letter}|[0-9])* +ref -?[0-9]+|{id}|"["{id}"]"|"$" + %% %{ @@ -167,15 +178,22 @@ splice (\\[ \f\t\v]*\n)* { - "$"("<"{tag}">")?(-?[0-9]+|"$") { + "$"("<"{tag}">")?{ref} { + ref_tail_fields = 0; handle_action_dollar (self->rule, yytext, *loc); + if (ref_tail_fields != NULL) { + obstack_sgrow (&obstack_for_string, ref_tail_fields); + } need_semicolon = true; } - "@"(-?[0-9]+|"$") { + "@"{ref} { + ref_tail_fields = 0; handle_action_at (self->rule, yytext, *loc); + if (ref_tail_fields != NULL) { + obstack_sgrow (&obstack_for_string, ref_tail_fields); + } need_semicolon = true; } - "$" { warn_at (*loc, _("stray `$'")); obstack_sgrow (&obstack_for_string, "$]["); @@ -267,6 +285,317 @@ splice (\\[ \f\t\v]*\n)* %% + +static inline bool +symbol_list_null(symbol_list *l) +{ + if (l && !(l->content_type == SYMLIST_SYMBOL && l->content.sym == NULL)) + return false; + else + return true; +} + +static inline bool +is_dot_or_dash(char ch) +{ + return ch == '.' || ch == '-'; +} + +static inline bool +is_digit(char ch) +{ + return '0' <= ch && ch <= '9'; +} + +static inline bool +contains_dot_or_dash(const char* str) +{ + return strpbrk(str, ".-") != NULL; +} + +#define VARIANT_HIDDEN (1 << 0) +#define VARIANT_BAD_BRACKETING (1 << 1) +#define VARIANT_NOT_VISIBLE_FROM_MIDRULE (1 << 2) + +typedef struct +{ + /* Index in symbol list. */ + long int ind; + + /* Matched symbol id and loc. */ + uniqstr id; + location loc; + + /* Hidding named reference. */ + named_ref* hidden_by; + + /* Error flags. */ + unsigned err; +} variant; + +static variant *variant_table = 0; +static unsigned variant_table_size = 0; +static unsigned variant_count = 0; + +static variant * +variant_table_grow() +{ + ++variant_count; + if (variant_count > variant_table_size) + { + while (variant_count > variant_table_size) + variant_table_size = 2 * variant_table_size + 3; + variant_table = xnrealloc (variant_table, variant_table_size, + sizeof *variant_table); + } + return &variant_table[variant_count - 1]; +} + +static char * +find_prefix_end(const char *prefix, char *begin, char *end) +{ + char *ptr = begin; + + while (*prefix && ptr != end) + { + if (*prefix != *ptr) + return 0; + ++prefix, ++ptr; + } + + if (*prefix) + return 0; + + return ptr; +} + +static variant * +variant_add(uniqstr id, location loc, long int ind, + char *cp, char *cp_end, bool exact_mode) +{ + char *prefix_end; + + prefix_end = find_prefix_end(id, cp, cp_end); + if (prefix_end && + (prefix_end == cp_end || + (!exact_mode && is_dot_or_dash(*prefix_end)))) + { + variant *r = variant_table_grow(); + r->ind = ind; + r->id = id; + r->loc = loc; + r->hidden_by = NULL; + r->err = 0; + return r; + } + else + return NULL; +} + +#define INVALID_REF (INT_MIN) +#define LHS_REF (INT_MIN + 1) + +static long int +parse_named_ref(char *cp, symbol_list *rule, int rule_length, + int midrule_rhs_index, char *text, location loc, + char dollar_or_at) +{ + symbol_list *l; + char *cp_end; + bool exact_mode; + bool has_error; + bool has_valid; + long int ind, i; + variant* variant; + char* p; + + if ('$' == *cp) + return LHS_REF; + + if (is_digit (*cp) || (*cp == '-' && is_digit (* (cp + 1)))) + { + long int num = strtol (cp, &cp, 10); + if (1 - INT_MAX + rule_length <= num && num <= rule_length) + return num; + else + { + complain_at (loc, _("integer out of range: %s"), quote (text)); + return INVALID_REF; + } + } + + if ('[' == *cp) + { + exact_mode = true; + + /* Ignore the brackets. */ + ++cp; + for (p = cp; *p != ']'; ++p); + cp_end = p; + } + else + { + exact_mode = false; + + /* Take all characters of the name. */ + for (p = cp; *p; ++p) + if (is_dot_or_dash(*p)) + { + ref_tail_fields = p; + break; + } + for (p = cp; *p; ++p); + cp_end = p; + } + + /* Add all relevant variants. */ + variant_count = 0; + for (ind = 0, l = rule; !symbol_list_null(l); ++ind, l = l->next) + { + if (l->content_type != SYMLIST_SYMBOL) + continue; + + variant = variant_add(l->content.sym->tag, l->sym_loc, ind, + cp, cp_end, exact_mode); + + if (variant && l->named_ref) + variant->hidden_by = l->named_ref; + + if (l->named_ref) + variant_add(l->named_ref->id, l->named_ref->loc, ind, + cp, cp_end, exact_mode); + } + + /* Check errors. */ + has_error = false; + has_valid = false; + for (i = 0; i < variant_count; ++i) + { + variant = &variant_table[i]; + ind = variant->ind; + + /* Check visibility from mid-rule actions. */ + if (midrule_rhs_index != 0 && + (ind == 0 || ind > midrule_rhs_index)) + { + variant->err |= VARIANT_NOT_VISIBLE_FROM_MIDRULE; + has_error = true; + } + + /* Check correct bracketing. */ + if (!exact_mode && contains_dot_or_dash(variant->id)) + { + variant->err |= VARIANT_BAD_BRACKETING; + has_error = true; + } + + /* Check using of hidden symbols. */ + if (variant->hidden_by != NULL) + { + variant->err |= VARIANT_HIDDEN; + has_error = true; + } + + if (!variant->err) + has_valid = true; + } + + if (variant_count == 1 && has_valid) + { + /* The only "good" case is here. */ + ind = variant_table[0].ind; + if (ind == midrule_rhs_index) + return LHS_REF; + else + return ind; + } + + /* Start complaining. */ + + if (variant_count == 0) + complain_at (loc, _("reference is invalid: %s, symbol not found"), + quote (text)); + else if (variant_count > 1 && !has_error) + complain_at (loc, _("reference is ambiguous: %s"), + quote (text)); + else if (variant_count > 1 && has_valid && has_error) + complain_at (loc, _("reference is misleading: %s"), + quote (text)); + else + complain_at (loc, _("reference is invalid: %s"), + quote (text)); + + for (i = 0; i < variant_count; ++i) + { + static char at_buf[20]; + + variant = &variant_table[i]; + + if (variant->ind == 0) + strcpy(at_buf, "$$"); + else + snprintf(at_buf, sizeof(at_buf), "$%d", variant->ind); + + if (variant->err == 0) + complain_at (variant->loc, _(" refers to: %c%s at %s"), + dollar_or_at, variant->id, at_buf); + else + { + static struct obstack msg_buf; + const char *tail = ""; + const char *id; + location loc; + + if (!exact_mode) + tail = cp + strlen(variant->id); + + if (variant->hidden_by) + { + id = variant->hidden_by->id; + loc = variant->hidden_by->loc; + } + else + { + id = variant->id; + loc = variant->loc; + } + + /* Create the explanation message. */ + + obstack_init (&msg_buf); + + obstack_fgrow1 (&msg_buf, " possibly meant: %c", dollar_or_at); + if (contains_dot_or_dash (id)) + obstack_fgrow1 (&msg_buf, "[%s]", id); + else + obstack_sgrow (&msg_buf, id); + obstack_sgrow (&msg_buf, tail); + + if (variant->err & VARIANT_HIDDEN) + { + obstack_fgrow1 (&msg_buf, ", hiding %c", dollar_or_at); + if (contains_dot_or_dash (variant->id)) + obstack_fgrow1 (&msg_buf, "[%s]", variant->id); + else + obstack_sgrow (&msg_buf, variant->id); + obstack_sgrow (&msg_buf, tail); + } + + obstack_fgrow1 (&msg_buf, " at %s", at_buf); + + if (variant->err & VARIANT_NOT_VISIBLE_FROM_MIDRULE) + obstack_fgrow1 (&msg_buf, ", cannot be accessed from " + "mid-rule action at $%d", midrule_rhs_index); + + obstack_1grow (&msg_buf, '\0'); + complain_at (loc, _("%s"), obstack_finish (&msg_buf)); + obstack_free (&msg_buf, 0); + } + } + + return INVALID_REF; +} + /* Keeps track of the maximum number of semantic values to the left of a handle (those referenced by $0, $-1, etc.) are required by the semantic actions of this grammar. */ @@ -286,8 +615,9 @@ handle_action_dollar (symbol_list *rule, char *text, location dollar_loc) { char const *type_name = NULL; char *cp = text + 1; + char *gt_ptr = 0; symbol_list *effective_rule; - int effective_rule_length; + int effective_rule_length, n; if (rule->midrule_parent_rule) { @@ -306,15 +636,28 @@ handle_action_dollar (symbol_list *rule, char *text, location dollar_loc) type_name = ++cp; while (*cp != '>') ++cp; - *cp = '\0'; + + /* The '>' symbol will be later replaced by '\0'. Original + 'text' is needed for error messages. */ + gt_ptr = cp; ++cp; if (untyped_var_seen) complain_at (dollar_loc, _("explicit type given in untyped grammar")); tag_seen = true; } - if (*cp == '$') + n = parse_named_ref (cp, effective_rule, effective_rule_length, + rule->midrule_parent_rhs_index, text, dollar_loc, '$'); + + if (gt_ptr) + *gt_ptr = '\0'; + + switch (n) { + case INVALID_REF: + break; + + case LHS_REF: if (!type_name) type_name = symbol_list_n_type_name_get (rule, dollar_loc, 0); @@ -340,39 +683,31 @@ handle_action_dollar (symbol_list *rule, char *text, location dollar_loc) obstack_fgrow1 (&obstack_for_string, "]b4_lhs_value([%s])[", type_name); rule->action_props.is_value_used = true; - } - else - { - long int num = strtol (cp, NULL, 10); - - if (1 - INT_MAX + effective_rule_length <= num - && num <= effective_rule_length) + break; + + default: + if (max_left_semantic_context < 1 - n) + max_left_semantic_context = 1 - n; + if (!type_name && 0 < n) + type_name = + symbol_list_n_type_name_get (effective_rule, dollar_loc, n); + if (!type_name) { - int n = num; - if (max_left_semantic_context < 1 - n) - max_left_semantic_context = 1 - n; - if (!type_name && 0 < n) - type_name = - symbol_list_n_type_name_get (effective_rule, dollar_loc, n); - if (!type_name) - { - if (union_seen | tag_seen) - complain_at (dollar_loc, _("$%d of `%s' has no declared type"), - n, effective_rule->content.sym->tag); - else - untyped_var_seen = true; - type_name = ""; - } - - obstack_fgrow3 (&obstack_for_string, - "]b4_rhs_value(%d, %d, [%s])[", - effective_rule_length, n, type_name); - if (n > 0) - symbol_list_n_get (effective_rule, n)->action_props.is_value_used = - true; + if (union_seen | tag_seen) + complain_at (dollar_loc, _("$%s of `%s' has no declared type"), + cp, effective_rule->content.sym->tag); + else + untyped_var_seen = true; + type_name = ""; } - else - complain_at (dollar_loc, _("integer out of range: %s"), quote (text)); + + obstack_fgrow3 (&obstack_for_string, + "]b4_rhs_value(%d, %d, [%s])[", + effective_rule_length, n, type_name); + if (n > 0) + symbol_list_n_get (effective_rule, n)->action_props.is_value_used = + true; + break; } } @@ -386,28 +721,37 @@ static void handle_action_at (symbol_list *rule, char *text, location at_loc) { char *cp = text + 1; - int effective_rule_length = - (rule->midrule_parent_rule - ? rule->midrule_parent_rhs_index - 1 - : symbol_list_length (rule->next)); + symbol_list *effective_rule; + int effective_rule_length, n; + + if (rule->midrule_parent_rule) + { + effective_rule = rule->midrule_parent_rule; + effective_rule_length = rule->midrule_parent_rhs_index - 1; + } + else + { + effective_rule = rule; + effective_rule_length = symbol_list_length (rule->next); + } muscle_percent_define_ensure("locations", at_loc, true); - if (*cp == '$') - obstack_sgrow (&obstack_for_string, "]b4_lhs_location["); - else + n = parse_named_ref (cp, effective_rule, effective_rule_length, + rule->midrule_parent_rhs_index, text, at_loc, '@'); + switch (n) { - long int num = strtol (cp, NULL, 10); + case INVALID_REF: + break; - if (1 - INT_MAX + effective_rule_length <= num - && num <= effective_rule_length) - { - int n = num; - obstack_fgrow2 (&obstack_for_string, "]b4_rhs_location(%d, %d)[", - effective_rule_length, n); - } - else - complain_at (at_loc, _("integer out of range: %s"), quote (text)); + case LHS_REF: + obstack_sgrow (&obstack_for_string, "]b4_lhs_location["); + break; + + default: + obstack_fgrow2 (&obstack_for_string, "]b4_rhs_location(%d, %d)[", + effective_rule_length, n); + break; } } @@ -459,6 +803,7 @@ code_props_plain_init (code_props *self, char const *code, location code_loc) self->location = code_loc; self->is_value_used = false; self->rule = NULL; + self->named_ref = NULL; } void @@ -470,17 +815,20 @@ code_props_symbol_action_init (code_props *self, char const *code, self->location = code_loc; self->is_value_used = false; self->rule = NULL; + self->named_ref = NULL; } void code_props_rule_action_init (code_props *self, char const *code, - location code_loc, symbol_list *rule) + location code_loc, symbol_list *rule, + named_ref *named_ref) { self->kind = CODE_PROPS_RULE_ACTION; self->code = code; self->location = code_loc; self->is_value_used = false; self->rule = rule; + self->named_ref = named_ref; } void diff --git a/src/scan-gram.l b/src/scan-gram.l index 7c5b6000..f71409ed 100644 --- a/src/scan-gram.l +++ b/src/scan-gram.l @@ -61,10 +61,21 @@ static size_t no_cr_read (FILE *, char *, size_t); return PERCENT_FLAG; \ } while (0) +#define ROLLBACK_CURRENT_TOKEN \ + do { \ + scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0); \ + yyless (0); \ + } while (0) /* A string representing the most recently saved token. */ static char *last_string; +/* Bracketed identifier */ +static uniqstr bracketed_id_str = 0; +static location bracketed_id_loc; +static boundary bracketed_id_start; +static int bracketed_id_context_state = 0; + void gram_scanner_last_string_free (void) { @@ -97,6 +108,8 @@ static void unexpected_newline (boundary, char const *); %x SC_COMMENT SC_LINE_COMMENT /* Strings and characters in code. */ %x SC_STRING SC_CHARACTER + /* Bracketed identifiers support */ +%x SC_BRACKETED_ID SC_RETURN_BRACKETED_ID letter [-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_] id {letter}({letter}|[0-9])* @@ -141,7 +154,7 @@ splice (\\[ \f\t\v]*\n)* | Scanning white space. | `-----------------------*/ - + { /* Comments and white space. */ "," warn_at (*loc, _("stray `,' treated as white space")); @@ -228,6 +241,7 @@ splice (\\[ \f\t\v]*\n)* {id} { val->uniqstr = uniqstr_new (yytext); id_loc = *loc; + bracketed_id_str = NULL; BEGIN SC_AFTER_IDENTIFIER; } @@ -286,6 +300,13 @@ splice (\\[ \f\t\v]*\n)* return PERCENT_PERCENT; } + "[" { + bracketed_id_str = NULL; + bracketed_id_start = loc->start; + bracketed_id_context_state = YY_START; + BEGIN SC_BRACKETED_ID; + } + . { complain_at (*loc, _("invalid character: %s"), quote (yytext)); } @@ -314,25 +335,94 @@ splice (\\[ \f\t\v]*\n)* { + "[" { + if (!bracketed_id_str) + { + bracketed_id_start = loc->start; + bracketed_id_context_state = YY_START; + BEGIN SC_BRACKETED_ID; + } + else + { + ROLLBACK_CURRENT_TOKEN; + BEGIN SC_RETURN_BRACKETED_ID; + *loc = id_loc; + return ID; + } + } ":" { + BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL); *loc = id_loc; - BEGIN INITIAL; return ID_COLON; } . { - scanner_cursor.column -= mbsnwidth (yytext, yyleng, 0); - yyless (0); + ROLLBACK_CURRENT_TOKEN; + BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL); *loc = id_loc; - BEGIN INITIAL; return ID; } <> { + BEGIN (bracketed_id_str ? SC_RETURN_BRACKETED_ID : INITIAL); *loc = id_loc; - BEGIN INITIAL; return ID; } } + /*--------------------------------. + | Scanning bracketed identifiers. | + `--------------------------------*/ + + +{ + {id} { + if (!bracketed_id_str) + { + bracketed_id_str = uniqstr_new (yytext); + bracketed_id_loc = *loc; + } + else + { + complain_at (*loc, _("redundant identifier in bracketed name: %s"), + quote (yytext)); + } + } + "]" { + BEGIN bracketed_id_context_state; + if (bracketed_id_str) + { + if (INITIAL == bracketed_id_context_state) + { + val->uniqstr = bracketed_id_str; + bracketed_id_str = 0; + *loc = bracketed_id_loc; + return BRACKETED_ID; + } + } + else + complain_at (*loc, _("a non empty identifier expected")); + } + . { + complain_at (*loc, _("invalid character in bracketed name: %s"), + quote (yytext)); + } + <> { + BEGIN bracketed_id_context_state; + unexpected_eof (bracketed_id_start, "]"); + } +} + + +{ + . { + ROLLBACK_CURRENT_TOKEN; + val->uniqstr = bracketed_id_str; + bracketed_id_str = 0; + *loc = bracketed_id_loc; + BEGIN INITIAL; + return BRACKETED_ID; + } +} + /*---------------------------------------------------------------. | Scanning a Yacc comment. The initial `/ *' is already eaten. | diff --git a/src/symlist.c b/src/symlist.c index 6c6b57da..974d974c 100644 --- a/src/symlist.c +++ b/src/symlist.c @@ -35,7 +35,7 @@ symbol_list_sym_new (symbol *sym, location loc) res->content_type = SYMLIST_SYMBOL; res->content.sym = sym; - res->location = loc; + res->location = res->sym_loc = loc; res->midrule = NULL; res->midrule_parent_rule = NULL; @@ -47,6 +47,8 @@ symbol_list_sym_new (symbol *sym, location loc) res->dprec = 0; res->merger = 0; + res->named_ref = NULL; + res->next = NULL; return res; @@ -64,7 +66,8 @@ symbol_list_type_new (uniqstr type_name, location loc) res->content_type = SYMLIST_TYPE; res->content.type_name = type_name; - res->location = loc; + res->location = res->sym_loc = loc; + res->named_ref = NULL; res->next = NULL; return res; @@ -81,7 +84,8 @@ symbol_list_default_tagged_new (location loc) symbol_list *res = xmalloc (sizeof *res); res->content_type = SYMLIST_DEFAULT_TAGGED; - res->location = loc; + res->location = res->sym_loc = loc; + res->named_ref = NULL; res->next = NULL; return res; @@ -98,7 +102,8 @@ symbol_list_default_tagless_new (location loc) symbol_list *res = xmalloc (sizeof *res); res->content_type = SYMLIST_DEFAULT_TAGLESS; - res->location = loc; + res->location = res->sym_loc = loc; + res->named_ref = NULL; res->next = NULL; return res; diff --git a/src/symlist.h b/src/symlist.h index 992fd4eb..df3a0421 100644 --- a/src/symlist.h +++ b/src/symlist.h @@ -23,6 +23,7 @@ # include "location.h" # include "scan-code.h" # include "symtab.h" +# include "named-ref.h" /* A list of symbols, used during the parsing to store the rules. */ typedef struct symbol_list @@ -48,6 +49,9 @@ typedef struct symbol_list } content; location location; + /* Proper location of the symbol, not all the rule */ + location sym_loc; + /* If this symbol is the generated lhs for a midrule but this is the rule in whose rhs it appears, MIDRULE = a pointer to that midrule. */ struct symbol_list *midrule; @@ -69,6 +73,9 @@ typedef struct symbol_list int merger; location merger_declaration_location; + /* Named reference. */ + named_ref *named_ref; + /* The list. */ struct symbol_list *next; } symbol_list; diff --git a/tests/named-refs.at b/tests/named-refs.at new file mode 100644 index 00000000..d8fd2ec3 --- /dev/null +++ b/tests/named-refs.at @@ -0,0 +1,457 @@ +# Named references test. -*- Autotest -*- + +# Copyright (C) 2009 Free Software +# Foundation, Inc. + +# 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 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# 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, see . + +AT_BANNER([[Named references tests.]]) + +AT_SETUP([Tutorial calculator]) + +AT_DATA_GRAMMAR([test.y], +[[ +%{ +#include +#include +#include +#include +typedef int semantic_value; +FILE *input; +static semantic_value global_result = 0; +static int global_count = 0; +static int power (int base, int exponent); +static void yyerror (const char *s); +int yylex (void); +%} + +%union +{ + semantic_value ival; +}; + +%token CALC_EOF 0 "end of input" +%token NUM "number" +%type exp + +%nonassoc '=' /* comparison */ +%left '-' '+' +%left '*' '/' +%precedence NEG /* negation--unary minus */ +%right '^' /* exponentiation */ + +%% +input: + line +| input line { } +; + +line: + '\n' +| exp '\n' { } +; + +exp: + NUM { $$ = $NUM; } +| exp[l] '=' exp[r] + { + if ($l != $r) + fprintf (stderr, "calc: error: %d != %d\n", $l, $r); + $$ = $l; + } +| exp[x] '+' { $$ = $x; } [l] exp[r] { $$ = $l + $r; } +| exp[l] '-' exp[r] { $$ = $l - $r; } +| exp[l] '*' exp[r] { $$ = $l * $r; } +| exp[l] '/' exp[r] { $$ = $l / $r; } +| '-' exp %prec NEG { $$ = -$2; } +| exp[l] '^' exp[r] { $$ = power ($l, $r); } +| '(' exp[e] ')' { $$ = $e; } +| '(' error ')' { $$ = 1111; yyerrok; } +| '!' { $$ = 0; YYERROR; } +| '-' error { $$ = 0; YYERROR; } +; +%% + +static void yyerror (const char *s) +{ + fprintf (stderr, "%s\n", s); +} + +static int get_char (void) +{ + int res = getc (input); + return res; +} + +static void unget_char (int c) +{ + ungetc (c, input); +} + +static int read_signed_integer (void) +{ + int c = get_char (); + int sign = 1; + int n = 0; + if (c == '-') + { + c = get_char (); + sign = -1; + } + while (isdigit (c)) + { + n = 10 * n + (c - '0'); + c = get_char (); + } + unget_char ( c); + return sign * n; +} + +int yylex (void) +{ + int c; + /* Skip white space. */ + while ((c = get_char ()) == ' ' || c == '\t') {} + + /* process numbers */ + if (c == '.' || isdigit (c)) + { + unget_char ( c); + (yylval).ival = read_signed_integer (); + return NUM; + } + + /* Return end-of-file. */ + if (c == EOF) + return CALC_EOF; + + /* Return single chars. */ + return c; +} + +static int power (int base, int exponent) +{ + int res = 1; + if (exponent < 0) + exit (3); + for (/* Niente */; exponent; --exponent) + res *= base; + return res; +} + +int main (int argc, const char **argv) +{ + semantic_value result = 0; + int count = 0; + int status; + if (argc == 2) + input = fopen (argv[1], "r"); + else + input = stdin; + if (!input) + { + perror (argv[1]); + return 3; + } + status = yyparse (); + fclose (input); + if (global_result != result) + abort (); + if (global_count != count) + abort (); + return status; +} +]]) + +AT_DATA([input.txt], +[[ +1 + 2 * 3 = 7 +1 + 2 * -3 = -5 +-1^2 = -1 +(-1)^2 = 1 +---1 = -1 +1 - 2 - 3 = -4 +1 - (2 - 3) = 2 +2^2^3 = 256 +(2^2)^3 = 64 +]]) + +AT_BISON_CHECK([-o test.c test.y]) +AT_COMPILE([[test]]) +AT_PARSER_CHECK([./test input.txt], 0, [], [stderr]) +AT_CLEANUP + + + +####################################################################### + + +AT_SETUP([Undefined and ambiguous references]) + +AT_DATA_GRAMMAR([test.y], +[[ +%{ +static int power (int base, int exponent); +static void yyerror (const char *s); +int yylex (void); +%} + +%union +{ + int ival; +}; + +%token CALC_EOF 0 "end of input" +%token NUM "number" +%type exp + +%nonassoc '=' /* comparison */ +%left '-' '+' +%left '*' '/' +%precedence NEG /* negation--unary minus */ +%right '^' /* exponentiation */ + +%% +input: + line +| input line { } +; + +line: + '\n' +| exp '\n' { } +; + +exp: + NUM { $$ = $NUM; } +| exp[l] '=' exp[r] + { + if ($l != $r) + fprintf (stderr, "calc: error: %d != %d\n", $l, $r); + $$ = $l; + } +| exp[x] '+' { $$ = $x; } [l] exp[r] { $$ = $lo9 + $r; } +| exp[x] '-' { $$ = $x; } [l] exp[r] { $$ = $exp - $r; } +| exp[x] '*' { $$ = $x; } [l] exp[r] { $$ = $l * $r; } +| exp[l] '/' exp[r] { $$ = $l / $r; } +| '-' exp %prec NEG { $$ = -$2; } +| exp[l] '^' exp[r] { $$ = power ($l, $r12); } +| '(' exp ')' { $$ = $expo; } +| '(' error ')' { $$ = 1111; yyerrok; } +| '!' { $$ = 0; YYERROR; } +| '-' error { $$ = 0; YYERROR; } +; +%% +]]) + +AT_BISON_CHECK([-o test.c test.y], 1, [], +[[test.y:50.51-60: reference is invalid: `$lo9', symbol not found +test.y:51.51-60: reference is misleading: `$exp' +test.y:42.1-3: refers to: $exp at $$ +test.y:51.7: possibly meant: $x, hiding $exp at $1 +test.y:51.41: possibly meant: $r, hiding $exp at $4 +test.y:52.51-52: $l of `exp' has no declared type +test.y:55.46-49: reference is invalid: `$r12', symbol not found +test.y:56.29-33: reference is invalid: `$expo', symbol not found +]]) +AT_CLEANUP + +####################################################################### + +AT_SETUP([Misleading references]) +AT_DATA_GRAMMAR([test.y], +[[ +%% +start: foo foo.bar { $foo.bar; } +foo: '1' +foo.bar: '2' +]]) +AT_BISON_CHECK([-o test.c test.y], 1, [], +[[test.y:11.22-29: reference is misleading: `$foo.bar' +test.y:11.8-10: refers to: $foo at $1 +test.y:11.12-18: possibly meant: $[foo.bar] at $2 +]]) +AT_CLEANUP + +####################################################################### + +AT_SETUP([Many kinds of errors]) +AT_DATA_GRAMMAR([test.y], +[[ +%token IDENT +%token NUMBER +%token ASSIGNOP +%token IF +%token IF1 +%token THEN +%token ELSE +%token FI +%token WHILE +%token DO +%token OD +%start program +%% +if_stmt1: IF expr[cond] THEN stmt[then] ELSE stmt.list[else] FI + { $if_stmt1 = new IfStmt($cond1, $then.f1, $else); }; +if_stmt2: IF expr[cond] THEN stmt[then] FI + { $if_stmt2 = new IfStmt($cond, $stmt.field, 0); }; +if_stmt3: IF expr[cond] THEN stmt.list FI + { $if_stmt3 = new IfStmt($cond, $stmt.list, 0); }; +if_stmt4: IF expr[cond] THEN stmt[xyz] ELSE stmt[xyz] FI + { $if_stmt4 = new IfStmt($cond, $xyz, $cond); }; +if_stmt5: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + { $if_stmt5 = new IfStmt($cond, $stmt.list, $else); }; +if_stmt6: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + { $if_stmt6 = new IfStmt($cond, $stmt.list.field, $else); }; +if_stmt7: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + { $if_stmt7 = new IfStmt($cond, $[stmt.list].field, $else); }; +if_stmt8: IF expr[cond] THEN stmt.list[then.1] ELSE stmt.list[else] FI + { $if_stmt8 = new IfStmt($cond, $then.1, $else); }; +if_stmt9: IF expr[cond] THEN stmt.list[then.1] ELSE stmt.list[else] FI + { $if_stmt9 = new IfStmt($cond, $then.1.field, $else); }; +if_stmt10: IF expr[cond] THEN stmt[stmt.x] FI + { $if_stmt10 = new IfStmt($cond, $stmt.x, 0); }; +if-stmt-a: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + { $if-stmt-a = new IfStmt($cond, $then, $else); }; +if-stmt-b: IF expr[cond] THEN if-stmt-a[then-a] ELSE stmt.list[else] FI + { $[if-stmt-b] = new IfStmt($cond, $then-a.f, $else); }; +program: stmt.list; +stmt.list: stmt ';' stmt.list { $3->insert($stmt); $$ = $3; } + | stmt ';' { SL = new StmtList(); SL->insert($1); $$ = SL; } + ; +stmt: assign_stmt { $$ = $1; } + | if_stmt { $$ = $1; } + | if_stmt1 { $$ = $1; } + | while_stmt { $$ = $1; } + ; +assign_stmt: IDENT ASSIGNOP expr + { $$ = new AssignStmt(string($1),$3); }; +if_stmt: IF expr[cond] THEN stmt.list FI + { $if_stmt = new IfStmt($cond, $[stmt.list], 0); }; +while_stmt[res]: WHILE expr DO stmt.list OD + { $res = new WhileStmt($[expr], $[stmt.list]); }; +expr: expr '+' term { $$ = new Plus($1,$3); } + | expr '-' term { $$ = new Minus($1,$3); } + | term { $$ = $1; } + ; +term: term '*' factor { $$ = new Times($1,$3); } + | factor { $$ = $1; } + ; +factor: '(' expr ')' { $$ = $2; } + | NUMBER { $$ = new Number($1); } + | IDENT { $$ = new Ident(string($1)); } + ; +]]) +AT_BISON_CHECK([-o test.c test.y], 1, [], +[[test.y:24.36-41: reference is invalid: `$cond1', symbol not found +test.y:26.43-53: reference is invalid: `$stmt.field' +test.y:25.35-38: possibly meant: $then.field, hiding $stmt.field at $4 +test.y:28.43-52: reference is invalid: `$stmt.list' +test.y:27.30-38: possibly meant: $[stmt.list] at $4 +test.y:30.43-46: reference is ambiguous: `$xyz' +test.y:29.35-37: refers to: $xyz at $4 +test.y:29.50-52: refers to: $xyz at $6 +test.y:32.43-52: reference is invalid: `$stmt.list' +test.y:31.40-43: possibly meant: $then, hiding $[stmt.list] at $4 +test.y:31.61-64: possibly meant: $else, hiding $[stmt.list] at $6 +test.y:34.43-58: reference is invalid: `$stmt.list.field' +test.y:33.40-43: possibly meant: $then.field, hiding $[stmt.list].field at $4 +test.y:33.61-64: possibly meant: $else.field, hiding $[stmt.list].field at $6 +test.y:36.43-54: reference is invalid: `$[stmt.list]' +test.y:35.40-43: possibly meant: $then, hiding $[stmt.list] at $4 +test.y:35.61-64: possibly meant: $else, hiding $[stmt.list] at $6 +test.y:38.43-49: reference is invalid: `$then.1' +test.y:37.40-45: possibly meant: $[then.1] at $4 +test.y:40.43-55: reference is invalid: `$then.1.field' +test.y:39.40-45: possibly meant: $[then.1].field at $4 +test.y:42.44-50: reference is invalid: `$stmt.x' +test.y:41.36-41: possibly meant: $[stmt.x].x, hiding $stmt.x at $4 +test.y:41.36-41: possibly meant: $[stmt.x] at $4 +test.y:44.13-22: reference is invalid: `$if-stmt-a' +test.y:43.1-9: possibly meant: $[if-stmt-a] at $$ +test.y:46.46-54: reference is invalid: `$then-a.f' +test.y:45.41-46: possibly meant: $[then-a].f at $4 +]]) +AT_CLEANUP + +####################################################################### + +AT_SETUP([Missing identifiers in brackets]) +AT_DATA_GRAMMAR([test.y], +[[ +%% +start: foo[] bar + { s = $foo; } +]]) +AT_BISON_CHECK([-o test.c test.y], 1, [], +[[test.y:11.12: a non empty identifier expected +]]) +AT_CLEANUP + +####################################################################### + +AT_SETUP([Redundant words in brackets]) +AT_DATA_GRAMMAR([test.y], +[[ +%% +start: foo[ a d ] bar + { s = $foo; } +]]) +AT_BISON_CHECK([-o test.c test.y], 1, [], +[[test.y:11.15: redundant identifier in bracketed name: `d' +]]) +AT_CLEANUP + +####################################################################### + +AT_SETUP([Comments in brackets]) +AT_DATA_GRAMMAR([test.y], +[[ +%% +start: foo[/* comment */] bar + { s = $foo; } +]]) +AT_BISON_CHECK([-o test.c test.y], 1, [], +[[test.y:11.25: a non empty identifier expected +]]) +AT_CLEANUP + +####################################################################### + +AT_SETUP([Stray symbols in brackets]) +AT_DATA_GRAMMAR([test.y], +[[ +%% +start: foo[ /* aaa */ *&-+ ] bar + { s = $foo; } +]]) +AT_BISON_CHECK([-o test.c test.y], 1, [], +[[test.y:11.23: invalid character in bracketed name: `*' +test.y:11.24: invalid character in bracketed name: `&' +test.y:11.26: invalid character in bracketed name: `+' +]]) +AT_CLEANUP + +####################################################################### + +AT_SETUP([Redundant words in LHS brackets]) +AT_DATA_GRAMMAR([test.y], +[[ +%% +start[a s]: foo +]]) +AT_BISON_CHECK([-o test.c test.y], 1, [], +[[test.y:11.9: redundant identifier in bracketed name: `s' +]]) +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index fa7d7482..36304e8e 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -20,6 +20,9 @@ # Testing resistance to user bugs. m4_include([input.at]) +# Testing named references. +m4_include([named-refs.at]) + # Testing output file names. m4_include([output.at]) -- 2.45.2