diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/common/Logger.hpp | 10 | ||||
| -rw-r--r-- | src/core/common/VariantReader.cpp | 17 | ||||
| -rw-r--r-- | src/core/common/VariantReader.hpp | 20 | ||||
| -rw-r--r-- | src/core/model/Document.cpp | 2 | ||||
| -rw-r--r-- | src/core/model/Document.hpp | 5 | ||||
| -rw-r--r-- | src/core/model/Domain.cpp | 4 | ||||
| -rw-r--r-- | src/core/model/Domain.hpp | 4 | ||||
| -rw-r--r-- | src/core/model/Node.cpp | 30 | ||||
| -rw-r--r-- | src/core/model/Node.hpp | 76 | ||||
| -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 | 
16 files changed, 428 insertions, 131 deletions
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 @@ -243,6 +243,16 @@ public:  	}  	/** +	 * 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.  	 * diff --git a/src/core/common/VariantReader.cpp b/src/core/common/VariantReader.cpp index faad40c..600fd9b 100644 --- a/src/core/common/VariantReader.cpp +++ b/src/core/common/VariantReader.cpp @@ -583,6 +583,7 @@ std::pair<bool, Variant> VariantReader::parseGenericToken(  				return std::make_pair(true, n.doubleValue());  			}  		} +		reader.resetPeek();  	}  	// Try to parse an object @@ -618,8 +619,22 @@ std::pair<bool, Variant> 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<bool, Variant> VariantReader::parseGenericString( +    const std::string &str, Logger &logger) +{ +	CharReader reader{str}; +	LoggerFork loggerFork = logger.fork(); +	std::pair<bool, Variant> res = +	    parseGenericToken(reader, loggerFork, std::unordered_set<char>{}, 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<char> &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<bool, Variant> parseGenericString( +	    const std::string &str, Logger &logger);  };  } diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index 0b0a42a..5f0ad4c 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -297,7 +297,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 18ec3d5..9410d17 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -512,6 +512,7 @@ class AnnotationEntity : public Node, public DocumentEntity {  private:  	Owned<Anchor> start;  	Owned<Anchor> end; +  protected:  	bool doValidate(Logger &logger) const override; @@ -567,7 +568,7 @@ private:  	NodeVector<AnnotationEntity> annotations;  	NodeVector<Domain> domains; -	void continueResolve(ResolutionState &state) override; +	void doResolve(ResolutionState &state) override;  protected:  	bool doValidate(Logger &logger) const override; @@ -625,7 +626,7 @@ public:  	{  		domains.insert(domains.end(), d.begin(), d.end());  	} -	 +  	/**  	 * Returns true if and only if the given StructureNode is part of this  	 * document, meaning that there is a path of parent references in the diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index 17f3e02..9a0ed0d 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -70,7 +70,7 @@ FieldDescriptor::FieldDescriptor(Manager &mgr, Handle<Descriptor> parent,  /* Class Descriptor */ -void Descriptor::continueResolve(ResolutionState &state) +void Descriptor::doResolve(ResolutionState &state)  {  	if (attributesDescriptor != nullptr) {  		const NodeVector<Attribute> &attributes = @@ -254,7 +254,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 5cc7874..d1ba44f 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -390,7 +390,7 @@ private:  	                  std::vector<Rooted<Node>> &path) const;  protected: -	void continueResolve(ResolutionState &state) override; +	void doResolve(ResolutionState &state) override;  	/**  	 * Adds a FieldDescriptor and checks for name uniqueness. @@ -695,7 +695,7 @@ private:  	NodeVector<Typesystem> typesystems;  protected: -	void continueResolve(ResolutionState &state) override; +	void doResolve(ResolutionState &state) override;  	void addStructuredClass(Handle<StructuredClass> s);  	void addAnnotationClass(Handle<AnnotationClass> a); diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index a935715..bd023e1 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -23,6 +23,7 @@  #include <core/common/Logger.hpp>  #include <core/common/Rtti.hpp>  #include <core/common/TypedRttiBuilder.hpp> +#include <core/common/Utils.hpp>  #include "Node.hpp" @@ -272,14 +273,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  } @@ -353,8 +354,33 @@ std::vector<ResolutionResult> Node::resolve(const std::string &name,  	return resolve(std::vector<std::string>{name}, type);  } +bool Node::checkDuplicate(Handle<Node> elem, +                          std::unordered_set<std::string> &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 in parent ") + +		             type().name + std::string(" \"") + +		             Utils::join(path(), ".") + std::string("\"")); +		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 0523c69..0168a3e 100644 --- a/src/core/model/Node.hpp +++ b/src/core/model/Node.hpp @@ -31,6 +31,7 @@  #include <cstdint>  #include <map>  #include <set> +#include <unordered_set>  #include <string>  #include <vector> @@ -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> node, +	                    std::unordered_set<std::string> &names, +	                    Logger &logger) const; +  protected:  	/**  	 * Function which should be overwritten by derived classes in order to @@ -207,7 +221,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 @@ -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 <class T> +	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 <class T> +	bool continueValidationCheckDuplicates(const T &list, Logger &logger) const +	{ +		bool res = true; +		std::unordered_set<std::string> 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<T, std::vector<Handle<T>>,                                  ListAccessor<Handle<T>>, Listener> {  public: -	using ManagedGenericList<T, std::vector<Handle<T>>, ListAccessor<Handle<T>>, -	                         Listener>::ManagedGenericList; +	using Base = ManagedGenericList<T, std::vector<Handle<T>>, ListAccessor<Handle<T>>, +	                         Listener>; +	using Base::Base;  	/**  	 * Returns the reference to the internal index. @@ -490,9 +555,10 @@ class NodeMap      : public ManagedGenericMap<K, T, std::map<K, Handle<T>>,                                 MapAccessor<std::pair<K, Handle<T>>>, Listener> {  public: -	using ManagedGenericMap<K, T, std::map<K, Handle<T>>, +	using Base = ManagedGenericMap<K, T, std::map<K, Handle<T>>,  	                        MapAccessor<std::pair<K, Handle<T>>>, -	                        Listener>::ManagedGenericMap; +	                        Listener>; +	using Base::Base;  	/**  	 * Returns the reference to the internal index. 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)  | 
