Next: Typed Mid-Rule Actions, 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.
Because the types of the semantic values of mid-rule actions are unknown to
Bison, type-based features (e.g., ‘%printer’, ‘%destructor’) do
not work, which could result in memory leaks. They also forbid the use of
the variant
implementation of the api.value.type
in C++
(see C++ Variants).
See Typed Mid-Rule Actions, for one way to address this issue, and Mid-Rule Action Translation, for another: turning mid-action actions into regular actions.
Next: Typed Mid-Rule Actions, Up: Mid-Rule Actions [Contents][Index]