summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/parser/ParserScope.cpp86
-rw-r--r--src/core/parser/ParserScope.hpp88
-rw-r--r--src/core/resource/ResourceManager.cpp114
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 &registry = 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,