summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/model/Typesystem.cpp101
-rw-r--r--src/core/model/Typesystem.hpp79
-rw-r--r--src/core/parser/ParserScope.cpp21
-rw-r--r--src/core/parser/ParserScope.hpp33
-rw-r--r--src/plugins/xml/XmlParser.cpp97
-rw-r--r--test/core/model/TypesystemTest.cpp1
-rw-r--r--testdata/xmlparser/color.oxm4
-rw-r--r--testdata/xmlparser/generic.oxm34
8 files changed, 278 insertions, 92 deletions
diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp
index 7dd5613..bd5e615 100644
--- a/src/core/model/Typesystem.cpp
+++ b/src/core/model/Typesystem.cpp
@@ -67,10 +67,7 @@ bool Type::build(Variant &data, Logger &logger) const
return build(data, logger, NullMagicCallback);
}
-bool Type::doCheckIsa(Handle<const Type> type) const
-{
- return false;
-}
+bool Type::doCheckIsa(Handle<const Type> type) const { return false; }
bool Type::checkIsa(Handle<const Type> type) const
{
@@ -114,6 +111,11 @@ bool StringType::doBuild(Variant &data, Logger &logger,
/* Class EnumType */
+EnumType::EnumType(Manager &mgr, std::string name, Handle<Typesystem> system)
+ : Type(mgr, std::move(name), system, false), nextOrdinalValue(0)
+{
+}
+
bool EnumType::doBuild(Variant &data, Logger &logger,
const MagicCallback &magicCallback) const
{
@@ -136,41 +138,74 @@ bool EnumType::doBuild(Variant &data, Logger &logger,
// Throw an execption if the given string value is not found
if (it == values.end()) {
throw LoggableException(std::string("Unknown enum constant: \"") +
- name + std::string("\""),
+ name +
+ std::string("\", expected one of ") +
+ Utils::join(names(), ", ", "{", "}"),
data);
}
data = it->second;
return true;
}
- throw LoggableException{"Expected integer or identifier", data};
+
+ // Throw an exception, list possible enum types
+ throw LoggableException{
+ std::string(
+ "Expected integer or one of the following enum constants: ") +
+ Utils::join(names(), ", ", "{", "}"),
+ data};
}
-Rooted<EnumType> EnumType::createValidated(
- Manager &mgr, std::string name, Handle<Typesystem> system,
- const std::vector<std::string> &values, Logger &logger)
+bool EnumType::doValidate(Logger &logger) const
{
- // Map used to store the unique values of the enum
- std::map<std::string, Ordinal> unique_values;
-
- // The given vector may not be empty
+ bool ok = true;
if (values.empty()) {
- logger.error("Enumeration constants may not be empty.");
+ logger.error("Enum type must have at least one entry", *this);
+ ok = false;
}
+ return ok & validateName(logger);
+}
- // Iterate over the input vector, check the constant names for validity and
- // uniqueness and insert them into the internal values map
- for (size_t i = 0; i < values.size(); i++) {
- if (!Utils::isIdentifier(values[i])) {
- logger.error(std::string("\"") + values[i] +
- "\" is no valid identifier.");
- }
+void EnumType::addEntry(const std::string &entry, Logger &logger)
+{
+ if (!Utils::isIdentifier(entry)) {
+ logger.error(std::string("\"") + entry +
+ "\" is not a valid identifier.");
+ return;
+ }
- if (!(unique_values.insert(std::make_pair(values[i], i))).second) {
- logger.error(std::string("The value ") + values[i] +
- " was duplicated.");
- }
+ if (!values.emplace(entry, nextOrdinalValue).second) {
+ logger.error(std::string("The enumeration entry ") + entry +
+ std::string(" was duplicated"));
+ return;
}
- return new EnumType{mgr, name, system, unique_values};
+ nextOrdinalValue++;
+}
+
+void EnumType::addEntries(const std::vector<std::string> &entries,
+ Logger &logger)
+{
+ for (const std::string &entry : entries) {
+ addEntry(entry, logger);
+ }
+}
+
+Rooted<EnumType> EnumType::createValidated(
+ Manager &mgr, std::string name, Handle<Typesystem> system,
+ const std::vector<std::string> &entries, Logger &logger)
+{
+ Rooted<EnumType> type = new EnumType{mgr, name, system};
+ type->addEntries(entries, logger);
+ return type;
+}
+
+std::vector<std::string> EnumType::names() const
+{
+ std::vector<std::string> res;
+ res.reserve(values.size());
+ for (const auto &v : values) {
+ res.emplace_back(v.first);
+ }
+ return res;
}
std::string EnumType::nameOf(Ordinal i) const
@@ -390,12 +425,15 @@ bool StructType::buildFromMap(Variant &data, Logger &logger,
ok = false;
logger.error(std::string("Invalid attribute key \"") + key +
std::string("\""),
- value);
+ data);
}
}
// Copy the built array to the result and insert missing default values
+ // TODO: Nicer way of assigning a new variant value and keeping location?
+ SourceLocation loc = data.getLocation();
data = arr;
+ data.setLocation(loc);
return insertDefaults(data, set, logger) && ok;
}
@@ -470,7 +508,6 @@ bool StructType::doCheckIsa(Handle<const Type> type) const
return false;
}
-
Rooted<StructType> StructType::createValidated(
Manager &mgr, std::string name, Handle<Typesystem> system,
Handle<StructType> parentStructure, const NodeVector<Attribute> &attributes,
@@ -625,7 +662,6 @@ bool ArrayType::doCheckIsa(Handle<const Type> type) const
return t1->checkIsa(t2);
}
-
/* Class UnknownType */
bool UnknownType::doBuild(Variant &, Logger &, const MagicCallback &) const
@@ -706,6 +742,13 @@ Rooted<StructType> Typesystem::createStructType(const std::string &name)
return structType;
}
+Rooted<EnumType> Typesystem::createEnumType(const std::string &name)
+{
+ Rooted<EnumType> enumType{new EnumType(getManager(), name, this)};
+ addType(enumType);
+ return enumType;
+}
+
Rooted<Constant> Typesystem::createConstant(const std::string &name,
Variant value)
{
diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp
index 7581626..8e3a3bc 100644
--- a/src/core/model/Typesystem.hpp
+++ b/src/core/model/Typesystem.hpp
@@ -371,26 +371,16 @@ public:
private:
/**
- * Map containing the enumeration type values and the associated integer
- * representation.
+ * Value holding the next ordinal value that is to be used when adding a new
+ * type.
*/
- const std::map<std::string, Ordinal> values;
+ Ordinal nextOrdinalValue;
/**
- * Private constructor of the EnumType class used to create a new EnumType
- * instance from a previously created name to value map. The parameters are
- * not checked for validity.
- *
- * @param mgr is the underlying Manager instance.
- * @param name is the name of the EnumType instance. Should be a valid
- * identifier.
- * @param values is a vector containing the enumeration type constants.
+ * Map containing the enumeration type values and the associated integer
+ * representation.
*/
- EnumType(Manager &mgr, std::string name, Handle<Typesystem> system,
- std::map<std::string, Ordinal> values)
- : Type(mgr, std::move(name), system, false), values(std::move(values))
- {
- }
+ std::map<std::string, Ordinal> values;
protected:
/**
@@ -405,22 +395,61 @@ protected:
bool doBuild(Variant &data, Logger &logger,
const MagicCallback &magicCallback) const override;
+ /**
+ * Returns true if the internal value list is non-empty.
+ *
+ * @param logger is the logger instance to which validation errors are
+ * logged.
+ */
+ bool doValidate(Logger &logger) const override;
+
public:
/**
+ * Constructor of the EnumType class.
+ *
+ * @param mgr is the underlying Manager instance.
+ * @param name is the name of the EnumType instance. Should be a valid
+ * identifier.
+ * @param system is the parent typesystem.
+ */
+ EnumType(Manager &mgr, std::string name, Handle<Typesystem> system);
+
+ /**
+ * Adds a new entry to the enum. The enum element is validated, errors
+ * are logged in the given logger instance.
+ *
+ * @param entry is the name of the enum element that should be added.
+ * @param logger is the logger instance that should be used to write error
+ * messages.
+ */
+ void addEntry(const std::string &entry, Logger &logger);
+
+ /**
+ * Adds a new entry to the enum. The enum element is validated, errors
+ * are logged in the given logger instance.
+ *
+ * @param entires is a list containing the enum elements that should be
+ * added.
+ * @param logger is the logger instance that should be used to write error
+ * messages.
+ */
+ void addEntries(const std::vector<std::string> &entries, Logger &logger);
+
+ /**
* Creates a new enum instance and validates the incomming value vector.
*
* @param mgr is the underlying Manager instance.
* @param name is the name of the EnumType instance. Should be a valid
* identifier.
* @param system is a reference to the parent Typesystem instance.
- * @param values is a vector containing the enumeration type constants.
+ * @param entries is a vector containing the enumeration type constants.
* The constants are checked for validity (must be a valid identifier) and
* uniqueness (each value must exist exactly once).
* @param logger is the Logger instance into which errors should be written.
*/
static Rooted<EnumType> createValidated(
Manager &mgr, std::string name, Handle<Typesystem> system,
- const std::vector<std::string> &values, Logger &logger);
+ const std::vector<std::string> &entries, Logger &logger);
/**
* Creates a Variant containing a valid representation of a variable of this
@@ -432,6 +461,11 @@ public:
Variant create() const override { return Variant{0}; }
/**
+ * Returns the names of all enum entries.
+ */
+ std::vector<std::string> names() const;
+
+ /**
* Returns the name of the given ordinal number. Throws a LoggableException
* if the ordinal number is out of range.
*/
@@ -1135,6 +1169,15 @@ public:
Rooted<StructType> createStructType(const std::string &name);
/**
+ * Creates a new EnumType instance with the given name. Adds the new
+ * EnumType as member to the typesystem.
+ *
+ * @param name is the name of the structure that should be created.
+ * @return the new EnumType instance.
+ */
+ Rooted<EnumType> createEnumType(const std::string &name);
+
+ /**
* Creates a new Constant instance with the given name. Adds the new
* Constant as member to the typesystem.
*
diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp
index 1937697..b97dabd 100644
--- a/src/core/parser/ParserScope.cpp
+++ b/src/core/parser/ParserScope.cpp
@@ -90,10 +90,23 @@ Rooted<Node> ParserScopeBase::select(RttiSet types, int maxDepth)
return nodes[i];
}
}
- throw LoggableException{
- std::string(
- "Expected be inside an element of one of the internal types ") +
- Utils::join(types, "\", \"", "\"", "\"")};
+ return nullptr;
+}
+
+Rooted<Node> ParserScopeBase::selectOrThrow(RttiSet types, int maxDepth)
+{
+ Rooted<Node> res = select(types, maxDepth);
+ if (res == nullptr) {
+ std::vector<std::string> typenames;
+ for (auto type : types) {
+ typenames.push_back(type->name);
+ }
+ throw LoggableException{std::string(
+ "Expected to be inside an element of one "
+ "of the internal types ") +
+ Utils::join(typenames, "\", \"", "\"", "\"")};
+ }
+ return res;
}
/* Class DeferredResolution */
diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp
index dc1b1bf..7be3c4a 100644
--- a/src/core/parser/ParserScope.hpp
+++ b/src/core/parser/ParserScope.hpp
@@ -145,6 +145,33 @@ public:
/**
* Ascends in the stack starting with the leaf node, returns the first node
+ * that matches the type given in the RttiSet or nullptr if none matches.
+ *
+ * @param types is a set of Rtti types for which should be searched in the
+ * stack.
+ * @param maxDepth is the maximum number of stack entries the selection
+ * function may ascend. A negative value indicates no limitation.
+ * @return the matching node or nullptr if the node was not found.
+ */
+ Rooted<Node> select(RttiSet types, int maxDepth = -1);
+
+ /**
+ * Ascends in the stack starting with the leaf node, returns the first node
+ * that matches the given type or nullptr if none matches.
+ *
+ * @tparam T is the type that should be searched in the stack.
+ * @param maxDepth is the maximum number of stack entries the selection
+ * function may ascend. A negative value indicates no limitation.
+ * @return the matching node or nullptr if the node was not found.
+ */
+ template <class T>
+ Rooted<T> select(int maxDepth = -1)
+ {
+ return select(RttiSet{&typeOf<T>()}, maxDepth).cast<T>();
+ }
+
+ /**
+ * Ascends in the stack starting with the leaf node, returns the first node
* that matches the type given in the RttiSet. Throws an exception if no
* node matches.
*
@@ -154,7 +181,7 @@ public:
* function may ascend. A negative value indicates no limitation.
* @return the matching node.
*/
- Rooted<Node> select(RttiSet types, int maxDepth = -1);
+ Rooted<Node> selectOrThrow(RttiSet types, int maxDepth = -1);
/**
* Ascends in the stack starting with the leaf node, returns the first node
@@ -166,9 +193,9 @@ public:
* @return the matching node.
*/
template <class T>
- Rooted<T> select(int maxDepth = -1)
+ Rooted<T> selectOrThrow(int maxDepth = -1)
{
- return select(RttiSet{&typeOf<T>()}, maxDepth).cast<T>();
+ return selectOrThrow(RttiSet{&typeOf<T>()}, maxDepth).cast<T>();
}
};
diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp
index 010b707..52176db 100644
--- a/src/plugins/xml/XmlParser.cpp
+++ b/src/plugins/xml/XmlParser.cpp
@@ -86,12 +86,6 @@ public:
project()->createTypesystem(args["name"].asString());
typesystem->setLocation(location());
- // Check whether this typesystem is a direct child of a domain
- Handle<Node> parent = scope().select({&RttiTypes::Domain});
- if (parent != nullptr) {
- parent.cast<Domain>()->referenceTypesystem(typesystem);
- }
-
// Push the typesystem onto the scope, set the POST_HEAD flag to true
scope().push(typesystem);
scope().setFlag(ParserFlag::POST_HEAD, false);
@@ -105,6 +99,61 @@ public:
}
};
+class TypesystemEnumHandler : public Handler {
+public:
+ using Handler::Handler;
+
+ void start(Variant::mapType &args) override
+ {
+ scope().setFlag(ParserFlag::POST_HEAD, true);
+
+ // Fetch the current typesystem and create the enum node
+ Rooted<Typesystem> typesystem = scope().selectOrThrow<Typesystem>();
+ Rooted<EnumType> enumType =
+ typesystem->createEnumType(args["name"].asString());
+ enumType->setLocation(location());
+
+ scope().push(enumType);
+ }
+
+ void end() override { scope().pop(); }
+
+ static Handler *create(const HandlerData &handlerData)
+ {
+ return new TypesystemEnumHandler{handlerData};
+ }
+};
+
+class TypesystemEnumEntryHandler : public Handler {
+public:
+ using Handler::Handler;
+
+ std::string entry;
+
+ void start(Variant::mapType &args) override {}
+
+ void end() override
+ {
+ Rooted<EnumType> enumType = scope().selectOrThrow<EnumType>();
+ enumType->addEntry(entry, logger());
+ }
+
+ void data(const std::string &data, int field) override
+ {
+ if (field != 0) {
+ // TODO: This should be stored in the HandlerData
+ logger().error("Enum entry only has one field.");
+ return;
+ }
+ entry.append(data);
+ }
+
+ static Handler *create(const HandlerData &handlerData)
+ {
+ return new TypesystemEnumEntryHandler{handlerData};
+ }
+};
+
class TypesystemStructHandler : public Handler {
public:
using Handler::Handler;
@@ -118,7 +167,7 @@ public:
const std::string &parent = args["parent"].asString();
// Fetch the current typesystem and create the struct node
- Rooted<Typesystem> typesystem = scope().select<Typesystem>();
+ Rooted<Typesystem> typesystem = scope().selectOrThrow<Typesystem>();
Rooted<StructType> structType = typesystem->createStructType(name);
structType->setLocation(location());
@@ -134,16 +183,10 @@ public:
}
});
}
-
- // Descend into the struct type
scope().push(structType);
}
- void end() override
- {
- // Descend from the struct type
- scope().pop();
- }
+ void end() override { scope().pop(); }
static Handler *create(const HandlerData &handlerData)
{
@@ -164,7 +207,7 @@ public:
const bool optional =
!(defaultValue.isObject() && defaultValue.asObject() == nullptr);
- Rooted<StructType> structType = scope().select<StructType>();
+ Rooted<StructType> structType = scope().selectOrThrow<StructType>();
Rooted<Attribute> attribute =
structType->createAttribute(name, defaultValue, optional, logger());
attribute->setLocation(location());
@@ -212,7 +255,7 @@ public:
const std::string &type = args["type"].asString();
const Variant &value = args["value"];
- Rooted<Typesystem> typesystem = scope().select<Typesystem>();
+ Rooted<Typesystem> typesystem = scope().selectOrThrow<Typesystem>();
Rooted<Constant> constant = typesystem->createConstant(name, value);
constant->setLocation(location());
@@ -268,7 +311,8 @@ public:
{
scope().setFlag(ParserFlag::POST_HEAD, true);
- Rooted<Domain> domain = scope().select<Domain>();
+ Rooted<Domain> domain = scope().selectOrThrow<Domain>();
+
Rooted<StructuredClass> structuredClass = domain->createStructuredClass(
args["name"].asString(), args["cardinality"].asCardinality(),
nullptr, nullptr, args["transparent"].asBool(),
@@ -391,8 +435,8 @@ public:
}
};
-//TODO: Add parent handler
-//TODO: Add annotation handler
+// TODO: Add parent handler
+// TODO: Add annotation handler
/*
* Import and Include Handler
@@ -549,13 +593,20 @@ static const ParserState Typesystem =
.elementHandler(TypesystemHandler::create)
.arguments({Argument::String("name", "")});
static const ParserState TypesystemEnum =
- ParserStateBuilder().createdNodeType(&RttiTypes::EnumType).parent(
- &Typesystem);
+ ParserStateBuilder()
+ .parent(&Typesystem)
+ .createdNodeType(&RttiTypes::EnumType)
+ .elementHandler(TypesystemEnumHandler::create)
+ .arguments({Argument::String("name")});
+static const ParserState TypesystemEnumEntry =
+ ParserStateBuilder()
+ .parent(&TypesystemEnum)
+ .elementHandler(TypesystemEnumEntryHandler::create)
+ .arguments({});
static const ParserState TypesystemStruct =
ParserStateBuilder()
.parent(&Typesystem)
.createdNodeType(&RttiTypes::StructType)
-
.elementHandler(TypesystemStructHandler::create)
.arguments({Argument::String("name"), Argument::String("parent", "")});
static const ParserState TypesystemStructField =
@@ -568,7 +619,6 @@ static const ParserState TypesystemConstant =
ParserStateBuilder()
.parent(&Typesystem)
.createdNodeType(&RttiTypes::Constant)
-
.elementHandler(TypesystemConstantHandler::create)
.arguments({Argument::String("name"), Argument::String("type"),
Argument::Any("value")});
@@ -596,6 +646,7 @@ static const std::multimap<std::string, const ParserState *> XmlStates{
{"child", &DomainStructChild},
{"typesystem", &Typesystem},
{"enum", &TypesystemEnum},
+ {"entry", &TypesystemEnumEntry},
{"struct", &TypesystemStruct},
{"field", &TypesystemStructField},
{"constant", &TypesystemConstant},
diff --git a/test/core/model/TypesystemTest.cpp b/test/core/model/TypesystemTest.cpp
index 2a2c81e..bf40356 100644
--- a/test/core/model/TypesystemTest.cpp
+++ b/test/core/model/TypesystemTest.cpp
@@ -400,6 +400,7 @@ TEST(EnumType, createValidated)
logger.reset();
Rooted<EnumType> enumType{
EnumType::createValidated(mgr, "enum", nullptr, {}, logger)};
+ ASSERT_FALSE(enumType->validate(logger));
ASSERT_EQ(Severity::ERROR, logger.getMaxEncounteredSeverity());
}
diff --git a/testdata/xmlparser/color.oxm b/testdata/xmlparser/color.oxm
index 8a12e7a..17adea4 100644
--- a/testdata/xmlparser/color.oxm
+++ b/testdata/xmlparser/color.oxm
@@ -1,5 +1,5 @@
<?xml version="1.0" standalone="yes"?>
-<ousia>
+<typesystem name="color">
<struct name="color">
<field name="r" type="int"/>
<field name="g" type="int"/>
@@ -15,4 +15,4 @@
<constant name="azure3" type="color" value="[193,205,205]"/>
<constant name="azure4" type="color" value="[131,139,139]"/>
<constant name="azure" type="color" value="[240,255,255]"/>
-</ousia>
+</typesystem>
diff --git a/testdata/xmlparser/generic.oxm b/testdata/xmlparser/generic.oxm
index 820deea..45803c8 100644
--- a/testdata/xmlparser/generic.oxm
+++ b/testdata/xmlparser/generic.oxm
@@ -1,21 +1,29 @@
<?xml version="1.0" standalone="yes"?>
-<typesystem name="bla">
- <import>color.oxm</import>
- <!--<struct name="color">
- <field name="r" type="int"/>
- <field name="g" type="int"/>
- <field name="b" type="int"/>
- </struct>-->
- <!--<include>color.oxm</include>-->
+<typesystem name="border">
+ <import>./color.oxm</import>
+
+ <enum name="border-style">
+ <entry>none</entry>
+ <entry>dotted</entry>
+ <entry>dashed</entry>
+ <entry>solid</entry>
+ <entry>double</entry>
+ <entry>groove</entry>
+ <entry>ridge</entry>
+ <entry>inset</entry>
+ <entry>outset</entry>
+ </enum>
<constant name="zero" value="0" type="int" />
- <constant name="zeros" value="[0, 0, 0]" type="int[]" />
- <constant name="manyZeros" value="[[eeeee, [2, 5], zeros], [42, 1000], [0, {3-5}]]" type="int[][][]" />
- <!--<constant name="black" value="[zero, zero, zero]" type="color" />-->
- <struct name="structWithColor">
+ <constant name="black" value="[zero, zero, zero]" type="color" />
+
+ <struct name="border">
+ <field name="style" type="border-style"/>
<field name="color" type="color" default="black" />
</struct>
- <constant name="aquamarineStructWithColor" value="[color=aquamarine]" type="structWithColor" />
+
+ <constant name="beautifulBorder" type="border" value="[color=aquamarine,style=solid]" />
+ <constant name="moreBeautifulBorder" type="border" value="[dotted, azure]" />
</typesystem>
<!--<domain name="color">
<struct name="bla" cardinality="{1,2}" isa="blub"/>