/* Ousía Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include "ParserScope.hpp" namespace ousia { /* Class ParserScopeBase */ ParserScopeBase::ParserScopeBase() {} ParserScopeBase::ParserScopeBase(const NodeVector &nodes) : nodes(nodes) { } Rooted ParserScopeBase::resolve(const Rtti *type, const std::vector &path, Logger &logger) { // if the last element of the path is the default field name, we want to // resolve the parent descriptor first. if (type == &RttiTypes::FieldDescriptor && path.back() == DEFAULT_FIELD_NAME) { std::vector descPath; descPath.insert(descPath.end(), path.begin(), path.end() - 1); Rooted res = resolve(&RttiTypes::Descriptor, descPath, logger); if (res == nullptr) { return nullptr; } return res.cast()->getFieldDescriptor(); } // Go up the stack and try to resolve the for (auto it = nodes.rbegin(); it != nodes.rend(); it++) { std::vector res = (*it)->resolve(type, path); // Abort if the object could not be resolved if (res.empty()) { continue; } // 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.note("Referenced objects are:", SourceLocation{}, MessageMode::NO_CONTEXT); for (const ResolutionResult &r : res) { logger.note(Utils::join(r.path(), "."), *(r.node)); } } return res[0].node; } return nullptr; } bool ParserScopeBase::isEmpty() const { return nodes.empty(); } Rooted ParserScopeBase::getRoot() const { return nodes.front(); } Rooted ParserScopeBase::getLeaf() const { return nodes.back(); } const NodeVector &ParserScopeBase::getStack() const { return nodes; } std::vector ParserScopeBase::getStackTypeSignature() const { std::vector res; res.reserve(nodes.size()); for (size_t i = 0; i < nodes.size(); i++) { res.push_back(nodes[i]->type()); } return res; } Rooted ParserScopeBase::select(RttiSet types, int maxDepth) { ssize_t minDepth = 0; if (maxDepth >= 0) { minDepth = static_cast(nodes.size()) - (maxDepth + 1); } for (ssize_t i = nodes.size() - 1; i >= minDepth; i--) { if (nodes[i]->type()->isOneOf(types)) { return nodes[i]; } } return nullptr; } Rooted ParserScopeBase::selectOrThrow(RttiSet types, int maxDepth) { Rooted res = select(types, maxDepth); if (res == nullptr) { std::vector typenames; for (auto type : types) { typenames.push_back(type->name); } throw LoggableException{std::string( "Expected to be inside an element of one " "of the internal types ") + Utils::join(typenames, "\", \"", "\"", "\"")}; } return res; } /* Class DeferredResolution */ DeferredResolution::DeferredResolution(const NodeVector &nodes, const std::vector &path, const Rtti *type, ResolutionResultCallback resultCallback, Handle owner) : scope(nodes), resultCallback(resultCallback), path(path), type(type), owner(owner) { } bool DeferredResolution::resolve( const std::unordered_multiset &ignore, Logger &logger) { // Fork the logger to prevent error messages from being shown if we actively // ignore the resolution result LoggerFork loggerFork = logger.fork(); Rooted 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, owner, logger); } catch (LoggableException ex) { logger.log(ex); } return true; } } else { loggerFork.commit(); } return false; } void DeferredResolution::fail(Logger &logger) { try { resultCallback(nullptr, owner, logger); } catch (LoggableException ex) { logger.log(ex); } } /* Class ParserScope */ ParserScope::ParserScope(const NodeVector &nodes, const std::vector &flags) : ParserScopeBase(nodes), flags(flags), topLevelDepth(nodes.size()) { } ParserScope::ParserScope() : topLevelDepth(0) {} bool ParserScope::checkUnwound(Logger &logger) const { if (nodes.size() != topLevelDepth) { logger.error("Not all open elements have been closed!", SourceLocation{}, MessageMode::NO_CONTEXT); logger.note("Still open elements are: ", SourceLocation{}, MessageMode::NO_CONTEXT); for (size_t i = topLevelDepth + 1; i < nodes.size(); i++) { logger.note(std::string("Element of interal type ") + nodes[i]->type()->name + std::string(" defined here:"), *nodes[i]); } return false; } return true; } ParserScope ParserScope::fork() { return ParserScope{nodes, flags}; } bool ParserScope::join(const ParserScope &fork, Logger &logger) { // Make sure the fork has been unwound if (!fork.checkUnwound(logger)) { return false; } // Insert the deferred resolutions of the fork into our own deferred // resolution list deferred.insert(deferred.end(), fork.deferred.begin(), fork.deferred.end()); awaitingResolution.insert(fork.awaitingResolution.begin(), fork.awaitingResolution.end()); return true; } void ParserScope::push(Handle node) { const size_t currentDepth = nodes.size(); if (currentDepth == topLevelDepth) { topLevelNodes.push_back(node); } nodes.push_back(node); } void ParserScope::pop() { // Make sure pop is not called without an element on the stack const size_t currentDepth = nodes.size(); if (currentDepth == topLevelDepth) { throw LoggableException{"No element here to end!"}; } // Remove all flags from the stack that were set for higher stack depths. size_t newLen = 0; for (ssize_t i = flags.size() - 1; i >= 0; i--) { if (flags[i].depth < currentDepth) { newLen = i + 1; break; } } flags.resize(newLen); // Remove the element from the stack nodes.pop_back(); } NodeVector ParserScope::getTopLevelNodes() const { return topLevelNodes; } void ParserScope::setFlag(ParserFlag flag, bool value) { // Fetch the current stack depth const size_t currentDepth = nodes.size(); // Try to change the value of the flag if it was already set on the same // stack depth for (auto it = flags.rbegin(); it != flags.rend(); it++) { if (it->depth == currentDepth) { if (it->flag == flag) { it->value = value; return; } } else { break; } } // Insert a new element into the flags list flags.emplace_back(currentDepth, flag, value); } bool ParserScope::getFlag(ParserFlag flag) { for (auto it = flags.crbegin(); it != flags.crend(); it++) { if (it->flag == flag) { return it->value; } } return false; } bool ParserScope::resolve(const Rtti *type, const std::vector &path, Handle owner, Logger &logger, ResolutionImposterCallback imposterCallback, ResolutionResultCallback resultCallback) { if (!resolve(type, path, owner, logger, resultCallback)) { resultCallback(imposterCallback(), owner, logger); return false; } return true; } bool ParserScope::resolve(const Rtti *type, const std::vector &path, Handle owner, Logger &logger, ResolutionResultCallback resultCallback) { // Try to directly resolve the node Rooted res = ParserScopeBase::resolve(type, path, logger); if (res != nullptr && !awaitingResolution.count(res.get())) { try { resultCallback(res, owner, logger); } catch (LoggableException ex) { logger.log(ex, *owner); } return true; } // Mark the owner as "awaitingResolution", preventing it from being returned // as resolution result if (owner != nullptr) { awaitingResolution.insert(owner.get()); } deferred.emplace_back(nodes, path, type, resultCallback, owner); return false; } bool ParserScope::resolveType(const std::vector &path, Handle 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 p = path; p.back() = p.back().substr(0, last.size() - 2); // Resolve the rest of the type return resolveType(p, owner, logger, [resultCallback](Handle resolved, Handle owner, Logger &logger) { if (resolved != nullptr) { Rooted arr{new ArrayType{resolved.cast()}}; 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 owner, Logger &logger, ResolutionResultCallback resultCallback) { return resolveType(Utils::split(name, '.'), owner, logger, resultCallback); } bool ParserScope::resolveValue(Variant &data, Handle type, Handle owner, Logger &logger) { return type->build( data, logger, [&](Variant &innerData, const Type *innerType) mutable -> Type::MagicCallbackResult { // Try to resolve the node Rooted constant = ParserScopeBase::resolve(&RttiTypes::Constant, Utils::split(innerData.asMagic(), '.'), logger).cast(); // Abort if nothing was found if (constant == nullptr) { return Type::MagicCallbackResult::NOT_FOUND; } // Check whether the inner type of the constant is correct Type::MagicCallbackResult res = Type::MagicCallbackResult::FOUND_VALID; Rooted constantType = constant->getType(); if (!constantType->checkIsa(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.", innerData); logger.note("Constant was defined here:", *constant); res = Type::MagicCallbackResult::FOUND_INVALID; } // Set the data to the value of the constant (even if an error // happend -- probably the type is was not that wrong -- who knows? innerData = constant->getValue(); return res; }); } bool ParserScope::resolveTypeWithValue(const std::vector &path, Handle owner, Variant &value, Logger &logger, ResolutionResultCallback resultCallback) { // Fork the parser scope -- constants need to be resolved in the same // context as this resolve call ParserScope scope = fork(); Variant *valuePtr = &value; return resolveType( path, owner, logger, [=](Handle resolved, Handle owner, Logger &logger) mutable { if (resolved != nullptr) { Rooted type = resolved.cast(); scope.resolveValue(*valuePtr, type, owner, logger); } // Call the result callback with the type resultCallback(resolved, owner, logger); }); } bool ParserScope::resolveTypeWithValue(const std::string &name, Handle 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 // last iteration (resolving a node may cause other nodes to be resolvable). while (true) { // Iterate over all deferred resolution processes, bool hasChange = false; for (auto it = deferred.begin(); it != deferred.end();) { if (it->resolve(awaitingResolution, logger)) { if (it->owner != nullptr) { awaitingResolution.erase(it->owner.get()); } it = deferred.erase(it); hasChange = true; } else { it++; } } // Abort if nothing has changed in the last iteration if (!hasChange) { // In a last step, clear the "awaitingResolution" list to allow // cyclical dependencies to be resolved if (!awaitingResolution.empty()) { awaitingResolution.clear(); } else { break; } } } // We were successful if there are no more deferred resolutions if (deferred.empty()) { return true; } // Output error messages for all elements for which resolution did not // succeed. 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("\""), *failed.owner); } deferred.clear(); return false; } }