Next: Mid-Rule Action Translation, Up: Mid-Rule Actions [Contents][Index]
A mid-rule action may refer to the components preceding it using
$n
, but it may not refer to subsequent components because
it is run before they are parsed.
The mid-rule action itself counts as one of the components of the rule.
This makes a difference when there is another action later in the same rule
(and usually there is another at the end): you have to count the actions
along with the symbols when working out which number n to use in
$n
.
The mid-rule action can also have a semantic value. The action can set
its value with an assignment to $$
, and actions later in the rule
can refer to the value using $n
. Since there is no symbol
to name the action, there is no way to declare a data type for the value
in advance, so you must use the ‘$<…>n’ construct to
specify a data type each time you refer to this value.
There is no way to set the value of the entire rule with a mid-rule
action, because assignments to $$
do not have that effect. The
only way to set the value for the entire rule is with an ordinary action
at the end of the rule.
Here is an example from a hypothetical compiler, handling a let
statement that looks like ‘let (variable) statement’ and
serves to create a variable named variable temporarily for the
duration of statement. To parse this construct, we must put
variable into the symbol table while statement is parsed, then
remove it afterward. Here is how it is done:
stmt: "let" '(' var ')' { $<context>$ = push_context (); declare_variable ($3); } stmt { $$ = $6; pop_context ($<context>5); }
As soon as ‘let (variable)’ has been recognized, the first
action is run. It saves a copy of the current semantic context (the
list of accessible variables) as its semantic value, using alternative
context
in the data-type union. Then it calls
declare_variable
to add the new variable to that list. Once the
first action is finished, the embedded statement stmt
can be
parsed.
Note that the mid-rule action is component number 5, so the ‘stmt’ is component number 6. Named references can be used to improve the readability and maintainability (see Named References):
stmt: "let" '(' var ')' { $<context>let = push_context (); declare_variable ($3); }[let] stmt { $$ = $6; pop_context ($<context>let); }
After the embedded statement is parsed, its semantic value becomes the
value of the entire let
-statement. Then the semantic value from the
earlier action is used to restore the prior list of variables. This
removes the temporary let
-variable from the list so that it won’t
appear to exist while the rest of the program is parsed.
In the above example, if the parser initiates error recovery (see Error Recovery) while parsing the tokens in the embedded statement stmt
,
it might discard the previous semantic context $<context>5
without
restoring it.
Thus, $<context>5
needs a destructor (see Freeing
Discarded Symbols).
However, Bison currently provides no means to declare a destructor specific to
a particular mid-rule action’s semantic value.
One solution is to bury the mid-rule action inside a nonterminal symbol and to declare a destructor for that symbol:
%type <context> let %destructor { pop_context ($$); } let
%%
stmt: let stmt { $$ = $2; pop_context ($let); };
let: "let" '(' var ')' { $let = push_context (); declare_variable ($3); };
Note that the action is now at the end of its rule. Any mid-rule action can be converted to an end-of-rule action in this way, and this is what Bison actually does to implement mid-rule actions.
Next: Mid-Rule Action Translation, Up: Mid-Rule Actions [Contents][Index]