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; } ; %%