diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/model/Document.hpp | 4 | ||||
-rw-r--r-- | src/core/model/Domain.hpp | 8 | ||||
-rw-r--r-- | src/core/model/Node.cpp | 74 | ||||
-rw-r--r-- | src/core/model/Node.hpp | 49 | ||||
-rw-r--r-- | src/core/model/Typesystem.cpp | 16 | ||||
-rw-r--r-- | src/core/model/Typesystem.hpp | 4 | ||||
-rw-r--r-- | src/core/parser/ParserContext.cpp | 2 | ||||
-rw-r--r-- | src/core/parser/ParserContext.hpp | 12 | ||||
-rw-r--r-- | src/core/parser/ParserScope.cpp | 14 | ||||
-rw-r--r-- | src/core/parser/ParserScope.hpp | 43 | ||||
-rw-r--r-- | src/core/resource/ResourceManager.cpp | 23 | ||||
-rw-r--r-- | src/core/resource/ResourceManager.hpp | 4 | ||||
-rw-r--r-- | src/plugins/xml/XmlParser.cpp | 157 |
13 files changed, 330 insertions, 80 deletions
diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp index 1f2fb37..dcb8966 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -849,7 +849,7 @@ public: /** * Adds a Domain reference to this Document. */ - void addDomain(Handle<Domain> d) + void referenceDomain(Handle<Domain> d) { invalidate(); domains.push_back(d); @@ -858,7 +858,7 @@ public: /** * Adds multiple Domain references to this Document. */ - void addDomains(const std::vector<Handle<Domain>> &d) + void referenceDomains(const std::vector<Handle<Domain>> &d) { invalidate(); domains.insert(domains.end(), d.begin(), d.end()); diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index 541a428..cd649d5 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -899,8 +899,6 @@ class Domain : public Node { private: NodeVector<StructuredClass> structuredClasses; NodeVector<AnnotationClass> annotationClasses; - // TODO: Is it wise to attach the type systems here? If not: What would be - // a good alternative. NodeVector<Typesystem> typesystems; protected: @@ -937,7 +935,7 @@ public: Domain(Manager &mgr, Handle<SystemTypesystem> sys, std::string name = "") : Domain(mgr, std::move(name)) { - includeTypesystem(sys); + referenceTypesystem(sys); } /** @@ -1071,12 +1069,12 @@ public: /** * Adds a Typesystem reference to this Domain. */ - void includeTypesystem(Handle<Typesystem> t) { typesystems.push_back(t); } + void referenceTypesystem(Handle<Typesystem> t) { typesystems.push_back(t); } /** * Adds multiple Typesystem references to this Domain. */ - void includeTypesystems(const std::vector<Handle<Typesystem>> &ts) + void referenceTypesystems(const std::vector<Handle<Typesystem>> &ts) { typesystems.insert(typesystems.end(), ts.begin(), ts.end()); } diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index dbc85e2..3b5f38c 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -361,26 +361,92 @@ bool Node::checkDuplicate(Handle<Node> elem, const std::string &name = elem->getName(); if (!names.emplace(name).second) { logger.error(std::string("Element with name \"") + name + - std::string("\" defined multiple times in parent ") + - type().name + std::string(" \"") + - Utils::join(path(), ".") + std::string("\""), *elem); + std::string("\" defined multiple times in parent ") + + type().name + std::string(" \"") + + Utils::join(path(), ".") + std::string("\""), + *elem); return false; } return true; } +bool Node::checkIsAcyclic(std::vector<const Node *> &path, + std::unordered_set<const Node *> &visited, + NodeReferenceCallback callback) const +{ + // Add this node to the path + path.push_back(this); + + // A cycle was found, abort, shorten the path to the actual cycle + if (visited.count(this)) { + return false; + } + visited.insert(this); + + // Continue allong the path + const Node *node = callback(this); + if (node != nullptr) { + if (!node->checkIsAcyclic(path, visited, callback)) { + return false; + } + } + + // Remove this node from the path + path.pop_back(); + return true; +} + bool Node::doValidate(Logger &logger) const { return true; } bool Node::validateName(Logger &logger) const { if (!Utils::isIdentifier(name)) { logger.error(type().name + std::string(" name \"") + name + - std::string("\" is not a valid identifier"), this); + std::string("\" is not a valid identifier"), + this); return false; } return true; } +bool Node::validateIsAcyclic(const std::string &name, + NodeReferenceCallback callback, + Logger &logger) const +{ + std::vector<const Node *> path; + std::unordered_set<const Node *> visited; + + if (!checkIsAcyclic(path, visited, callback)) { + logger.error(std::string("Attribute \"") + name + ("\" is cyclic."), + this); + logger.note("The following nodes are included in the cycle: ", + SourceLocation{}, MessageMode::NO_CONTEXT); + for (const Node *node : path) { + const std::string &name = node->getName(); + const std::string &typeName = node->type().name; + if (name.empty()) { + logger.note(std::string("Node of internal type ") + typeName + + std::string(" declared here:"), + node); + } else { + logger.note(std::string("Node \"") + name + + std::string("\" of internal type ") + typeName + + std::string(" declared here:"), + node); + } + } + return false; + } + return true; +} + +bool Node::validateParentIsAcyclic(Logger &logger) const +{ + return validateIsAcyclic("parent", [](const Node *thisRef) -> const Node * + { return thisRef->parent.get(); }, + logger); +} + void Node::invalidate() { // Only perform the invalidation if necessary diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp index 60d22e0..036bcae 100644 --- a/src/core/model/Node.hpp +++ b/src/core/model/Node.hpp @@ -31,8 +31,8 @@ #include <cstdint> #include <map> #include <set> -#include <unordered_set> #include <string> +#include <unordered_set> #include <vector> #include <core/common/Location.hpp> @@ -155,6 +155,7 @@ private: * vector. * * @param p is the list the path should be constructed in. + * @param root is a node at which building the path should be aborted. */ void path(std::vector<std::string> &p, Handle<Node> root) const; @@ -215,6 +216,30 @@ private: std::unordered_set<std::string> &names, Logger &logger) const; + /** + * Callback function used to access a Node reference stored inside another + * Node. + * + * @param thisRef is the Node of which the reference should be returned. + * @return the value of the reference. + */ + using NodeReferenceCallback = const Node* (const Node* thisRef); + + /** + * Checks whether the a certain property is acyclic. + * + * @param path is a path containing the cycle. If no cycle is found, the + * path will be empty. + * @param visited a set of visited nodes used for cycle detection. + * @param callback is the callback that is used to access the underlying + * property that should be checked for acyclicity. + * @return true if the node is acyclic regarding this property, false if + * a cycle was detected. The cycle is stored in the "path". + */ + bool checkIsAcyclic(std::vector<const Node *> &path, + std::unordered_set<const Node *> &visited, + NodeReferenceCallback callback) const; + protected: /** * Sets the parent node. @@ -365,6 +390,28 @@ protected: bool validateName(Logger &logger) const; /** + * Makes sure the property accessed by the callback is not cyclic. + * + * @param name is the name of the property. The passed name is used to build + * a nice error message. + * @param callback is the callback that is used to access the property. + * @param logger is the logger instance to which an error message containing + * the cycle is logged. + * @return true if the parent reference is acyclic, false otherwise. + */ + bool validateIsAcyclic(const std::string &name, + NodeReferenceCallback callback, Logger &logger) const; + + /** + * Makes sure the "parent" reference is not cyclic. + * + * @param logger is the logger instance to which an error message containing + * the cycle is logged. + * @return true if the parent reference is acyclic, false otherwise. + */ + bool validateParentIsAcyclic(Logger &logger) const; + + /** * Helper function that can be used to forward the validation process to * child nodes. * diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp index 5f8f613..c10fc02 100644 --- a/src/core/model/Typesystem.cpp +++ b/src/core/model/Typesystem.cpp @@ -383,6 +383,11 @@ bool StructType::doBuild(Variant &data, Logger &logger) const bool StructType::doValidate(Logger &logger) const { return validateName(logger) & + validateIsAcyclic("parent", [](const Node *thisRef) -> const Node * { + return dynamic_cast<const StructType *>( + thisRef)->parentStructure.get(); + }, + logger) & continueValidationCheckDuplicates(attributes, logger); } @@ -542,7 +547,7 @@ Rooted<StructType> Typesystem::createStructType(const std::string &name) return structType; } -void Typesystem::includeTypesystem(Handle<Typesystem> typesystem) +void Typesystem::referenceTypesystem(Handle<Typesystem> typesystem) { typesystems.push_back(typesystem); } @@ -573,8 +578,9 @@ const Rtti DoubleType = RttiBuilder<ousia::DoubleType>("DoubleType").parent(&Type); const Rtti BoolType = RttiBuilder<ousia::BoolType>("BoolType").parent(&Type); const Rtti EnumType = RttiBuilder<ousia::EnumType>("EnumType").parent(&Type); -const Rtti StructType = - RttiBuilder<ousia::StructType>("StructType").parent(&Type).composedOf(&Attribute); +const Rtti StructType = RttiBuilder<ousia::StructType>("StructType") + .parent(&Type) + .composedOf(&Attribute); const Rtti ArrayType = RttiBuilder<ousia::ArrayType>("ArrayType").parent(&Type); const Rtti UnknownType = RttiBuilder<ousia::UnknownType>("UnknownType").parent(&Type); @@ -584,8 +590,8 @@ const Rtti Typesystem = RttiBuilder<ousia::Typesystem>("Typesystem").parent(&Node).composedOf( {&StringType, &IntType, &DoubleType, &BoolType, &EnumType, &StructType, &Constant}); -const Rtti SystemTypesystem = - RttiBuilder<ousia::SystemTypesystem> ("SystemTypesystem").parent(&Typesystem); +const Rtti SystemTypesystem = RttiBuilder<ousia::SystemTypesystem>( + "SystemTypesystem").parent(&Typesystem); } } diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp index 1405ed6..e06938e 100644 --- a/src/core/model/Typesystem.hpp +++ b/src/core/model/Typesystem.hpp @@ -989,7 +989,7 @@ public: Typesystem(Manager &mgr, Handle<SystemTypesystem> sys, std::string name) : Typesystem(mgr, std::move(name)) { - includeTypesystem(sys); + referenceTypesystem(sys); } /** @@ -1007,7 +1007,7 @@ public: * @param typesystem is the typesystem that should be added to the * referenced typesystems list. */ - void includeTypesystem(Handle<Typesystem> typesystem); + void referenceTypesystem(Handle<Typesystem> typesystem); /** * Adds the given type to the to the type list. diff --git a/src/core/parser/ParserContext.cpp b/src/core/parser/ParserContext.cpp index b4e2a9a..14b02df 100644 --- a/src/core/parser/ParserContext.cpp +++ b/src/core/parser/ParserContext.cpp @@ -39,7 +39,7 @@ ParserContext::ParserContext(Registry ®istry, { } -NodeVector<Node> ParserContext::import(const std::string &path, +Rooted<Node> ParserContext::import(const std::string &path, const std::string mimetype, const std::string rel, const RttiSet &supportedTypes) diff --git a/src/core/parser/ParserContext.hpp b/src/core/parser/ParserContext.hpp index 2787225..1b889b1 100644 --- a/src/core/parser/ParserContext.hpp +++ b/src/core/parser/ParserContext.hpp @@ -117,8 +117,8 @@ public: * checked, not the actual result. * @return the parsed node or nullptr if something goes wrong. */ - NodeVector<Node> import(const std::string &path, const std::string mimetype, - const std::string rel, const RttiSet &supportedTypes); + Rooted<Node> import(const std::string &path, const std::string mimetype, + const std::string rel, const RttiSet &supportedTypes); /** * Parses a file with ParserContext and the current ParserScope. In contrast @@ -136,10 +136,12 @@ public: * @param supportedTypes contains the types of the returned Node the caller * can deal with. Note that only the types the parser claims to return are * checked, not the actual result. - * @return the parsed node or nullptr if something goes wrong. + * @return the parsed nodes or an empty list if something goes wrong (or + * there were indeed no objects to be parsed). */ - NodeVector<Node> include(const std::string &path, const std::string mimetype, - const std::string rel, const RttiSet &supportedTypes); + NodeVector<Node> include(const std::string &path, + const std::string mimetype, const std::string rel, + const RttiSet &supportedTypes); /** * Clones the ParserContext instance but exchanges the ParserScope instance diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp index 0e2350f..3d1ba78 100644 --- a/src/core/parser/ParserScope.cpp +++ b/src/core/parser/ParserScope.cpp @@ -168,6 +168,20 @@ Rooted<Node> ParserScope::getRoot() const { return nodes.front(); } Rooted<Node> ParserScope::getLeaf() const { return nodes.back(); } +Rooted<Node> ParserScope::select(RttiSet types, int maxDepth) +{ + ssize_t minDepth = 0; + if (maxDepth >= 0) { + minDepth = static_cast<ssize_t>(nodes.size()) - (maxDepth + 1); + } + for (ssize_t i = nodes.size() - 1; i >= minDepth; i--) { + if (nodes[i]->type().isOneOf(types)) { + return nodes[i]; + } + } + return nullptr; +} + void ParserScope::setFlag(ParserFlag flag, bool value) { // Fetch the current stack depth diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp index 2c6093f..1191cbc 100644 --- a/src/core/parser/ParserScope.hpp +++ b/src/core/parser/ParserScope.hpp @@ -324,14 +324,24 @@ public: /** * Returns the bottom-most Node instance in the ParserScope hirarchy, e.g. - *the - * node that was pushed last onto the stack. + * the node that was pushed last onto the stack. * * @return a reference at the leaf node. */ Rooted<Node> getLeaf() const; /** + * 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. + */ + Rooted<Node> select(RttiSet types, int maxDepth = -1); + + /** * Sets a parser flag for the current stack depth. * * @param flag is the flag that should be set. @@ -353,12 +363,10 @@ public: * Tries to resolve a node for the given type and path for all nodes * currently on the stack, starting with the topmost node on the stack. * Calls the "imposterCallback" function for obtaining a temporary - *result if - * a node cannot be resolved right now. The "resultCallback" is at most - * called twice: Once when this method is called (probably with the + * result if a node cannot be resolved right now. The "resultCallback" is + * at most called twice: Once when this method is called (probably with the * temporary) and another time if the resolution turned out to be - *successful - * at a later point in time. + * successful at a later point in time. * * @param path is the path for which a node should be resolved. * @param type is the type of the node that should be resolved. @@ -367,24 +375,17 @@ public: * @param imposterCallback is the callback function that is called if * the node cannot be resolved at this moment. It gives the caller the * possibility to create an imposter (a temporary object) that may be - *used - * later in the resolution process. + * used later in the resolution process. * @param resultCallback is the callback function to which the result of * the resolution process is passed. This function is called at least - *once - * either with the imposter (if the resolution was not successful) or - *the - * resolved object directly when this function is called. If the - *resolution - * was not successful the first time, it may be called another time - *later - * in the context of the "performDeferredResolution" function. + * once either with the imposter (if the resolution was not successful) or + * the resolved object directly when this function is called. If the + * resolution was not successful the first time, it may be called another + * time later in the context of the "performDeferredResolution" function. * @param location is the location in the current source file in which - *the - * resolution was triggered. + * the resolution was triggered. * @return true if the resolution was immediately successful. This does - *not - * mean, that the resolved object does not exist, as it may be resolved + * not mean, that the resolved object does not exist, as it may be resolved * later. */ bool resolve(const std::vector<std::string> &path, const Rtti &type, diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp index 2484cb2..1d32a4d 100644 --- a/src/core/resource/ResourceManager.cpp +++ b/src/core/resource/ResourceManager.cpp @@ -122,8 +122,12 @@ NodeVector<Node> ResourceManager::parse( innerScope.checkUnwound(logger); innerScope.performDeferredResolution(logger); - // Fetch the nodes that were parsed by this parser instance + // Fetch the nodes that were parsed by this parser instance and + // validate them parsedNodes = innerScope.getTopLevelNodes(); + for (auto parsedNode : parsedNodes) { + parsedNode->validate(logger); + } // Make sure the number of elements is exactly one -- we can // only store one element per top-level node. @@ -184,13 +188,18 @@ NodeVector<Node> ResourceManager::parse( return parsedNodes; } -NodeVector<Node> ResourceManager::import(ParserContext &ctx, - const std::string &path, - const std::string &mimetype, - const std::string &rel, - const RttiSet &supportedTypes) +Rooted<Node> ResourceManager::import(ParserContext &ctx, + const std::string &path, + const std::string &mimetype, + const std::string &rel, + const RttiSet &supportedTypes) { - return parse(ctx, path, mimetype, rel, supportedTypes, ParseMode::IMPORT); + NodeVector<Node> res = + parse(ctx, path, mimetype, rel, supportedTypes, ParseMode::IMPORT); + if (res.size() == 1U) { + return res[0]; + } + return nullptr; } NodeVector<Node> ResourceManager::include(ParserContext &ctx, diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp index 83556aa..1279bee 100644 --- a/src/core/resource/ResourceManager.hpp +++ b/src/core/resource/ResourceManager.hpp @@ -158,9 +158,9 @@ public: * @param supportedTypes contains the types of the returned Node the caller * can deal with. Note that only the types the parser claims to return are * checked, not the actual result. - * @return the parsed nodes or an empty list if something went wrong. + * @return the parsed node or nullptr if something went wrong. */ - NodeVector<Node> import(ParserContext &ctx, const std::string &path, + Rooted<Node> import(ParserContext &ctx, const std::string &path, const std::string &mimetype, const std::string &rel, const RttiSet &supportedTypes); diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index 51c52bc..d71ceac 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -22,34 +22,103 @@ #include <expat.h> #include <core/common/CharReader.hpp> +#include <core/common/RttiBuilder.hpp> #include <core/common/Utils.hpp> #include <core/common/VariantReader.hpp> #include <core/parser/ParserStack.hpp> #include <core/parser/ParserScope.hpp> +#include <core/model/Document.hpp> +#include <core/model/Domain.hpp> +#include <core/model/Project.hpp> #include <core/model/Typesystem.hpp> #include "XmlParser.hpp" namespace ousia { -/* Document structure */ -static const State STATE_DOCUMENT = 0; -static const State STATE_HEAD = 1; -static const State STATE_BODY = 2; +/* HeadNode Helper class */ -/* Special commands */ -static const State STATE_USE = 100; -static const State STATE_INCLUDE = 101; -static const State STATE_INLINE = 102; +namespace { +class HeadNode : public Node { +public: + using Node::Node; +}; +} -/* Type system definitions */ -static const State STATE_TYPESYSTEM = 200; -static const State STATE_TYPES = 201; -static const State STATE_CONSTANTS = 202; -static const State STATE_CONSTANT = 203; -static const State STATE_ENUM = 204; -static const State STATE_STRUCT = 205; -static const State STATE_FIELD = 206; +namespace RttiTypes { +static Rtti HeadNode = RttiBuilder<ousia::HeadNode>("HeadNode"); +} + +/* Element Handler Classes */ + +class DocumentHandler : public Handler { +public: + using Handler::Handler; + + void start(Variant::mapType &args) override + { + Rooted<Document> document = + project()->createDocument(args["name"].asString()); + document->setLocation(location()); + scope().push(document); + scope().setFlag(ParserFlag::POST_HEAD, false); + } + + void end() override { scope().pop(); } + + static Handler *create(const HandlerData &handlerData) + { + return new DocumentHandler{handlerData}; + } +}; + +class HeadHandler : public Handler { +public: + using Handler::Handler; + + void start(Variant::mapType &args) override + { + // Make sure the "HEAD" node is actually allowed here + if (scope().getFlag(ParserFlag::POST_HEAD)) { + throw LoggableException{ + "\"head\" tag not allowed here, head was already specified or " + "another command was given first", + location()}; + } + + // Insert a new HeadNode instance + scope().push(new HeadNode{manager()}); + } + + void end() override + { + // Remove the HeadNode instance from the stack + scope().pop(); + scope().setFlag(ParserFlag::POST_HEAD, true); + } + + static Handler *create(const HandlerData &handlerData) + { + return new HeadHandler{handlerData}; + } +}; + +class DisableHeadHandler : public Handler { +public: + using Handler::Handler; + + void start(Variant::mapType &args) override + { + scope().setFlag(ParserFlag::POST_HEAD, true); + } + + void end() override {} + + static Handler *create(const HandlerData &handlerData) + { + return new DisableHeadHandler{handlerData}; + } +}; class TypesystemHandler : public Handler { public: @@ -57,10 +126,20 @@ public: void start(Variant::mapType &args) override { + // Create the typesystem instance Rooted<Typesystem> typesystem = 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); } void end() override { scope().pop(); } @@ -148,25 +227,52 @@ public: } }; +/* Document structure */ +static const State STATE_DOCUMENT = 0; +static const State STATE_DOCUMENT_HEAD = 1; + +/* Special commands */ +static const State STATE_IMPORT = 100; +static const State STATE_INCLUDE = 101; + +/* Type system definitions */ +static const State STATE_TYPESYSTEM = 200; +static const State STATE_TYPESYSTEM_HEAD = 201; +static const State STATE_TYPES = 202; +static const State STATE_CONSTANTS = 203; +static const State STATE_CONSTANT = 204; +static const State STATE_ENUM = 205; +static const State STATE_STRUCT = 206; +static const State STATE_FIELD = 207; + +/* Domain definitions */ +static const State STATE_DOMAIN = 300; +static const State STATE_DOMAIN_HEAD = 301; + static const std::multimap<std::string, HandlerDescriptor> XML_HANDLERS{ /* Document tags */ - {"document", {{STATE_NONE}, nullptr, STATE_DOCUMENT}}, - {"head", {{STATE_DOCUMENT}, nullptr, STATE_HEAD}}, - {"body", {{STATE_DOCUMENT}, nullptr, STATE_BODY, true}}, + {"document", + {{STATE_NONE}, + DocumentHandler::create, + STATE_DOCUMENT, + true, + {Argument::String("name", "")}}}, + {"head", {{STATE_DOCUMENT}, HeadHandler::create, STATE_DOCUMENT_HEAD}}, /* Special commands */ - {"use", {{STATE_HEAD}, nullptr, STATE_USE}}, + {"import", + {{STATE_DOCUMENT_HEAD, STATE_TYPESYSTEM_HEAD}, nullptr, STATE_IMPORT}}, {"include", {{STATE_ALL}, nullptr, STATE_INCLUDE}}, - {"inline", {{STATE_ALL}, nullptr, STATE_INLINE}}, /* Typesystem */ {"typesystem", - {{STATE_NONE, STATE_HEAD}, + {{STATE_NONE, STATE_DOMAIN_HEAD}, TypesystemHandler::create, STATE_TYPESYSTEM, false, {Argument::String("name")}}}, - {"types", {{STATE_TYPESYSTEM}, nullptr, STATE_TYPES}}, + {"head", {{STATE_TYPESYSTEM}, HeadHandler::create, STATE_TYPESYSTEM}}, + {"types", {{STATE_TYPESYSTEM}, DisableHeadHandler::create, STATE_TYPES}}, {"enum", {{STATE_TYPES}, nullptr, STATE_ENUM}}, {"struct", {{STATE_TYPES}, @@ -175,13 +281,14 @@ static const std::multimap<std::string, HandlerDescriptor> XML_HANDLERS{ false, {Argument::String("name"), Argument::String("parent", "")}}}, {"field", - {{{STATE_STRUCT}}, + {{STATE_STRUCT}, StructFieldHandler::create, STATE_FIELD, false, {Argument::String("name"), Argument::String("type"), Argument::Any("default", Variant::fromObject(nullptr))}}}, - {"constants", {{STATE_TYPESYSTEM}, nullptr, STATE_CONSTANTS}}, + {"constants", + {{STATE_TYPESYSTEM}, DisableHeadHandler::create, STATE_CONSTANTS}}, {"constant", {{STATE_CONSTANTS}, nullptr, STATE_CONSTANT}}}; /** |