From d935bf07d15ddd117d07d83bb3818b6abc23cc0b Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Sun, 18 Jan 2015 00:39:21 +0100 Subject: Renamed continueResolve to doResolve for more consistency with other virtual protected methods --- src/core/model/Document.cpp | 2 +- src/core/model/Document.hpp | 2 +- src/core/model/Domain.cpp | 4 ++-- src/core/model/Domain.hpp | 4 ++-- src/core/model/Node.cpp | 4 ++-- src/core/model/Node.hpp | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index 0de750f..ee15e31 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -292,7 +292,7 @@ bool AnnotationEntity::doValidate(Logger &logger) const /* Class Document */ -void Document::continueResolve(ResolutionState &state) +void Document::doResolve(ResolutionState &state) { continueResolveComposita(annotations, annotations.getIndex(), state); if (root != nullptr) { diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp index 5af3ce2..cb7f394 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -567,7 +567,7 @@ private: NodeVector annotations; NodeVector domains; - void continueResolve(ResolutionState &state) override; + void doResolve(ResolutionState &state) override; public: Document(Manager &mgr, std::string name) diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index 2ac2d8d..bae0f4f 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -70,7 +70,7 @@ FieldDescriptor::FieldDescriptor(Manager &mgr, Handle parent, /* Class Descriptor */ -void Descriptor::continueResolve(ResolutionState &state) +void Descriptor::doResolve(ResolutionState &state) { if (attributesDescriptor != nullptr) { const NodeVector &attributes = @@ -244,7 +244,7 @@ AnnotationClass::AnnotationClass( /* Class Domain */ -void Domain::continueResolve(ResolutionState &state) +void Domain::doResolve(ResolutionState &state) { if (!continueResolveComposita(structuredClasses, structuredClasses.getIndex(), state) | diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index ac02ec7..a8d4a9e 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -394,7 +394,7 @@ private: bool exploreSubclasses = true) const; protected: - void continueResolve(ResolutionState &state) override; + void doResolve(ResolutionState &state) override; /** * Adds a FieldDescriptor and checks for name uniqueness. @@ -682,7 +682,7 @@ private: NodeVector typesystems; protected: - void continueResolve(ResolutionState &state) override; + void doResolve(ResolutionState &state) override; void addStructuredClass(Handle s); void addAnnotationClass(Handle a); diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index da83598..7565ba9 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -272,14 +272,14 @@ bool Node::resolve(ResolutionState &state) } } else { size_t resCount = state.resultCount(); - continueResolve(state); + doResolve(state); return state.resultCount() > resCount; } } return false; } -void Node::continueResolve(ResolutionState &state) +void Node::doResolve(ResolutionState &state) { // Do nothing in the default implementation } diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp index 0523c69..f8893a7 100644 --- a/src/core/model/Node.hpp +++ b/src/core/model/Node.hpp @@ -207,7 +207,7 @@ protected: * * @param state is used internally to manage the resolution process. */ - virtual void continueResolve(ResolutionState &state); + virtual void doResolve(ResolutionState &state); /** * Tries to advance the resolution process with the compositum pointed at -- cgit v1.2.3 From 5c53ea8fb93f611a90099000fb0ba3b0874f99c5 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Sun, 18 Jan 2015 00:41:56 +0100 Subject: Implemented some helper functions to facilitate implementing doValidate methods. This includes the "validateName" function (which makes sure the Node has a valid identifier), the "continueValidation" function (which descends into the given list of child nodes) and the "continueValidationCheckDuplicates" which descends into the given child list and makes sure the names of the child nodes are unique. --- src/core/model/Node.cpp | 24 ++++++++++++++++ src/core/model/Node.hpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 94 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index 7565ba9..e432aff 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "Node.hpp" @@ -353,8 +354,31 @@ std::vector Node::resolve(const std::string &name, return resolve(std::vector{name}, type); } +bool Node::checkDuplicate(Handle elem, + std::unordered_set &names, + Logger &logger) const +{ + const std::string &name = elem->getName(); + if (!names.emplace(name).second) { + logger.error(std::string("Element with name \"") + name + + std::string("\" defined multiple times.")); + return false; + } + 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")); + return false; + } + return true; +} + void Node::invalidate() { // Only perform the invalidation if necessary diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp index f8893a7..0168a3e 100644 --- a/src/core/model/Node.hpp +++ b/src/core/model/Node.hpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -196,6 +197,19 @@ private: */ bool continueResolveIndex(const Index &index, ResolutionState &state); + /** + * Checks whether the name of the given node is already stored in the given + * set, if yes, logs a corresponding error message. + * + * @param node is the node of which the name should be checked. + * @param names is a set in which all encountered names are stored. + * @param logger is the logger instance to which error messages are written. + * @return true if the given node has a unique name, false otherwise. + */ + bool checkDuplicate(Handle node, + std::unordered_set &names, + Logger &logger) const; + protected: /** * Function which should be overwritten by derived classes in order to @@ -329,6 +343,56 @@ protected: */ virtual bool doValidate(Logger &logger) const; + /** + * Makes sure the name of this node is a valid identifier and loggs a + * corresponding error message. + * + * @param logger is the logger to which the error message is logged. + * @return true if the name is valid, false otherwise. + */ + bool validateName(Logger &logger) const; + + /** + * Helper function that can be used to forward the validation process to + * child nodes. + * + * @tparam T is the type of the list that should be handled. + * @param list is a list of arbitrary kind. The "validate" function is + * called for all elementsd of the list. + * @param logger is the logger to which any errors should be reported. + */ + template + bool continueValidation(const T &list, Logger &logger) const + { + bool res = true; + for (auto elem : list) { + res = elem->validate(logger) & res; + } + return res; + } + + /** + * Helper function that can be used to forward the validation process to + * child nodes while at the same time checking that the children have no + * duplicated names. + * + * @tparam T is the type of the list that should be handled. + * @param list is a list of arbitrary kind. The "validate" function is + * called for all elementsd of the list. + * @param logger is the logger to which any errors should be reported. + */ + template + bool continueValidationCheckDuplicates(const T &list, Logger &logger) const + { + bool res = true; + std::unordered_set names; + for (auto elem : list) { + res = elem->validate(logger) & checkDuplicate(elem, names, logger) & + res; + } + return res; + } + public: /** * Initializes the node with empty name and parent. @@ -460,8 +524,9 @@ class NodeVector : public ManagedGenericList>, ListAccessor>, Listener> { public: - using ManagedGenericList>, ListAccessor>, - Listener>::ManagedGenericList; + using Base = ManagedGenericList>, ListAccessor>, + Listener>; + using Base::Base; /** * Returns the reference to the internal index. @@ -490,9 +555,10 @@ class NodeMap : public ManagedGenericMap>, MapAccessor>>, Listener> { public: - using ManagedGenericMap>, + using Base = ManagedGenericMap>, MapAccessor>>, - Listener>::ManagedGenericMap; + Listener>; + using Base::Base; /** * Returns the reference to the internal index. -- cgit v1.2.3 From 86d11c73f637f5e858ecb84fe6dfb90281f266a9 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Sun, 18 Jan 2015 13:28:15 +0100 Subject: Allowing to override location when logging exceptions. --- src/core/common/Logger.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/core/common/Logger.hpp b/src/core/common/Logger.hpp index b365a39..767d8ab 100644 --- a/src/core/common/Logger.hpp +++ b/src/core/common/Logger.hpp @@ -242,6 +242,16 @@ public: log(Severity::ERROR, ex.msg, ex.getLocation()); } + /** + * Logs the given loggable exception at the given location. + * + * @param ex is the exception that should be logged. + */ + void log(const LoggableException &ex, const SourceLocation &loc) + { + log(Severity::ERROR, ex.msg, loc.valid() ? loc : ex.getLocation()); + } + /** * Logs the given message. The file name is set to the topmost file name on * the file name stack. -- cgit v1.2.3 From 89e0b786d01246d1e8e57cc5da855372e27e7b63 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Sun, 18 Jan 2015 13:28:36 +0100 Subject: Added function for parsing complete strings (as needed in the xml parser) --- src/core/common/VariantReader.cpp | 16 +++++++++++++++- src/core/common/VariantReader.hpp | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/common/VariantReader.cpp b/src/core/common/VariantReader.cpp index faad40c..bded618 100644 --- a/src/core/common/VariantReader.cpp +++ b/src/core/common/VariantReader.cpp @@ -618,8 +618,22 @@ std::pair VariantReader::parseGenericToken( v.setMagic(res.second.c_str()); return std::make_pair(res.first, v); } else { - return std::make_pair(res.first, Variant{res.second.c_str()}); + return std::make_pair(res.first, Variant::fromString(res.second)); } } + +std::pair VariantReader::parseGenericString( + const std::string &str, Logger &logger) +{ + CharReader reader{str}; + LoggerFork loggerFork = logger.fork(); + std::pair res = + parseGenericToken(reader, loggerFork, std::unordered_set{}, true); + if (reader.atEnd()) { + loggerFork.commit(); + return res; + } + return std::make_pair(true, Variant::fromString(str)); +} } diff --git a/src/core/common/VariantReader.hpp b/src/core/common/VariantReader.hpp index abf529c..8aaffd8 100644 --- a/src/core/common/VariantReader.hpp +++ b/src/core/common/VariantReader.hpp @@ -229,6 +229,26 @@ public: CharReader &reader, Logger &logger, const std::unordered_set &delims, bool extractUnescapedStrings = false); + + /** + * Tries to parse the most specific item from the given string. The + * resulting variant represents the value that has been read. If the end of + * the string was not reached while parsing an element, the result is + * returned as string. + * + * @param str is the string from which the value should be read. + * @param logger is the logger instance to which errors or warnings will be + * written. + * @return a pair indicating whether the operation was successful and the + * extracted variant value. Note that the variant value most times contains + * some meaningful data that can be worked with even if the operation was + * not successful (e.g. if a syntax error is encountered while reading an + * array, the successfully read elements will still be in the returned + * variant.) Information on why the operation has failed is passed to the + * logger. + */ + static std::pair parseGenericString( + const std::string &str, Logger &logger); }; } -- cgit v1.2.3 From 5aaafebebe26a7205533d752503e50449bfcdd80 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Sun, 18 Jan 2015 13:29:36 +0100 Subject: Improved validation error message --- src/core/model/Node.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index e432aff..1c627fc 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -361,7 +361,9 @@ bool Node::checkDuplicate(Handle 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.")); + std::string("\" defined multiple times in parent ") + + type().name + std::string(" \"") + + Utils::join(path(), ".") + std::string("\"")); return false; } return true; -- cgit v1.2.3 From f723034fe9769ac4e482194b96d59be8e6ad0b11 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Sun, 18 Jan 2015 13:45:01 +0100 Subject: Fixed bug in VariantReader::parseGenericToken when a number is at the beginning of the string --- src/core/common/VariantReader.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/core/common/VariantReader.cpp b/src/core/common/VariantReader.cpp index bded618..600fd9b 100644 --- a/src/core/common/VariantReader.cpp +++ b/src/core/common/VariantReader.cpp @@ -583,6 +583,7 @@ std::pair VariantReader::parseGenericToken( return std::make_pair(true, n.doubleValue()); } } + reader.resetPeek(); } // Try to parse an object -- cgit v1.2.3 From db51a874964b038c69f1336a8a659ae40471e26b Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Sun, 18 Jan 2015 13:46:06 +0100 Subject: Implemented storing locations in the ParserStack, improved parsing typesystems --- src/core/model/Typesystem.cpp | 47 +++++++++++----------- src/core/model/Typesystem.hpp | 81 ++++++++++++++++++++++++++++++++++---- src/core/parser/ParserStack.cpp | 15 +++---- src/core/parser/ParserStack.hpp | 41 +++++++++++++------ src/core/parser/Scope.cpp | 66 ++++++++++++++++++------------- src/core/parser/Scope.hpp | 66 ++++++++++++++++++++++--------- src/plugins/xml/XmlParser.cpp | 75 ++++++++++++++++++++++++----------- test/plugins/xml/XmlParserTest.cpp | 9 +++-- 8 files changed, 281 insertions(+), 119 deletions(-) (limited to 'src') diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp index 726de3e..a3d354c 100644 --- a/src/core/model/Typesystem.cpp +++ b/src/core/model/Typesystem.cpp @@ -151,12 +151,7 @@ EnumType::Ordinal EnumType::valueOf(const std::string &name) const bool Attribute::doValidate(Logger &logger) const { - if (!Utils::isIdentifier(getName())) { - logger.error("Attribute name \"" + getName() + - "\" is not a valid identifier."); - return false; - } - return true; + return validateName(logger); } /* Class StructType */ @@ -332,22 +327,8 @@ bool StructType::doBuild(Variant &data, Logger &logger) const bool StructType::doValidate(Logger &logger) const { - // Check whether all attributes are valid and unique - std::unordered_set names; - bool res = true; - for (Handle a : attributes) { - res = a->validate(logger) && res; - const std::string &name = a->getName(); - if (!names.emplace(name).second) { - logger.error( - std::string("Attribute with name \"") + name + - std::string("\" defined multiple times in structure \"") + - Utils::join(path(), ".") + std::string("\"")); - res = false; - } - } - - return res; + return validateName(logger) & + continueValidationCheckDuplicates(attributes, logger); } Rooted StructType::createValidated( @@ -473,6 +454,28 @@ bool ArrayType::doBuild(Variant &data, Logger &logger) const return res; } +/* Class Typesystem */ + +void Typesystem::doResolve(ResolutionState &state) +{ + continueResolveComposita(constants, constants.getIndex(), state); + continueResolveComposita(types, constants.getIndex(), state); +} + +bool Typesystem::doValidate(Logger &logger) const +{ + return validateName(logger) & + continueValidationCheckDuplicates(constants, logger) & + continueValidationCheckDuplicates(types, logger); +} + +Rooted Typesystem::createStructType(const std::string &name) +{ + Rooted structType{new StructType(getManager(), name, this)}; + addType(structType); + return structType; +} + /* Class SystemTypesystem */ SystemTypesystem::SystemTypesystem(Manager &mgr) diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp index 7bc8950..64922f0 100644 --- a/src/core/model/Typesystem.hpp +++ b/src/core/model/Typesystem.hpp @@ -368,10 +368,7 @@ public: */ class Attribute : public Node { private: - /** - * Reference to the actual type of the attribute. - */ - const Owned type; + protected: /** @@ -383,15 +380,26 @@ protected: bool doValidate(Logger &logger) const override; public: + /** + * Reference to the actual type of the attribute. + */ + Owned type; + + /** + * Initial default value passed to the constructor of the Attribute class + * that has not been passed through the "build" method of the type. + */ + Variant rawDefaultValue; + /** * Default value of the attribute. */ - const Variant defaultValue; + Variant defaultValue; /** * Flag indicating whether this attribute is actually optional or not. */ - const bool optional; + bool optional; /** * Constructor of the Attribute class. @@ -429,6 +437,50 @@ public: { } + /** + * Sets a new default value. This makes the Attribute optional. The given + * default value is passed through the "build" function of the current + * type. + * + * @param defaultValue is the new default value. + * @param logger is the logger instance to which errors that occur during + * reinterpretion of the default value. + */ + void setDefaultValue(const Variant &defaultValue, Logger &logger); + + /** + * Returns the default value of the attribute. + * + * @return the default value of the attribute. If no default value has been + * given a null variant is returned (the opposite does not hold). + */ + Variant getDefaultValue() const; + + /** + * Removes any default value from the attribute, making this attribute + * non-optional. + */ + void removeDefaultValue(); + + /** + * Returns true if the attribute is optional (a default value has been + * supplied by the user). + * + * @return true if the attribute is optional, false otherwise. + */ + bool isOptional() const {return optional; } + + /** + * Sets the type of the attribute to the specified value. This will + * reinterpret the default value that has been passed to the attribute (if + * available). + * + * @param type is the new type that should be used for the attribute. + * @param logger is the logger instance to which errors that occur during + * reinterpretion of the default value. + */ + void setType(Handle type, Logger &logger); + /** * Returns a reference to the type descriptor holding the type of the * attribute. @@ -859,6 +911,12 @@ private: */ NodeVector constants; +protected: + + void doResolve(ResolutionState &state) override; + + bool doValidate(Logger &logger) const override; + public: /** * Constructor of the Typesystem class. @@ -867,10 +925,19 @@ public: * @param name is the name of the typesystem. */ Typesystem(Manager &mgr, std::string name) - : Node(mgr, name), types(this), constants(this) + : Node(mgr, std::move(name)), types(this), constants(this) { } + /** + * Creates a new StructType instance with the given name. Adds the new + * StructType as member to the typesystem. + * + * @param name is the name of the structure that should be created. + * @return the new StructType instance. + */ + Rooted createStructType(const std::string &name); + /** * Adds the given type to the to the type list. * diff --git a/src/core/parser/ParserStack.cpp b/src/core/parser/ParserStack.cpp index caf2116..9cf782f 100644 --- a/src/core/parser/ParserStack.cpp +++ b/src/core/parser/ParserStack.cpp @@ -63,11 +63,11 @@ void Handler::child(std::shared_ptr handler) HandlerInstance HandlerDescriptor::create(const ParserContext &ctx, std::string name, State parentState, - bool isChild, - Variant::mapType &args) const + bool isChild, Variant::mapType &args, + const SourceLocation &location) const { Handler *h; - HandlerData data{ctx, name, targetState, parentState, isChild}; + HandlerData data{ctx, name, targetState, parentState, isChild, location}; if (ctor) { h = ctor(data); } else { @@ -115,7 +115,8 @@ std::set ParserStack::expectedCommands(State state) return res; } -void ParserStack::start(std::string name, Variant::mapType &args) +void ParserStack::start(std::string name, Variant::mapType &args, + const SourceLocation &location) { // Fetch the current handler and the current state const HandlerInstance *h = stack.empty() ? nullptr : &stack.top(); @@ -143,11 +144,11 @@ void ParserStack::start(std::string name, Variant::mapType &args) } // Instantiate the handler and call its start function - stack.emplace( - descr->create(ctx, name, curState, isChild, args)); + stack.emplace(descr->create(ctx, name, curState, isChild, args, location)); } -void ParserStack::start(std::string name, const Variant::mapType &args) +void ParserStack::start(std::string name, const Variant::mapType &args, + const SourceLocation &location) { Variant::mapType argsCopy(args); start(name, argsCopy); diff --git a/src/core/parser/ParserStack.hpp b/src/core/parser/ParserStack.hpp index 43d6529..aa196e7 100644 --- a/src/core/parser/ParserStack.hpp +++ b/src/core/parser/ParserStack.hpp @@ -85,6 +85,11 @@ struct HandlerData { */ const bool isChild; + /** + * Current source code location. + */ + const SourceLocation location; + /** * Constructor of the HandlerData class. * @@ -94,14 +99,16 @@ struct HandlerData { * @param parentState is the state of the parent command. * @param isChild specifies whether this handler was called not for the * command that was specified in the state machine but a child command. + * @param location is the location at which the handler is created. */ HandlerData(const ParserContext &ctx, std::string name, State state, - State parentState, bool isChild) + State parentState, bool isChild, const SourceLocation location) : ctx(ctx), name(std::move(name)), state(state), parentState(parentState), - isChild(isChild){}; + isChild(isChild), + location(location){}; }; /** @@ -123,28 +130,29 @@ public: * @param data is a structure containing all data being passed to the * handler. */ - Handler(const HandlerData &handlerData) : handlerData(handlerData) {}; + Handler(const HandlerData &handlerData) : handlerData(handlerData){}; /** * Virtual destructor. */ virtual ~Handler(){}; - - const std::string& name() {return handlerData.name;} + const std::string &name() { return handlerData.name; } - Scope &scope() {return handlerData.ctx.scope;} + Scope &scope() { return handlerData.ctx.scope; } - Registry ®istry() {return handlerData.ctx.registry;} + Registry ®istry() { return handlerData.ctx.registry; } Manager &manager() { return handlerData.ctx.manager; } Logger &logger() { return handlerData.ctx.logger; } - State state() {return handlerData.state; } + State state() { return handlerData.state; } State parentState() { return handlerData.parentState; } + SourceLocation location() { return handlerData.location; } + bool isChild() { return handlerData.isChild; } /** @@ -279,7 +287,8 @@ struct HandlerDescriptor { */ HandlerInstance create(const ParserContext &ctx, std::string name, State parentState, bool isChild, - Variant::mapType &args) const; + Variant::mapType &args, + const SourceLocation &location) const; }; /** @@ -294,9 +303,9 @@ private: ParserContext &ctx; /** - * User specified data that will be passed to all handlers. + * Current location in the source code. */ - void *userData; + SourceLocation location; /** * Map containing all registered command names and the corresponding @@ -369,16 +378,22 @@ public: * * @param name is the name of the command. * @param args is a map from strings to variants (argument name and value). + * @param location is the location in the source file at which the command + * starts. */ - void start(std::string name, Variant::mapType &args); + void start(std::string name, Variant::mapType &args, + const SourceLocation &location = SourceLocation{}); /** * Function that should be called whenever a new command starts. * * @param name is the name of the command. * @param args is a map from strings to variants (argument name and value). + * @param location is the location in the source file at which the command + * starts. */ - void start(std::string name, const Variant::mapType &args); + void start(std::string name, const Variant::mapType &args, + const SourceLocation &location = SourceLocation{}); /** * Function called whenever a command ends. diff --git a/src/core/parser/Scope.cpp b/src/core/parser/Scope.cpp index d76af9c..6e7dceb 100644 --- a/src/core/parser/Scope.cpp +++ b/src/core/parser/Scope.cpp @@ -59,11 +59,11 @@ Rooted ScopeBase::resolve(const std::vector &path, // Log an error if the object is not unique if (res.size() > 1) { - logger.error(std::string("The reference ") + - Utils::join(path, ".") + (" is ambigous!")); + logger.error(std::string("The reference \"") + + Utils::join(path, ".") + ("\" is ambigous!")); logger.note("Referenced objects are:"); for (const ResolutionResult &r : res) { - logger.note(std::string("\t") + Utils::join(r.path(), ".")); + logger.note(Utils::join(r.path(), ".")); } } return res[0].node; @@ -73,10 +73,16 @@ Rooted ScopeBase::resolve(const std::vector &path, /* Class DeferredResolution */ -DeferredResolution::DeferredResolution( - const NodeVector &nodes, const std::vector &path, - const RttiType &type, std::function)> resultCallback) - : scope(nodes), resultCallback(resultCallback), path(path), type(type) +DeferredResolution::DeferredResolution(const NodeVector &nodes, + const std::vector &path, + const RttiType &type, + ResolutionResultCallback resultCallback, + const SourceLocation &location) + : scope(nodes), + resultCallback(resultCallback), + path(path), + type(type), + location(location) { } @@ -84,7 +90,12 @@ bool DeferredResolution::resolve(Logger &logger) { Rooted res = scope.resolve(path, type, logger); if (res != nullptr) { - resultCallback(res); + try { + resultCallback(res, logger); + } + catch (LoggableException ex) { + logger.log(ex); + } return true; } return false; @@ -106,30 +117,32 @@ Rooted Scope::getRoot() const { return nodes.front(); } Rooted Scope::getLeaf() { return nodes.back(); } bool Scope::resolve(const std::vector &path, const RttiType &type, - Logger &logger, - std::function()> imposterCallback, - std::function)> resultCallback) + Logger &logger, ResolutionImposterCallback imposterCallback, + ResolutionResultCallback resultCallback, + const SourceLocation &location) { - Rooted res = ScopeBase::resolve(path, type, logger); - if (res != nullptr) { - resultCallback(res); - return true; + if (!resolve(path, type, logger, resultCallback, location)) { + resultCallback(imposterCallback(), logger); + return false; } - resultCallback(imposterCallback()); - deferred.emplace_back(nodes, path, type, resultCallback); - return false; + return true; } bool Scope::resolve(const std::vector &path, const RttiType &type, - Logger &logger, - std::function)> successCallback) + Logger &logger, ResolutionResultCallback resultCallback, + const SourceLocation &location) { Rooted res = ScopeBase::resolve(path, type, logger); if (res != nullptr) { - successCallback(res); + try { + resultCallback(res, logger); + } + catch (LoggableException ex) { + logger.log(ex, location); + } return true; } - deferred.emplace_back(nodes, path, type, successCallback); + deferred.emplace_back(nodes, path, type, resultCallback, location); return false; } @@ -157,14 +170,13 @@ bool Scope::performDeferredResolution(Logger &logger) // Output an error message if there are still deferred elements left that // could not be resolved - // TODO: Log this at the position at which the resolution was originally - // triggered if (!deferred.empty()) { for (const auto &failed : deferred) { logger.error( - std::string("Could not resolve \"") + - Utils::join(failed.path, ".") + - std::string("\" of internal type " + failed.type.name)); + std::string("Could not resolve a reference to \"") + + Utils::join(failed.path, ".") + + std::string("\" of type " + failed.type.name), + failed.location); } } diff --git a/src/core/parser/Scope.hpp b/src/core/parser/Scope.hpp index 2713c41..c99aa65 100644 --- a/src/core/parser/Scope.hpp +++ b/src/core/parser/Scope.hpp @@ -42,6 +42,18 @@ namespace parser { // Forward declaration class Scope; +/** + * Callback function type used for creating a dummy object while no correct + * object is available for resolution. + */ +using ResolutionImposterCallback = std::function()>; + +/** + * Callback function type called whenever the result of a resolution is + * available. + */ +using ResolutionResultCallback = std::function, Logger &logger)>; + /** * The GuardedScope class takes care of pushing a Node instance into the * name resolution stack of a Scope instance and poping this node once the @@ -148,7 +160,7 @@ private: /** * Callback function to be called when an element is successfully resolved. */ - std::function)> resultCallback; + ResolutionResultCallback resultCallback; public: /** @@ -161,6 +173,11 @@ public: */ const RttiType &type; + /** + * Position at which the resolution was triggered. + */ + const SourceLocation location; + /** * Constructor of the DeferredResolutionScope class. Copies the given * arguments. @@ -172,11 +189,13 @@ public: * @param type is the RttiType of the element that should be queried. * @param resultCallback is the callback function that should be called if * the desired element has indeed been found. + * @param location is the location at which the resolution was triggered. */ DeferredResolution(const NodeVector &nodes, const std::vector &path, const RttiType &type, - std::function)> resultCallback); + ResolutionResultCallback resultCallback, + const SourceLocation &location = SourceLocation{}); /** * Performs the actual deferred resolution and calls the resultCallback @@ -267,34 +286,39 @@ public: * 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. * @return true if the resolution was immediately successful. This does not * mean, that the resolved object does not exist, as it may be resolved * later. */ bool resolve(const std::vector &path, const RttiType &type, - Logger &logger, std::function()> imposterCallback, - std::function)> resultCallback); + Logger &logger, ResolutionImposterCallback imposterCallback, + ResolutionResultCallback resultCallback, + const SourceLocation &location = SourceLocation{}); /** * 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. - * The "successCallback" is called when the resolution was successful, which + * The "resultCallback" is called when the resolution was successful, which * may be 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. * @param logger is the logger instance into which resolution problems * should be logged. - * @param successCallback is the callback function to which the result of + * @param resultCallback is the callback function to which the result of * the resolution process is passed. This function is called once the * resolution was successful. + * @param location is the location in the current source file in which 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 * later. */ bool resolve(const std::vector &path, const RttiType &type, - Logger &logger, - std::function)> successCallback); + Logger &logger, ResolutionResultCallback resultCallback, + const SourceLocation &location = SourceLocation{}); /** * Tries to resolve a node for the given type and path for all nodes @@ -319,6 +343,8 @@ public: * 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. * @return true if the resolution was immediately successful. This does not * mean, that the resolved object does not exist, as it may be resolved * later. @@ -326,41 +352,45 @@ public: template bool resolve(const std::vector &path, Logger &logger, std::function()> imposterCallback, - std::function)> successCallback) + std::function, Logger&)> resultCallback, + const SourceLocation &location = SourceLocation{}) { return resolve( path, typeOf(), logger, [imposterCallback]() -> Rooted { return imposterCallback(); }, - [successCallback](Handle node) { - successCallback(node.cast()); - }); + [resultCallback](Handle node, Logger &logger) { + resultCallback(node.cast(), logger); + }, location); } /** * 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. - * The "successCallback" is called when the resolution was successful, which + * The "resultCallback" is called when the resolution was successful, which * may be at a later point in time. * * @tparam is the type of the node that should be resolved. * @param path is the path for which a node should be resolved. * @param logger is the logger instance into which resolution problems * should be logged. - * @param successCallback is the callback function to which the result of + * @param resultCallback is the callback function to which the result of * the resolution process is passed. This function is called once the * resolution was successful. + * @param location is the location in the current source file in which 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 * later. */ template bool resolve(const std::vector &path, Logger &logger, - std::function)> resultCallback) + std::function, Logger&)> resultCallback, + const SourceLocation &location = SourceLocation{}) { return resolve(path, typeOf(), logger, - [resultCallback](Handle node) { - resultCallback(node.cast()); - }); + [resultCallback](Handle node, Logger &logger) { + resultCallback(node.cast(), logger); + }, location); } /** diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index ced61ee..cd220a9 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -32,6 +33,8 @@ namespace ousia { namespace parser { namespace xml { +using namespace ousia::model; + /* Document structure */ static const State STATE_DOCUMENT = 0; static const State STATE_HEAD = 1; @@ -63,6 +66,8 @@ public: void end() override { scope().performDeferredResolution(logger()); + // TODO: Automatically call validate in "pop"? + scope().getLeaf()->validate(logger()); scope().pop(); } @@ -76,22 +81,36 @@ class StructHandler : public Handler { public: using Handler::Handler; - std::string name; - std::string parent; - - NodeVector attributes; - void start(Variant::mapType &args) override { - this->name = args["name"].asString(); - this->parent = args["parent"].asString(); - } + // Fetch the arguments used for creating this type + const std::string &name = args["name"].asString(); + const std::string &parent = args["parent"].asString(); + + // Fetch the current typesystem and create the struct node + Rooted typesystem = scope().getLeaf().cast(); + Rooted structType = typesystem->createStructType(name); + + // Try to resolve the parent type and set it as parent structure + if (!parent.empty()) { + scope().resolve(Utils::split(parent, '.'), logger(), + [structType](Handle parent, + Logger &logger) mutable { + structType->setParentStructure( + parent, logger); + }, + location()); + } - void end() override { - + // Descend into the struct type + scope().push(structType); } - void child(std::shared_ptr handler) {} + void end() override + { + // Descend from the struct type + scope().pop(); + } static Handler *create(const HandlerData &handlerData) { @@ -103,15 +122,24 @@ class StructFieldHandler : public Handler { public: using Handler::Handler; - Rooted attribute; - void start(Variant::mapType &args) override { - /* this->name = args["name"].asString(); - this->type = args["parent"].asString();*/ + // Read the argument values + /* const std::string &name = args["name"].asString(); + const std::string &type = args["parent"].asString(); + const Variant &defaultValue = args["default"]; + const bool optional = !(defaultValue.isObject() && + defaultValue.asObject() == nullptr);*/ + + // Try to resolve the } void end() override {} + + static Handler *create(const HandlerData &handlerData) + { + return new StructFieldHandler{handlerData}; + } }; static const std::multimap XML_HANDLERS{ @@ -142,7 +170,7 @@ static const std::multimap XML_HANDLERS{ {Argument::String("name"), Argument::String("parent", "")}}}, {"field", {{{STATE_STRUCT}}, - nullptr, + StructFieldHandler::create, STATE_FIELD, false, {Argument::String("name"), Argument::String("type"), @@ -198,24 +226,25 @@ public: /* Adapter Expat -> ParserStack */ -static void syncLoggerPosition(XML_Parser p) +static SourceLocation syncLoggerPosition(XML_Parser p) { // Fetch the current location in the XML file int line = XML_GetCurrentLineNumber(p); int column = XML_GetCurrentColumnNumber(p); size_t offs = XML_GetCurrentByteIndex(p); + SourceLocation loc{line, column, offs}; // Update the default location of the current logger instance ParserStack *stack = static_cast(XML_GetUserData(p)); - stack->getContext().logger.setDefaultLocation( - SourceLocation{line, column, offs}); + stack->getContext().logger.setDefaultLocation(loc); + return loc; } static void xmlStartElementHandler(void *p, const XML_Char *name, const XML_Char **attrs) { XML_Parser parser = static_cast(p); - syncLoggerPosition(parser); + SourceLocation loc = syncLoggerPosition(parser); ParserStack *stack = static_cast(XML_GetUserData(parser)); @@ -223,9 +252,11 @@ static void xmlStartElementHandler(void *p, const XML_Char *name, const XML_Char **attr = attrs; while (*attr) { const std::string key{*(attr++)}; - args.emplace(std::make_pair(key, Variant{*(attr++)})); + std::pair value = VariantReader::parseGenericString( + *(attr++), stack->getContext().logger); + args.emplace(std::make_pair(key, value.second)); } - stack->start(std::string(name), args); + stack->start(std::string(name), args, loc); } static void xmlEndElementHandler(void *p, const XML_Char *name) diff --git a/test/plugins/xml/XmlParserTest.cpp b/test/plugins/xml/XmlParserTest.cpp index ce53eb3..2046940 100644 --- a/test/plugins/xml/XmlParserTest.cpp +++ b/test/plugins/xml/XmlParserTest.cpp @@ -53,14 +53,17 @@ const char *TEST_DATA = " \n" " \n" " \n" - " \n" - " \n" - " \n" " \n" " \n" " \n" " \n" " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" " \n" " \n" " \n" -- cgit v1.2.3