1
votes

The bison version I was using before (2.4.1) did not explicitly write the default action for every semantic rule which used it -- but probably simply assigned $$ with $1 once without regard to any types for all default action rules.

bison 3.5.1 insists on dumping this out by himself -- assuming that the stack is made up of a C style union.

Is there any way to convince bison 3.5.1 to follow the old style?

My workaround is to explicit write the default semantic action everywhere it is being used.

I'm already went over several bison changes in the past -- which can be glimpsed from many commented out parser instructions.

The only parser instruction I've left is

%skeleton "lalr1.cc"
%define namespace "smc"
%define parser_class_name "smc_parser"
%lex-param {location *const, symphony::smc::parserDriver& driver }
%parse-param { symphony::smc::parserDriver& driver }
%locations

Explanation of my approach: I'm using this already since many years in different projects. bison wants to convert the type into a member access. I expanded this into set/get methods. With YYSTYPE deriving from std::variant. So assume that a certain rule yields TYPE, then bison converts $$ automatically into $$.TYPE. The code written inside any rule then would look like this:

$$($1());

which results in

$$.TYPE($1.TYPE());

TYPE(argument) is a set method. TYPE(void) is a get method. These methods simply access the underlying std::variant.

Bison now creates the default implementation as

$$.TYPE = $1.TYPE;

which does not compile. It used to simply dump out one copy of $$=$1; for all actions (and all types) needing a default implementation -- means without any specific member.

1
What is the default action you manually insert? Is it not $$ = $1;?rici
Sorry for the delay. I was going to include a tested code snippet but my C++ is a bit rusty and my time is a bit limited. In the end I decide to just outline the idea. If I manage to invent some spare time this week, I'll try to finish and test the snippet.rici

1 Answers

0
votes

This change, which I believe was introduced in v3.2, applies only to the C++ skeleton (which happens to be the one you are requesting, lalr1.cc.) As far as I know, there is no way to disable it.

Although both yacc and bison describe $$ = $1 as the "default action", that's not really the case. The historic implementations perform $$ = $1 for all reductions, whether or not there is an explicit action. (Even if $1 doesn't exist, which would be the case for a production with an empty right-hand side.) That's probably not much of an issue for C parsers, but in C++ it could have unfortunate consequences. For example, the semantic type might not have an assignment operator. Or the assignment might do something which cannot easily be undone. The intent of the change was to make the default action actually a default action, so that it doesn't occur if there is an explicit action.

There was some discussion about this at the time. Originally, the change was proposed for both C and C++, but it was argued -- or, more accurately, I argued -- that many existing bison parsers assume the historic implementation. A classic case is an action which appends an element to a list by doing something like append($1, $3) or even append($$, $3) and assuming that's OK, because the parser already set $$ = $1. In the end, the bison maintainers decided to restrict the change to C++ in order to avoid breaking legacy C programs. Evidently, some legacy C++ programs were broken. Apparently this includes yours.

Using getters and setters to make std::variant work is actually pretty clever, but I don't know a simple way of making it work with new bison versions. You could compile your own bison executable, changing the string literal at line 301 of reader.c to whatever you want the default action to be. (A better fix would be to make the default action a bison %define, but that would be more work.) But if you don't want to recompile and maintain your own bison, you'd probably be better off finding a different solution.

In this context, it's worth noting that bison doesn't really care what a type tag looks like, within reason. You can even include parentheses, as long as they are balanced; whatever you use will be added to the stack reference after a member access operator (.). So instead of defining getter and setter methods, you could define your wrapper type with per-type member functions which return a reference (or reference proxy), and then use type declarations like %type <TYPE()> non_terminal. That should make it possible for $$ = $1; to have the correct semantics, so that the default action works. That might make the rest of your code more readable, too.