diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-02-15 00:12:04 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-02-15 00:12:04 +0100 |
commit | 26766a588d988e635112878aba71c69c8f057c16 (patch) | |
tree | 3c68d600232365e1b6daad4b6441eabe70556d45 | |
parent | c5fde12cbac6907da4e267492206b2df3dad01f8 (diff) |
Renamed StateStack to Stack
-rw-r--r-- | src/core/parser/stack/Stack.cpp | 151 | ||||
-rw-r--r-- | src/core/parser/stack/Stack.hpp | 195 |
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 ¤tState(); + const State ¤tState(); /** * 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_ */ |