bug-bison
[Top][All Lists]
Advanced

[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






reply via email to

[Prev in Thread] Current Thread [Next in Thread]