问题
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.yylocation_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:
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 ->
.
来源:https://stackoverflow.com/questions/43218920/bison-using-the-union-semantic-type-with-a-c-parser