/* 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 "Scope.hpp" namespace ousia { namespace parser { /* Class GuardedScope */ GuardedScope::GuardedScope(Scope *scope, Handle node) : scope(scope) { scope->push(node); } GuardedScope::~GuardedScope() { if (scope) { scope->pop(); } } GuardedScope::GuardedScope(GuardedScope &&s) { scope = s.scope; s.scope = nullptr; } /* Class ScopeBase */ Rooted ScopeBase::resolve(const std::vector &path, const RttiType &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:"); for (const ResolutionResult &r : res) { logger.note(Utils::join(r.path(), ".")); } } return res[0].node; } return nullptr; } /* Class DeferredResolution */ DeferredResolution::DeferredResolution(const NodeVector &nodes, const std::vector &path, const RttiType &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 Scope */ void Scope::push(Handle node) { nodes.push_back(node); } void Scope::pop() { nodes.pop_back(); } GuardedScope Scope::descend(Handle node) { return GuardedScope{this, node}; } Rooted Scope::getRoot() const { return nodes.front(); } Rooted Scope::getLeaf() { return nodes.back(); } bool Scope::resolve(const std::vector &path, const RttiType &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 Scope::resolve(const std::vector &path, const RttiType &type, Logger &logger, ResolutionResultCallback resultCallback, const SourceLocation &location) { Rooted res = ScopeBase::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 Scope::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; } } // Output an error message if there are still deferred elements left that // could not be resolved if (!deferred.empty()) { for (const auto &failed : deferred) { logger.error( std::string("Could not resolve a reference to \"") + Utils::join(failed.path, ".") + std::string("\" of type " + failed.type.name), failed.location); } } // We were successful if there are no more deferred resolutions return deferred.empty(); } void Scope::purgeDeferredResolutions() { deferred.clear(); } } }