mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-09 12:23:04 +00:00
The current approach was too adhoc: the symbols were not sufficiently self-contained, in particular wrt memory management. The "new" guideline is the one that should have been followed from the start: let the symbols handle themslves, instead of leaving their users to it. It was justified by the will to avoid gratuitious moves and copies, but the current approach does not seem to be slower, yet it will probably be simpler to adjust to support move semantics from C++11. The documentation says that the %parse-param are available from the %destructor. In retrospect, that was a silly design decision, which we can break for variants, as its a new feature. It should be phased out for non-variants too. * data/variant.hh: A variant never knows if it stores something or not, it is up to its users to store this information. Yet, in parse.assert mode, make sure the empty/filled variants are properly used. (b4_symbol_constructor_define_): Don't call directly the symbol constructor, to save a useless temporary. * data/stack.hh (push): Steal the pushed value instead of duplicating it. This will simplify the callers of push, who handled this "move" approach themselves. * data/c++.m4 (basic_symbol): Let -1, as kind, denote the fact that a symbol is empty. This is needed for instance when shifting the lookahead: yyla is given as argument to "push", and its value is then moved on the stack. But then yyla must be declared "empty" so that its destructor won't be called. (basic_symbol::move): New. Move the responsibility of calling the destructor from yy_destroy to ~basic_symbol in the case of variants. * data/lalr1.cc (stack_symbol_type): Now a derived class from its previous value, so that we can add a constructor from a symbol_type. (by_state): State -1 means empty. (yypush_): Factor, by calling one overload from the other one, and using the new semantics of stack::push. No longer reclaim by hand the memory from rhs symbols, since now that we store objects with proper destructors, they will be reclaimed automatically. Conversely, be sure to delete yylhs. * tests/c++.at (C++ Variant-based Symbols): New "unit" test for symbols.
340 lines
8.8 KiB
C++
340 lines
8.8 KiB
C++
# C++ skeleton for Bison
|
|
|
|
# Copyright (C) 2002-2013 Free Software Foundation, Inc.
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
## --------- ##
|
|
## variant. ##
|
|
## --------- ##
|
|
|
|
# b4_symbol_variant(YYTYPE, YYVAL, ACTION, [ARGS])
|
|
# ------------------------------------------------
|
|
# Run some ACTION ("build", or "destroy") on YYVAL of symbol type
|
|
# YYTYPE.
|
|
m4_define([b4_symbol_variant],
|
|
[m4_pushdef([b4_dollar_dollar],
|
|
[$2.$3< $][3 >(m4_shift3($@))])dnl
|
|
switch ($1)
|
|
{
|
|
b4_type_foreach([b4_type_action_])[]dnl
|
|
default:
|
|
break;
|
|
}
|
|
m4_popdef([b4_dollar_dollar])dnl
|
|
])
|
|
|
|
|
|
# _b4_char_sizeof_counter
|
|
# -----------------------
|
|
# A counter used by _b4_char_sizeof_dummy to create fresh symbols.
|
|
m4_define([_b4_char_sizeof_counter],
|
|
[0])
|
|
|
|
# _b4_char_sizeof_dummy
|
|
# ---------------------
|
|
# At each call return a new C++ identifier.
|
|
m4_define([_b4_char_sizeof_dummy],
|
|
[m4_define([_b4_char_sizeof_counter], m4_incr(_b4_char_sizeof_counter))dnl
|
|
dummy[]_b4_char_sizeof_counter])
|
|
|
|
|
|
# b4_char_sizeof(SYMBOL-NUMS)
|
|
# ---------------------------
|
|
# To be mapped on the list of type names to produce:
|
|
#
|
|
# char dummy1[sizeof(type_name_1)];
|
|
# char dummy2[sizeof(type_name_2)];
|
|
#
|
|
# for defined type names.
|
|
m4_define([b4_char_sizeof],
|
|
[b4_symbol_if([$1], [has_type],
|
|
[
|
|
m4_map([ b4_symbol_tag_comment], [$@])dnl
|
|
char _b4_char_sizeof_dummy@{sizeof([b4_symbol([$1], [type])])@};
|
|
])])
|
|
|
|
|
|
# b4_variant_includes
|
|
# -------------------
|
|
# The needed includes for variants support.
|
|
m4_define([b4_variant_includes],
|
|
[b4_parse_assert_if([[#include <typeinfo>]])[
|
|
#include <cstdlib> // abort
|
|
#ifndef YYASSERT
|
|
# include <cassert>
|
|
# define YYASSERT assert
|
|
#endif
|
|
]])
|
|
|
|
# b4_variant_define
|
|
# -----------------
|
|
# Define "variant".
|
|
m4_define([b4_variant_define],
|
|
[[ /// A char[S] buffer to store and retrieve objects.
|
|
///
|
|
/// Sort of a variant, but does not keep track of the nature
|
|
/// of the stored data, since that knowledge is available
|
|
/// via the current state.
|
|
template <size_t S>
|
|
struct variant
|
|
{
|
|
/// Type of *this.
|
|
typedef variant<S> self_type;
|
|
|
|
/// Empty construction.
|
|
variant ()]b4_parse_assert_if([
|
|
: built (false)
|
|
, tname (YY_NULL)])[
|
|
{}
|
|
|
|
/// Construct and fill.
|
|
template <typename T>
|
|
variant (const T& t)]b4_parse_assert_if([
|
|
: built (true)
|
|
, tname (typeid (T).name ())])[
|
|
{
|
|
YYASSERT (sizeof (T) <= S);
|
|
new (buffer.raw) T (t);
|
|
}
|
|
|
|
/// Destruction, allowed only if empty.
|
|
~variant ()
|
|
{]b4_parse_assert_if([
|
|
YYASSERT (!built);
|
|
])[}
|
|
|
|
/// Instantiate an empty \a T in here.
|
|
template <typename T>
|
|
T&
|
|
build ()
|
|
{]b4_parse_assert_if([
|
|
YYASSERT (!built);
|
|
YYASSERT (!tname);
|
|
YYASSERT (sizeof (T) <= S);
|
|
built = true;
|
|
tname = typeid (T).name ();])[
|
|
return *new (buffer.raw) T;
|
|
}
|
|
|
|
/// Instantiate a \a T in here from \a t.
|
|
template <typename T>
|
|
T&
|
|
build (const T& t)
|
|
{]b4_parse_assert_if([
|
|
YYASSERT (!built);
|
|
YYASSERT (!tname);
|
|
YYASSERT (sizeof (T) <= S);
|
|
built = true;
|
|
tname = typeid (T).name ();])[
|
|
return *new (buffer.raw) T (t);
|
|
}
|
|
|
|
/// Accessor to a built \a T.
|
|
template <typename T>
|
|
T&
|
|
as ()
|
|
{]b4_parse_assert_if([
|
|
YYASSERT (built);
|
|
YYASSERT (tname == typeid (T).name ());
|
|
YYASSERT (sizeof (T) <= S);])[
|
|
return reinterpret_cast<T&> (buffer.raw);
|
|
}
|
|
|
|
/// Const accessor to a built \a T (for %printer).
|
|
template <typename T>
|
|
const T&
|
|
as () const
|
|
{]b4_parse_assert_if([
|
|
YYASSERT (built);
|
|
YYASSERT (tname == typeid (T).name ());
|
|
YYASSERT (sizeof (T) <= S);])[
|
|
return reinterpret_cast<const T&> (buffer.raw);
|
|
}
|
|
|
|
/// Swap the content with \a other, of same type.
|
|
///
|
|
/// Both variants must be built beforehand, because swapping the actual
|
|
/// data requires reading it (with as()), and this is not possible on
|
|
/// unconstructed variants: it would require some dynamic testing, which
|
|
/// should not be the variant's responsability.
|
|
/// Swapping between built and (possibly) non-built is done with
|
|
/// variant::move ().
|
|
template <typename T>
|
|
void
|
|
swap (variant<S>& other)
|
|
{]b4_parse_assert_if([
|
|
YYASSERT (built);
|
|
YYASSERT (other.built);
|
|
YYASSERT (tname == other.tname);])[
|
|
std::swap (as<T>(), other.as<T>());
|
|
}
|
|
|
|
/// Move the content of \a other to this.
|
|
///
|
|
/// Destroys \a other.
|
|
template <typename T>
|
|
void
|
|
move (variant<S>& other)
|
|
{]b4_parse_assert_if([
|
|
YYASSERT (! built);])[
|
|
build<T>();
|
|
swap<T>(other);
|
|
other.destroy<T>();
|
|
}
|
|
|
|
/// Copy the content of \a other to this.
|
|
template <typename T>
|
|
void
|
|
copy (const variant<S>& other)
|
|
{
|
|
build<T> (other.as<T> ());
|
|
}
|
|
|
|
/// Destroy the stored \a T.
|
|
template <typename T>
|
|
void
|
|
destroy ()
|
|
{
|
|
as<T> ().~T ();]b4_parse_assert_if([
|
|
built = false;
|
|
tname = YY_NULL;])[
|
|
}
|
|
|
|
/// Prohibit blind copies.
|
|
private:
|
|
self_type& operator=(const self_type&)
|
|
{
|
|
abort ();
|
|
}
|
|
|
|
variant (const self_type&)
|
|
{
|
|
abort ();
|
|
}
|
|
|
|
private:
|
|
/// A buffer large enough to store any of the semantic values.
|
|
/// Long double is chosen as it has the strongest alignment
|
|
/// constraints.
|
|
union
|
|
{
|
|
long double align_me;
|
|
char raw[S];
|
|
} buffer;]b4_parse_assert_if([
|
|
|
|
/// Whether the content is built.
|
|
bool built;
|
|
/// If defined, the name of the stored type.
|
|
const char* tname;])[
|
|
};
|
|
]])
|
|
|
|
|
|
## -------------------------- ##
|
|
## Adjustments for variants. ##
|
|
## -------------------------- ##
|
|
|
|
|
|
# b4_semantic_type_declare
|
|
# ------------------------
|
|
# Declare semantic_type.
|
|
m4_define([b4_semantic_type_declare],
|
|
[ /// An auxiliary type to compute the largest semantic type.
|
|
union union_type
|
|
{]b4_type_foreach([b4_char_sizeof])[};
|
|
|
|
/// Symbol semantic values.
|
|
typedef variant<sizeof(union_type)> semantic_type;dnl
|
|
])
|
|
|
|
|
|
# How the semantic value is extracted when using variants.
|
|
|
|
# b4_symbol_value(VAL, [TYPE])
|
|
# ----------------------------
|
|
m4_define([b4_symbol_value],
|
|
[m4_ifval([$2],
|
|
[$1.as< $2 >()],
|
|
[$1])])
|
|
|
|
# b4_symbol_value_template(VAL, [TYPE])
|
|
# -------------------------------------
|
|
# Same as b4_symbol_value, but used in a template method.
|
|
m4_define([b4_symbol_value_template],
|
|
[m4_ifval([$2],
|
|
[$1.template as< $2 >()],
|
|
[$1])])
|
|
|
|
|
|
|
|
## ------------- ##
|
|
## make_SYMBOL. ##
|
|
## ------------- ##
|
|
|
|
|
|
# b4_symbol_constructor_declare_(SYMBOL-NUMBER)
|
|
# ---------------------------------------------
|
|
# Declare the overloaded version of make_symbol for the (common) type of
|
|
# these SYMBOL-NUMBERS. Use at class-level.
|
|
m4_define([b4_symbol_constructor_declare_],
|
|
[b4_symbol_if([$1], [is_token], [b4_symbol_if([$1], [has_id],
|
|
[ static inline
|
|
symbol_type
|
|
make_[]b4_symbol_([$1], [id]) (dnl
|
|
b4_join(b4_symbol_if([$1], [has_type],
|
|
[const b4_symbol([$1], [type])& v]),
|
|
b4_locations_if([const location_type& l])));
|
|
|
|
])])])
|
|
|
|
|
|
# b4_symbol_constructor_declare
|
|
# -----------------------------
|
|
# Declare symbol constructors for all the value types.
|
|
# Use at class-level.
|
|
m4_define([b4_symbol_constructor_declare],
|
|
[ // Symbol constructors declarations.
|
|
b4_symbol_foreach([b4_symbol_constructor_declare_])])
|
|
|
|
|
|
|
|
# b4_symbol_constructor_define_(SYMBOL-NUMBER)
|
|
# --------------------------------------------
|
|
# Define symbol constructor for this SYMBOL-NUMBER.
|
|
m4_define([b4_symbol_constructor_define_],
|
|
[b4_symbol_if([$1], [is_token], [b4_symbol_if([$1], [has_id],
|
|
[ b4_parser_class_name::symbol_type
|
|
b4_parser_class_name::make_[]b4_symbol_([$1], [id]) (dnl
|
|
b4_join(b4_symbol_if([$1], [has_type],
|
|
[const b4_symbol([$1], [type])& v]),
|
|
b4_locations_if([const location_type& l])))[
|
|
{
|
|
symbol_type res (token::]b4_symbol([$1], [id])[]b4_locations_if([, l])[);
|
|
]b4_symbol_if([$1], [has_type], [res.value.build (v);])[
|
|
// ]b4_locations_if([res.location = l;])[
|
|
return res;
|
|
}
|
|
|
|
]])])])
|
|
|
|
|
|
# b4_symbol_constructor_define
|
|
# ----------------------------
|
|
# Define the overloaded versions of make_symbol for all the value types.
|
|
m4_define([b4_symbol_constructor_define],
|
|
[ // Implementation of make_symbol for each symbol type.
|
|
b4_symbol_foreach([b4_symbol_constructor_define_])])
|