bison-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] Factor %FLAG at scan level.


From: Akim Demaille
Subject: Re: [PATCH] Factor %FLAG at scan level.
Date: Thu, 9 Apr 2009 22:48:13 +0200


Le 9 avr. 09 à 06:45, Joel E. Denny a écrit :

On Wed, 8 Apr 2009, Akim Demaille wrote:

Even though that approach is
not actually possible, the user still has an opportunity to share code between the two by specifying that %printer invoke operator<< instead. If we make the symbol_type object directly accessible in %printer, the user can even write a single %printer that invokes operator<< for all
symbols, which are specified by <*> and <>.

This would be a significant change of interface: %printer and %destructor work on the semantic value only, after the dispatch on the type, while symbol_type
is the triple.

%printer and %destructor have access to $$ and @$. The dispatch on symbol type effectively means they have access to the symbol type too. If they
have access to all the parts, why not give them access to the whole as
well?

That would be fine, indeed, but I had the impression that you suggested to have %printer use the symbol_type to perform the printing, which is likely to be an endless recursion. What kind of use of the symbol_type do you have in mind? I think I don't understand what you mean :) In the example variant.yy, it looks like this:

  template <typename Exact>
  void
  parser::yy_print_ (std::ostream& yyo,
const symbol_base_type<Exact>& yysym) const
  {
    int yytype = yysym.type_get ();
    yyo << (yytype < yyntokens_ ? "token" : "nterm")
        << ' ' << yytname_[yytype] << " ("
        << yysym.location << ": ";
    switch (yytype)
      {
            case 3: // TEXT

/* Line 564 of lalr1.cc  */
#line 53 "examples/variant.yy"
{ debug_stream () << yysym.value.template as< ::std::string >(); }
/* Line 564 of lalr1.cc  */
#line 249 "examples/variant.cc"
        break;

            case 4: // NUMBER

/* Line 564 of lalr1.cc  */
#line 53 "examples/variant.yy"
        { debug_stream () << yysym.value.template as< int >(); }
/* Line 564 of lalr1.cc  */
#line 258 "examples/variant.cc"
        break;

            case 7: // list

/* Line 564 of lalr1.cc  */
#line 53 "examples/variant.yy"
{ debug_stream () << yysym.value.template as< ::std::list<std::string> >(); }
/* Line 564 of lalr1.cc  */
#line 267 "examples/variant.cc"
        break;

            case 8: // item

/* Line 564 of lalr1.cc  */
#line 53 "examples/variant.yy"
{ debug_stream () << yysym.value.template as< ::std::string >(); }
/* Line 564 of lalr1.cc  */
#line 276 "examples/variant.cc"
        break;

       default:
          break;
      }
    yyo << ')';
  }
#endif

symbol_base_type is the based from which symbol_type (external symbols, the type is coded by the type number) and stack_symbol_type (internal symbols, the type is coded by the state number).



And this is a nice feature IMHO, as it is modular: you don't
have to write a single %printer which must handle the dispatching, so you would have to concentrate the code there. Rather, it is scattered along the
%type directives.

I'm not suggesting we change that. I'm just suggesting that %printer also have access to the symbol_type object as well. Otherwise, the user will
have to put the parts together by constructing his own temporary
symbol_type object in order to invoke operator<< from within %printer.

I'm sorry, I don't understand here. Sure, we can provided access to it, but to what end? operator<< of a symbol_type bounces to the various %printer implementations, so these should not bounce back to it.

By the way, why "symbol_type"? Why not just "symbol"? Isn't the symbol type just one field of the triple? Maybe I'm misunderstanding something
fundamental.

What I call a symbol (implemented as the type symbol_type) is the tripple, value and location included. Here is what symbol_type looks like:

    /// External form of a symbol: its type and attributes.
    struct symbol_type : symbol_base_type<symbol_type>
    {
      /// The parent class.
      typedef symbol_base_type<symbol_type> super_type;

      /// Default constructor.
      inline symbol_type ();

      /// Constructor.
inline symbol_type (int t, const semantic_type& v, const location_type& l);

      inline symbol_type (int t, const location_type& l);

      /// The symbol type.
      int type;

      /// Return the type corresponding to this state.
      inline int type_get_ () const;

      /// Its token.
      inline token_type token () const;
    };

I'm using the Curiously Recurring Template/Pattern here to avoid dynamic dispatch. The base "class" is:

    /// A complete symbol, with its type.
    template <typename Exact>
    struct symbol_base_type
    {
      /// Default constructor.
      inline symbol_base_type ();

      /// Constructor.
      inline symbol_base_type (const location_type& l);
inline symbol_base_type (const semantic_type& v, const location_type& l);

      /// Return this with its exact type.
      const Exact& self () const;
      Exact& self ();

      /// Return the type of this symbol.
      int type_get () const;

      /// The semantic value.
      semantic_type value;

      /// The location.
      location_type location;
    };


One issue with the current interface for %printer is also that I encourage users to output on debug_stream(). So of course we could

std::ostream&
operator<<(std::ostream& yyostream, const symbol_type& yysymbol)
{
#  define debug_stream() yystream
   // Paste the %printers together.
#  undef debug_stream()
  return yyostream;
}

but that's ugly. Also, what should it look like in C? There is nothing said about debug_stream in C, we just let the user understand stderr is the right place, but some yystream (or yyfile) would be nicer too. Should we start play with new symbols? $stream? %stream?

%printer { %stream << $$; } <*>

?

Of course we could decide that Bison is responsible for the "yystream <<" part and ask the user to write

%printer { $$; } <*>

but this is not very nice: in the case of pointers, you want to derefence if nonnull, how would you write this here? In the case of containers, you'd like to iterate etc. Each time the user can address this issue by simply implementing her operator<< for that type, but that's asking for too much in my opinion (maybe displaying in the traces is not the same as elsewhere in the program etc.). Besides, this approach does not extend well to other languages such as C.





reply via email to

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