summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-01 21:30:15 +0100
committerAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-02-01 21:30:15 +0100
commit28fc6ac92cf8b058da22c54c939fc225779ac26d (patch)
tree6caf90848f577109fd2e89ff07af88f93d39d0c1
parentba5439849b72ac341344d55f2bf05ccf11e37410 (diff)
Improved ParserStack state description
-rw-r--r--CMakeLists.txt1
-rw-r--r--src/core/parser/ParserStack.cpp114
-rw-r--r--src/core/parser/ParserStack.hpp223
-rw-r--r--src/core/parser/ParserState.cpp105
-rw-r--r--src/core/parser/ParserState.hpp254
-rw-r--r--src/plugins/xml/XmlParser.cpp297
-rw-r--r--test/core/parser/ParserStackTest.cpp131
-rw-r--r--testdata/xmlparser/generic.oxm58
8 files changed, 675 insertions, 508 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6d55112..dc62b75 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -152,6 +152,7 @@ ADD_LIBRARY(ousia_core
src/core/parser/ParserContext
src/core/parser/ParserScope
src/core/parser/ParserStack
+ src/core/parser/ParserState
src/core/resource/Resource
src/core/resource/ResourceLocator
src/core/resource/ResourceManager
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 &currentState();
/**
* 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 &copy(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);
diff --git a/test/core/parser/ParserStackTest.cpp b/test/core/parser/ParserStackTest.cpp
index e0c68cc..7ffcf3a 100644
--- a/test/core/parser/ParserStackTest.cpp
+++ b/test/core/parser/ParserStackTest.cpp
@@ -27,14 +27,9 @@ namespace ousia {
ConcreteLogger logger;
-static const State STATE_DOCUMENT = 0;
-static const State STATE_BODY = 1;
-static const State STATE_EMPTY = 2;
-
static int startCount = 0;
static int endCount = 0;
static int dataCount = 0;
-static int childCount = 0;
class TestHandler : public Handler {
public:
@@ -46,105 +41,111 @@ public:
void data(const std::string &data, int field) override { dataCount++; }
- void child(std::shared_ptr<Handler> handler) override { childCount++; }
+ static Handler *create(const HandlerData &data)
+ {
+ return new TestHandler(data);
+ }
};
-static Handler *createTestHandler(const HandlerData &data)
-{
- return new TestHandler(data);
+namespace ParserStates {
+static const ParserState Document =
+ ParserStateBuilder().parent(&None).elementHandler(TestHandler::create);
+static const ParserState Body = ParserStateBuilder()
+ .parent(&Document)
+ .elementHandler(TestHandler::create)
+ .childHandler(TestHandler::create);
+static const ParserState Empty =
+ ParserStateBuilder().parent(&Document).elementHandler(TestHandler::create);
+static const ParserState Special =
+ ParserStateBuilder().parent(&All).elementHandler(TestHandler::create);
+static const ParserState Arguments =
+ ParserStateBuilder()
+ .parent(&None)
+ .elementHandler(TestHandler::create)
+ .arguments({Argument::Int("a"), Argument::String("b")});
+
+static const std::multimap<std::string, const ParserState *> TestHandlers{
+ {"document", &Document},
+ {"body", &Body},
+ {"empty", &Empty},
+ {"special", &Special},
+ {"arguments", &Arguments}};
}
-static const std::multimap<std::string, HandlerDescriptor> TEST_HANDLERS{
- {"document", {{STATE_NONE}, createTestHandler, STATE_DOCUMENT}},
- {"body", {{STATE_DOCUMENT}, createTestHandler, STATE_BODY, true}},
- {"empty", {{STATE_DOCUMENT}, createTestHandler, STATE_EMPTY}},
- {"special", {{STATE_ALL}, createTestHandler, STATE_EMPTY}},
- {"arguments",
- {{STATE_NONE},
- createTestHandler,
- STATE_EMPTY,
- false,
- {Argument::Int("a"), Argument::String("b")}}},
-};
-
TEST(ParserStack, simpleTest)
{
StandaloneEnvironment env(logger);
- ParserStack s{env.context, TEST_HANDLERS};
+ ParserStack s{env.context, ParserStates::TestHandlers};
startCount = 0;
endCount = 0;
dataCount = 0;
- childCount = 0;
- ASSERT_EQ("", s.currentName());
- ASSERT_EQ(STATE_NONE, s.currentState());
+ EXPECT_EQ("", s.currentCommandName());
+ EXPECT_EQ(&ParserStates::None, &s.currentState());
s.start("document", {});
s.data("test1");
- ASSERT_EQ("document", s.currentName());
- ASSERT_EQ(STATE_DOCUMENT, s.currentState());
- ASSERT_EQ(1, startCount);
- ASSERT_EQ(1, dataCount);
+ EXPECT_EQ("document", s.currentCommandName());
+ EXPECT_EQ(&ParserStates::Document, &s.currentState());
+ EXPECT_EQ(1, startCount);
+ EXPECT_EQ(1, dataCount);
s.start("body", {});
s.data("test2");
- ASSERT_EQ("body", s.currentName());
- ASSERT_EQ(STATE_BODY, s.currentState());
- ASSERT_EQ(2, startCount);
- ASSERT_EQ(2, dataCount);
+ EXPECT_EQ("body", s.currentCommandName());
+ EXPECT_EQ(&ParserStates::Body, &s.currentState());
+ EXPECT_EQ(2, startCount);
+ EXPECT_EQ(2, dataCount);
s.start("inner", {});
- ASSERT_EQ("inner", s.currentName());
- ASSERT_EQ(STATE_BODY, s.currentState());
+ EXPECT_EQ("inner", s.currentCommandName());
+ EXPECT_EQ(&ParserStates::Body, &s.currentState());
s.end();
- ASSERT_EQ(3, startCount);
- ASSERT_EQ(1, childCount);
- ASSERT_EQ(1, endCount);
+ EXPECT_EQ(3, startCount);
+ EXPECT_EQ(1, endCount);
s.end();
- ASSERT_EQ(2, childCount);
- ASSERT_EQ(2, endCount);
+ EXPECT_EQ(2, endCount);
- ASSERT_EQ("document", s.currentName());
- ASSERT_EQ(STATE_DOCUMENT, s.currentState());
+ EXPECT_EQ("document", s.currentCommandName());
+ EXPECT_EQ(&ParserStates::Document, &s.currentState());
s.start("body", {});
s.data("test3");
- ASSERT_EQ("body", s.currentName());
- ASSERT_EQ(STATE_BODY, s.currentState());
+ EXPECT_EQ("body", s.currentCommandName());
+ EXPECT_EQ(&ParserStates::Body, &s.currentState());
s.end();
- ASSERT_EQ(4, startCount);
- ASSERT_EQ(3, dataCount);
- ASSERT_EQ(3, childCount);
- ASSERT_EQ(3, endCount);
+ EXPECT_EQ(4, startCount);
+ EXPECT_EQ(3, dataCount);
+ EXPECT_EQ(3, endCount);
- ASSERT_EQ("document", s.currentName());
- ASSERT_EQ(STATE_DOCUMENT, s.currentState());
+ EXPECT_EQ("document", s.currentCommandName());
+ EXPECT_EQ(&ParserStates::Document, &s.currentState());
s.end();
- ASSERT_EQ(4, endCount);
+ EXPECT_EQ(4, endCount);
- ASSERT_EQ("", s.currentName());
- ASSERT_EQ(STATE_NONE, s.currentState());
+ EXPECT_EQ("", s.currentCommandName());
+ EXPECT_EQ(&ParserStates::None, &s.currentState());
}
TEST(ParserStack, errorHandling)
{
StandaloneEnvironment env(logger);
- ParserStack s{env.context, TEST_HANDLERS};
+ ParserStack s{env.context, ParserStates::TestHandlers};
- ASSERT_THROW(s.start("body", {}), OusiaException);
+ EXPECT_THROW(s.start("body", {}), OusiaException);
s.start("document", {});
- ASSERT_THROW(s.start("document", {}), OusiaException);
+ EXPECT_THROW(s.start("document", {}), OusiaException);
s.start("empty", {});
- ASSERT_THROW(s.start("body", {}), OusiaException);
+ EXPECT_THROW(s.start("body", {}), OusiaException);
s.start("special", {});
s.end();
s.end();
s.end();
- ASSERT_EQ(STATE_NONE, s.currentState());
+ EXPECT_EQ(&ParserStates::None, &s.currentState());
ASSERT_THROW(s.end(), OusiaException);
ASSERT_THROW(s.data("test", 1), OusiaException);
}
@@ -152,20 +153,20 @@ TEST(ParserStack, errorHandling)
TEST(ParserStack, validation)
{
StandaloneEnvironment env(logger);
- ParserStack s{env.context, TEST_HANDLERS};
+ ParserStack s{env.context, ParserStates::TestHandlers};
- s.start("arguments", {});
- ASSERT_TRUE(logger.hasError());
logger.reset();
+ s.start("arguments", {});
+ EXPECT_TRUE(logger.hasError());
s.end();
s.start("arguments", {{"a", 5}});
- ASSERT_TRUE(logger.hasError());
- logger.reset();
+ EXPECT_TRUE(logger.hasError());
s.end();
+ logger.reset();
s.start("arguments", {{"a", 5}, {"b", "test"}});
- ASSERT_FALSE(logger.hasError());
+ EXPECT_FALSE(logger.hasError());
s.end();
}
}
diff --git a/testdata/xmlparser/generic.oxm b/testdata/xmlparser/generic.oxm
index 6a06f1e..b148364 100644
--- a/testdata/xmlparser/generic.oxm
+++ b/testdata/xmlparser/generic.oxm
@@ -1,36 +1,26 @@
<?xml version="1.0" standalone="yes"?>
-<!--<typesystem name="color">
- <types>
- <struct name="color">
- <field name="r" type="int"/>
- <field name="g" type="int"/>
- <field name="b" type="int"/>
- </struct>
- </types>
- <constants>
- <constant name="zero" value="0" type="int" />
- <constant name="zeros" value="[0, 0, 0]" type="int[]" />
- <constant name="manyZeros" value="[[0, 0], [0, 0], [0, 0]]" type="int[][]" />
- <constant name="black" value="[zero, zero, zero]" type="color" />
- </constants>
- <types>
- <struct name="structWithColor">
- <field name="color" type="color" default="black" />
- </struct>
- </types>
- <constants>
- <constant name="blackStructWithColor" value="[color=black]" type="structWithColor" />
- </constants>
-</typesystem>-->
-<domain name="color">
- <structs>
- <struct name="bla" cardinality="{1,2}" isa="blub"/>
- <struct name="blub" cardinality="{1-3,5,>7}">
- <fields>
- <field></field>
- <primitive/>
- </fields>
- </struct>
- </structs>
-</domain>
+<typesystem name="color">
+ <struct name="color">
+ <field name="r" type="int"/>
+ <field name="g" type="int"/>
+ <field name="b" type="int"/>
+ </struct>
+ <constant name="zero" value="0" type="int" />
+ <constant name="zeros" value="[0, 0, 0]" type="int[]" />
+ <constant name="manyZeros" value="[[0, 0], [0, 0], [0, 0]]" type="int[][][]" />
+ <constant name="black" value="[zero, zero, zero]" type="color" />
+ <struct name="structWithColor">
+ <field name="color" type="color" default="black" />
+ </struct>
+ <constant name="blackStructWithColor" value="[color=black]" type="structWithColor" />
+</typesystem>
+<!--<domain name="color">
+ <struct name="bla" cardinality="{1,2}" isa="blub"/>
+ <struct name="blub" cardinality="{1-3,5,>7}">
+ <fields>
+ <field></field>
+ <primitive type="bla"/>
+ </fields>
+ </struct>
+</domain>-->