diff options
author | Benjamin Paassen <bpaassen@techfak.uni-bielefeld.de> | 2015-01-30 10:41:39 +0100 |
---|---|---|
committer | Benjamin Paassen <bpaassen@techfak.uni-bielefeld.de> | 2015-01-30 10:41:39 +0100 |
commit | 33628e1f35ab1eb593391e87faaf9115b203c9b3 (patch) | |
tree | 2ea052b03f74cad35129c79d3cd5c09c0b969337 /src/core/parser | |
parent | d6d7d7f2858d33bb3bcd950aa866b9a09047082f (diff) | |
parent | c4da68ba28e742810d05d35f0a26ef1d9b8c5b6c (diff) |
Merge branch 'master' of somweyr.de:ousia
Diffstat (limited to 'src/core/parser')
-rw-r--r-- | src/core/parser/ParserScope.cpp | 192 | ||||
-rw-r--r-- | src/core/parser/ParserScope.hpp | 196 |
2 files changed, 319 insertions, 69 deletions
diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp index 0de0dbf..c7a9f2a 100644 --- a/src/core/parser/ParserScope.cpp +++ b/src/core/parser/ParserScope.cpp @@ -18,6 +18,7 @@ #include <core/common/Exceptions.hpp> #include <core/common/Utils.hpp> +#include <core/model/Typesystem.hpp> #include "ParserScope.hpp" @@ -31,12 +32,13 @@ ParserScopeBase::ParserScopeBase(const NodeVector<Node> &nodes) : nodes(nodes) { } -Rooted<Node> ParserScopeBase::resolve(const std::vector<std::string> &path, - const Rtti &type, Logger &logger) +Rooted<Node> ParserScopeBase::resolve(const Rtti &type, + const std::vector<std::string> &path, + Logger &logger) { // Go up the stack and try to resolve the for (auto it = nodes.rbegin(); it != nodes.rend(); it++) { - std::vector<ResolutionResult> res = (*it)->resolve(path, type); + std::vector<ResolutionResult> res = (*it)->resolve(type, path); // Abort if the object could not be resolved if (res.empty()) { @@ -79,14 +81,14 @@ bool DeferredResolution::resolve( // Fork the logger to prevent error messages from being shown if we actively // ignore the resolution result LoggerFork loggerFork = logger.fork(); - Rooted<Node> res = scope.resolve(path, type, loggerFork); + Rooted<Node> res = scope.resolve(type, path, loggerFork); if (res != nullptr) { if (!ignore.count(res.get())) { loggerFork.commit(); try { // Push the location onto the logger default location stack GuardedLogger loggerGuard(logger, *owner); - resultCallback(res, logger); + resultCallback(res, owner, logger); } catch (LoggableException ex) { logger.log(ex); @@ -99,6 +101,16 @@ bool DeferredResolution::resolve( return false; } +void DeferredResolution::fail(Logger &logger) +{ + try { + resultCallback(nullptr, owner, logger); + } + catch (LoggableException ex) { + logger.log(ex); + } +} + /* Class ParserScope */ ParserScope::ParserScope(const NodeVector<Node> &nodes, @@ -227,29 +239,29 @@ bool ParserScope::getFlag(ParserFlag flag) return false; } -bool ParserScope::resolve(const std::vector<std::string> &path, - const Rtti &type, Logger &logger, +bool ParserScope::resolve(const Rtti &type, + const std::vector<std::string> &path, + Handle<Node> owner, Logger &logger, ResolutionImposterCallback imposterCallback, - ResolutionResultCallback resultCallback, - Handle<Node> owner) + ResolutionResultCallback resultCallback) { - if (!resolve(path, type, logger, resultCallback, owner)) { - resultCallback(imposterCallback(), logger); + if (!resolve(type, path, owner, logger, resultCallback)) { + resultCallback(imposterCallback(), owner, logger); return false; } return true; } -bool ParserScope::resolve(const std::vector<std::string> &path, - const Rtti &type, Logger &logger, - ResolutionResultCallback resultCallback, - Handle<Node> owner) +bool ParserScope::resolve(const Rtti &type, + const std::vector<std::string> &path, + Handle<Node> owner, Logger &logger, + ResolutionResultCallback resultCallback) { // Try to directly resolve the node - Rooted<Node> res = ParserScopeBase::resolve(path, type, logger); + Rooted<Node> res = ParserScopeBase::resolve(type, path, logger); if (res != nullptr && !awaitingResolution.count(res.get())) { try { - resultCallback(res, logger); + resultCallback(res, owner, logger); } catch (LoggableException ex) { logger.log(ex, *owner); @@ -266,6 +278,149 @@ bool ParserScope::resolve(const std::vector<std::string> &path, return false; } +bool ParserScope::resolveType(const std::vector<std::string> &path, + Handle<Node> owner, Logger &logger, + ResolutionResultCallback resultCallback) +{ + // Check whether the given path denotes an array, if yes recursively resolve + // the inner type and wrap it in an array type (this allows multi + // dimensional arrays). + if (!path.empty()) { + const std::string &last = path.back(); + if (last.size() >= 2 && last.substr(last.size() - 2, 2) == "[]") { + // Type ends with "[]", remove this from the last element in the + // list + std::vector<std::string> p = path; + p.back() = p.back().substr(0, last.size() - 2); + + // Resolve the rest of the type + return resolveType(p, owner, logger, + [resultCallback](Handle<Node> resolved, + Handle<Node> owner, + Logger &logger) { + if (resolved != nullptr) { + Rooted<ArrayType> arr{new ArrayType{resolved.cast<Type>()}}; + resultCallback(arr, owner, logger); + } else { + resultCallback(nullptr, owner, logger); + } + }); + } + } + + // Requested type is not an array, call the usual resolve function + return resolve(RttiTypes::Type, path, owner, logger, resultCallback); +} + +bool ParserScope::resolveType(const std::string &name, Handle<Node> owner, + Logger &logger, + ResolutionResultCallback resultCallback) +{ + return resolveType(Utils::split(name, '.'), owner, logger, resultCallback); +} + +bool ParserScope::resolveTypeWithValue(const std::vector<std::string> &path, + Handle<Node> owner, Variant &value, + Logger &logger, + ResolutionResultCallback resultCallback) +{ + // Fork the parser scope -- constants need to be resolved in the same + // context as this resolve call + std::shared_ptr<ParserScope> scope = std::make_shared<ParserScope>(fork()); + + return resolveType( + path, owner, logger, + [=](Handle<Node> resolved, Handle<Node> owner, Logger &logger) mutable { + // Abort if the lookup failed + if (resolved == nullptr) { + resultCallback(resolved, owner, logger); + return; + } + + // Fetch the type reference and the manager reference + Rooted<Type> type = resolved.cast<Type>(); + Manager *mgr = &type->getManager(); + + // The type has been resolved, try to resolve magic values as + // constants and postpone calling the callback function until + // all magic values have been resolved + std::shared_ptr<bool> isAsync = std::make_shared<bool>(false); + std::shared_ptr<int> magicCount = std::make_shared<int>(0); + type->build(value, logger, [=](Variant &magicValue, bool isValid, + ManagedUid innerTypeUid) mutable { + // Fetch the inner type + Rooted<Type> innerType = + dynamic_cast<Type *>(mgr->getManaged(innerTypeUid)); + if (innerType == nullptr) { + return; + } + + // Fetch a pointer at the variant + Variant *magicValuePtr = &magicValue; + + // Increment the number of encountered magic values + (*magicCount)++; + + // Try to resolve the value as constant + std::string constantName = magicValue.asMagic(); + scope->resolve<Constant>(constantName, owner, logger, + [=](Handle<Node> resolved, + Handle<Node> owner, + Logger &logger) mutable { + if (resolved != nullptr) { + // Make sure the constant is of the correct inner type + Rooted<Constant> constant = resolved.cast<Constant>(); + Rooted<Type> constantType = constant->getType(); + if (constantType != innerType) { + logger.error( + std::string("Expected value of type \"") + + innerType->getName() + + std::string("\" but found constant \"") + + constant->getName() + + std::string(" of type \"") + + constantType->getName() + "\" instead.", + *owner); + } else if (!isValid) { + logger.error("Identifier \"" + constantName + + "\" is not a valid " + + innerType->getName(), + *owner); + } + + // Nevertheless, no matter what happened, set the value + // of the original magic variant to the given constant + *magicValuePtr = constant->getValue(); + } + + // Decrement the number of magic values, call the callback + // function if all magic values have been resolved + (*magicCount)--; + if ((*magicCount) == 0 && (*isAsync)) { + resultCallback(resolved, owner, logger); + } + }); + }); + + // Now we are asynchronous + (*isAsync) = true; + + // Directly call the callback function if there were no magic values + // involved + if ((*magicCount) == 0) { + resultCallback(resolved, owner, logger); + } + }); +} + +bool ParserScope::resolveTypeWithValue(const std::string &name, + Handle<Node> owner, Variant &value, + Logger &logger, + ResolutionResultCallback resultCallback) +{ + return resolveTypeWithValue(Utils::split(name, '.'), owner, value, logger, + resultCallback); +} + bool ParserScope::performDeferredResolution(Logger &logger) { // Repeat the resolution process as long as something has changed in the @@ -304,7 +459,8 @@ bool ParserScope::performDeferredResolution(Logger &logger) // Output error messages for all elements for which resolution did not // succeed. - for (const auto &failed : deferred) { + for (auto &failed : deferred) { + failed.fail(logger); logger.error(std::string("Could not resolve ") + failed.type.name + std::string(" \"") + Utils::join(failed.path, ".") + std::string("\""), diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp index 2378967..c49acd3 100644 --- a/src/core/parser/ParserScope.hpp +++ b/src/core/parser/ParserScope.hpp @@ -33,8 +33,7 @@ * @file ParserScope.hpp * * Contains the ParserScope class used for resolving references based on the - *current - * parser state. + * current parser state. * * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) */ @@ -45,6 +44,7 @@ namespace ousia { class CharReader; class Logger; class ParserScope; +class Variant; /** * Callback function type used for creating a dummy object while no correct @@ -55,9 +55,13 @@ using ResolutionImposterCallback = std::function<Rooted<Node>()>; /** * Callback function type called whenever the result of a resolution is * available. + * + * @param resolved is the new, resolved node. + * @param owner is the node that was passed as "owner". + * @param logger is the logger to which errors should be logged. */ -using ResolutionResultCallback = - std::function<void(Handle<Node>, Logger &logger)>; +using ResolutionResultCallback = std::function<void(Handle<Node> resolved, + Handle<Node> owner, Logger &logger)>; /** * Base class for the @@ -90,14 +94,14 @@ public: * Tries to resolve a node for the given type and path for all nodes that * are currently in the stack, starting with the topmost node on the stack. * - * @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 path is the path for which a node should be resolved. * @param logger is the logger instance into which resolution problems * should be logged. * @return a reference at a resolved node or nullptr if no node could be * found. */ - Rooted<Node> resolve(const std::vector<std::string> &path, const Rtti &type, + Rooted<Node> resolve(const Rtti &type, const std::vector<std::string> &path, Logger &logger); }; @@ -167,6 +171,15 @@ public: */ bool resolve(const std::unordered_multiset<const Node *> &ignore, Logger &logger); + + /** + * Inform the callee about the failure by calling the callback function with + * "nullptr" as resolved element. + * + * @param logger is the logger instance to which error messages should be + * logged. + */ + void fail(Logger &logger); }; /** @@ -396,8 +409,9 @@ public: * temporary) and another time if the resolution turned out to be * successful at a later point in time. * - * @param path is the path for which a node should be resolved. * @param type is the type of the node that should be resolved. + * @param path is the path for which a node should be resolved. + * @param owner is the node for which the resolution takes place. * @param logger is the logger instance into which resolution problems * should be logged. * @param imposterCallback is the callback function that is called if @@ -410,15 +424,14 @@ public: * the resolved object directly when this function is called. If the * resolution was not successful the first time, it may be called another * time later in the context of the "performDeferredResolution" function. - * @param owner is the node for which the resolution takes place. * @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 Rtti &type, - Logger &logger, ResolutionImposterCallback imposterCallback, - ResolutionResultCallback resultCallback, - Handle<Node> owner = nullptr); + bool resolve(const Rtti &type, const std::vector<std::string> &path, + Handle<Node> owner, Logger &logger, + ResolutionImposterCallback imposterCallback, + ResolutionResultCallback resultCallback); /** * Tries to resolve a node for the given type and path for all nodes @@ -426,21 +439,21 @@ public: * 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 path is the path for which a node should be resolved. + * @param owner is the node for which the resolution takes place. * @param logger is the logger instance into which resolution problems * should be logged. * @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 owner is the node for which the resolution takes place. * @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 Rtti &type, - Logger &logger, ResolutionResultCallback resultCallback, - Handle<Node> owner = nullptr); + bool resolve(const Rtti &type, const std::vector<std::string> &path, + Handle<Node> owner, Logger &logger, + ResolutionResultCallback resultCallback); /** * Tries to resolve a node for the given type and path for all nodes @@ -453,6 +466,7 @@ public: * * @tparam T is the type of the node that should be resolved. * @param path is the path for which a node should be resolved. + * @param owner is the node for which the resolution takes place. * @param logger is the logger instance into which resolution problems * should be logged. * @param imposterCallback is the callback function that is called if @@ -465,24 +479,17 @@ 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 owner is the node for which the resolution takes place. * @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<Rooted<T>()> imposterCallback, - std::function<void(Handle<T>, Logger &)> resultCallback, - Handle<Node> owner = nullptr) + bool resolve(const std::vector<std::string> &path, Handle<Node> owner, + Logger &logger, ResolutionImposterCallback imposterCallback, + ResolutionResultCallback resultCallback) { - return resolve( - path, typeOf<T>(), logger, - [imposterCallback]() -> Rooted<Node> { return imposterCallback(); }, - [resultCallback](Handle<Node> node, Logger &logger) { - resultCallback(node.cast<T>(), logger); - }, - owner); + return resolve(typeOf<T>(), path, owner, logger, imposterCallback, + resultCallback); } /** @@ -493,26 +500,21 @@ public: * * @tparam T is the type of the node that should be resolved. * @param path is the path for which a node should be resolved. + * @param owner is the node for which the resolution takes place. * @param logger is the logger instance into which resolution problems * should be logged. * @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 owner is the node for which the resolution takes place. * @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>, Logger &)> resultCallback, - Handle<Node> owner = nullptr) + bool resolve(const std::vector<std::string> &path, Handle<Node> owner, + Logger &logger, ResolutionResultCallback resultCallback) { - return resolve(path, typeOf<T>(), logger, - [resultCallback](Handle<Node> node, Logger &logger) { - resultCallback(node.cast<T>(), logger); - }, - owner); + return resolve(typeOf<T>(), path, owner, logger, resultCallback); } /** @@ -527,6 +529,7 @@ public: * @tparam T is the type of the node that should be resolved. * @param name is the path for which a node should be resolved. The name is * split at '.' to form a path. + * @param owner is the node for which the resolution takes place. * @param logger is the logger instance into which resolution problems * should be logged. * @param imposterCallback is the callback function that is called if @@ -539,19 +542,17 @@ 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 owner is the node for which the resolution takes place. * @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::string &name, Logger &logger, - std::function<Rooted<T>()> imposterCallback, - std::function<void(Handle<T>, Logger &)> resultCallback, - Handle<Node> owner = nullptr) + bool resolve(const std::string &name, Handle<Node> owner, Logger &logger, + ResolutionImposterCallback imposterCallback, + ResolutionResultCallback resultCallback) { - return resolve<T>(Utils::split(name, '.'), logger, imposterCallback, - resultCallback, owner); + return resolve<T>(Utils::split(name, '.'), owner, logger, + imposterCallback, resultCallback); } /** @@ -563,26 +564,119 @@ public: * @tparam T is the type of the node that should be resolved. * @param name is the path for which a node should be resolved. The name is * split at '.' to form a path. + * @param owner is the node for which the resolution takes place. * @param logger is the logger instance into which resolution problems * should be logged. * @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 owner is the node for which the resolution takes place. * @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::string &name, Logger &logger, - std::function<void(Handle<T>, Logger &)> resultCallback, - Handle<Node> owner = nullptr) + bool resolve(const std::string &name, Handle<Node> owner, Logger &logger, + ResolutionResultCallback resultCallback) { - return resolve<T>(Utils::split(name, '.'), logger, resultCallback, - owner); + return resolve<T>(Utils::split(name, '.'), owner, logger, + resultCallback); } /** + * Resolves a typesystem type. Makes sure an array type is returned if an + * array type is requested. + * + * @param path is the path for which a node should be resolved. + * @param owner is the node for which the resolution takes place. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @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. + * @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 resolveType(const std::vector<std::string> &path, Handle<Node> owner, + Logger &logger, ResolutionResultCallback resultCallback); + + /** + * Resolves a typesystem type. Makes sure an array type is returned if an + * array type is requested. + * + * @tparam T is the type of the node that should be resolved. + * @param name is the path for which a node should be resolved. The name is + * split at '.' to form a path. + * @param owner is the node for which the resolution takes place. + * @param logger is the logger instance into which resolution problems + * should be logged. + * @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. + * @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 resolveType(const std::string &name, Handle<Node> owner, + Logger &logger, ResolutionResultCallback resultCallback); + + /** + * Resolves a type and makes sure the corresponding value is of the correct + * type. + * + * <b>Warning:</b> This function is extremely dangerous as you have to make + * sure that the "value" reference stays alife as long as the "owner" is + * valid. This is especially problematic as internally references at parts + * of "value" may be kept. Test usages of this function well! + * + * @tparam T is the type of the node that should be resolved. + * @param path is the path for which a node should be resolved. + * @param owner is the node for which the resolution takes place. + * @param value is a reference at the Variant that represents the value for + * which the type should be looked up. The value must be valid as long as + * the owner node is valid (so it should be a part of the owner). + * @param logger is the logger instance into which resolution problems + * should be logged. + * @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. + * @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 resolveTypeWithValue(const std::vector<std::string> &path, + Handle<Node> owner, Variant &value, + Logger &logger, ResolutionResultCallback resultCallback); + + /** + * Resolves a type and makes sure the corresponding value is of the correct + * type. + * + * <b>Warning:</b> This function is extremely dangerous as you have to make + * sure that the "value" reference stays alife as long as the "owner" is + * valid. This is especially problematic as internally references at parts + * of "value" may be kept. Test usages of this function well! + * + * @tparam T is the type of the node that should be resolved. + * @param name is the path for which a node should be resolved. The name is + * split at '.' to form a path. + * @param owner is the node for which the resolution takes place. + * @param value is a reference at the Variant that represents the value for + * which the type should be looked up. The value must be valid as long as + * the owner node is valid (so it should be a part of the owner). + * @param logger is the logger instance into which resolution problems + * should be logged. + * @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. + * @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 resolveTypeWithValue(const std::string &name, Handle<Node> owner, + Variant &value, Logger &logger, ResolutionResultCallback resultCallback); + + /** * Tries to resolve all currently deferred resolution steps. The list of * pending deferred resolutions is cleared after this function has run. * |