diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-18 13:46:06 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-18 13:46:06 +0100 |
commit | db51a874964b038c69f1336a8a659ae40471e26b (patch) | |
tree | b0eae3727241be330321c5bd1de4d1695ff6578d /src | |
parent | 5c832e5cc0d2ff948816bd4a492189435efa8578 (diff) |
Implemented storing locations in the ParserStack, improved parsing typesystems
Diffstat (limited to 'src')
-rw-r--r-- | src/core/model/Typesystem.cpp | 47 | ||||
-rw-r--r-- | src/core/model/Typesystem.hpp | 81 | ||||
-rw-r--r-- | src/core/parser/ParserStack.cpp | 15 | ||||
-rw-r--r-- | src/core/parser/ParserStack.hpp | 41 | ||||
-rw-r--r-- | src/core/parser/Scope.cpp | 66 | ||||
-rw-r--r-- | src/core/parser/Scope.hpp | 66 | ||||
-rw-r--r-- | src/plugins/xml/XmlParser.cpp | 75 |
7 files changed, 275 insertions, 116 deletions
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<std::string> names; - bool res = true; - for (Handle<Attribute> 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> 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<StructType> Typesystem::createStructType(const std::string &name) +{ + Rooted<StructType> 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> type; + protected: /** @@ -384,14 +381,25 @@ protected: public: /** + * Reference to the actual type of the attribute. + */ + Owned<Type> 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. @@ -430,6 +438,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> type, Logger &logger); + + /** * Returns a reference to the type descriptor holding the type of the * attribute. * @@ -859,6 +911,12 @@ private: */ NodeVector<Constant> constants; +protected: + + void doResolve(ResolutionState &state) override; + + bool doValidate(Logger &logger) const override; + public: /** * Constructor of the Typesystem class. @@ -867,11 +925,20 @@ 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<StructType> createStructType(const std::string &name); + + /** * Adds the given type to the to the type list. * * @param type is the Type that should be stored in this Typesystem 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> 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<std::string> 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 @@ -86,6 +86,11 @@ struct HandlerData { const bool isChild; /** + * Current source code location. + */ + const SourceLocation location; + + /** * Constructor of the HandlerData class. * * @param ctx is the parser context the handler should be executed in. @@ -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<Node> ScopeBase::resolve(const std::vector<std::string> &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<Node> ScopeBase::resolve(const std::vector<std::string> &path, /* Class DeferredResolution */ -DeferredResolution::DeferredResolution( - const NodeVector<Node> &nodes, const std::vector<std::string> &path, - const RttiType &type, std::function<void(Handle<Node>)> resultCallback) - : scope(nodes), resultCallback(resultCallback), path(path), type(type) +DeferredResolution::DeferredResolution(const NodeVector<Node> &nodes, + const std::vector<std::string> &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<Node> 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<Node> Scope::getRoot() const { return nodes.front(); } Rooted<Node> Scope::getLeaf() { return nodes.back(); } bool Scope::resolve(const std::vector<std::string> &path, const RttiType &type, - Logger &logger, - std::function<Rooted<Node>()> imposterCallback, - std::function<void(Handle<Node>)> resultCallback) + Logger &logger, ResolutionImposterCallback imposterCallback, + ResolutionResultCallback resultCallback, + const SourceLocation &location) { - Rooted<Node> 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<std::string> &path, const RttiType &type, - Logger &logger, - std::function<void(Handle<Node>)> successCallback) + Logger &logger, ResolutionResultCallback resultCallback, + const SourceLocation &location) { Rooted<Node> 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 @@ -43,6 +43,18 @@ namespace parser { class Scope; /** + * Callback function type used for creating a dummy object while no correct + * object is available for resolution. + */ +using ResolutionImposterCallback = std::function<Rooted<Node>()>; + +/** + * Callback function type called whenever the result of a resolution is + * available. + */ +using ResolutionResultCallback = std::function<void(Handle<Node>, 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 * ScopedScope instance is deletes. This way you cannot forget to pop a Node @@ -148,7 +160,7 @@ private: /** * Callback function to be called when an element is successfully resolved. */ - std::function<void(Handle<Node>)> resultCallback; + ResolutionResultCallback resultCallback; public: /** @@ -162,6 +174,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<Node> &nodes, const std::vector<std::string> &path, const RttiType &type, - std::function<void(Handle<Node>)> 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<std::string> &path, const RttiType &type, - Logger &logger, std::function<Rooted<Node>()> imposterCallback, - std::function<void(Handle<Node>)> 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<std::string> &path, const RttiType &type, - Logger &logger, - std::function<void(Handle<Node>)> 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 <class T> bool resolve(const std::vector<std::string> &path, Logger &logger, std::function<Rooted<T>()> imposterCallback, - std::function<void(Handle<T>)> successCallback) + std::function<void(Handle<T>, Logger&)> resultCallback, + const SourceLocation &location = SourceLocation{}) { return resolve( path, typeOf<T>(), logger, [imposterCallback]() -> Rooted<Node> { return imposterCallback(); }, - [successCallback](Handle<Node> node) { - successCallback(node.cast<T>()); - }); + [resultCallback](Handle<Node> node, Logger &logger) { + resultCallback(node.cast<T>(), 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 <class T> bool resolve(const std::vector<std::string> &path, Logger &logger, - std::function<void(Handle<T>)> resultCallback) + std::function<void(Handle<T>, Logger&)> resultCallback, + const SourceLocation &location = SourceLocation{}) { return resolve(path, typeOf<T>(), logger, - [resultCallback](Handle<Node> node) { - resultCallback(node.cast<T>()); - }); + [resultCallback](Handle<Node> node, Logger &logger) { + resultCallback(node.cast<T>(), 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 <core/common/CharReader.hpp> #include <core/common/Utils.hpp> +#include <core/common/VariantReader.hpp> #include <core/parser/ParserStack.hpp> #include <core/model/Typesystem.hpp> @@ -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<model::Attribute> 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> typesystem = scope().getLeaf().cast<Typesystem>(); + Rooted<StructType> structType = typesystem->createStructType(name); + + // Try to resolve the parent type and set it as parent structure + if (!parent.empty()) { + scope().resolve<StructType>(Utils::split(parent, '.'), logger(), + [structType](Handle<StructType> 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> 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<model::Attribute> 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<std::string, HandlerDescriptor> XML_HANDLERS{ @@ -142,7 +170,7 @@ static const std::multimap<std::string, HandlerDescriptor> 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<ParserStack *>(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<XML_Parser>(p); - syncLoggerPosition(parser); + SourceLocation loc = syncLoggerPosition(parser); ParserStack *stack = static_cast<ParserStack *>(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<bool, Variant> 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) |