diff options
| author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-02-01 21:30:15 +0100 | 
|---|---|---|
| committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-02-01 21:30:15 +0100 | 
| commit | 28fc6ac92cf8b058da22c54c939fc225779ac26d (patch) | |
| tree | 6caf90848f577109fd2e89ff07af88f93d39d0c1 /src | |
| parent | ba5439849b72ac341344d55f2bf05ccf11e37410 (diff) | |
Improved ParserStack state description
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/parser/ParserStack.cpp | 114 | ||||
| -rw-r--r-- | src/core/parser/ParserStack.hpp | 223 | ||||
| -rw-r--r-- | src/core/parser/ParserState.cpp | 105 | ||||
| -rw-r--r-- | src/core/parser/ParserState.hpp | 254 | ||||
| -rw-r--r-- | src/plugins/xml/XmlParser.cpp | 297 | 
5 files changed, 584 insertions, 409 deletions
| diff --git a/src/core/parser/ParserStack.cpp b/src/core/parser/ParserStack.cpp index 02b142a..51a7a13 100644 --- a/src/core/parser/ParserStack.cpp +++ b/src/core/parser/ParserStack.cpp @@ -28,6 +28,10 @@ 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; @@ -35,12 +39,12 @@ public:  	void start(Variant::mapType &args) override {}  	void end() override {} -}; -static Handler *createDefaultHandler(const HandlerData &handlerData) -{ -	return new DefaultHandler{handlerData}; -} +	static Handler *create(const HandlerData &handlerData) +	{ +		return new DefaultHandler{handlerData}; +	} +};  /* Class Handler */ @@ -54,40 +58,13 @@ void Handler::data(const std::string &data, int field)  	}  } -void Handler::child(std::shared_ptr<Handler> handler) -{ -	// Do nothing here -} - -/* Class HandlerDescriptor */ - -HandlerInstance HandlerDescriptor::create(const ParserContext &ctx, -                                          std::string name, State parentState, -                                          bool isChild, Variant::mapType &args, -                                          const SourceLocation &location) const -{ -	Handler *h; -	HandlerData data{ctx, name, targetState, parentState, isChild, location}; -	if (ctor) { -		h = ctor(data); -	} else { -		h = createDefaultHandler(data); -	} - -	// Canonicalize the arguments -	arguments.validateMap(args, ctx.getLogger(), true); - -	h->start(args); -	return HandlerInstance(h, this); -} -  /* Class ParserStack */  /**   * Returns an Exception that should be thrown when a currently invalid command   * is thrown.   */ -static LoggableException invalidCommand(const std::string &name, +static LoggableException InvalidCommand(const std::string &name,                                          const std::set<std::string> &expected)  {  	if (expected.empty()) { @@ -104,47 +81,72 @@ static LoggableException invalidCommand(const std::string &name,  	}  } -std::set<std::string> ParserStack::expectedCommands(State state) +ParserStack::ParserStack( +    ParserContext &ctx, +    const std::multimap<std::string, const ParserState *> &states) +    : ctx(ctx), states(states) +{ +} + +std::set<std::string> ParserStack::expectedCommands(const ParserState &state)  {  	std::set<std::string> res; -	for (const auto &v : handlers) { -		if (v.second.parentStates.count(state)) { +	for (const auto &v : states) { +		if (v.second->parents.count(&state)) {  			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(); +} +  void ParserStack::start(std::string name, Variant::mapType &args,                          const SourceLocation &location)  {  	// Fetch the current handler and the current state -	const HandlerInstance *h = stack.empty() ? nullptr : &stack.top(); -	const State curState = currentState(); -	bool isChild = false; +	ParserState const *currentState = &(this->currentState());  	// Fetch the correct Handler descriptor for this -	const HandlerDescriptor *descr = nullptr; -	auto range = handlers.equal_range(name); +	ParserState const *targetState = nullptr; +	HandlerConstructor ctor = nullptr; +	auto range = states.equal_range(name);  	for (auto it = range.first; it != range.second; it++) { -		const std::set<State> &parentStates = it->second.parentStates; -		if (parentStates.count(curState) || parentStates.count(STATE_ALL)) { -			descr = &(it->second); +		const ParserStateSet &parents = it->second->parents; +		if (parents.count(currentState) || parents.count(&ParserStates::All)) { +			targetState = it->second; +			ctor = targetState->elementHandler ? targetState->elementHandler +			                                   : DefaultHandler::create;  			break;  		}  	} -	if (!descr && currentArbitraryChildren()) { -		isChild = true; -		descr = h->descr; + +	// Try to use the child handler if one was given +	if (!targetState && currentState->childHandler) { +		targetState = currentState; +		ctor = targetState->childHandler;  	}  	// No descriptor found, throw an exception. -	if (!descr) { -		throw invalidCommand(name, expectedCommands(curState)); +	if (!targetState || !ctor) { +		throw InvalidCommand(name, expectedCommands(*currentState));  	} +	// Canonicalize the arguments, allow additional arguments +	targetState->arguments.validateMap(args, ctx.getLogger(), true); +  	// Instantiate the handler and call its start function -	stack.emplace(descr->create(ctx, name, curState, isChild, args, location)); +	Handler *handler = ctor({ctx, name, *targetState, *currentState, location}); +	handler->start(args); +	stack.emplace(handler);  }  void ParserStack::start(std::string name, const Variant::mapType &args, @@ -162,17 +164,11 @@ void ParserStack::end()  	}  	// Remove the current HandlerInstance from the stack -	HandlerInstance inst{stack.top()}; +	std::shared_ptr<Handler> inst{stack.top()};  	stack.pop();  	// Call the end function of the last Handler -	inst.handler->end(); - -	// Call the "child" function of the parent Handler in the stack -	// (if one exists). -	if (!stack.empty()) { -		stack.top().handler->child(inst.handler); -	} +	inst->end();  }  void ParserStack::data(const std::string &data, int field) @@ -183,7 +179,7 @@ void ParserStack::data(const std::string &data, int field)  	}  	// Pass the data to the current Handler instance -	stack.top().handler->data(data, field); +	stack.top()->data(data, field);  }  } diff --git a/src/core/parser/ParserStack.hpp b/src/core/parser/ParserStack.hpp index 031ce68..9bb080e 100644 --- a/src/core/parser/ParserStack.hpp +++ b/src/core/parser/ParserStack.hpp @@ -43,18 +43,11 @@  #include "Parser.hpp"  #include "ParserContext.hpp" +#include "ParserState.hpp"  namespace ousia {  /** - * The State type alias is used to - */ -using State = int16_t; - -static const State STATE_ALL = -2; -static const State STATE_NONE = -1; - -/**   * Struct collecting all the data that is being passed to a Handler instance.   */  struct HandlerData { @@ -72,18 +65,12 @@ struct HandlerData {  	/**  	 * Contains the current state of the state machine.  	 */ -	const State state; +	const ParserState &state;  	/**  	 * Contains the state of the state machine when the parent node was handled.  	 */ -	const State parentState; - -	/** -	 * Set to true if the tag that is being handled is not the tag that was -	 * specified in the state machine but a child tag of that tag. -	 */ -	const bool isChild; +	const ParserState &parentState;  	/**  	 * Current source code location. @@ -97,17 +84,14 @@ struct HandlerData {  	 * @param name is the name of the string.  	 * @param state is the state this handler was called for.  	 * @param parentState is the state of the parent command. -	 * @param isChild specifies whether this handler was called not for the -	 * command that was specified in the state machine but a child command.  	 * @param location is the location at which the handler is created.  	 */ -	HandlerData(const ParserContext &ctx, std::string name, State state, -	            State parentState, bool isChild, const SourceLocation location) +	HandlerData(const ParserContext &ctx, std::string name, const ParserState &state, +	            const ParserState &parentState, const SourceLocation location)  	    : ctx(ctx),  	      name(std::move(name)),  	      state(state),  	      parentState(parentState), -	      isChild(isChild),  	      location(location){};  }; @@ -137,24 +121,68 @@ public:  	 */  	virtual ~Handler(){}; +	/** +	 * Returns the command name for which the handler was created. +	 * +	 * @return a const reference at the command name. +	 */  	const std::string &name() { return handlerData.name; } +	/** +	 * Returns a reference at the ParserScope instance. +	 * +	 * @return a reference at the ParserScope instance. +	 */  	ParserScope &scope() { return handlerData.ctx.getScope(); } +	/** +	 * Returns a reference at the Manager instance which manages all nodes. +	 * +	 * @return a referance at the Manager instance. +	 */  	Manager &manager() { return handlerData.ctx.getManager(); } +	/** +	 * Returns a reference at the Logger instance used for logging error +	 * messages. +	 * +	 * @return a reference at the Logger instance. +	 */  	Logger &logger() { return handlerData.ctx.getLogger(); } +	/** +	 * Returns a reference at the Project Node, representing the project into +	 * which the file is currently being parsed. +	 * +	 * @return a referance at the Project Node. +	 */  	Rooted<Project> project() { return handlerData.ctx.getProject(); } -	State state() { return handlerData.state; } +	/** +	 * Reference at the ParserState descriptor for which this Handler was +	 * created. +	 * +	 * @return a const reference at the constructing ParserState descriptor. +	 */ +	const ParserState &state() { return handlerData.state; } -	State parentState() { return handlerData.parentState; } +	/** +	 * Reference at the ParserState descriptor of the parent state of the state +	 * for which this Handler was created. Set to ParserStates::None if there +	 * is no parent state. +	 * +	 * @return a const reference at the parent state of the constructing +	 * ParserState descriptor. +	 */ +	const ParserState &parentState() { return handlerData.parentState; } +	/** +	 * Returns the current location in the source file. +	 * +	 * @return the current location in the source file. +	 */  	SourceLocation location() { return handlerData.location; } -	bool isChild() { return handlerData.isChild; } -  	/**  	 * Called when the command that was specified in the constructor is  	 * instanciated. @@ -179,13 +207,6 @@ public:  	 * depends on the format that is being parsed).  	 */  	virtual void data(const std::string &data, int field); - -	/** -	 * Called whenever a direct child element was created and has ended. -	 * -	 * @param handler is a reference at the child Handler instance. -	 */ -	virtual void child(std::shared_ptr<Handler> handler);  };  /** @@ -198,99 +219,6 @@ public:   */  using HandlerConstructor = Handler *(*)(const HandlerData &handlerData); -struct HandlerDescriptor; - -/** - * Used internlly by StateStack to store Handler instances and parameters - * from HandlerDescriptor that are not stored in the Handler instance - * itself. Instances of the HandlerInstance class can be created using the - * HandlerDescriptor "create" method. - */ -struct HandlerInstance { -	/** -	 * Pointer at the actual handler instance. -	 */ -	std::shared_ptr<Handler> handler; - -	/** -	 * Pointer pointing at the descriptor from which the handler instance was -	 * derived. -	 */ -	const HandlerDescriptor *descr; - -	HandlerInstance(Handler *handler, const HandlerDescriptor *descr) -	    : handler(handler), descr(descr) -	{ -	} -}; - -/** - * Used internally by StateStack to store the pushdown automaton - * description. - */ -struct HandlerDescriptor { -	/** -	 * The valid parent states. -	 */ -	const std::set<State> parentStates; - -	/** -	 * Pointer at a function which creates a new concrete Handler instance. -	 */ -	const HandlerConstructor ctor; - -	/** -	 * The target state for the registered handler. -	 */ -	const State targetState; - -	/** -	 * Set to true if this handler instance allows arbitrary children as -	 * tags. -	 */ -	const bool arbitraryChildren; - -	/** -	 * Reference at an argument descriptor that should be used for validating -	 * the incomming arguments. -	 */ -	const Arguments arguments; - -	/** -	 * Constructor of the HandlerDescriptor class. -	 * -	 * @param parentStates is a set of states in which a new handler of this -	 * type may be instantiated. -	 * @param ctor is a function pointer pointing at a function that -	 * instantiates the acutal Handler instance. -	 * @param targetState is the state the ParserStack switches to after -	 * instantiating an in instance of the described Handler instances. -	 * @param arbitraryChildren allows the Handler instance to handle any child -	 * node. -	 * @param arguments is an optional argument descriptor used for validating -	 * the arguments that are passed to the instantiation of a handler function. -	 */ -	HandlerDescriptor(std::set<State> parentStates, HandlerConstructor ctor, -	                  State targetState, bool arbitraryChildren = false, -	                  Arguments arguments = Arguments::None) -	    : parentStates(std::move(parentStates)), -	      ctor(ctor), -	      targetState(targetState), -	      arbitraryChildren(arbitraryChildren), -	      arguments(std::move(arguments)) -	{ -	} - -	/** -	 * Creates an instance of the concrete Handler class represented by the -	 * HandlerDescriptor and calls its start function. -	 */ -	HandlerInstance create(const ParserContext &ctx, std::string name, -	                       State parentState, bool isChild, -	                       Variant::mapType &args, -	                       const SourceLocation &location) const; -}; -  /**   * The ParserStack class is a pushdown automaton responsible for turning a   * command stream into a tree of Node instances. @@ -303,21 +231,15 @@ private:  	ParserContext &ctx;  	/** -	 * Current location in the source code. -	 */ -	SourceLocation location; - -	/**  	 * Map containing all registered command names and the corresponding -	 * handler -	 * descriptor. +	 * state descriptors.  	 */ -	const std::multimap<std::string, HandlerDescriptor> &handlers; +	const std::multimap<std::string, const ParserState *> &states;  	/**  	 * Internal stack used for managing the currently active Handler instances.  	 */ -	std::stack<HandlerInstance> stack; +	std::stack<std::shared_ptr<Handler>> stack;  	/**  	 * Used internally to get all expected command names for the given state @@ -327,19 +249,18 @@ private:  	 * @param state is the state for which all expected command names should be  	 * returned.  	 */ -	std::set<std::string> expectedCommands(State state); +	std::set<std::string> expectedCommands(const ParserState &state);  public:  	/**  	 * Creates a new instance of the ParserStack class.  	 *  	 * @param ctx is the parser context the parser stack is working on. -	 * @param handlers is a map containing the command names and the -	 * corresponding HandlerDescriptor instances. +	 * @param states is a map containing the command names and pointers at the +	 * corresponding ParserState instances.  	 */  	ParserStack(ParserContext &ctx, -	            const std::multimap<std::string, HandlerDescriptor> &handlers) -	    : ctx(ctx), handlers(handlers){}; +	            const std::multimap<std::string, const ParserState*> &states);  	/**  	 * Returns the state the ParserStack instance currently is in. @@ -347,10 +268,7 @@ public:  	 * @return the state of the currently active Handler instance or STATE_NONE  	 * if no handler is on the stack.  	 */ -	State currentState() -	{ -		return stack.empty() ? STATE_NONE : stack.top().handler->state(); -	} +	const ParserState ¤tState();  	/**  	 * Returns the command name that is currently being handled. @@ -358,26 +276,14 @@ public:  	 * @return the name of the command currently being handled by the active  	 * Handler instance or an empty string if no handler is currently active.  	 */ -	std::string currentName() -	{ -		return stack.empty() ? std::string{} : stack.top().handler->name(); -	} - -	/** -	 * Returns whether the current command handler allows arbitrary children. -	 * -	 * @return true if the handler allows arbitrary children, false otherwise. -	 */ -	bool currentArbitraryChildren() -	{ -		return stack.empty() ? false : stack.top().descr->arbitraryChildren; -	} +	std::string currentCommandName();  	/**  	 * Function that should be called whenever a new command starts.  	 *  	 * @param name is the name of the command.  	 * @param args is a map from strings to variants (argument name and value). +	 * Note that the passed map will be modified.  	 * @param location is the location in the source file at which the command  	 * starts.  	 */ @@ -392,7 +298,8 @@ public:  	 * @param location is the location in the source file at which the command  	 * starts.  	 */ -	void start(std::string name, const Variant::mapType &args, +	void start(std::string name, +	           const Variant::mapType &args = Variant::mapType{},  	           const SourceLocation &location = SourceLocation{});  	/** diff --git a/src/core/parser/ParserState.cpp b/src/core/parser/ParserState.cpp new file mode 100644 index 0000000..825ab84 --- /dev/null +++ b/src/core/parser/ParserState.cpp @@ -0,0 +1,105 @@ +/* +    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 "ParserState.hpp" + +namespace ousia { + +/* Class ParserState */ + +ParserState::ParserState() : elementHandler(nullptr), childHandler(nullptr) {} + +ParserState::ParserState(ParserStateSet parents, Arguments arguments, +                         RttiSet supportedTypes, +                         HandlerConstructor elementHandler, +                         HandlerConstructor childHandler) +    : parents(parents), +      arguments(arguments), +      supportedTypes(supportedTypes), +      elementHandler(elementHandler), +      childHandler(childHandler) +{ +} + +ParserState::ParserState(const ParserStateBuilder &builder) +    : ParserState(builder.build()) +{ +} + +/* Class ParserStateBuilder */ + +ParserStateBuilder &ParserStateBuilder::copy(const ParserState &state) +{ +	this->state = state; +	return *this; +} + +ParserStateBuilder &ParserStateBuilder::parent(const ParserState *parent) +{ +	state.parents.insert(parent); +	return *this; +} + +ParserStateBuilder &ParserStateBuilder::parents(const ParserStateSet &parents) +{ +	state.parents.insert(parents.begin(), parents.end()); +	return *this; +} + +ParserStateBuilder &ParserStateBuilder::arguments(const Arguments &arguments) +{ +	state.arguments = arguments; +	return *this; +} + +ParserStateBuilder &ParserStateBuilder::supportedType(const Rtti *type) +{ +	state.supportedTypes.insert(type); +	return *this; +} + +ParserStateBuilder &ParserStateBuilder::supportedTypes(const RttiSet &types) +{ +	state.supportedTypes.insert(types.begin(), types.end()); +	return *this; +} + +ParserStateBuilder &ParserStateBuilder::elementHandler( +    HandlerConstructor elementHandler) +{ +	state.elementHandler = elementHandler; +	return *this; +} + +ParserStateBuilder &ParserStateBuilder::childHandler( +    HandlerConstructor childHandler) +{ +	state.childHandler = childHandler; +	return *this; +} + +const ParserState &ParserStateBuilder::build() const { return state; } + +/* Constant initializations */ + +namespace ParserStates { +const ParserState All; +const ParserState None; +} +} + diff --git a/src/core/parser/ParserState.hpp b/src/core/parser/ParserState.hpp new file mode 100644 index 0000000..6b7182d --- /dev/null +++ b/src/core/parser/ParserState.hpp @@ -0,0 +1,254 @@ +/* +    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/>. +*/ + +/** + * @file ParserState.hpp + * + * Defines the ParserState class used within the ParserStack pushdown + * automaton and the ParserStateBuilder class for convenient construction of + * such classes. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + +#ifndef _OUSIA_PARSER_STATE_HPP_ +#define _OUSIA_PARSER_STATE_HPP_ + +#include <unordered_set> + +#include <core/common/Rtti.hpp> +#include <core/common/Argument.hpp> + +namespace ousia { + +// Forward declarations +class ParserStateBuilder; +class ParserState; +class HandlerData; +class Handler; +using HandlerConstructor = Handler *(*)(const HandlerData &handlerData); + +/** + * Set of pointers of parser states -- used for specifying a set of parent + * states. + */ +using ParserStateSet = std::unordered_set<const ParserState *>; + +/** + * Class used for the complete specification of a ParserState. Stores possible + * parent states, state handlers and arguments to be passed to that state. + */ +struct ParserState { +	/** +	 * Vector containing all possible parent states. +	 */ +	ParserStateSet parents; + +	/** +	 * Descriptor of the arguments that should be passed to the handler. +	 */ +	Arguments arguments; + +	/** +	 * Rtti types that are reported as supported when including or importing new +	 * files while in this state. This value is passed as "supportedTypes" to +	 * either the "import" or "include" function. +	 */ +	RttiSet supportedTypes; + +	/** +	 * 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. +	 */ +	HandlerConstructor elementHandler; + +	/** +	 * Pointer at a function which creates a new concrete Handler instance for +	 * all child elements for which no matching state is defined. May be nullptr +	 * in which case no such elements are allowed. +	 */ +	HandlerConstructor childHandler; + +	/** +	 * Default constructor, initializes the handlers with nullptr. +	 */ +	ParserState(); + +	/** +	 * Constructor taking values for all fields. Use the ParserStateBuilder +	 * class for a more convenient construction of ParserState instances. +	 * +	 * @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 supportedTypes is a set of Rtti types that are reported as +	 * supported when including or importing new files while in this state. +	 * @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. +	 * @param childHandler is a pointer at a function which creates a new +	 * concrete Handler instance for all child elements for which no matching +	 * state is defined. May be nullptr in which case no such elements are +	 * allowed. +	 */ +	ParserState(ParserStateSet parents, Arguments arguments = Arguments{}, +	            RttiSet supportedTypes = RttiSet{}, +	            HandlerConstructor elementHandler = nullptr, +	            HandlerConstructor childHandler = nullptr); + +	/** +	 * Creates this ParserState from the given ParserStateBuilder instance. +	 */ +	ParserState(const ParserStateBuilder &builder); +}; + +/** + * The ParserStateBuilder class is a class used for conveniently building new + * ParserState instances. + */ +class ParserStateBuilder { +private: +	/** +	 * ParserState instance that is currently being built by the +	 * ParserStateBuilder. +	 */ +	ParserState state; + +public: +	/** +	 * Copies the ParserState instance and uses it as internal state. Overrides +	 * all changes made by the ParserStateBuilder. +	 * +	 * @param state is the state that should be copied. +	 * @return a reference at this ParserStateBuilder instance for method +	 * chaining. +	 */ +	ParserStateBuilder ©(const ParserState &state); + +	/** +	 * Adds the given ParserState to the list of supported parent states. +	 * +	 * @param parent is a pointer at the parent ParserState instance that should +	 * be added as possible parent state. +	 * @return a reference at this ParserStateBuilder instance for method +	 * chaining. +	 */ +	ParserStateBuilder &parent(const ParserState *parent); + +	/** +	 * Adds the ParserState instances in the given ParserStateSet to the list of +	 * supported parent states. +	 * +	 * @param parents is a set of pointers at ParserState instances that should +	 * be added as possible parent states. +	 * @return a reference at this ParserStateBuilder instance for method +	 * chaining. +	 */ +	ParserStateBuilder &parents(const ParserStateSet &parents); + +	/** +	 * Sets the arguments that should be passed to the parser state handler to +	 * those given as argument. +	 * +	 * @param arguments is the Arguments instance describing the Arguments that +	 * should be parsed to a Handler for this ParserState. +	 * @return a reference at this ParserStateBuilder instance for method +	 * chaining. +	 */ +	ParserStateBuilder &arguments(const Arguments &arguments); + +	/** +	 * Adds the type described by the given Rtti pointer to the set of supported +	 * types. These "supported types" describe a set of Rtti types that are +	 * reported as supported when including or importing new files while in this +	 * state. +	 * +	 * @param type is the type that should be added to the SupportedTypes list. +	 * @return a reference at this ParserStateBuilder instance for method +	 * chaining. +	 */ +	ParserStateBuilder &supportedType(const Rtti *type); + +	/** +	 * Adds the type described by the given Rtti pointer to the set of supported +	 * types. These "supported types" describe a set of Rtti types that are +	 * reported as supported when including or importing new files while in this +	 * state. +	 * +	 * @param type is the type that should be added to the SupportedTypes list. +	 * @return a reference at this ParserStateBuilder instance for method +	 * chaining. +	 */ +	ParserStateBuilder &supportedTypes(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 +	 * the default value). +	 * +	 * @param elementHandler is the HandlerConstructor that should create a +	 * new Handler instance. +	 * @return a reference at this ParserStateBuilder instance for method +	 * chaining. +	 */ +	ParserStateBuilder &elementHandler(HandlerConstructor elementHandler); + +	/** +	 * Sets the constructor for the child handler. The constructor creates a new +	 * concrete Handler instance for all child elements for which no matching +	 * state is defined. May be nullptr in which case no such elements are +	 * allowed. +	 * +	 * @param childHandler is the HandlerConstructor that should point at the +	 * constructor of the Handler instance for child elements. +	 * @return a reference at this ParserStateBuilder instance for method +	 * chaining. +	 */ +	ParserStateBuilder &childHandler(HandlerConstructor childHandler); + +	/** +	 * Returns a reference at the internal ParserState instance that was built +	 * using the ParserStateBuilder. +	 * +	 * @return the built ParserState. +	 */ +	const ParserState &build() const; +}; + +/** + * The ParserStates namespace contains all the global state constants used + * in the ParserStack class. + */ +namespace ParserStates { +/** + * State representing all states. + */ +extern const ParserState All; + +/** + * State representing the initial state. + */ +extern const ParserState None; +} + +} + +#endif /* _OUSIA_PARSER_STATE_HPP_ */ + diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp index 3b7e37f..42620c3 100644 --- a/src/plugins/xml/XmlParser.cpp +++ b/src/plugins/xml/XmlParser.cpp @@ -72,54 +72,6 @@ public:  	}  }; -class HeadHandler : public Handler { -public: -	using Handler::Handler; - -	void start(Variant::mapType &args) override -	{ -		// Make sure the "HEAD" node is actually allowed here -		if (scope().getFlag(ParserFlag::POST_HEAD)) { -			throw LoggableException{ -			    "\"head\" tag not allowed here, head was already specified or " -			    "another command was given first", -			    location()}; -		} - -		// Insert a new HeadNode instance -		scope().push(new HeadNode{manager()}); -	} - -	void end() override -	{ -		// Remove the HeadNode instance from the stack -		scope().pop(); -		scope().setFlag(ParserFlag::POST_HEAD, true); -	} - -	static Handler *create(const HandlerData &handlerData) -	{ -		return new HeadHandler{handlerData}; -	} -}; - -class DisableHeadHandler : public Handler { -public: -	using Handler::Handler; - -	void start(Variant::mapType &args) override -	{ -		scope().setFlag(ParserFlag::POST_HEAD, true); -	} - -	void end() override {} - -	static Handler *create(const HandlerData &handlerData) -	{ -		return new DisableHeadHandler{handlerData}; -	} -}; -  class TypesystemHandler : public Handler {  public:  	using Handler::Handler; @@ -194,7 +146,7 @@ public:  	}  }; -class StructFieldHandler : public Handler { +class TypesystemStructFieldHandler : public Handler {  public:  	using Handler::Handler; @@ -238,11 +190,11 @@ public:  	static Handler *create(const HandlerData &handlerData)  	{ -		return new StructFieldHandler{handlerData}; +		return new TypesystemStructFieldHandler{handlerData};  	}  }; -class ConstantHandler : public Handler { +class TypesystemConstantHandler : public Handler {  public:  	using Handler::Handler; @@ -272,7 +224,7 @@ public:  	static Handler *create(const HandlerData &handlerData)  	{ -		return new ConstantHandler{handlerData}; +		return new TypesystemConstantHandler{handlerData};  	}  }; @@ -335,146 +287,106 @@ public:  	}  }; -/* Document structure */ -static const State STATE_DOCUMENT = 0; -static const State STATE_DOCUMENT_HEAD = 1; - -/* Special commands */ -static const State STATE_IMPORT = 100; -static const State STATE_INCLUDE = 101; - -/* Type system definitions */ -static const State STATE_TYPESYSTEM = 200; -static const State STATE_TYPESYSTEM_HEAD = 201; -static const State STATE_TYPES = 202; -static const State STATE_CONSTANTS = 203; -static const State STATE_CONSTANT = 204; -static const State STATE_ENUM = 205; -static const State STATE_STRUCT = 206; -static const State STATE_FIELD = 207; - -/* Domain definitions */ -static const State STATE_DOMAIN = 300; -static const State STATE_DOMAIN_HEAD = 301; -static const State STATE_DOMAIN_STRUCTS = 302; -static const State STATE_DOMAIN_STRUCT = 303; -static const State STATE_DOMAIN_FIELDS = 304; -static const State STATE_DOMAIN_FIELD = 305; -static const State STATE_DOMAIN_PRIMITIVE_FIELD = 306; -static const State STATE_DOMAIN_CHILDREN = 307; -static const State STATE_DOMAIN_CHILD = 308; -static const State STATE_DOMAIN_CHILD_REF = 309; -static const State STATE_DOMAIN_PARENTS = 310; -static const State STATE_DOMAIN_PARENT = 311; -static const State STATE_DOMAIN_PARENT_FIELD = 312; -static const State STATE_DOMAIN_PARENT_FIELD_REF = 313; -static const State STATE_DOMAIN_ANNOTATIONS = 314; -static const State STATE_DOMAIN_ANNOTATION = 315; - -static const std::multimap<std::string, HandlerDescriptor> XML_HANDLERS{ -    /* Document tags */ -    {"document", -     {{STATE_NONE}, -      DocumentHandler::create, -      STATE_DOCUMENT, -      true, -      {Argument::String("name", "")}}}, -    {"head", {{STATE_DOCUMENT}, HeadHandler::create, STATE_DOCUMENT_HEAD}}, - -    /* Special commands */ -    {"import", -     {{STATE_DOCUMENT_HEAD, STATE_TYPESYSTEM_HEAD}, nullptr, STATE_IMPORT}}, -    {"include", {{STATE_ALL}, nullptr, STATE_INCLUDE}}, - -    /* Typesystem */ -    {"typesystem", -     {{STATE_NONE, STATE_DOMAIN_HEAD}, -      TypesystemHandler::create, -      STATE_TYPESYSTEM, -      false, -      {Argument::String("name")}}}, -    {"head", {{STATE_TYPESYSTEM}, HeadHandler::create, STATE_TYPESYSTEM}}, -    {"types", {{STATE_TYPESYSTEM}, DisableHeadHandler::create, STATE_TYPES}}, -    {"enum", {{STATE_TYPES}, nullptr, STATE_ENUM}}, -    {"struct", -     {{STATE_TYPES}, -      TypesystemStructHandler::create, -      STATE_STRUCT, -      false, -      {Argument::String("name"), Argument::String("parent", "")}}}, -    {"field", -     {{STATE_STRUCT}, -      StructFieldHandler::create, -      STATE_FIELD, -      false, -      {Argument::String("name"), Argument::String("type"), -       Argument::Any("default", Variant::fromObject(nullptr))}}}, -    {"constants", -     {{STATE_TYPESYSTEM}, DisableHeadHandler::create, STATE_CONSTANTS}}, -    {"constant", -     {{STATE_CONSTANTS}, -      ConstantHandler::create, -      STATE_CONSTANT, -      false, -      {Argument::String("name"), Argument::String("type"), -       Argument::Any("value")}}}, - -    /* Domain */ -    {"domain", -     {{STATE_NONE, STATE_DOCUMENT_HEAD}, -      DomainHandler::create, -      STATE_DOMAIN, -      false, -      {Argument::String("name")}}}, -    {"head", -     {{STATE_DOMAIN}, -      HeadHandler::create, -      STATE_DOMAIN_HEAD, -      false, -      Arguments{}}}, -    {"structs", -     {{STATE_DOMAIN}, -      DisableHeadHandler::create, -      STATE_DOMAIN_STRUCTS, -      false, -      Arguments{}}}, -    {"struct", -     {{STATE_DOMAIN_STRUCTS}, -      DomainStructHandler::create, -      STATE_DOMAIN_STRUCT, -      false, -      Arguments{Argument::String("name"), -                Argument::Cardinality("cardinality", AnyCardinality), -                Argument::Bool("isRoot", false), -                Argument::Bool("transparent", false), -                Argument::String("isa", "")}}}, -    {"fields", -     {{STATE_DOMAIN_STRUCT, STATE_DOMAIN_ANNOTATIONS}, -     nullptr, -     STATE_DOMAIN_FIELDS, -     false, -     Arguments{}}}, -    {"field", -     {{STATE_DOMAIN_FIELDS}, -     nullptr, -     STATE_DOMAIN_FIELD, -     false, -     Arguments{Argument::String("name", ""), Argument::Bool("isSubtree", false), -               Argument::Bool("optional", false)}}}, -    {"primitive", -     {{STATE_DOMAIN_FIELDS}, -     nullptr, -     STATE_DOMAIN_PRIMITIVE_FIELD, -     false, -     Arguments{Argument::String("name", ""), Argument::Bool("optional", false), -               Argument::String("type")}}}, -    {"annotations", -     {{STATE_DOMAIN}, -      DisableHeadHandler::create, -      STATE_DOMAIN_ANNOTATIONS, -      false, -      Arguments{}}}}; +class ImportHandler : public Handler { +public: +	using Handler::Handler; + +	void start(Variant::mapType &args) override +	{ +		//	scope().import(); +	} + +	void end() override {} + +	static Handler *create(const HandlerData &handlerData) +	{ +		return new ImportHandler{handlerData}; +	} +}; +namespace ParserStates { +/* Document states */ +static const ParserState Document = +    ParserStateBuilder() +        .parent(&None) +        .elementHandler(DocumentHandler::create) +        .arguments({Argument::String("name", "")}); + +/* Domain states */ +static const ParserState Domain = ParserStateBuilder() +                                      .parents({&None, &Document}) +                                      .elementHandler(DomainHandler::create) +                                      .arguments({Argument::String("name")}); +static const ParserState DomainStruct = +    ParserStateBuilder() +        .parent(&Domain) +        .elementHandler(DomainStructHandler::create) +        .arguments({Argument::String("name"), +                    Argument::Cardinality("cardinality", AnyCardinality), +                    Argument::Bool("isRoot", false), +                    Argument::Bool("transparent", false), +                    Argument::String("isa", "")}); +static const ParserState DomainStructFields = +    ParserStateBuilder().parent(&DomainStruct).arguments({}); +static const ParserState DomainStructField = +    ParserStateBuilder().parent(&DomainStructFields).arguments( +        {Argument::String("name", ""), Argument::Bool("isSubtree", false), +         Argument::Bool("optional", false)}); +static const ParserState DomainStructPrimitive = +    ParserStateBuilder().parent(&DomainStructFields).arguments( +        {Argument::String("name", ""), Argument::Bool("optional", false), +         Argument::String("type")}); + +/* Typesystem states */ +static const ParserState Typesystem = +    ParserStateBuilder() +        .parents({&None, &Domain}) +        .elementHandler(TypesystemHandler::create) +        .arguments({Argument::String("name", "")}); +static const ParserState TypesystemEnum = +    ParserStateBuilder().parent(&Typesystem); +static const ParserState TypesystemStruct = +    ParserStateBuilder() +        .parent(&Typesystem) +        .elementHandler(TypesystemStructHandler::create) +        .arguments({Argument::String("name"), Argument::String("parent", "")}); +static const ParserState TypesystemStructField = +    ParserStateBuilder() +        .parent(&TypesystemStruct) +        .elementHandler(TypesystemStructFieldHandler::create) +        .arguments({Argument::String("name"), Argument::String("type"), +                    Argument::Any("default", Variant::fromObject(nullptr))}); +static const ParserState TypesystemConstant = +    ParserStateBuilder() +        .parent(&Typesystem) +        .elementHandler(TypesystemConstantHandler::create) +        .arguments({Argument::String("name"), Argument::String("type"), +                    Argument::Any("value")}); + +/* Special states for import and include */ +static const ParserState Import = +    ParserStateBuilder().parents({&Document, &Typesystem, &Domain}).arguments( +        {Argument::String("rel", ""), Argument::String("type", ""), +         Argument::String("src")}); +static const ParserState Include = ParserStateBuilder().parent(&All).arguments( +    {Argument::String("rel", ""), Argument::String("type", ""), +     Argument::String("src")}); + +static const std::multimap<std::string, const ParserState *> XmlStates{ +    {"document", &Document}, +    {"domain", &Domain}, +    {"struct", &DomainStruct}, +    {"fields", &DomainStructFields}, +    {"field", &DomainStructField}, +    {"primitive", &DomainStructPrimitive}, +    {"typesystem", &Typesystem}, +    {"enum", &TypesystemEnum}, +    {"struct", &TypesystemStruct}, +    {"field", &TypesystemStructField}, +    {"constant", &TypesystemConstant}, +    {"import", &Import}, +    {"include", &Include}}; +}  /**   * Wrapper class around the XML_Parser pointer which safely frees it whenever   * the scope is left (e.g. because an exception was thrown). @@ -572,6 +484,7 @@ static void xmlCharacterDataHandler(void *p, const XML_Char *s, int len)  	XML_Parser parser = static_cast<XML_Parser>(p);  	ParserStack *stack = static_cast<ParserStack *>(XML_GetUserData(parser)); +	syncLoggerPosition(parser);  	const std::string data =  	    Utils::trim(std::string{s, static_cast<size_t>(len)});  	if (!data.empty()) { @@ -588,7 +501,7 @@ void XmlParser::doParse(CharReader &reader, ParserContext &ctx)  	// Create the parser stack instance and pass the reference to the state  	// machine descriptor -	ParserStack stack{ctx, XML_HANDLERS}; +	ParserStack stack{ctx, ParserStates::XmlStates};  	XML_SetUserData(&p, &stack);  	XML_UseParserAsHandlerArg(&p); | 
