1
#ifndef INCLUDED_SEMBASE_
2
#define INCLUDED_SEMBASE_
12
enum class Tag // defines the various semantic values
13
{ // Tags are used to instantiate the proper
14
INT, // Semantic type, deriving from a polymorphic base
20
// In separate anonymous namespace sections parts of the various semantic
21
// types are defined. Sections labeled 'IUO' are `Internal Use Only' and
22
// contain no serviceable parts
26
struct Mutable // Semantic values may or may
27
{ // not be mutable. By deriving
28
enum: bool { isMutable = true }; // BasicTrait, below, from
29
}; // either Mutable or Immutable
30
// this trait is associated with
31
struct Immutable // a semantic value BasicTrait.
33
enum: bool { isMutable = false };
37
// For each semantic value type define a TagTrait specialization which
38
// is either derived from Immutable (in which case the Parser cannot
39
// change the semantic value) or from Mutable (In which case changing the
40
// semantic value on the semantic value stack *is* allowed)
41
// In addition, define the data type to use, as received in the
42
// initialization (e.g., int, std::string, vector<spSemBase>).
48
struct TagTrait<Tag::INT>: public Immutable
54
struct TagTrait<Tag::TEXT>: public Immutable
56
typedef std::string DataType;
60
struct TagTrait<Tag::VECTOR>: public Mutable
62
typedef std::vector<std::shared_ptr<SemBase const>> DataType;
66
// In this section of the anonymous namespace it is determined whether a
67
// semantic value type is a basic type (in which case the return type of
68
// the Semantic class's conversion operator can be equal to the type
69
// itself) or a class-type (in which case a reference is returned).
70
/// Its logic is based on two function templates 'test' expecting either
71
// any type of argument or expecting a pointer to a member function of the
72
// type provided to the template. Since basic types have no members, the
73
// `any argument' variant is selected by the compiler for basic
74
// types. These two functions return values having different sizes,
75
// allowing the template to determine whether the type is a basic type or
76
// not. The enum value 'isBasicType' is initialized accordingly.
77
/// For the ReturnType (i.e., the conversion operator's return type the
78
// std::conditional template meta programming function is used). For a
79
// mutable data type a reference to the Semantic class's data memebr is
80
// returned, for an immutable basic data type a copy of the data member's
81
// value is returned, otherwise a const & is returned.
97
C2 test(void (T::*)());
100
struct Trait: public TagTrait<tg_>
102
typedef typename Trait<tg_>::DataType DataType;
105
isMutable = Trait<tg_>::isMutable,
106
isBasicType = sizeof(test<DataType>(0)) == sizeof(C1)
113
typename std::conditional<
123
/// The implementation of the polymorphic semantic value in fact implements
124
// a type-safe polymorphic semantic union. This is a non-existing data
125
// type in C++, but the parser may handle semantic values as unions: it
126
// always knows the actual type of semantic value that's used at a
127
// particular point in the grammar, and if not, it can inspect the
128
// SemBase's tag fields. Since the parser knows, or can determine what the
129
// actual semantic type is, it can always perform a downcast, resulting in
130
// a *very* lightweight SemBase base class: there's only a virtual
131
// destructor. No other virtual members are required. Downcasting itself
132
// is encapsulated in its 'as' member, allowing constructions like
133
/// $1->as<Tag::STRING>()
134
/// to downcast the $1 SemBase to a Semantic<Tag::STRING>, and then,
135
// usually by implication, to a std::string, using the Semantic's
136
// conversion operator.
143
SemBase(SemBase const &other) = delete;
148
typename Trait<tg_>::ReturnType as() const;
154
inline Tag SemBase::tag() const
159
inline SemBase::SemBase(Tag tag)
165
// The Semantic class template is derived from SemBase. It always defines
166
// its data member as mutable (I can't do this under template control, but
167
// I could define, e.g., two anonymous structs holding, respectively, a
168
// mutable and non-mutable DataType data member, but Semantic isn't doing
169
// anything with d_data anyway, so by always declaring d_data as mutable
170
// it can be returned from the conversion operator, which itself is a
172
/// Again: Semantic is extremely light-weight. Only initialization and
173
// conversion to the embedded data member need to be supported.
176
class Semantic: public SemBase
178
typedef typename Trait<tg_>::DataType DataType;
179
enum: bool { isMutable = Trait<tg_>::isMutable };
181
mutable DataType d_data;
184
typedef typename Trait<tg_>::ReturnType ReturnType;
186
Semantic(DataType const &data);
187
operator ReturnType() const;
191
Semantic<tg_>::Semantic(DataType const &data)
198
Semantic<tg_>::operator ReturnType() const
204
// The 'as' member simply performs the required downcast. It is
205
// implemented here, since it refers to Semantic<Tag>. Since this class
206
// has just been defined, no forward declaration is required anymore.
209
inline typename Trait<tg_>::ReturnType SemBase::as() const
211
return dynamic_cast<Semantic<tg_> const &>(*this);