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); |