diff options
Diffstat (limited to 'src/core')
| -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_ */  | 
