summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/core/parser/ParserState.cpp79
-rw-r--r--src/core/parser/ParserState.hpp91
-rw-r--r--test/core/parser/ParserStateTest.cpp77
4 files changed, 247 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 04af853..94c89dc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -259,6 +259,7 @@ IF(TEST)
test/core/model/TypesystemTest
test/core/parser/ParserScopeTest
test/core/parser/ParserStackTest
+ test/core/parser/ParserStateTest
test/core/resource/ResourceLocatorTest
test/core/resource/ResourceRequestTest
# test/core/script/FunctionTest
diff --git a/src/core/parser/ParserState.cpp b/src/core/parser/ParserState.cpp
index b0b02d2..9d3aa7e 100644
--- a/src/core/parser/ParserState.cpp
+++ b/src/core/parser/ParserState.cpp
@@ -25,10 +25,12 @@ namespace ousia {
ParserState::ParserState() : elementHandler(nullptr), childHandler(nullptr) {}
ParserState::ParserState(ParserStateSet parents, Arguments arguments,
+ RttiSet createdNodeTypes,
HandlerConstructor elementHandler,
HandlerConstructor childHandler)
: parents(parents),
arguments(arguments),
+ createdNodeTypes(createdNodeTypes),
elementHandler(elementHandler),
childHandler(childHandler)
{
@@ -65,6 +67,18 @@ ParserStateBuilder &ParserStateBuilder::arguments(const Arguments &arguments)
return *this;
}
+ParserStateBuilder &ParserStateBuilder::createdNodeType(const Rtti *type)
+{
+ state.createdNodeTypes = RttiSet{type};
+ return *this;
+}
+
+ParserStateBuilder &ParserStateBuilder::createdNodeTypes(const RttiSet &types)
+{
+ state.createdNodeTypes = types;
+ return *this;
+}
+
ParserStateBuilder &ParserStateBuilder::elementHandler(
HandlerConstructor elementHandler)
{
@@ -81,6 +95,71 @@ ParserStateBuilder &ParserStateBuilder::childHandler(
const ParserState &ParserStateBuilder::build() const { return state; }
+/* Class ParserStateDeductor */
+
+ParserStateDeductor::ParserStateDeductor(
+ std::vector<const Rtti *> signature,
+ std::vector<const ParserState *> states)
+ : tbl(signature.size()),
+ signature(std::move(signature)),
+ states(std::move(states))
+{
+}
+
+bool ParserStateDeductor::isActive(size_t d, const ParserState *s)
+{
+ // Lookup the "active" state of (d, s), if it was not already set
+ // (e.second is true) we'll have to calculate it
+ auto e = tbl[d].emplace(s, false);
+ bool &res = e.first->second;
+ if (!e.second) {
+ return res;
+ }
+
+ // Check whether this node is generative (may have produced the Node
+ // described by the current Signature element)
+ bool isGenerative = signature[d]->isOneOf(s->createdNodeTypes);
+
+ if (isGenerative && d == 0) {
+ // End of recursion -- the last signature element is reached and the
+ // node was generative
+ res = true;
+ } else {
+ // Try repetition of this node
+ if (isGenerative && isActive(d - 1, s)) {
+ res = true;
+ } else {
+ // Check whether any of the parent nodes were active -- either for
+ // the previous element (if this one is generative) or for the
+ // current element (assuming this node was not generative)
+ for (const ParserState *parent : s->parents) {
+ if ((isGenerative && isActive(d - 1, parent)) ||
+ isActive(d, parent)) {
+ res = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return res;
+}
+
+std::vector<const ParserState *> ParserStateDeductor::deduce()
+{
+ std::vector<const ParserState *> res;
+ if (!signature.empty()) {
+ const size_t D = signature.size();
+ for (auto s : states) {
+ if (signature[D - 1]->isOneOf(s->createdNodeTypes) &&
+ isActive(D - 1, s)) {
+ res.push_back(s);
+ }
+ }
+ }
+ return res;
+}
+
/* Constant initializations */
namespace ParserStates {
diff --git a/src/core/parser/ParserState.hpp b/src/core/parser/ParserState.hpp
index 81e118d..43b8035 100644
--- a/src/core/parser/ParserState.hpp
+++ b/src/core/parser/ParserState.hpp
@@ -65,6 +65,13 @@ struct ParserState {
Arguments arguments;
/**
+ * Set containing the types of the nodes that may be created in this
+ * ParserState. This information is needed for Parsers to reconstruct the
+ * current ParserState from a given ParserScope when a file is included.
+ */
+ RttiSet createdNodeTypes;
+
+ /**
* Pointer at a function which creates a new concrete Handler instance for
* the elements described by this state. May be nullptr in which case no
* handler instance is created.
@@ -90,6 +97,10 @@ struct ParserState {
* @param parents is a vector containing all possible parent states.
* @param arguments is a descriptor of arguments that should be passed to
* the handler.
+ * @param createdNodeTypes is a set containing the types of the nodes tha
+ * may be created in this ParserState. This information is needed for
+ * Parsers to reconstruct the current ParserState from a given ParserScope
+ * when a file is included.
* @param elementHandler is a pointer at a function which creates a new
* concrete Handler instance for the elements described by this state. May
* be nullptr in which case no handler instance is created.
@@ -99,6 +110,7 @@ struct ParserState {
* allowed.
*/
ParserState(ParserStateSet parents, Arguments arguments = Arguments{},
+ RttiSet createdNodeTypes = RttiSet{},
HandlerConstructor elementHandler = nullptr,
HandlerConstructor childHandler = nullptr);
@@ -164,6 +176,26 @@ public:
ParserStateBuilder &arguments(const Arguments &arguments);
/**
+ * Sets the Node types this state may produce to the given Rtti descriptor.
+ *
+ * @param type is the Rtti descriptor of the Type that may be produced by
+ * this state.
+ * @return a reference at this ParserStateBuilder instance for method
+ * chaining.
+ */
+ ParserStateBuilder &createdNodeType(const Rtti *type);
+
+ /**
+ * Sets the Node types this state may produce to the given Rtti descriptors.
+ *
+ * @param types is a set of Rtti descriptors of the Types that may be
+ * produced by this state.
+ * @return a reference at this ParserStateBuilder instance for method
+ * chaining.
+ */
+ ParserStateBuilder &createdNodeTypes(const RttiSet &types);
+
+ /**
* Sets the constructor for the element handler. The constructor creates a
* new concrete Handler instance for the elements described by this state.
* May be nullptr in which case no handler instance is created (this is
@@ -199,6 +231,64 @@ public:
};
/**
+ * Class used to deduce the ParserState a Parser is currently in based on the
+ * types of the Nodes that currently are on the ParserStack. Uses dynamic
+ * programming in order to solve this problem.
+ */
+class ParserStateDeductor {
+public:
+ /**
+ * Type containing the dynamic programming table.
+ */
+ using Table = std::vector<std::unordered_map<const ParserState *, bool>>;
+
+private:
+ /**
+ * Dynamic programming table.
+ */
+ Table tbl;
+
+ /**
+ * Signature given in the constructor.
+ */
+ const std::vector<const Rtti *> signature;
+
+ /**
+ * List of states that should be checked for being active.
+ */
+ const std::vector<const ParserState *> states;
+
+ /**
+ * Used internally to check whether the given parser stack s may have been
+ * active for signature element d.
+ *
+ * @param d is the signature element.
+ * @param s is the parser state.
+ * @return true if the the given ParserState may have been active.
+ */
+ bool isActive(size_t d, const ParserState *s);
+
+public:
+ /**
+ * Constructor of the ParserStateDeductor class.
+ *
+ * @param signature a Node type signature describing the types of the nodes
+ * which currently reside on e.g. the ParserScope stack.
+ * @param states is a list of states that should be checked.
+ */
+ ParserStateDeductor(std::vector<const Rtti *> signature,
+ std::vector<const ParserState *> states);
+
+ /**
+ * Selects all active states from the given states. Only considers those
+ * states that may have produced the last signature element.
+ *
+ * @return a list of states that may actually have been active.
+ */
+ std::vector<const ParserState *> deduce();
+};
+
+/**
* The ParserStates namespace contains all the global state constants used
* in the ParserStack class.
*/
@@ -213,7 +303,6 @@ extern const ParserState All;
*/
extern const ParserState None;
}
-
}
#endif /* _OUSIA_PARSER_STATE_HPP_ */
diff --git a/test/core/parser/ParserStateTest.cpp b/test/core/parser/ParserStateTest.cpp
new file mode 100644
index 0000000..91d8dcd
--- /dev/null
+++ b/test/core/parser/ParserStateTest.cpp
@@ -0,0 +1,77 @@
+/*
+ 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/common/Rtti.hpp>
+#include <core/parser/ParserState.hpp>
+
+namespace ousia {
+
+static const Rtti t1;
+static const Rtti t2;
+static const Rtti t3;
+static const Rtti t4;
+static const Rtti t5;
+
+static const ParserState s1 = ParserStateBuilder().createdNodeType(&t1);
+static const ParserState s2a =
+ ParserStateBuilder().parent(&s1).createdNodeType(&t2);
+static const ParserState s2b =
+ ParserStateBuilder().parent(&s1).createdNodeType(&t2);
+static const ParserState s3 =
+ ParserStateBuilder().parents({&s2a, &s1}).createdNodeType(&t3);
+static const ParserState s4 =
+ ParserStateBuilder().parent(&s3).createdNodeType(&t4);
+static const ParserState s5 =
+ ParserStateBuilder().parent(&s2b).createdNodeType(&t5);
+
+TEST(ParserStateDeductor, deduce)
+{
+ using Result = std::vector<const ParserState *>;
+ using Signature = std::vector<const Rtti *>;
+ std::vector<const ParserState *> states{&s1, &s2a, &s2b, &s3, &s4, &s5};
+
+ // Should not crash on empty signature
+ ASSERT_EQ(Result{}, ParserStateDeductor(Signature{}, states).deduce());
+
+ // Try repeating signature elements
+ ASSERT_EQ(Result({&s1}),
+ ParserStateDeductor(Signature({&t1}), states).deduce());
+ ASSERT_EQ(Result({&s1}),
+ ParserStateDeductor(Signature({&t1, &t1}), states).deduce());
+ ASSERT_EQ(Result({&s1}),
+ ParserStateDeductor(Signature({&t1, &t1, &t1}), states).deduce());
+
+ // Go to another state
+ ASSERT_EQ(Result({&s2a, &s2b}),
+ ParserStateDeductor(Signature({&t1, &t1, &t2}), states).deduce());
+ ASSERT_EQ(Result({&s4}),
+ ParserStateDeductor(Signature({&t1, &t3, &t4}), states).deduce());
+
+ // Skip one state
+ ASSERT_EQ(Result({&s4}),
+ ParserStateDeductor(Signature({&t2, &t4}), states).deduce());
+
+ // Impossible signature
+ ASSERT_EQ(Result({}),
+ ParserStateDeductor(Signature({&t4, &t5}), states).deduce());
+
+}
+}
+