/* 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 "ParserScope.hpp" namespace ousia { /* Class ParserScopeBase */ ParserScopeBase::ParserScopeBase() {} ParserScopeBase::ParserScopeBase(const NodeVector &nodes) : nodes(nodes) { } Rooted ParserScopeBase::resolve(const std::vector &path, const Rtti &type, 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(path, type); // 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, const SourceLocation &location) : scope(nodes), resultCallback(resultCallback), path(path), type(type), location(location) { } bool DeferredResolution::resolve(Logger &logger) { Rooted res = scope.resolve(path, type, logger); if (res != nullptr) { try { resultCallback(res, logger); } catch (LoggableException ex) { logger.log(ex); } return true; } return false; } /* Class ParserScope */ ParserScope::ParserScope(const NodeVector &nodes) : ParserScopeBase(nodes), topLevelDepth(nodes.size()) { } 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]->getLocation()); } return false; } return true; } ParserScope ParserScope::fork() { return ParserScope{nodes}; } 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()); return true; } ParserScope::ParserScope() : topLevelDepth(0) {} void ParserScope::push(Handle node) { if (nodes.size() == topLevelDepth) { topLevelNodes.push_back(node); } nodes.push_back(node); } void ParserScope::pop() { if (nodes.size() == topLevelDepth) { throw LoggableException{"No element here to end!"}; } nodes.pop_back(); } NodeVector ParserScope::getTopLevelNodes() const { return topLevelNodes; } Rooted ParserScope::getRoot() const { return nodes.front(); } Rooted ParserScope::getLeaf() const { return nodes.back(); } bool ParserScope::resolve(const std::vector &path, const Rtti &type, Logger &logger, ResolutionImposterCallback imposterCallback, ResolutionResultCallback resultCallback, const SourceLocation &location) { if (!resolve(path, type, logger, resultCallback, location)) { resultCallback(imposterCallback(), logger); return false; } return true; } bool ParserScope::resolve(const std::vector &path, const Rtti &type, Logger &logger, ResolutionResultCallback resultCallback, const SourceLocation &location) { Rooted res = ParserScopeBase::resolve(path, type, logger); if (res != nullptr) { try { resultCallback(res, logger); } catch (LoggableException ex) { logger.log(ex, location); } return true; } deferred.emplace_back(nodes, path, type, resultCallback, location); return false; } 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(logger)) { it = deferred.erase(it); hasChange = true; } else { it++; } } // Abort if nothing has changed in the last iteration if (!hasChange) { 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 (const auto &failed : deferred) { logger.error(std::string("Could not resolve ") + failed.type.name + std::string(" \"") + Utils::join(failed.path, ".") + std::string("\""), failed.location); } deferred.clear(); return false; } }