summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/common/Logger.hpp10
-rw-r--r--src/core/common/VariantReader.cpp17
-rw-r--r--src/core/common/VariantReader.hpp20
-rw-r--r--src/core/model/Document.cpp2
-rw-r--r--src/core/model/Document.hpp5
-rw-r--r--src/core/model/Domain.cpp4
-rw-r--r--src/core/model/Domain.hpp4
-rw-r--r--src/core/model/Node.cpp30
-rw-r--r--src/core/model/Node.hpp76
-rw-r--r--src/core/model/Typesystem.cpp47
-rw-r--r--src/core/model/Typesystem.hpp81
-rw-r--r--src/core/parser/ParserStack.cpp15
-rw-r--r--src/core/parser/ParserStack.hpp41
-rw-r--r--src/core/parser/Scope.cpp66
-rw-r--r--src/core/parser/Scope.hpp66
15 files changed, 375 insertions, 109 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 bf8bfde..cf6ded3 100644
--- a/src/core/model/Document.cpp
+++ b/src/core/model/Document.cpp
@@ -333,7 +333,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 4b95079..b4ee429 100644
--- a/src/core/model/Document.hpp
+++ b/src/core/model/Document.hpp
@@ -501,6 +501,7 @@ class AnnotationEntity : public Node, public DocumentEntity {
private:
Owned<Anchor> start;
Owned<Anchor> end;
+
protected:
bool doValidate(Logger &logger) const override;
@@ -556,7 +557,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;
@@ -614,7 +615,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 &registry() {return handlerData.ctx.registry;}
+ Registry &registry() { 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);
}
/**