If a particular nonterminal will have an attribute, you tell bison about that in the same section where you declare tokens. Line
%type <intval> expr
tells Bison that nonterminal expr has an attribute, and that the attribute uses the intval field of union type YYSTYPE.
You will need to write a %type line for each nonterminal that has an attribute.
Consider production N → α.
The action written after N → α defines the attribute of N in terms of the attributes of the tokens and nonterminals in α, by setting $$ equal to the attribute.
To illustrate, let's write a simple calculator in Bison using the simple expression grammar. It reads an expression and writes the value of the expression.
%token <intval> TOK_NUMBER
%type <intval> expr
%type <intval> term
%type <intval> fact
%%
program : expr
{
printf("%d", $1);
}
;
expr : term
{
$$ = $1;
}
| expr '+' term
{
$$ = $1 + $3;
}
;
term : fact
{
$$ = $1;
}
| term '*' fact
{
$$ = $1 * $3;
}
;
fact : TOK_NUMBER
{
$$ = $1;
}
| '(' expr ')'
{
$$ = $2;
}
;
%%
Imagine that
AST is the type of an abstract syntax tree (that is, a pointer to an ASTNODE),
Field ast of YYSTYPE has type AST,
newLeaf(n) returns a pointer to a leaf node holding integer n,
newOpNode(sym, A, B) returns a pointer to a node labeled by character sym, with subtrees A and B, and
printAST(T) writes tree T.
The following Bison program builds an abstract syntax tree for an expression and prints it.
%token <intval> TOK_NUMBER
%type <ast> expr
%type <ast> term
%type <ast> fact
%%
program : expr
{
printAST($1);
}
;
expr : term
{
$$ = $1;
}
| expr '+' term
{
$$ = newNode('+', $1, $3);
}
;
term : fact
{
$$ = $1;
}
| term '*' fact
{
$$ = newNode('*', $1, $3);
}
;
fact : TOK_NUMBER
{
$$ = newLeaf($1);
}
| '(' expr ')'
{
$$ = $2;
}
;
%%