/* 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 "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) { // 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; } /* 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; } Rooted ParserScope::getRoot() const { return nodes.front(); } Rooted ParserScope::getLeaf() const { return nodes.back(); } Rooted ParserScope::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; } 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::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 std::shared_ptr scope = std::make_shared(fork()); return resolveType( path, owner, logger, [=](Handle resolved, Handle 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 = resolved.cast(); 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 isAsync = std::make_shared(false); std::shared_ptr magicCount = std::make_shared(0); type->build(value, logger, [=](Variant &magicValue, bool isValid, ManagedUid innerTypeUid) mutable { // Fetch the inner type Rooted innerType = dynamic_cast(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(constantName, owner, logger, [=](Handle resolved, Handle owner, Logger &logger) mutable { if (resolved != nullptr) { // Make sure the constant is of the correct inner type Rooted constant = resolved.cast(); Rooted 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 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; } }