1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
Polymorphic semantic values are used when the %polymorphic directive is
used.
E.g., in the specification '%polymorphic TEXT: std::string;'
'TEXT' is the semantic value tag name used in the grammar specification,
and
'std::string' is the C++ type that's used when referring to TEXT.
The (polymorphic) semantic value type used by the grammar is
'Parser::STYPE_'. By associating non-terminals with a polymorphic semantic
value type (using %type specifications, like '%type <TEXT> identifier') $$
values used by the specified non-terminal automatically use the indicated
semantic value tag name. E.g.,
identifier:
IDENTIFIER
{
$$ = d_matched;
}
;
When using %type specifications a C++ type can directly be assigned to $$,
and the intended semantic value tag does not have to be specified.
Parser::STYPE_ is available in Parser, but it's defined in parserbase.h. Thus
the type is 'owned' by bisonc++, and cannot be modified in the Parser class.
In addition to the polymorphic tag types defined in the grammar file, a final
%polymorphic specification is added to the generated grammar:
%polymorphic END_TAG_: EndPolyType_
where both END_TAG_ and EndPolyType_ are (because of the trailing underscores)
names that cannot be used otherwise. A default constructed STYPE_ object
refers to END_TAG_ and contains an End_PolyType value: it is merely used to
ensure that an STYPE_ value is always defined. E.g., it is used in
Parser::setPrecedence.
The parserbase.h file is generated by members of the class Generator, starting
with the skeletons/bisonc++base.h file (in this README file the names used by
default are used).
When generating files the skeletons use $insert specifications which are
recognized by Generator::filter, calling the corresponding functions specified
in Generator::s_insert (generator/data.cc). The names of those functions are
identical to the names used at the $insert specifications (e.g. '$insert
polymorphic' Generator::polymorphic is called to generate that part of the
generated file). It starts by defining the enum class Tag_ followed by the
definiton of the (empty) class EndPolyType_. E.g., for bisonc++ itself it is:
// $insert polymorphic
enum class Tag_
{
SYMBOL,
TERMINAL,
BLOCK,
TEXT,
SIZE_T,
BOOL,
END_TAG_
};
class EndPolyType_
{};
Semantic value handling is defined in the namespace Meta_ (covered
below). Once the Meta_ namespace has been defined the class ParserBase is
defined ($insert parserbase). Here, STYPE_ is defined by
using STYPE_ = Meta_::SType;
For each polymorphic tag / C++ type combination the Meta_ namespace defines
template <typename Tp_>
struct TagOf;
and
template <Tag_ tag>
struct TypeOf;
and also specializations for the semantic value tags / C++ types, defining,
respectively the associated C++ types and semantic value tags. E.g.,
template <>
struct TagOf<std::string>
{
static Tag_ const tag = Tag_::TEXT;
};
and
template <>
struct TypeOf<Tag_::TEXT>
{
using type = std::string;
};
Meta_::SType is a wrapper class, derived from std::unique_ptr<Meta_::Base>
It contains the common constructors (default, copy, move), operator= members
for all polymorphic C++ types, an explicit assign member template
handling the assignments in the operator= members, as in:
inline SType &SType::operator=(std::string const &value)
{
assign< Tag_::TEXT >(value);
return *this;
}
and get() members returning the C++ data values of the polymorphic semantic
types (called, assuming STYPE_ value is available) as
value.get<Tag_::TEXT>(). If 'value' does holds a different polymorphic
semantic value then a fatal error results. Finally, it has a member 'tag()'
returning the STYPE_'s Tag_ value.
Next, the Meta_ namespace defines the polymorphic base class Base. Each STYPE_
object contains a pointer to a Base object, which in fact points to an
instantiation of the class template Semantic (see below), where each Semantic
object is constructed for a particular Tag_ (like Semantic_<Tag_::TEXT>,
constructed for the %polymorphic TEXT: std::string polymorphic value. As Base
is the polymorphic base class, all characteristics of the various Semantic
types are accessed via Base members:
Tag_ tag() const; returns the Semantic value's tag
Base *clone() const; returns a pointer to a newly allocated
copy of the current Semantic object
void *data() const; returns a pointer to the data of the
Semantic object.
The class template Semantic has standard constructors and a template
constructor member specifying the required argument(s) and Tag_. Other than
that, its vClone and vData member implementations are provided in parserbase.h
|