mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-10 12:53:03 +00:00
doc: typed mid-rule actions
* doc/bison.texi (Mid-Rule Actions): Restructure to insert... (Typed Mid-Rule Actions): this new section. Move the manual translation of mid-rule actions into regular actions to... (Mid-Rule Action Translation): here.
This commit is contained in:
152
doc/bison.texi
152
doc/bison.texi
@@ -225,6 +225,7 @@ Defining Language Semantics
|
||||
Actions in Mid-Rule
|
||||
|
||||
* Using Mid-Rule Actions:: Putting an action in the middle of a rule.
|
||||
* Typed Mid-Rule Actions:: Specifying the semantic type of their values.
|
||||
* Mid-Rule Action Translation:: How mid-rule actions are actually processed.
|
||||
* Mid-Rule Conflicts:: Mid-rule actions can cause conflicts.
|
||||
|
||||
@@ -4071,6 +4072,7 @@ are executed before the parser even recognizes the following components.
|
||||
|
||||
@menu
|
||||
* Using Mid-Rule Actions:: Putting an action in the middle of a rule.
|
||||
* Typed Mid-Rule Actions:: Specifying the semantic type of their values.
|
||||
* Mid-Rule Action Translation:: How mid-rule actions are actually processed.
|
||||
* Mid-Rule Conflicts:: Mid-rule actions can cause conflicts.
|
||||
@end menu
|
||||
@@ -4158,64 +4160,86 @@ earlier action is used to restore the prior list of variables. This
|
||||
removes the temporary @code{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., @samp{%printer}, @samp{%destructor}) do
|
||||
not work, which could result in memory leaks. They also forbid the use of
|
||||
the @code{variant} implementation of the @code{api.value.type} in C++
|
||||
(@pxref{C++ Variants}).
|
||||
|
||||
@xref{Typed Mid-Rule Actions}, for one way to address this issue, and
|
||||
@ref{Mid-Rule Action Translation}, for another: turning mid-action actions
|
||||
into regular actions.
|
||||
|
||||
|
||||
@node Typed Mid-Rule Actions
|
||||
@subsubsection Typed Mid-Rule Actions
|
||||
|
||||
@findex %destructor
|
||||
@cindex discarded symbols, mid-rule actions
|
||||
@cindex error recovery, mid-rule actions
|
||||
In the above example, if the parser initiates error recovery (@pxref{Error
|
||||
Recovery}) while parsing the tokens in the embedded statement @code{stmt},
|
||||
it might discard the previous semantic context @code{$<context>5} without
|
||||
restoring it.
|
||||
Thus, @code{$<context>5} needs a destructor (@pxref{Destructor Decl, , Freeing
|
||||
Discarded Symbols}).
|
||||
However, Bison currently provides no means to declare a destructor specific to
|
||||
a particular mid-rule action's semantic value.
|
||||
restoring it. Thus, @code{$<context>5} needs a destructor
|
||||
(@pxref{Destructor Decl, , Freeing Discarded Symbols}), and Bison needs the
|
||||
type of the semantic value (@code{context}) to select the right destructor.
|
||||
|
||||
One solution is to bury the mid-rule action inside a nonterminal symbol and to
|
||||
declare a destructor for that symbol:
|
||||
As an extension to Yacc's mid-rule actions, Bison offers a means to type
|
||||
their semantic value: specify its type tag (@samp{<...>} before the mid-rule
|
||||
action.
|
||||
|
||||
Consider the previous example, with an untyped mid-rule action:
|
||||
|
||||
@example
|
||||
@group
|
||||
%type <context> let
|
||||
%destructor @{ pop_context ($$); @} let
|
||||
@end group
|
||||
|
||||
%%
|
||||
|
||||
@group
|
||||
stmt:
|
||||
let stmt
|
||||
@{
|
||||
$$ = $2;
|
||||
pop_context ($let);
|
||||
@};
|
||||
@end group
|
||||
|
||||
@group
|
||||
let:
|
||||
"let" '(' var ')'
|
||||
@{
|
||||
$let = push_context ();
|
||||
$<context>$ = push_context (); // ***
|
||||
declare_variable ($3);
|
||||
@};
|
||||
|
||||
@}
|
||||
stmt
|
||||
@{
|
||||
$$ = $6;
|
||||
pop_context ($<context>5); // ***
|
||||
@}
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
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.
|
||||
If instead you write:
|
||||
|
||||
@example
|
||||
@group
|
||||
stmt:
|
||||
"let" '(' var ')'
|
||||
<context>@{ // ***
|
||||
$$ = push_context (); // ***
|
||||
declare_variable ($3);
|
||||
@}
|
||||
stmt
|
||||
@{
|
||||
$$ = $6;
|
||||
pop_context ($5); // ***
|
||||
@}
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
then @code{%printer} and @code{%destructor} work properly (no more leaks!),
|
||||
C++ @code{variant}s can be used, and redundancy is reduced (@code{<context>}
|
||||
is specified once).
|
||||
|
||||
|
||||
@node Mid-Rule Action Translation
|
||||
@subsubsection Mid-Rule Action Translation
|
||||
@vindex $@@@var{n}
|
||||
@vindex @@@var{n}
|
||||
|
||||
As hinted earlier, mid-rule actions are actually transformed into regular
|
||||
rules and actions. The various reports generated by Bison (textual,
|
||||
graphical, etc., see @ref{Understanding, , Understanding Your Parser})
|
||||
reveal this translation, best explained by means of an example. The
|
||||
following rule:
|
||||
Mid-rule actions are actually transformed into regular rules and actions.
|
||||
The various reports generated by Bison (textual, graphical, etc., see
|
||||
@ref{Understanding, , Understanding Your Parser}) reveal this translation,
|
||||
best explained by means of an example. The following rule:
|
||||
|
||||
@example
|
||||
exp: @{ a(); @} "b" @{ c(); @} @{ d(); @} "e" @{ f(); @};
|
||||
@@ -4273,6 +4297,45 @@ mid.y:2.19-31: warning: unused value: $3
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@sp 1
|
||||
|
||||
It is sometimes useful to turn mid-rule actions into regular actions, e.g.,
|
||||
to factor them, or to escape from their limitations. For instance, as an
|
||||
alternative to @emph{typed} mid-rule action, you may bury the mid-rule
|
||||
action inside a nonterminal symbol and to declare a printer and a destructor
|
||||
for that symbol:
|
||||
|
||||
@example
|
||||
@group
|
||||
%type <context> let
|
||||
%destructor @{ pop_context ($$); @} let
|
||||
%printer @{ print_context (yyo, $$); @} let
|
||||
@end group
|
||||
|
||||
%%
|
||||
|
||||
@group
|
||||
stmt:
|
||||
let stmt
|
||||
@{
|
||||
$$ = $2;
|
||||
pop_context ($let);
|
||||
@};
|
||||
@end group
|
||||
|
||||
@group
|
||||
let:
|
||||
"let" '(' var ')'
|
||||
@{
|
||||
$let = push_context ();
|
||||
declare_variable ($var);
|
||||
@};
|
||||
|
||||
@end group
|
||||
@end example
|
||||
|
||||
|
||||
|
||||
|
||||
@node Mid-Rule Conflicts
|
||||
@subsubsection Conflicts due to Mid-Rule Actions
|
||||
@@ -10523,7 +10586,7 @@ To enable variant-based semantic values, set @code{%define} variable
|
||||
@code{%union} is ignored, and instead of using the name of the fields of the
|
||||
@code{%union} to ``type'' the symbols, use genuine types.
|
||||
|
||||
For instance, instead of
|
||||
For instance, instead of:
|
||||
|
||||
@example
|
||||
%union
|
||||
@@ -10536,7 +10599,7 @@ For instance, instead of
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
write
|
||||
write:
|
||||
|
||||
@example
|
||||
%token <int> NUMBER;
|
||||
@@ -10555,7 +10618,10 @@ Variants are stricter than unions. When based on unions, you may play any
|
||||
dirty game with @code{yylval}, say storing an @code{int}, reading a
|
||||
@code{char*}, and then storing a @code{double} in it. This is no longer
|
||||
possible with variants: they must be initialized, then assigned to, and
|
||||
eventually, destroyed.
|
||||
eventually, destroyed. As a matter of fact, Bison variants forbid the use
|
||||
of alternative types such as @samp{$<int>2} or @samp{$<std::string>$}, even
|
||||
in mid-rule actions. It is mandatory to use typed mid-rule actions
|
||||
(@pxref{Typed Mid-Rule Actions}).
|
||||
|
||||
@deftypemethod {semantic_type} {T&} build<T> ()
|
||||
Initialize, but leave empty. Returns the address where the actual value may
|
||||
@@ -10575,10 +10641,13 @@ Boost.Variant not only stores the value, but also a tag specifying its
|
||||
type. But the parser already ``knows'' the type of the semantic value, so
|
||||
that would be duplicating the information.
|
||||
|
||||
We do not use C++17's @code{std::variant} either: we want to support all the
|
||||
C++ standards, and of course @code{std::variant} also stores a tag to record
|
||||
the current type.
|
||||
|
||||
Therefore we developed light-weight variants whose type tag is external (so
|
||||
they are really like @code{unions} for C++ actually). But our code is much
|
||||
less mature that Boost.Variant. So there is a number of limitations in
|
||||
(the current implementation of) variants:
|
||||
they are really like @code{unions} for C++ actually). There is a number of
|
||||
limitations in (the current implementation of) variants:
|
||||
@itemize
|
||||
@item
|
||||
Alignment must be enforced: values should be aligned in memory according to
|
||||
@@ -10588,6 +10657,9 @@ therefore, since, as far as we know, @code{double} is the most demanding
|
||||
type on all platforms, alignments are enforced for @code{double} whatever
|
||||
types are actually used. This may waste space in some cases.
|
||||
|
||||
@item
|
||||
Move semantics is not yet supported, but will soon be added.
|
||||
|
||||
@item
|
||||
There might be portability issues we are not aware of.
|
||||
@end itemize
|
||||
|
||||
Reference in New Issue
Block a user