Search code examples
bisonyacc

Bison: how to access of value of previous non-terminal without using globals?


I have two rules in my large grammar:

indexed_component
    : name '(' val_list ')' 
    ;

val_list
    : val          
    | val_list ',' val
    ;

How can access the value of 'name' within val_list ?

I know I can use a global variable, but this is highly recursive so I would have to use a stack and I want to avoid the bother.


Solution

  • What you want is what is called an inherited attribute, which, unfortunately, yacc and bison don't really support. You can "hack" it by accessing attributes with negative indexes, but that is error prone.

    If you want to try using btyacc, it has some syntactic sugar to help with this (allowing them to be typechecked, at least). You would write this as

    indexed_component : name '(' val_list($1) ')' ;
    val_list($name) : val
             | val_list($name) ',' val ;
    

    and then in the actions on val_list you could access $name. With a %union you would need to declare this properly with %type:

    %union {
        char *str;
        struct list *list;
    }
    
    %type <str> name                // name gives a string
    %type <list> val_list(<str>)    // val_list takes a string and gives a list
    

    as I mentioned, this is just syntactic sugar in btyacc -- it ends up getting converted into inline actions with accesses to $0. So it will get converted to:

    indexed_component : name '(' { $<str>$ = $1; } val_list ')' ;
    val_list : val
             | val_list ',' val ;
    

    and uses of $name in the val_list actions would become $<str>0;

    The way this works is that $0 ends up being whatever is left on top of the yacc stack when the val_list rule is reduced and the values for the RHS of the rule are (notionally) popped -- it is the value for the last thing immediately before val_list on the RHS of whatever rule val_list appears on the RHS of.