summaryrefslogtreecommitdiff
path: root/src/core/parser/generic/ParserStateStack.cpp
diff options
context:
space:
mode:
authorAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-14 23:42:05 +0100
committerAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-14 23:42:05 +0100
commitefe60ac3c3a8725ac71329c0bb19fa9d9c58f399 (patch)
tree97802b0eec92d51be02b94c939c1285feeaf3b4f /src/core/parser/generic/ParserStateStack.cpp
parentc1776468bc3daab431d0e2b51589dd12df595227 (diff)
Moved specific file format parsers to formats/ folder, moved old tokenizer to css code (this is the only place where it is actually used)
Diffstat (limited to 'src/core/parser/generic/ParserStateStack.cpp')
-rw-r--r--src/core/parser/generic/ParserStateStack.cpp216
1 files changed, 216 insertions, 0 deletions
diff --git a/src/core/parser/generic/ParserStateStack.cpp b/src/core/parser/generic/ParserStateStack.cpp
new file mode 100644
index 0000000..1265851
--- /dev/null
+++ b/src/core/parser/generic/ParserStateStack.cpp
@@ -0,0 +1,216 @@
+/*
+ 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 <sstream>
+
+#include <core/common/Utils.hpp>
+#include <core/common/Exceptions.hpp>
+#include <core/model/Project.hpp>
+
+#include "ParserScope.hpp"
+#include "ParserStack.hpp"
+
+namespace ousia {
+
+/* A default handler */
+
+/**
+ * The DefaultHandler class is used in case no element handler is specified in
+ * the ParserState descriptor.
+ */
+class DefaultHandler : public Handler {
+public:
+ using Handler::Handler;
+
+ void start(Variant::mapType &args) override {}
+
+ void end() override {}
+
+ static Handler *create(const HandlerData &handlerData)
+ {
+ return new DefaultHandler{handlerData};
+ }
+};
+
+/* Class Handler */
+
+void Handler::data(const std::string &data, int field)
+{
+ if (Utils::hasNonWhitepaceChar(data)) {
+ logger().error("Expected command but found character data.");
+ }
+}
+
+/* Class ParserStack */
+
+/**
+ * Returns an Exception that should be thrown when a currently invalid command
+ * is thrown.
+ */
+static LoggableException InvalidCommand(const std::string &name,
+ const std::set<std::string> &expected)
+{
+ if (expected.empty()) {
+ return LoggableException{
+ std::string{"No nested elements allowed, but got \""} + name +
+ std::string{"\""}};
+ } else {
+ return LoggableException{
+ std::string{"Expected "} +
+ (expected.size() == 1 ? std::string{"\""}
+ : std::string{"one of \""}) +
+ Utils::join(expected, "\", \"") + std::string{"\", but got \""} +
+ name + std::string{"\""}};
+ }
+}
+
+ParserStack::ParserStack(
+ ParserContext &ctx,
+ const std::multimap<std::string, const ParserState *> &states)
+ : ctx(ctx), states(states)
+{
+}
+
+bool ParserStack::deduceState()
+{
+ // Assemble all states
+ std::vector<const ParserState *> states;
+ for (const auto &e : this->states) {
+ states.push_back(e.second);
+ }
+
+ // Fetch the type signature of the scope and derive all possible states,
+ // abort if no unique parser state was found
+ std::vector<const ParserState *> possibleStates =
+ ParserStateDeductor(ctx.getScope().getStackTypeSignature(), states)
+ .deduce();
+ if (possibleStates.size() != 1) {
+ ctx.getLogger().error(
+ "Error while including file: Cannot deduce parser state.");
+ return false;
+ }
+
+ // Switch to this state by creating a dummy handler
+ const ParserState *state = possibleStates[0];
+ Handler *handler =
+ DefaultHandler::create({ctx, "", *state, *state, SourceLocation{}});
+ stack.emplace(handler);
+ return true;
+}
+
+std::set<std::string> ParserStack::expectedCommands()
+{
+ const ParserState *currentState = &(this->currentState());
+ std::set<std::string> res;
+ for (const auto &v : states) {
+ if (v.second->parents.count(currentState)) {
+ res.insert(v.first);
+ }
+ }
+ return res;
+}
+
+const ParserState &ParserStack::currentState()
+{
+ return stack.empty() ? ParserStates::None : stack.top()->state();
+}
+
+std::string ParserStack::currentCommandName()
+{
+ return stack.empty() ? std::string{} : stack.top()->name();
+}
+
+const ParserState *ParserStack::findTargetState(const std::string &name)
+{
+ const ParserState *currentState = &(this->currentState());
+ auto range = states.equal_range(name);
+ for (auto it = range.first; it != range.second; it++) {
+ const ParserStateSet &parents = it->second->parents;
+ if (parents.count(currentState) || parents.count(&ParserStates::All)) {
+ return it->second;
+ }
+ }
+
+ return nullptr;
+}
+
+void ParserStack::start(const std::string &name, Variant::mapType &args,
+ const SourceLocation &location)
+{
+ ParserState const *targetState = findTargetState(name);
+// TODO: Andreas, please improve this.
+// if (!Utils::isIdentifier(name)) {
+// throw LoggableException(std::string("Invalid identifier \"") + name +
+// std::string("\""));
+// }
+
+ if (targetState == nullptr) {
+ targetState = findTargetState("*");
+ }
+ if (targetState == nullptr) {
+ throw InvalidCommand(name, expectedCommands());
+ }
+
+ // Fetch the associated constructor
+ HandlerConstructor ctor = targetState->elementHandler
+ ? targetState->elementHandler
+ : DefaultHandler::create;
+
+ // Canonicalize the arguments, allow additional arguments
+ targetState->arguments.validateMap(args, ctx.getLogger(), true);
+
+ // Instantiate the handler and call its start function
+ Handler *handler = ctor({ctx, name, *targetState, currentState(), location});
+ handler->start(args);
+ stack.emplace(handler);
+}
+
+void ParserStack::start(std::string name, const Variant::mapType &args,
+ const SourceLocation &location)
+{
+ Variant::mapType argsCopy(args);
+ start(name, argsCopy);
+}
+
+void ParserStack::end()
+{
+ // Check whether the current command could be ended
+ if (stack.empty()) {
+ throw LoggableException{"No command to end."};
+ }
+
+ // Remove the current HandlerInstance from the stack
+ std::shared_ptr<Handler> inst{stack.top()};
+ stack.pop();
+
+ // Call the end function of the last Handler
+ inst->end();
+}
+
+void ParserStack::data(const std::string &data, int field)
+{
+ // Check whether there is any command the data can be sent to
+ if (stack.empty()) {
+ throw LoggableException{"No command to receive data."};
+ }
+
+ // Pass the data to the current Handler instance
+ stack.top()->data(data, field);
+}
+}
+