diff options
-rw-r--r-- | src/core/model/Typesystem.cpp | 101 | ||||
-rw-r--r-- | src/core/model/Typesystem.hpp | 79 | ||||
-rw-r--r-- | src/core/parser/ParserScope.cpp | 21 | ||||
-rw-r--r-- | src/core/parser/ParserScope.hpp | 33 | ||||
-rw-r--r-- | src/plugins/xml/XmlParser.cpp | 97 | ||||
-rw-r--r-- | test/core/model/TypesystemTest.cpp | 1 | ||||
-rw-r--r-- | testdata/xmlparser/color.oxm | 4 | ||||
-rw-r--r-- | testdata/xmlparser/generic.oxm | 34 |
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"/> |