1
votes

I want to create a calculator using flex/bison. I have created the following %union:

%union{
     int intValue;
     double realValue;
     char * numType;
}

So when I want to reduce to a rule like term: primary I check the numType if it is "INT" or "REAL" and i do something like this:

$<realValue>$=$<realValue>1; $<numType>$=$<numType>1;

(the same thing for intValue). Now the problem is that when i have rules like:

term:
  expr '+' expr |
  expr '*' expr |                          
  ...

I have to check too many times if the terms are real or ints so as to put them into the $realValue$ or $intValue$. The question is: Is it possible or is there any feature of bison/yacc that can reduce all this checking?

For example something like

%type <intValue,realValue>

Where you can assign a type two values and then for example if you have a rule like this:

a: primary ;

From now on when a is used to reduce to another rule and a is accessed then the realValue or intValue (the one that has value) will be accessed.Or something like that.... ?

2

2 Answers

1
votes

This is really a C question -- no C does not have dynamic types like that, so as long as you are using bison to generate a C parser, you can't do that. If instead you use C++, you can do some limited dynamic typing, but you can't put many C++ types in a %union, so that may cause other difficulties.

Also, note that the %union is a union -- you can only use one value in it at a time. So when you do $<realValue>$=$<realValue>1; $<numType>$=$<numType>1;, the second assignment overwrites (and corrupts) the first. If you want to store multiple values with a rule, you need to put a struct into your union:

%union {
    enum type_t { INT, DOUBLE } type;
    struct {
        type_t type;
        int value;
    } intval;
    struct {
        type_t type;
        double value;
    } floatval;
}

But as you note, checking the type tags all over the place is hard.

1
votes

All bison/yacc do is generate a C program. They do not change the semantics of C. Unions are still unions, so they are only capable of holding one of their alternatives members, and furthermore there is no inherent way to tell which type is being contained.

So you cannot simultaneously hold an integer in intValue and also a char* in numType. It's one or the other, never both.

Bison doesn't forced you to use a union type for the semantic type. You could use your own tagged union data structure. But bison's <type> mechanism won't help you; you'll need to do all the work yourself. (Although it's not a lot more work than what you're attempting; you'll end up writing things like:

expr: expr '+' expr { if ($1.type == INT && $3.type == INT) {
                        $$.type = INT;
                        $$.intValue = $1.intValue + $3.intvalue;
                      } else {
                        double v1 = $1.type == INT ? (double)$1.intValue
                                                   : $1.realValue;
                        double v2 = $3.type == INT ? (double)$3.intValue
                                                   : $3.realValue;
                        $$.type = REAL;
                        $$.realValue = v1 + v2;
                      }
                    }