summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/core/parser/ParserScope.cpp62
-rw-r--r--src/core/parser/ParserScope.hpp105
-rw-r--r--test/core/parser/ParserScopeTest.cpp54
4 files changed, 205 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 10927db..0ef91d7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -253,6 +253,7 @@ IF(TEST)
test/core/model/NodeTest
test/core/model/StyleTest
test/core/model/TypesystemTest
+ test/core/parser/ParserScopeTest
test/core/parser/ParserStackTest
test/core/resource/ResourceLocatorTest
test/core/resource/ResourceRequestTest
diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp
index df123df..0e2350f 100644
--- a/src/core/parser/ParserScope.cpp
+++ b/src/core/parser/ParserScope.cpp
@@ -90,11 +90,14 @@ bool DeferredResolution::resolve(Logger &logger)
/* Class ParserScope */
-ParserScope::ParserScope(const NodeVector<Node> &nodes)
- : ParserScopeBase(nodes), topLevelDepth(nodes.size())
+ParserScope::ParserScope(const NodeVector<Node> &nodes,
+ const std::vector<ParserFlagDescriptor> &flags)
+ : ParserScopeBase(nodes), flags(flags), topLevelDepth(nodes.size())
{
}
+ParserScope::ParserScope() : topLevelDepth(0) {}
+
bool ParserScope::checkUnwound(Logger &logger) const
{
if (nodes.size() != topLevelDepth) {
@@ -113,7 +116,7 @@ bool ParserScope::checkUnwound(Logger &logger) const
return true;
}
-ParserScope ParserScope::fork() { return ParserScope{nodes}; }
+ParserScope ParserScope::fork() { return ParserScope{nodes, flags}; }
bool ParserScope::join(const ParserScope &fork, Logger &logger)
{
@@ -128,11 +131,10 @@ bool ParserScope::join(const ParserScope &fork, Logger &logger)
return true;
}
-ParserScope::ParserScope() : topLevelDepth(0) {}
-
void ParserScope::push(Handle<Node> node)
{
- if (nodes.size() == topLevelDepth) {
+ const size_t currentDepth = nodes.size();
+ if (currentDepth == topLevelDepth) {
topLevelNodes.push_back(node);
}
nodes.push_back(node);
@@ -140,9 +142,23 @@ void ParserScope::push(Handle<Node> node)
void ParserScope::pop()
{
- if (nodes.size() == topLevelDepth) {
+ // 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();
}
@@ -152,6 +168,38 @@ Rooted<Node> ParserScope::getRoot() const { return nodes.front(); }
Rooted<Node> ParserScope::getLeaf() const { return nodes.back(); }
+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 std::vector<std::string> &path,
const Rtti &type, Logger &logger,
ResolutionImposterCallback imposterCallback,
diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp
index 191d08b..2c6093f 100644
--- a/src/core/parser/ParserScope.hpp
+++ b/src/core/parser/ParserScope.hpp
@@ -166,12 +166,63 @@ public:
};
/**
+ * Enum containing all possible parser flags that can be used by parsers to
+ * signal states that cannot be (explicitly or implicitly) stored in the node
+ * graph itself.
+ */
+enum class ParserFlag {
+ /**
+ * Set to the boolean value "true" if the head section of a file has passed.
+ * This happens once the first non-import tag is reached.
+ */
+ POST_HEAD
+};
+
+/**
* Provides an interface for document parsers to resolve references based on the
* current position in the created document tree. The ParserScope class itself
* is represented as a chain of ParserScope objects where each element has a
* reference to a Node object attached to it.
*/
class ParserScope : public ParserScopeBase {
+public:
+ /**
+ * Struct describing a set parser flag.
+ */
+ struct ParserFlagDescriptor {
+ /**
+ * Stack depth at which the flag has been set.
+ */
+ size_t depth;
+
+ /**
+ * Flag that has been set.
+ */
+ ParserFlag flag;
+
+ /**
+ * Value of that flag.
+ */
+ bool value;
+
+ /**
+ * Default constructor.
+ */
+ ParserFlagDescriptor() {}
+
+ /**
+ * Constructor of the parser flag descriptor class.
+ *
+ * @param depth is the depth at which the flag was set.
+ * @param flag is the flag that has been set.
+ * @param value is the value that has been set for that flag.
+ */
+ ParserFlagDescriptor(size_t depth, ParserFlag flag, bool value)
+ : depth(depth), flag(flag), value(value)
+ {
+ }
+ };
+
private:
/**
* List containing all deferred resolution descriptors.
@@ -179,6 +230,12 @@ private:
std::list<DeferredResolution> deferred;
/**
+ * Vector containing all set flags. The vector contains triples of the
+ * depth at which the flag was set, the flag itself and the value.
+ */
+ std::vector<ParserFlagDescriptor> flags;
+
+ /**
* Depth of the "nodes" list when the ParserScope was created.
*/
size_t topLevelDepth;
@@ -192,7 +249,8 @@ private:
/**
* Private constructor used to create a ParserScope fork.
*/
- ParserScope(const NodeVector<Node> &nodes);
+ ParserScope(const NodeVector<Node> &nodes,
+ const std::vector<ParserFlagDescriptor> &flags);
public:
/**
@@ -274,12 +332,32 @@ public:
Rooted<Node> getLeaf() const;
/**
+ * Sets a parser flag for the current stack depth.
+ *
+ * @param flag is the flag that should be set.
+ * @param value is the value to which the flag should be set.
+ */
+ void setFlag(ParserFlag flag, bool value);
+
+ /**
+ * Gets the parser flag for the current stack depth, ascends the stack until
+ * a set for this flag is found. Returns false if the flag is not set.
+ *
+ * @param flag is the flag for which the value should be returned.
+ * @return the value that was previously set by setParserFlag or false if no
+ * value has been set.
+ */
+ bool getFlag(ParserFlag flag);
+
+ /**
* Tries to resolve a node for the given type and path for all nodes
* currently on the stack, starting with the topmost node on the stack.
- * Calls the "imposterCallback" function for obtaining a temporary result if
+ * Calls the "imposterCallback" function for obtaining a temporary
+ *result if
* a node cannot be resolved right now. The "resultCallback" is at most
* called twice: Once when this method is called (probably with the
- * temporary) and another time if the resolution turned out to be successful
+ * temporary) and another time if the resolution turned out to be
+ *successful
* at a later point in time.
*
* @param path is the path for which a node should be resolved.
@@ -288,17 +366,24 @@ public:
* should be logged.
* @param imposterCallback is the callback function that is called if
* the node cannot be resolved at this moment. It gives the caller the
- * possibility to create an imposter (a temporary object) that may be used
+ * possibility to create an imposter (a temporary object) that may be
+ *used
* later in the resolution process.
* @param resultCallback is the callback function to which the result of
- * the resolution process is passed. This function is called at least once
- * either with the imposter (if the resolution was not successful) or the
- * resolved object directly when this function is called. If the resolution
- * was not successful the first time, it may be called another time later
+ * the resolution process is passed. This function is called at least
+ *once
+ * either with the imposter (if the resolution was not successful) or
+ *the
+ * resolved object directly when this function is called. If the
+ *resolution
+ * was not successful the first time, it may be called another time
+ *later
* in the context of the "performDeferredResolution" function.
- * @param location is the location in the current source file in which the
+ * @param location is the location in the current source file in which
+ *the
* resolution was triggered.
- * @return true if the resolution was immediately successful. This does not
+ * @return true if the resolution was immediately successful. This does
+ *not
* mean, that the resolved object does not exist, as it may be resolved
* later.
*/
diff --git a/test/core/parser/ParserScopeTest.cpp b/test/core/parser/ParserScopeTest.cpp
new file mode 100644
index 0000000..7f89f2c
--- /dev/null
+++ b/test/core/parser/ParserScopeTest.cpp
@@ -0,0 +1,54 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <gtest/gtest.h>
+
+#include <core/managed/Manager.hpp>
+#include <core/model/Node.hpp>
+#include <core/parser/ParserScope.hpp>
+
+namespace ousia {
+
+TEST(ParserScope, flags)
+{
+ Manager mgr;
+ ParserScope scope;
+
+ ASSERT_FALSE(scope.getFlag(ParserFlag::POST_HEAD));
+ scope.push(new Node{mgr});
+ ASSERT_FALSE(scope.getFlag(ParserFlag::POST_HEAD));
+ scope.setFlag(ParserFlag::POST_HEAD, true);
+ ASSERT_TRUE(scope.getFlag(ParserFlag::POST_HEAD));
+ scope.setFlag(ParserFlag::POST_HEAD, false);
+ ASSERT_FALSE(scope.getFlag(ParserFlag::POST_HEAD));
+ scope.setFlag(ParserFlag::POST_HEAD, true);
+ ASSERT_TRUE(scope.getFlag(ParserFlag::POST_HEAD));
+ scope.push(new Node{mgr});
+ ASSERT_TRUE(scope.getFlag(ParserFlag::POST_HEAD));
+ scope.setFlag(ParserFlag::POST_HEAD, false);
+ ASSERT_FALSE(scope.getFlag(ParserFlag::POST_HEAD));
+ scope.pop();
+ ASSERT_TRUE(scope.getFlag(ParserFlag::POST_HEAD));
+ scope.pop();
+ ASSERT_FALSE(scope.getFlag(ParserFlag::POST_HEAD));
+ scope.setFlag(ParserFlag::POST_HEAD, true);
+ ASSERT_TRUE(scope.getFlag(ParserFlag::POST_HEAD));
+}
+
+}
+