grammar: introduce %empty

Provide a means to explicitly denote empty right-hand sides of rules:
instead of

  exp:  { ... }

allow

  exp: %empty { ... }

Make sure that %empty is properly used.

With help from Joel E. Denny and Gabriel Rassoul.
http://lists.gnu.org/archive/html/bison-patches/2013-01/msg00142.html

* src/reader.h, src/reader.c (grammar_current_rule_empty_set): New.
* src/parse-gram.y (%empty): New token.
Use it.
* src/scan-gram.l (%empty): Scan it.
* src/reader.c (grammar_rule_check): Check that %empty is properly used.
* tests/actions.at (Invalid uses of %empty, Valid uses of %empty): New.
This commit is contained in:
Akim Demaille
2013-01-29 14:19:40 +01:00
parent 9e4917b2a1
commit ae2b48f5c0
8 changed files with 98 additions and 0 deletions

1
THANKS
View File

@@ -44,6 +44,7 @@ Fabrice Bauzac noon@cote-dazur.com
Florian Krohm florian@edamail.fishkill.ibm.com Florian Krohm florian@edamail.fishkill.ibm.com
Frank Heckenbach frank@g-n-u.de Frank Heckenbach frank@g-n-u.de
Frans Englich frans.englich@telia.com Frans Englich frans.englich@telia.com
Gabriel Rassoul gabriel.rassoul@epita.fr
Georg Sauthoff gsauthof@TechFak.Uni-Bielefeld.DE Georg Sauthoff gsauthof@TechFak.Uni-Bielefeld.DE
George Neuner gneuner2@comcast.net George Neuner gneuner2@comcast.net
Gilles Espinasse g.esp@free.fr Gilles Espinasse g.esp@free.fr

View File

@@ -605,6 +605,7 @@ rhses.1:
| rhses.1 ";" | rhses.1 ";"
; ;
%token PERCENT_EMPTY "%empty";
rhs: rhs:
/* Nothing. */ /* Nothing. */
{ grammar_current_rule_begin (current_lhs_symbol, current_lhs_location, { grammar_current_rule_begin (current_lhs_symbol, current_lhs_location,
@@ -615,6 +616,8 @@ rhs:
{ grammar_current_rule_action_append ($2, @2, $3, false); } { grammar_current_rule_action_append ($2, @2, $3, false); }
| rhs "%?{...}" | rhs "%?{...}"
{ grammar_current_rule_action_append ($2, @2, NULL, true); } { grammar_current_rule_action_append ($2, @2, NULL, true); }
| rhs "%empty"
{ grammar_current_rule_empty_set (@2); }
| rhs "%prec" symbol | rhs "%prec" symbol
{ grammar_current_rule_prec_set ($3, @3); } { grammar_current_rule_prec_set ($3, @3); }
| rhs "%dprec" INT | rhs "%dprec" INT

View File

@@ -333,6 +333,12 @@ grammar_rule_check (const symbol_list *r)
} }
} }
/* Check that %empty => empty rule. */
if (r->percent_empty_loc.start.file
&& r->next && r->next->content.sym)
complain (&r->percent_empty_loc, complaint,
_("%%empty on non-empty rule"));
/* See comments in grammar_current_rule_prec_set for how POSIX /* See comments in grammar_current_rule_prec_set for how POSIX
mandates this complaint. It's only for identifiers, so skip mandates this complaint. It's only for identifiers, so skip
it for char literals and strings, which are always tokens. */ it for char literals and strings, which are always tokens. */
@@ -434,6 +440,17 @@ grammar_current_rule_prec_set (symbol *precsym, location loc)
current_rule->ruleprec = precsym; current_rule->ruleprec = precsym;
} }
/* Set %empty for the current rule. */
void
grammar_current_rule_empty_set (location loc)
{
if (current_rule->percent_empty_loc.start.file)
complain (&loc, complaint, _("only one %s allowed per rule"), "%empty");
else
current_rule->percent_empty_loc = loc;
}
/* Attach dynamic precedence DPREC to the current rule. */ /* Attach dynamic precedence DPREC to the current rule. */
void void

View File

@@ -47,6 +47,8 @@ void grammar_current_rule_begin (symbol *lhs, location loc,
named_ref *lhs_named_ref); named_ref *lhs_named_ref);
void grammar_current_rule_end (location loc); void grammar_current_rule_end (location loc);
void grammar_midrule_action (void); void grammar_midrule_action (void);
/* Apply %empty to the current rule. */
void grammar_current_rule_empty_set (location loc);
void grammar_current_rule_prec_set (symbol *precsym, 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_dprec_set (int dprec, location loc);
void grammar_current_rule_merge_set (uniqstr name, location loc); void grammar_current_rule_merge_set (uniqstr name, location loc);

View File

@@ -217,6 +217,7 @@ eqopt ([[:space:]]*=)?
"%defines" return PERCENT_DEFINES; "%defines" return PERCENT_DEFINES;
"%destructor" return PERCENT_DESTRUCTOR; "%destructor" return PERCENT_DESTRUCTOR;
"%dprec" return PERCENT_DPREC; "%dprec" return PERCENT_DPREC;
"%empty" return PERCENT_EMPTY;
"%error-verbose" return PERCENT_ERROR_VERBOSE; "%error-verbose" return PERCENT_ERROR_VERBOSE;
"%expect" return PERCENT_EXPECT; "%expect" return PERCENT_EXPECT;
"%expect-rr" return PERCENT_EXPECT_RR; "%expect-rr" return PERCENT_EXPECT_RR;

View File

@@ -44,6 +44,7 @@ symbol_list_sym_new (symbol *sym, location loc)
/* Members used for LHS only. */ /* Members used for LHS only. */
res->ruleprec = NULL; res->ruleprec = NULL;
res->percent_empty_loc = empty_location;
code_props_none_init (&res->action_props); code_props_none_init (&res->action_props);
res->dprec = 0; res->dprec = 0;
res->merger = 0; res->merger = 0;

View File

@@ -78,6 +78,10 @@ typedef struct symbol_list
* each RHS are also stored here. */ * each RHS are also stored here. */
code_props action_props; code_props action_props;
/* The location of the first %empty for this rule, or \a
empty_location. */
location percent_empty_loc;
int dprec; int dprec;
int merger; int merger;
location merger_declaration_location; location merger_declaration_location;

View File

@@ -64,6 +64,75 @@ AT_PARSER_CHECK([./input], 0,
AT_CLEANUP AT_CLEANUP
## ------------------------ ##
## Invalid uses of %empty. ##
## ------------------------ ##
AT_SETUP([Invalid uses of %empty])
AT_DATA_GRAMMAR([[one.y]],
[[%%
exp:
%empty %empty {}
;
]])
AT_BISON_CHECK([-fcaret one.y], [1], [],
[[one.y:11.10-15: error: only one %empty allowed per rule
%empty %empty {}
^^^^^^
]])
AT_DATA_GRAMMAR([[two.y]],
[[%%
exp:
'a' %empty {}
| %empty 'a' {}
| %empty {} {}
;
]])
AT_BISON_CHECK([-fcaret two.y], [1], [],
[[two.y:11.7-12: error: %empty on non-empty rule
'a' %empty {}
^^^^^^
two.y:12.3-8: error: %empty on non-empty rule
| %empty 'a' {}
^^^^^^
two.y:13.3-8: error: %empty on non-empty rule
| %empty {} {}
^^^^^^
]])
AT_CLEANUP
## ---------------------- ##
## Valid uses of %empty. ##
## ---------------------- ##
AT_SETUP([Valid uses of %empty])
AT_BISON_OPTION_PUSHDEFS
AT_DATA_GRAMMAR([[input.y]],
[[
%debug
%code
{
]AT_YYERROR_DECLARE[
]AT_YYLEX_DECLARE[
}
%%
exp: %empty {}
%%
]AT_YYERROR_DEFINE[
]AT_YYLEX_DEFINE[
]AT_MAIN_DEFINE[
]])
AT_FULL_COMPILE([input])
AT_PARSER_CHECK([./input])
AT_BISON_OPTION_POPDEFS
AT_CLEANUP
## ------------------ ## ## ------------------ ##
## Initial location. ## ## Initial location. ##