summaryrefslogtreecommitdiff
path: root/src/core/parser/stack
diff options
context:
space:
mode:
authorAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-15 00:12:04 +0100
committerAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-15 00:12:04 +0100
commit26766a588d988e635112878aba71c69c8f057c16 (patch)
tree3c68d600232365e1b6daad4b6441eabe70556d45 /src/core/parser/stack
parentc5fde12cbac6907da4e267492206b2df3dad01f8 (diff)
Renamed StateStack to Stack
Diffstat (limited to 'src/core/parser/stack')
-rw-r--r--src/core/parser/stack/Stack.cpp151
-rw-r--r--src/core/parser/stack/Stack.hpp195
2 files changed, 256 insertions, 90 deletions
diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp
index 1d83a68..b0df39b 100644
--- a/src/core/parser/stack/Stack.cpp
+++ b/src/core/parser/stack/Stack.cpp
@@ -21,20 +21,67 @@
#include <core/common/Utils.hpp>
#include <core/common/Exceptions.hpp>
#include <core/parser/ParserScope.hpp>
+#include <core/parser/ParserContext.hpp>
+#include "Handler.hpp"
#include "Stack.hpp"
+#include "State.hpp"
namespace ousia {
namespace parser_stack {
-/* Class StateStack */
+/* Class HandlerInfo */
+
+HandlerInfo::HandlerInfo() : HandlerInfo(nullptr) {}
+
+HandlerInfo::HandlerInfo(std::shared_ptr<Handler> handler)
+ : handler(handler),
+ fieldIdx(0),
+ inField(false),
+ inDefaultField(false),
+ inImplicitDefaultField(false),
+ hasDefaultField(false)
+{
+}
+
+HandlerInfo::~HandlerInfo()
+{
+ // Do nothing
+}
+
+void HandlerInfo::fieldStart(bool isDefault, bool isImplicit, bool isValid)
+{
+ inField = true;
+ inDefaultField = isDefault || isImplicit;
+ inImplicitDefaultField = isImplicit;
+ inValidField = isValid;
+ hasDefaultField = hasDefaultField || inDefaultField;
+ fieldIdx++;
+}
+
+void HandlerInfo::fieldEnd()
+{
+ inField = false;
+ inDefaultField = false;
+ inImplicitDefaultField = false;
+ inValidField = false;
+ if (fieldIdx > 0) {
+ fieldIdx--;
+ }
+}
+
+/* Helper functions */
/**
* Returns an Exception that should be thrown when a currently invalid command
* is thrown.
+ *
+ * @param name is the name of the command for which no state transition is
+ * found.
+ * @param expected is a set containing the names of the expected commands.
*/
-static LoggableException InvalidCommand(const std::string &name,
- const std::set<std::string> &expected)
+static LoggableException buildInvalidCommandException(
+ const std::string &name, const std::set<std::string> &expected)
{
if (expected.empty()) {
return LoggableException{
@@ -50,14 +97,22 @@ static LoggableException InvalidCommand(const std::string &name,
}
}
-StateStack::StateStack(
- ParserContext &ctx,
- const std::multimap<std::string, const State *> &states)
+/* Class Stack */
+
+Stack::Stack(ParserContext &ctx,
+ const std::multimap<std::string, const State *> &states)
: ctx(ctx), states(states)
{
+ // If the scope instance is not empty we need to deduce the current parser
+ // state
+ if (!ctx.getScope().isEmpty()) {
+ deduceState();
+ }
}
-bool StateStack::deduceState()
+Stack::~Stack() {}
+
+bool Stack::deduceState()
{
// Assemble all states
std::vector<const State *> states;
@@ -68,23 +123,28 @@ bool StateStack::deduceState()
// Fetch the type signature of the scope and derive all possible states,
// abort if no unique parser state was found
std::vector<const State *> possibleStates =
- StateDeductor(ctx.getScope().getStackTypeSignature(), states)
- .deduce();
- if (possibleStates.size() != 1) {
- ctx.getLogger().error(
- "Error while including file: Cannot deduce parser state.");
- return false;
+ StateDeductor(ctx.getScope().getStackTypeSignature(), states).deduce();
+ if (possibleStates.size() != 1U) {
+ throw LoggableException{
+ "Error while including file: Cannot deduce parser state."};
}
// Switch to this state by creating a dummy handler
const State *state = possibleStates[0];
- Handler *handler =
- DefaultHandler::create({ctx, "", *state, *state, SourceLocation{}});
- stack.emplace(handler);
+ stack.emplace(std::shared_ptr<Handler>{EmptyHandler::create({ctx, "", *state, *state, SourceLocation{}})});
+}
+
+bool Stack::handlersValid()
+{
+ for (auto it = stack.crbegin(); it != stack.crend(); it++) {
+ if (!it->valid) {
+ return false;
+ }
+ }
return true;
}
-std::set<std::string> StateStack::expectedCommands()
+std::set<std::string> Stack::expectedCommands()
{
const State *currentState = &(this->currentState());
std::set<std::string> res;
@@ -96,17 +156,17 @@ std::set<std::string> StateStack::expectedCommands()
return res;
}
-const State &StateStack::currentState()
+const State &Stack::currentState()
{
return stack.empty() ? States::None : stack.top()->state();
}
-std::string StateStack::currentCommandName()
+std::string Stack::currentCommandName()
{
return stack.empty() ? std::string{} : stack.top()->name();
}
-const State *StateStack::findTargetState(const std::string &name)
+const State *Stack::findTargetState(const std::string &name)
{
const State *currentState = &(this->currentState());
auto range = states.equal_range(name);
@@ -120,21 +180,26 @@ const State *StateStack::findTargetState(const std::string &name)
return nullptr;
}
-void StateStack::start(const std::string &name, Variant::mapType &args,
- const SourceLocation &location)
+void Stack::command(const Variant &name, const Variant::mapType &args)
{
- State const *targetState = findTargetState(name);
-// TODO: Andreas, please improve this.
-// if (!Utils::isIdentifier(name)) {
-// throw LoggableException(std::string("Invalid identifier \"") + name +
-// std::string("\""));
-// }
+ // Make sure the given identifier is valid
+ if (!Utils::isNamespacedIdentifier(name.asString())) {
+ throw LoggableException(std::string("Invalid identifier \"") +
+ name.asString() + std::string("\""), name);
+ }
+
+ // Try to find a target state for the given command
+ State const *targetState = findTargetState(name.asString());
+ // No target state is found, try to find a wildcard handler for the current
+ // state
if (targetState == nullptr) {
targetState = findTargetState("*");
}
+
+ // No handler has been found at all,
if (targetState == nullptr) {
- throw InvalidCommand(name, expectedCommands());
+ throw buildInvalidCommandException(name.asString(), expectedCommands());
}
// Fetch the associated constructor
@@ -145,20 +210,24 @@ void StateStack::start(const std::string &name, Variant::mapType &args,
// 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);
-}
+ // Instantiate the handler and push it onto the stack
+ Handler *handler =
+ ctor({ctx, name.asString(), *targetState, currentState(), name.getLocation()});
+ stack.emplace_back(std::shared_ptr<Handler>{handler});
-void StateStack::start(std::string name, const Variant::mapType &args,
- const SourceLocation &location)
-{
- Variant::mapType argsCopy(args);
- start(name, argsCopy);
+ // Call the "start" method of the handler, store the result of the start
+ // method as the validity of the handler -- do not call the start method
+ // if the stack is currently invalid (as this may cause further, unwanted
+ // errors)
+ try {
+ stack.back().valid = handlersValid() && handler->start(args);
+ } catch (LoggableException ex) {
+ stack.back().valid = false;
+ logger.log(ex, )
+ }
}
-void StateStack::end()
+void Stack::end()
{
// Check whether the current command could be ended
if (stack.empty()) {
@@ -173,7 +242,7 @@ void StateStack::end()
inst->end();
}
-void StateStack::data(const std::string &data, int field)
+void Stack::data(const std::string &data, int field)
{
// Check whether there is any command the data can be sent to
if (stack.empty()) {
diff --git a/src/core/parser/stack/Stack.hpp b/src/core/parser/stack/Stack.hpp
index b106475..294f7ec 100644
--- a/src/core/parser/stack/Stack.hpp
+++ b/src/core/parser/stack/Stack.hpp
@@ -17,41 +17,126 @@
*/
/**
- * @file ParserStateStack.hpp
+ * @file Stack.hpp
*
* Helper classes for document or description parsers. Contains the
- * ParserStateStack class, which is an pushdown automaton responsible for
+ * Stack class, which is an pushdown automaton responsible for
* accepting commands in the correct order and calling specified handlers.
*
* @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
*/
-#ifndef _OUSIA_PARSER_STATE_STACK_HPP_
-#define _OUSIA_PARSER_STATE_STACK_HPP_
+#ifndef _OUSIA_PARSER_STACK_STACK_HPP_
+#define _OUSIA_PARSER_STACK_STACK_HPP_
#include <cstdint>
#include <map>
#include <memory>
#include <set>
-#include <stack>
#include <vector>
#include <core/common/Variant.hpp>
-#include <core/common/Logger.hpp>
-#include <core/common/Argument.hpp>
-
-#include "Parser.hpp"
-#include "ParserContext.hpp"
-#include "ParserState.hpp"
+#include <core/parser/Parser.hpp>
namespace ousia {
+// Forward declarations
+class ParserContext;
+
+namespace parser_stack {
+
+// Forward declarations
+class Handler;
+class State;
+
/**
- * The ParserStateStack class is a pushdown automaton responsible for turning a
- * command stream into a tree of Node instances.
+ * The HandlerInfo class is used internally by the stack to associate additional
+ * (mutable) data with a handler instance.
*/
-class ParserStateStack {
+class HandlerInfo {
+public:
+ /**
+ * Pointer pointing at the actual handler instance.
+ */
+ std::shared_ptr<Handler> handler;
+
+ /**
+ * Next field index to be passed to the "fieldStart" function of the Handler
+ * class.
+ */
+ size_t fieldIdx;
+
+ /**
+ * Set to true if the handler is valid (which is the case if the "start"
+ * method has returned true). If the handler is invalid, no more calls are
+ * directed at it until it can be removed from the stack.
+ */
+ bool valid : 1;
+
+ /**
+ * Set to true if the handler currently is in a filed.
+ */
+ bool inField : 1;
+
+ /**
+ * Set to true if the handler currently is in the default field.
+ */
+ bool inDefaultField : 1;
+
+ /**
+ * Set to true if the handler currently is in an implicitly started default
+ * field.
+ */
+ bool inImplicitDefaultField : 1;
+
+ /**
+ * Set to false if this field is only opened pro-forma and does not accept
+ * any data. Otherwise set to true.
+ */
+ bool inValidField : 1;
+
+ /**
+ * Set to true, if the default field was already started.
+ */
+ bool hasDefaultField : 1;
+
+ /**
+ * Default constructor of the HandlerInfo class.
+ */
+ HandlerInfo();
+
+ /**
+ * Constructor of the HandlerInfo class, taking a shared_ptr to the handler
+ * to which additional information should be attached.
+ */
+ HandlerInfo(std::shared_ptr<Handler> handler);
+
+ /**
+ * Destructor of the HandlerInfo class (to allow Handler to be forward
+ * declared).
+ */
+ ~HandlerInfo();
+
+ /**
+ * Updates the "field" flags according to a "fieldStart" event.
+ */
+ void fieldStart(bool isDefault, bool isImplicit, bool isValid);
+
+ /**
+ * Updates the "fields" flags according to a "fieldEnd" event.
+ */
+ void fieldEnd();
+};
+
+
+/**
+ * The Stack class is a pushdown automaton responsible for turning a command
+ * stream into a tree of Node instances. It does so by following a state
+ * transition graph and creating a set of Handler instances, which are placed
+ * on the stack.
+ */
+class Stack {
private:
/**
* Reference at the parser context.
@@ -62,12 +147,12 @@ private:
* Map containing all registered command names and the corresponding
* state descriptors.
*/
- const std::multimap<std::string, const ParserState *> &states;
+ const std::multimap<std::string, const State *> &states;
/**
* Internal stack used for managing the currently active Handler instances.
*/
- std::stack<std::shared_ptr<Handler>> stack;
+ std::vector<HandlerInfo> stack;
/**
* Used internally to get all expected command names for the current state.
@@ -83,44 +168,50 @@ private:
*
* @param name is the name of the requested command.
* @return nullptr if no target state was found, a pointer at the target
- *state
- * otherwise.
+ * state otherwise.
*/
- const ParserState *findTargetState(const std::string &name);
+ const State *findTargetState(const std::string &name);
+
+ /**
+ * Tries to reconstruct the parser state from the Scope instance of the
+ * ParserContext given in the constructor. This functionality is needed for
+ * including files,as the Parser of the included file needs to be brought to
+ * an equivalent state as the one in the including file.
+ */
+ void deduceState();
+
+ /**
+ * Returns true if all handlers on the stack are currently valid, or false
+ * if at least one handler is invalid.
+ *
+ * @return true if all handlers on the stack are valid.
+ */
+ bool handlersValid();
public:
/**
- * Creates a new instance of the ParserStateStack class.
+ * Creates a new instance of the Stack class.
*
* @param ctx is the parser context the parser stack is working on.
* @param states is a map containing the command names and pointers at the
- * corresponding ParserState instances.
+ * corresponding State instances.
*/
- ParserStateStack(
+ Stack(
ParserContext &ctx,
- const std::multimap<std::string, const ParserState *> &states);
+ const std::multimap<std::string, const State *> &states);
/**
- * Tries to reconstruct the parser state from the Scope instance of the
- * ParserContext given in the constructor. This functionality is needed for
- * including files,as the Parser of the included file needs to be brought to
- + an equivalent state as the one in the including file.
- *
- * @param scope is the ParserScope instance from which the ParserState
- * should be reconstructed.
- * @param logger is the logger instance to which error messages should be
- * written.
- * @return true if the operation was sucessful, false otherwise.
+ * Destructor of the Stack class.
*/
- bool deduceState();
+ ~Stack();
/**
- * Returns the state the ParserStateStack instance currently is in.
+ * Returns the state the Stack instance currently is in.
*
* @return the state of the currently active Handler instance or STATE_NONE
* if no handler is on the stack.
*/
- const ParserState &currentState();
+ const State &currentState();
/**
* Returns the command name that is currently being handled.
@@ -135,30 +226,35 @@ public:
*
* @param name is the name of the command (including the namespace
* separator ':') and its corresponding location. Must be a string variant.
- * @param args is a map variant containing the arguments that were passed to
- * the command.
+ * @param args is a map containing the arguments that were passed to the
+ * command.
*/
- void command(Variant name, Variant args);
+ void command(const Variant &name, const Variant::mapType &args);
/**
* Function that should be called whenever a new field starts. Fields of the
- * same command may not be separated by calls to
+ * same command may not be separated by calls to data or annotations. Doing
+ * so will result in a LoggableException.
+ *
+ * @param isDefault should be set to true if the started field explicitly
+ * is the default field.
*/
- void fieldStart();
+ void fieldStart(bool isDefault);
/**
- * Function that should be called whenever a field ends.
+ * Function that should be called whenever a field ends. Calling this
+ * function if there is no field to end will result in a LoggableException.
*/
void fieldEnd();
/**
* Function that shuold be called whenever character data is found in the
- * input stream.
+ * input stream. May only be called if the currently is a command on the
+ * stack.
*
- * @param data is a variant of any type containing the data that was parsed
- * as data.
+ * @param data is a string variant containing the data that has been found.
*/
- void data(Variant data);
+ void data(const Variant &data);
/**
* Function that should be called whenever an annotation starts.
@@ -167,7 +263,7 @@ public:
* @param args is a map variant containing the arguments that were passed
* to the annotation.
*/
- void annotationStart(Variant name, Variant args);
+ void annotationStart(const Variant &className, const Variant &args);
/**
* Function that should be called whenever an annotation ends.
@@ -175,7 +271,7 @@ public:
* @param name is the name of the annotation class that was ended.
* @param annotationName is the name of the annotation that was ended.
*/
- void annotationEnd(Variant name, Variant annotationName);
+ void annotationEnd(const Variant &className, const Variant &elementName);
/**
* Function that should be called whenever a previously registered token
@@ -186,6 +282,7 @@ public:
void token(Variant token);
};
}
+}
-#endif /* _OUSIA_PARSER_STATE_STACK_HPP_ */
+#endif /* _OUSIA_STACK_HPP_ */