user2881080
user2881080

Reputation: 115

How to use std::variant type with %type directive

I am trying to write some grammar in bison that parses C Code. I am new to bison and I am trying to learn from examples that I find online. I am in the process of writing AST. If this is the grammar that I define (most basic use case)

declarator
: IDENTIFIER         { $$ = $1;}
| declarator '(' ')' { $$ = new functionDecl($1); }

Now when I compile this code, an error message is thrown that 'declarator' does not have a type.

I understand that I can define the type using %type declarative. But I want "declarator" to be associated with a variant type:

%code {

class Stmt 
{
   public:
     std::string id;
     Stmt(std::string aId)
     : id(aId)
     {}  
};
typedef std::variant<std::string, Stmt> decl_type;
}

%define api.value.type variant
%token <std::string> IDENTIFIER
%type <decl_type> declarator

I am unable to compile this code as well. It throws an error that decl_type is unknown. What am I missing?

Upvotes: 1

Views: 432

Answers (1)

rici
rici

Reputation: 241791

When you write

%define api.value.type variant

you are telling bison to implement semantic values with its own implementation of a variant type.

This is highlighted in the bison manual in a note prominently marked Warning::

Warning: We do not use Boost.Variant, for two reasons. First, it appeared unacceptable to require Boost on the user’s machine (i.e., the machine on which the generated parser will be compiled, not the machine on which bison was run). Second, for each possible semantic value, Boost.Variant not only stores the value, but also a tag specifying its type. But the parser already “knows” the type of the semantic value, so that would be duplicating the information.

We do not use C++17’s std::variant either: we want to support all the C++ standards, and of course std::variant also stores a tag to record the current type.

(It's worth reading the entire section on Bison variants if you want to use them.)

You could define bison's semantic type to be a std::variant, of course:

%define api.value.type std::variant<std::string, Stmt>

But that might not be what you want, because bison doesn't know anything about std::variant and it will not do anything to help you with the syntax of accessing the value of the variant. Normally, as the page on Bison variants points out, Bison can deduce the type of the semantic value of a grammar symbol (using your %type declarations, of course), but if you don't use a union or Bison variant type, then all Bison knows is that the value is a std::variant. If you happen to know that it is a particular type (for example, because you know what the types of your terminal symbols are), and you want to examine the value using that type, you'll have to use std::variant::get, something like $2.get<NodeList>.

There has been some discussion on the Bison mailing list about improving Bison's handling of variant types. Sadly, I haven't been following it in detail, so you might want to take a look yourself.

Upvotes: 3

Related Questions