1
votes

I've been trying to set up a little parser in Bison, but when I try to build it I get:

stone.tab.cc: In member function ‘virtual int yy::StoneParser::parse()’: stone.tab.cc:507:81: error: invalid initialization of non-const reference of type ‘StoneDriver&’ from an rvalue of type ‘yy::StoneParser::semantic_type*’ yyla.type = yytranslate_ (yylex (&yyla.value, &yyla.location, driver));

I suspect the reason is there's a problem using union-defined semantic types with a bison-generated C++ compiler, but I can't seem to resolve it...

How can I generate a valid C++ bison parser with 'union' semantic definitions?

stone.yy:

%skeleton "lalr1.cc"
%require "3.0.4"

%defines
%define parser_class_name {StoneParser}

%code requires
{
# include <string>
class StoneDriver;
}

%param { StoneDriver& driver }

%locations

%code
{
# include "StoneDriver.hpp"
}

%union
{
   int intVal;
   char* stringVal;
}

%token <stringVal> KEY_ENUM
%token <stringVal> KEY_CLASS

%token K_TEST
%token T_ID

%type <stringVal> class
%type <stringVal> enum

%%

input: toplevel_declarations

toplevel_declarations: toplevel_declarations toplevel_declaration
    | %empty

toplevel_declaration: class
    | enum

class: KEY_CLASS { $$ = "test"; }

enum: KEY_ENUM { $$ = "test"; }

StoneDriver.hpp

#ifndef STONEDRIVER_INCLUDED_HPP
#define STONEDRIVER_INCLUDED_HPP
#include <string>
#include <map>
#include "stone.tab.hh"

class StoneDriver;

//Tell Flex the lexer's prototype ...
#define YY_DECL \
  yy::StoneParser::symbol_type yylex (StoneDriver& driver)
// ... and declare it for the parser's sake.
YY_DECL;

//Scans and parses Stone
class StoneDriver
{
   public:
      //Constructor
      StoneDriver();

      //Destructor
      virtual ~StoneDriver();

  std::map<std::string, int> variables;

  int result;
  // Handling the scanner.
  void scan_begin ();
  void scan_end ();
  bool trace_scanning;
  // Run the parser on file F.
  // Return 0 on success.
  int parse (const std::string& f);
  // The name of the file being parsed.
  // Used later to pass the file name to the location tracker.
  std::string file;
  // Whether parser traces should be generated.
  bool trace_parsing;
  // Error handling.
  void error (const yy::location& l, const std::string& m);
  void error (const std::string& m);
};//StoneDriver

#endif

StoneDriver.cpp:

#include "StoneDriver.hpp"
#include "stone.tab.hh"

StoneDriver::StoneDriver()
  : trace_scanning (false), trace_parsing (false)
{
   variables["one"] = 1;
   variables["two"] = 2;
}//constructor

StoneDriver::~StoneDriver()
{
}//destructor

int StoneDriver::parse(const std::string &f)
{
   file = f;
   scan_begin();
   yy::StoneParser parser(*this);

   #if YYDEBUG
      parser.set_debug_level(trace_parsing);
   #endif

   int res = parser.parse();
   scan_end();

   return res;
}//parse

void StoneDriver::error(const yy::location& l, const std::string& m)
{
  std::cerr << l << ": " << m << std::endl;
}//error

void StoneDriver::error(const std::string& m)
{
  std::cerr << m << std::endl;
}//error

SOLUTION: Rici showed that the method signature for YY_DECL should be defined like so:

int yylex (semantic_type* YYLVAL, location_type* YYLLOC, TYPE1 ARG1, ...)

In my case, the magic line was:

#define YY_DECL int yylex( yy::StoneParser::semantic_type* const lval, yy::StoneParser::location_type* location, StoneDriver& driver )
  • yy:: is the namespace from my Bison file (stone.yy in my case)
  • StoneParser is the defined parser_class_name from stone.yy
  • location_type is required because stone.yy has %locations defined.
  • StoneDriver& is the parsing context from stone.yy, defined as %param { StoneDriver& driver }

Thanks for the help!

1

1 Answers

3
votes

The error message provides a somewhat clear indication of what the problem is, although it is oddly focused on the type mismatch in the first argument to yylex rather than the fact that the prototype you provide for yylex has only one parameter whereas the call has three arguments. Clang is clearer:

stone.tab.cc:474:39: error: no matching function for call to 'yylex'
            yyla.type = yytranslate_ (yylex (&yyla.value, &yyla.location, driver));
                                      ^~~~~
./stone_driver.hpp:14:1: note: candidate function not viable: requires single argument 'driver', but 3 arguments were provided
YY_DECL;
^

The bison info file, section 10.1.5.1, describes the calling convention of yylex from C++ scanners with a union semantic type:

The interface is as follows.

 -- Method on parser: int yylex (semantic_type* YYLVAL, location_type*
          YYLLOC, TYPE1 ARG1, ...)
 -- Method on parser: int yylex (semantic_type* YYLVAL, TYPE1 ARG1, ...)
     Return the next token.  Its type is the return value, its semantic
     value and location (if enabled) being YYLVAL and YYLLOC.
     Invocations of ‘%lex-param {TYPE1 ARG1}’ yield additional
     arguments.

This is effectively the same calling convention as is used with pure parsers in the C API: the first two arguments to yylex (if locations are enabled, as is the case in your program) are the address of the semantic value object and the address of the location object; the third and subsequent arguments are the parameters specified by %param (if present).

So you should fix the definition of YY_DECL according to the above prototype.

You will also have to change your flex actions to treat yylval (or whatever you call the first parameter) as a pointer rather than a union, which mostly means changing . to ->.