diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/parser/ParserScope.cpp | 86 | ||||
-rw-r--r-- | src/core/parser/ParserScope.hpp | 88 | ||||
-rw-r--r-- | src/core/resource/ResourceManager.cpp | 114 |
3 files changed, 225 insertions, 63 deletions
diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp index d2f5800..df123df 100644 --- a/src/core/parser/ParserScope.cpp +++ b/src/core/parser/ParserScope.cpp @@ -16,6 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <core/common/Exceptions.hpp> #include <core/common/Utils.hpp> #include "ParserScope.hpp" @@ -24,6 +25,12 @@ namespace ousia { /* Class ParserScopeBase */ +ParserScopeBase::ParserScopeBase() {} + +ParserScopeBase::ParserScopeBase(const NodeVector<Node> &nodes) : nodes(nodes) +{ +} + Rooted<Node> ParserScopeBase::resolve(const std::vector<std::string> &path, const Rtti &type, Logger &logger) { @@ -40,7 +47,8 @@ Rooted<Node> ParserScopeBase::resolve(const std::vector<std::string> &path, if (res.size() > 1) { logger.error(std::string("The reference \"") + Utils::join(path, ".") + ("\" is ambigous!")); - logger.note("Referenced objects are:", SourceLocation{}, MessageMode::NO_CONTEXT); + logger.note("Referenced objects are:", SourceLocation{}, + MessageMode::NO_CONTEXT); for (const ResolutionResult &r : res) { logger.note(Utils::join(r.path(), "."), *(r.node)); } @@ -82,18 +90,73 @@ bool DeferredResolution::resolve(Logger &logger) /* Class ParserScope */ -void ParserScope::push(Handle<Node> node) { nodes.push_back(node); } +ParserScope::ParserScope(const NodeVector<Node> &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> 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(); +} -void ParserScope::pop() { nodes.pop_back(); } +NodeVector<Node> ParserScope::getTopLevelNodes() const { return topLevelNodes; } Rooted<Node> ParserScope::getRoot() const { return nodes.front(); } -Rooted<Node> ParserScope::getLeaf() { return nodes.back(); } +Rooted<Node> ParserScope::getLeaf() const { return nodes.back(); } -bool ParserScope::resolve(const std::vector<std::string> &path, const Rtti &type, - Logger &logger, ResolutionImposterCallback imposterCallback, - ResolutionResultCallback resultCallback, - const SourceLocation &location) +bool ParserScope::resolve(const std::vector<std::string> &path, + const Rtti &type, Logger &logger, + ResolutionImposterCallback imposterCallback, + ResolutionResultCallback resultCallback, + const SourceLocation &location) { if (!resolve(path, type, logger, resultCallback, location)) { resultCallback(imposterCallback(), logger); @@ -102,9 +165,10 @@ bool ParserScope::resolve(const std::vector<std::string> &path, const Rtti &type return true; } -bool ParserScope::resolve(const std::vector<std::string> &path, const Rtti &type, - Logger &logger, ResolutionResultCallback resultCallback, - const SourceLocation &location) +bool ParserScope::resolve(const std::vector<std::string> &path, + const Rtti &type, Logger &logger, + ResolutionResultCallback resultCallback, + const SourceLocation &location) { Rooted<Node> res = ParserScopeBase::resolve(path, type, logger); if (res != nullptr) { diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp index c1369dd..191d08b 100644 --- a/src/core/parser/ParserScope.hpp +++ b/src/core/parser/ParserScope.hpp @@ -31,7 +31,8 @@ /** * @file ParserScope.hpp * - * Contains the ParserScope class used for resolving references based on the current + * Contains the ParserScope class used for resolving references based on the + *current * parser state. * * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) @@ -72,16 +73,17 @@ public: /** * Default constructor, creates an empty ParserScope instance. */ - ParserScopeBase() {} + ParserScopeBase(); /** - * Creates a new instance of the ParserScopeBase class, copying the the given + * Creates a new instance of the ParserScopeBase class, copying the the + *given * nodes as initial start value of the node stack. This could for example * be initialized with the path of a node. * * @param nodes is a node vector containing the current node stack. */ - ParserScopeBase(const NodeVector<Node> &nodes) : nodes(nodes) {} + ParserScopeBase(const NodeVector<Node> &nodes); /** * Tries to resolve a node for the given type and path for all nodes that @@ -94,8 +96,8 @@ public: * @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, Logger &logger); + Rooted<Node> resolve(const std::vector<std::string> &path, const Rtti &type, + Logger &logger); }; /** @@ -147,8 +149,7 @@ public: * @param location is the location at which the resolution was triggered. */ DeferredResolution(const NodeVector<Node> &nodes, - const std::vector<std::string> &path, - const Rtti &type, + const std::vector<std::string> &path, const Rtti &type, ResolutionResultCallback resultCallback, const SourceLocation &location = SourceLocation{}); @@ -177,12 +178,63 @@ private: */ std::list<DeferredResolution> deferred; + /** + * Depth of the "nodes" list when the ParserScope was created. + */ + size_t topLevelDepth; + + /** + * List of a all nodes that have been pushed onto the scope at the top level + * depth. + */ + NodeVector<Node> topLevelNodes; + + /** + * Private constructor used to create a ParserScope fork. + */ + ParserScope(const NodeVector<Node> &nodes); + public: /** - * Default constructor of the ParserScope class, creates an empty ParserScope with no - * element on the internal stack. + * Default constructor of the ParserScope class, creates an empty + * ParserScope with no element on the internal stack. + */ + ParserScope(); + + /** + * Makes sure all elements on the scope have been unwound. Loggs an error + * message if this is not the case and returns false. + * + * @param logger is the Logger instance to which information in case of + * failure should be written. + * @return true if the stack is unwound, false otherwise. */ - ParserScope() {} + bool checkUnwound(Logger &logger) const; + + /** + * Returns a new ParserScope instance with a copy of the current node stack + * but empty deferred resolutions list and empty topLevelNodes. + * + * @return a forked ParserScope instance, which starts with a copy of the + * node stack. + */ + ParserScope fork(); + + /** + * Joins a previously forked ParserScope instance with this ParserScope. + * Copies all pending deferred resolutions from this ParserScope instance. + * Joining only works if the node stack of the given ParserScope has the + * same depth as the node stack of this ParserScope instance (has been + * unwound). This is assured by calling the "checkUnwound" function of + * the fork. + * + * @param fork is the ParserScope fork that should be joined with this + * ParserScope instance. + * @param logger is the Logger instance to which information in case of + * failure should be written. + * @return true if the operation was successful, false otherwise. + */ + bool join(const ParserScope &fork, Logger &logger); /** * Pushes a new node onto the scope. @@ -197,6 +249,15 @@ public: void pop(); /** + * Returns the top-level nodes. These are the nodes that are pushed onto the + * scope instance while the node stack has the depth it had during the + * creation of this ParserScope instance. + * + * @return a node vector containing the top-level nodes. + */ + NodeVector<Node> getTopLevelNodes() const; + + /** * Returns the top-most Node instance in the ParserScope hirarchy. * * @return a reference at the root node. @@ -204,12 +265,13 @@ public: Rooted<Node> getRoot() const; /** - * Returns the bottom-most Node instance in the ParserScope hirarchy, e.g. the + * Returns the bottom-most Node instance in the ParserScope hirarchy, e.g. + *the * node that was pushed last onto the stack. * * @return a reference at the leaf node. */ - Rooted<Node> getLeaf(); + Rooted<Node> getLeaf() const; /** * Tries to resolve a node for the given type and path for all nodes diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp index d149e49..610176b 100644 --- a/src/core/resource/ResourceManager.cpp +++ b/src/core/resource/ResourceManager.cpp @@ -79,6 +79,7 @@ Rooted<Node> ResourceManager::parse(ParserContext &ctx, const std::string &path, // Some references used for convenience Registry ®istry = ctx.getRegistry(); Logger &logger = ctx.getLogger(); + ParserScope &scope = ctx.getScope(); Resource relativeTo = getResource(ctx.getSourceId()); // Locate the resource relative to the old resource, abort if this did not @@ -94,61 +95,96 @@ Rooted<Node> ResourceManager::parse(ParserContext &ctx, const std::string &path, SourceId sourceId = allocateSourceId(resource); // We can now try to parse the given file - Rooted<Node> node; + NodeVector<Node> parsedNodes; try { - { - // Set the current source id in the logger instance. Note that this - // modifies the logger instance -- the GuardedLogger is just used to - // make sure the default location is popped from the stack again. - GuardedLogger guardedLogger(logger, SourceLocation{sourceId}); - - // Fetch the input stream and create a char reader - std::unique_ptr<std::istream> is = resource.stream(); + // Set the current source id in the logger instance. Note that this + // modifies the logger instance -- the GuardedLogger is just used to + // make sure the default location is popped from the stack again. + GuardedLogger guardedLogger(logger, SourceLocation{sourceId}); + + // Fetch the input stream and create a char reader + std::unique_ptr<std::istream> is = resource.stream(); CharReader reader(*is, sourceId); - // Actually parse the input stream, distinguish the LINK and the - // INCLUDE mode - switch (mode) { - case ParseMode::LINK: { - ParserScope scope; // New empty parser scope instance - ParserContext childCtx = ctx.clone(scope, sourceId); - node = req.getParser()->parse(reader, childCtx); - - // Perform all deferred resolutions - scope.performDeferredResolution(logger); - - // Validate the parsed node - if (node != nullptr) { - node->validate(logger); - } - break; + // Actually parse the input stream, distinguish the LINK and the + // INCLUDE mode + switch (mode) { + case ParseMode::LINK: { + // Create a new, empty parser scope instance and a new parser + // context with this instance in place + ParserScope innerScope; + ParserContext childCtx = ctx.clone(innerScope, sourceId); + + // Run the parser + req.getParser()->parse(reader, childCtx); + + // Make sure the scope has been unwound and perform all + // deferred resolutions + innerScope.checkUnwound(logger); + innerScope.performDeferredResolution(logger); + + // Fetch the nodes that were parsed by this parser instance + parsedNodes = innerScope.getTopLevelNodes(); + + // Make sure the number of elements is exactly one -- we can + // only store one element per top-level node. + if (parsedNodes.empty()) { + throw LoggableException{"Module is empty."}; } - case ParseMode::INCLUDE: { - ParserContext childCtx = ctx.clone(sourceId); - node = req.getParser()->parse(reader, childCtx); - break; + if (parsedNodes.size() > 1) { + throw LoggableException{ + std::string( + "Expected exactly one top-level node but got ") + + std::to_string(parsedNodes.size())}; } + + // Store the parsed node along with the sourceId + storeNode(sourceId, parsedNodes[0]); + + break; + } + case ParseMode::INCLUDE: { + // Fork the scope instance and create a new parser context with + // this instanc ein place + ParserScope forkedScope = scope.fork(); + ParserContext childCtx = ctx.clone(forkedScope, sourceId); + + // Run the parser + req.getParser()->parse(reader, childCtx); + + // Join the forked scope with the outer scope + scope.join(forkedScope, logger); + + // Fetch the nodes that were parsed by this parser instance + parsedNodes = forkedScope.getTopLevelNodes(); + + break; } - } - if (node == nullptr) { - throw LoggableException{"Requested file \"" + - resource.getLocation() + - "\" cannot be parsed."}; } } catch (LoggableException ex) { // Log the exception and return nullptr logger.log(ex); return nullptr; + // return NodeVector<Node>{}; } - // Store the parsed node along with the sourceId, if we are in the LINK mode - if (mode == ParseMode::LINK) { - storeNode(sourceId, node); + // Make sure the parsed nodes fulfill the "supportedTypes" constraint, + // remove nodes that do not the result + for (auto it = parsedNodes.begin(); it != parsedNodes.end(); ) { + const Rtti &type = (*it)->type(); + if (!type.isOneOf(supportedTypes)) { + logger.error(std::string("Node of internal type ") + type.name + + std::string(" not supported here"), + **it); + it = parsedNodes.erase(it); + } else { + it++; + } } - // Return the parsed node - return node; + // TODO: Return parsed nodes + return nullptr; } Rooted<Node> ResourceManager::link(ParserContext &ctx, const std::string &path, |