[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Default rule
From: |
Hans Aberg |
Subject: |
Default rule |
Date: |
Wed, 16 Oct 2002 13:45:06 +0200 |
I noticed that yacc.c still implements the default rule as:
yyreduce:
/* yyn is the number of a rule to reduce with. */
yylen = yyr2[yyn];
/* If YYLEN is nonzero, implement the default value of the action:
`$$ = $1'.
Otherwise, the following line sets YYVAL to garbage.
This behavior is undocumented and Bison
users should not rely upon it. Assigning to YYVAL
unconditionally makes the parser a bit smaller, and it avoids a
GCC warning that YYVAL may be used uninitialized. */
yyval = yyvsp[1-yylen];
...
switch (yyn)
/* actions */
This means that the default rule will always be applied.
I changed this under C++ in my private skeleton file so that the default
rule is only applied when {} is omitted in the .y grammar. The first reason
is purely C++, one can have copy constructors where two applications are
not the same as one (happens for example with std::auto_ptr). Under C, this
cannot happen.
But I discovered that when I changed it that the current implementation
invites sloppy programming: One can drop some $$.field = $1.field
assignments in the grammar, even when the rule is not default. If one then
switches to a skeleton file that implements according to the rules, one
gets mysterious semantic parser errors.
So I changed the implementation under C++ (for my own use) so that it becomes:
// Action default value:
// When rule length > 0: $$ = $1.
// When rule length = 0, $$ = semantic_type() (initialization value).
And similar for location values.
This then assumes that the initialization value semantic_type() exists. But
C++ is different in this respect in the sense that C++ integral types now
have initialization values, at least when invoked explicitly. (For example
in C++ bool() produces the value "false".
The implementation is simple: Just move the stuff into the "default" part
of the switch statement:
switch (n_) {
default:
// Action default value:
// When rule length > 0: $$ = $1.
// When rule length = 0, $$ = semantic_type() (initialization value).
if (rule_length_ > 0) {
yyval = *(semantic_stack_.end() - rule_length_ + 1);
#if YYLSP_NEEDED
yyloc = *(location_stack_.end() - rule_length_ + 1);
#endif
}
else {
yyval = semantic_type();
#if YYLSP_NEEDED
yyloc = location_type();
#endif
}
break;
b4_actions
}
Under C (in yacc.c), the change would become (I think):
switch (yyn) {
default:
// Action default value:
// When rule length > 0: $$ = $1.
// When rule length = 0, no action.
if (yylen > 0)
yyval = yyvsp[1-yylen];
break;
b4_actions
}
-- I think it would be safer to do nothing in the case of rule length zero
than producing some ad hoc garbage. The reason for omitting "if (yylen >
0)" was surely once upon the time to save some parsing time, but that is
not an issue anymore on todays computers, I think, as CPU clocks usually
runs much faster than the memory clock.
An alternative under C might be to invent a macro (when not using %union)
#ifndef
#define YYSVALDEFAULT(yyval)
#endif
and the code becomes:
switch (yyn) {
default:
// Action default value:
// When rule length > 0: $$ = $1.
// When rule length = 0, YYSVALDEFAULT($$).
if (yylen > 0)
yyval = yyvsp[1-yylen];
else
YYSVALDEFAULT(yyval);
break;
b4_actions
}
One can then choose ones zero rule length default value if one so wishes.
-- When using %union, YYSVALDEFAULT would need an extra argument:
#ifndef
#define YYSVALDEFAULT(yyval, type) /* yyval.type = ... */
#endif
Hans Aberg
- Default rule,
Hans Aberg <=