summaryrefslogtreecommitdiff
path: root/src/core/parser/stack
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/parser/stack')
-rw-r--r--src/core/parser/stack/Callbacks.cpp23
-rw-r--r--src/core/parser/stack/Callbacks.hpp99
-rw-r--r--src/core/parser/stack/DocumentHandler.hpp15
-rw-r--r--src/core/parser/stack/DomainHandler.hpp69
-rw-r--r--src/core/parser/stack/Handler.cpp252
-rw-r--r--src/core/parser/stack/Handler.hpp414
-rw-r--r--src/core/parser/stack/ImportIncludeHandler.hpp72
-rw-r--r--src/core/parser/stack/Stack.cpp554
-rw-r--r--src/core/parser/stack/Stack.hpp341
-rw-r--r--src/core/parser/stack/State.cpp171
-rw-r--r--src/core/parser/stack/State.hpp308
11 files changed, 2248 insertions, 70 deletions
diff --git a/src/core/parser/stack/Callbacks.cpp b/src/core/parser/stack/Callbacks.cpp
new file mode 100644
index 0000000..6ebc549
--- /dev/null
+++ b/src/core/parser/stack/Callbacks.cpp
@@ -0,0 +1,23 @@
+/*
+ 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 "Callbacks.hpp"
+
+namespace ousia {
+}
+
diff --git a/src/core/parser/stack/Callbacks.hpp b/src/core/parser/stack/Callbacks.hpp
new file mode 100644
index 0000000..9c61000
--- /dev/null
+++ b/src/core/parser/stack/Callbacks.hpp
@@ -0,0 +1,99 @@
+/*
+ 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 Callbacks.hpp
+ *
+ * Contains an interface defining the callbacks that can be directed from a
+ * StateHandler to the StateStack, and from the StateStack to
+ * the actual parser.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_PARSER_STACK_CALLBACKS_HPP_
+#define _OUSIA_PARSER_STACK_CALLBACKS_HPP_
+
+#include <string>
+
+#include <core/common/Whitespace.hpp>
+
+namespace ousia {
+namespace parser_stack {
+
+/**
+ * Interface defining a set of callback functions that act as a basis for the
+ * StateStackCallbacks and the ParserCallbacks.
+ */
+class Callbacks {
+public:
+ /**
+ * Virtual descructor.
+ */
+ virtual ~Callbacks() {};
+
+ /**
+ * Sets the whitespace mode that specifies how string data should be
+ * processed.
+ *
+ * @param whitespaceMode specifies one of the three WhitespaceMode constants
+ * PRESERVE, TRIM or COLLAPSE.
+ */
+ virtual void setWhitespaceMode(WhitespaceMode whitespaceMode) = 0;
+
+ /**
+ * Registers the given token as token that should be reported to the handler
+ * using the "token" function.
+ *
+ * @param token is the token string that should be reported.
+ */
+ virtual void registerToken(const std::string &token) = 0;
+
+ /**
+ * Unregisters the given token, it will no longer be reported to the handler
+ * using the "token" function.
+ *
+ * @param token is the token string that should be unregistered.
+ */
+ virtual void unregisterToken(const std::string &token) = 0;
+};
+
+/**
+ * Interface defining the callback functions that can be passed from a
+ * StateStack to the underlying parser.
+ */
+class ParserCallbacks : public Callbacks {
+ /**
+ * Checks whether the given token is supported by the parser. The parser
+ * returns true, if the token is supported, false if this token cannot be
+ * registered. Note that parsers that do not support the registration of
+ * tokens at all should always return "true".
+ *
+ * @param token is the token that should be checked for support.
+ * @return true if the token is generally supported (or the parser does not
+ * support registering tokens at all), false if the token is not supported,
+ * because e.g. it is a reserved token or it interferes with other tokens.
+ */
+ virtual bool supportsToken(const std::string &token) = 0;
+};
+
+}
+}
+
+#endif /* _OUSIA_PARSER_STACK_CALLBACKS_HPP_ */
+
diff --git a/src/core/parser/stack/DocumentHandler.hpp b/src/core/parser/stack/DocumentHandler.hpp
index 697f9e7..475fe69 100644
--- a/src/core/parser/stack/DocumentHandler.hpp
+++ b/src/core/parser/stack/DocumentHandler.hpp
@@ -26,7 +26,8 @@
#define _OUSIA_DOCUMENT_HANDLER_HPP_
#include <core/common/Variant.hpp>
-#include <core/parser/ParserStack.hpp>
+
+#include "Handler.hpp"
namespace ousia {
@@ -35,11 +36,11 @@ class Rtti;
class DocumentEntity;
class FieldDescriptor;
-class DocumentHandler : public Handler {
+class DocumentHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -54,7 +55,7 @@ public:
using Node::Node;
};
-class DocumentChildHandler : public Handler {
+class DocumentChildHandler : public StaticHandler {
private:
void preamble(Handle<Node> parentNode, std::string &fieldName,
DocumentEntity *&parent, bool &inField);
@@ -68,11 +69,11 @@ private:
public:
using Handler::Handler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
- void data(const std::string &data, int fieldIdx) override;
+ bool data(const Variant &data) override;
static Handler *create(const HandlerData &handlerData)
{
diff --git a/src/core/parser/stack/DomainHandler.hpp b/src/core/parser/stack/DomainHandler.hpp
index 7398812..5e8ea60 100644
--- a/src/core/parser/stack/DomainHandler.hpp
+++ b/src/core/parser/stack/DomainHandler.hpp
@@ -26,18 +26,19 @@
#define _OUSIA_DOMAIN_HANDLER_HPP_
#include <core/common/Variant.hpp>
-#include <core/parser/ParserStack.hpp>
+
+#include "Handler.hpp"
namespace ousia {
// Forward declarations
class Rtti;
-class DomainHandler : public Handler {
+class DomainHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -47,11 +48,11 @@ public:
}
};
-class DomainStructHandler : public Handler {
+class DomainStructHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -61,11 +62,11 @@ public:
}
};
-class DomainAnnotationHandler : public Handler {
+class DomainAnnotationHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -75,11 +76,11 @@ public:
}
};
-class DomainAttributesHandler : public Handler {
+class DomainAttributesHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -89,11 +90,11 @@ public:
}
};
-class DomainFieldHandler : public Handler {
+class DomainFieldHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -103,11 +104,11 @@ public:
}
};
-class DomainFieldRefHandler : public Handler {
+class DomainFieldRefHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -117,11 +118,11 @@ public:
}
};
-class DomainPrimitiveHandler : public Handler {
+class DomainPrimitiveHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -131,11 +132,11 @@ public:
}
};
-class DomainChildHandler : public Handler {
+class DomainChildHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -154,11 +155,11 @@ namespace RttiTypes {
extern const Rtti DomainParent;
}
-class DomainParentHandler : public Handler {
+class DomainParentHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -168,11 +169,11 @@ public:
}
};
-class DomainParentFieldHandler : public Handler {
+class DomainParentFieldHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
@@ -182,11 +183,11 @@ public:
}
};
-class DomainParentFieldRefHandler : public Handler {
+class DomainParentFieldRefHandler : public StaticHandler {
public:
- using Handler::Handler;
+ using StaticHandler::StaticHandler;
- void start(Variant::mapType &args) override;
+ bool start(Variant::mapType &args) override;
void end() override;
diff --git a/src/core/parser/stack/Handler.cpp b/src/core/parser/stack/Handler.cpp
new file mode 100644
index 0000000..a608f7f
--- /dev/null
+++ b/src/core/parser/stack/Handler.cpp
@@ -0,0 +1,252 @@
+/*
+ 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 <core/common/Exceptions.hpp>
+#include <core/common/Logger.hpp>
+#include <core/parser/ParserContext.hpp>
+
+#include "Callbacks.hpp"
+#include "Handler.hpp"
+#include "State.hpp"
+
+namespace ousia {
+namespace parser_stack {
+
+/* Class HandlerData */
+
+HandlerData::HandlerData(ParserContext &ctx, /*Callbacks &callbacks,*/
+ const std::string &name, const State &state,
+ const SourceLocation &location)
+ : ctx(ctx),
+ /*callbacks(callbacks),*/
+ name(name),
+ state(state),
+ location(location)
+{
+}
+
+/* Class Handler */
+
+Handler::Handler(const HandlerData &handlerData)
+ : handlerData(handlerData), internalLogger(nullptr)
+{
+}
+
+Handler::~Handler() {}
+
+ParserContext &Handler::context() { return handlerData.ctx; }
+
+ParserScope &Handler::scope() { return handlerData.ctx.getScope(); }
+
+Manager &Handler::manager() { return handlerData.ctx.getManager(); }
+
+Logger &Handler::logger()
+{
+ if (internalLogger != nullptr) {
+ return *internalLogger;
+ }
+ return handlerData.ctx.getLogger();
+}
+
+const SourceLocation &Handler::location() const { return handlerData.location; }
+
+void Handler::setWhitespaceMode(WhitespaceMode whitespaceMode)
+{
+ /*handlerData.callbacks.setWhitespaceMode(whitespaceMode);*/
+}
+
+void Handler::registerToken(const std::string &token)
+{
+ /*handlerData.callbacks.registerToken(token);*/
+}
+
+void Handler::unregisterToken(const std::string &token)
+{
+ /*handlerData.callbacks.unregisterToken(token);*/
+}
+
+const std::string &Handler::getName() const { return handlerData.name; }
+
+const State &Handler::getState() const { return handlerData.state; }
+
+void Handler::setLogger(Logger &logger) { internalLogger = &logger; }
+
+void Handler::resetLogger() { internalLogger = nullptr; }
+
+const SourceLocation &Handler::getLocation() const { return location(); }
+
+/* Class EmptyHandler */
+
+bool EmptyHandler::start(const Variant::mapType &args)
+{
+ // Just accept anything
+ return true;
+}
+
+void EmptyHandler::end()
+{
+ // Do nothing if a command ends
+}
+
+bool EmptyHandler::fieldStart(bool &isDefaultField, size_t fieldIndex)
+{
+ // Accept any field
+ return true;
+}
+
+void EmptyHandler::fieldEnd()
+{
+ // Do not handle fields
+}
+
+bool EmptyHandler::annotationStart(const Variant &className,
+ const Variant::mapType &args)
+{
+ // Accept any data
+ return true;
+}
+
+bool EmptyHandler::annotationEnd(const Variant &className,
+ const Variant &elementName)
+{
+ // Accept any annotation
+ return true;
+}
+
+bool EmptyHandler::data(const Variant &data)
+{
+ // Support any data
+ return true;
+}
+
+Handler *EmptyHandler::create(const HandlerData &handlerData)
+{
+ return new EmptyHandler(handlerData);
+}
+
+/* Class StaticHandler */
+
+bool StaticHandler::start(const Variant::mapType &args)
+{
+ // Do nothing in the default implementation, accept anything
+ return true;
+}
+
+void StaticHandler::end()
+{
+ // Do nothing here
+}
+
+bool StaticHandler::fieldStart(bool &isDefault, size_t fieldIdx)
+{
+ // Return true if either the default field is requested or the field index
+ // is zero. This simulates that there is exactly one field (a default field)
+ if (fieldIdx == 0) {
+ isDefault = true;
+ return true;
+ }
+ return false;
+}
+
+void StaticHandler::fieldEnd()
+{
+ // Do nothing here
+}
+
+bool StaticHandler::annotationStart(const Variant &className,
+ const Variant::mapType &args)
+{
+ // No annotations supported
+ return false;
+}
+
+bool StaticHandler::annotationEnd(const Variant &className,
+ const Variant &elementName)
+{
+ // No annotations supported
+ return false;
+}
+
+bool StaticHandler::data(const Variant &data)
+{
+ logger().error("Did not expect any data here", data);
+ return false;
+}
+
+/* Class StaticFieldHandler */
+
+StaticFieldHandler::StaticFieldHandler(const HandlerData &handlerData,
+ const std::string &argName)
+ : StaticHandler(handlerData), argName(argName), handled(false)
+{
+}
+
+bool StaticFieldHandler::start(const Variant::mapType &args)
+{
+ if (!argName.empty()) {
+ auto it = args.find(argName);
+ if (it != args.end()) {
+ handled = true;
+ doHandle(it->second, args);
+ return true;
+ }
+ }
+
+ this->args = args;
+ return true;
+}
+
+void StaticFieldHandler::end()
+{
+ if (!handled) {
+ if (!argName.empty()) {
+ logger().error(std::string("Required argument \"") + argName +
+ std::string("\" is missing."),
+ location());
+ } else {
+ logger().error("Command requires data, but no data given",
+ location());
+ }
+ }
+}
+
+bool StaticFieldHandler::data(const Variant &data)
+{
+ // Call the doHandle function if this has not been done before
+ if (!handled) {
+ handled = true;
+ doHandle(data, args);
+ return true;
+ }
+
+ // The doHandle function was already called, print an error message
+ logger().error(
+ std::string("Found data, but the corresponding argument \"") + argName +
+ std::string("\" was already specified"),
+ data);
+
+ // Print the location at which the attribute was originally specified
+ auto it = args.find(argName);
+ if (it != args.end()) {
+ logger().note(std::string("Attribute was specified here:"), it->second);
+ }
+ return false;
+}
+}
+}
+
diff --git a/src/core/parser/stack/Handler.hpp b/src/core/parser/stack/Handler.hpp
new file mode 100644
index 0000000..eeaf555
--- /dev/null
+++ b/src/core/parser/stack/Handler.hpp
@@ -0,0 +1,414 @@
+/*
+ Ousía
+ Copyright (C) 2014 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/>.
+*/
+
+#ifndef _OUSIA_PARSER_STACK_HANDLER_HPP_
+#define _OUSIA_PARSER_STACK_HANDLER_HPP_
+
+#include <string>
+
+#include <core/common/Location.hpp>
+#include <core/common/Variant.hpp>
+#include <core/common/Whitespace.hpp>
+
+namespace ousia {
+
+// Forward declarations
+class ParserScope;
+class ParserContext;
+class Logger;
+
+namespace parser_stack {
+
+// More forward declarations
+class Callbacks;
+class State;
+
+/**
+ * Class collecting all the data that is being passed to a Handler
+ * instance.
+ */
+class HandlerData {
+public:
+ /**
+ * Reference to the ParserContext instance that should be used to resolve
+ * references to nodes in the Graph.
+ */
+ ParserContext &ctx;
+
+ /**
+ * Reference at an instance of the Callbacks class, used for
+ * modifying the behaviour of the parser (like registering tokens, setting
+ * the data type or changing the whitespace handling mode).
+ */
+ // Callbacks &callbacks;
+
+ /**
+ * Contains the name of the command that is being handled.
+ */
+ std::string name;
+
+ /**
+ * Contains the current state of the state machine.
+ */
+ const State &state;
+
+ /**
+ * Current source code location.
+ */
+ SourceLocation location;
+
+ /**
+ * Constructor of the HandlerData class.
+ *
+ * @param ctx is the parser context the handler should be executed in.
+ * @param callbacks is an instance of Callbacks used to notify
+ * the parser about certain state changes.
+ * @param name is the name of the string.
+ * @param state is the state this handler was called for.
+ * @param location is the location at which the handler is created.
+ */
+ HandlerData(ParserContext &ctx,
+ /*Callbacks &callbacks,*/ const std::string &name,
+ const State &state, const SourceLocation &location);
+};
+
+/**
+ * The Handler class provides a context for handling a generic stack element.
+ * It has to beoverridden and registered in the StateStack class to form
+ * handlers for concrete XML tags.
+ */
+class Handler {
+private:
+ /**
+ * Structure containing the internal handler data.
+ */
+ const HandlerData handlerData;
+
+ /**
+ * Reference at the current logger. If not nullptr, this will override the
+ * logger from the ParserContext specified in the handlerData.
+ */
+ Logger *internalLogger;
+
+protected:
+ /**
+ * Constructor of the Handler class.
+ *
+ * @param data is a structure containing all data being passed to the
+ * handler.
+ */
+ Handler(const HandlerData &handlerData);
+
+ /**
+ * Returns a reference at the ParserContext.
+ *
+ * @return a reference at the ParserContext.
+ */
+ ParserContext &context();
+
+ /**
+ * Returns a reference at the ParserScope instance.
+ *
+ * @return a reference at the ParserScope instance.
+ */
+ ParserScope &scope();
+
+ /**
+ * Returns a reference at the Manager instance which manages all nodes.
+ *
+ * @return a referance at the Manager instance.
+ */
+ Manager &manager();
+
+ /**
+ * Returns a reference at the Logger instance used for logging error
+ * messages.
+ *
+ * @return a reference at the Logger instance.
+ */
+ Logger &logger();
+
+ /**
+ * Returns the location of the element in the source file, for which this
+ * Handler was created.
+ *
+ * @return the location of the Handler in the source file.
+ */
+ const SourceLocation &location() const;
+
+public:
+ /**
+ * Virtual destructor.
+ */
+ virtual ~Handler();
+
+ /**
+ * Calls the corresponding function in the Callbacks instance. Sets the
+ * whitespace mode that specifies how string data should be processed. The
+ * calls to this function are placed on a stack by the underlying Stack
+ * class.
+ *
+ * @param whitespaceMode specifies one of the three WhitespaceMode constants
+ * PRESERVE, TRIM or COLLAPSE.
+ */
+ void setWhitespaceMode(WhitespaceMode whitespaceMode);
+
+ /**
+ * Calls the corresponding function in the Callbacks instance.
+ * Registers the given token as token that should be reported to the handler
+ * using the "token" function.
+ *
+ * @param token is the token string that should be reported.
+ */
+ void registerToken(const std::string &token);
+
+ /**
+ * Calls the corresponding function in the Callbacks instance.
+ * Unregisters the given token, it will no longer be reported to the handler
+ * using the "token" function.
+ *
+ * @param token is the token string that should be unregistered.
+ */
+ void unregisterToken(const std::string &token);
+
+ /**
+ * Returns the command name for which the handler was created.
+ *
+ * @return a const reference at the command name.
+ */
+ const std::string &getName() const;
+
+ /**
+ * Reference at the State descriptor for which this Handler was created.
+ *
+ * @return a const reference at the constructing State descriptor.
+ */
+ const State &getState() const;
+
+ /**
+ * Sets the internal logger to the given logger instance.
+ *
+ * @param logger is the Logger instance to which the logger should be set.
+ */
+ void setLogger(Logger &logger);
+
+ /**
+ * Resets the logger instance to the logger instance provided in the
+ * ParserContext.
+ */
+ void resetLogger();
+
+ /**
+ * Returns the location of the element in the source file, for which this
+ * Handler was created.
+ *
+ * @return the location of the Handler in the source file.
+ */
+ const SourceLocation &getLocation() const;
+
+ /**
+ * Called when the command that was specified in the constructor is
+ * instanciated.
+ *
+ * @param args is a map from strings to variants (argument name and value).
+ * @return true if the handler was successful in starting the element it
+ * represents, false otherwise.
+ */
+ virtual bool start(const Variant::mapType &args) = 0;
+
+ /**
+ * Called before the command for which this handler is defined ends (is
+ * forever removed from the stack).
+ */
+ virtual void end() = 0;
+
+ /**
+ * Called when a new field starts, while the handler is active. This
+ * function should return true if the field is supported, false otherwise.
+ * No error should be logged if the field cannot be started, the caller will
+ * take care of that (since it is always valid to start a default field,
+ * even though the corresponding structure does not have a field, as long as
+ * no data is fed into the field).
+ *
+ * @param isDefault is set to true if the field that is being started is the
+ * default/tree field. The handler should set the value of this variable to
+ * true if the referenced field is indeed the default field.
+ * @param fieldIdx is the numerical index of the field.
+ */
+ virtual bool fieldStart(bool &isDefault, size_t fieldIdx) = 0;
+
+ /**
+ * Called when a previously opened field ends, while the handler is active.
+ * Note that a "fieldStart" and "fieldEnd" are always called alternately.
+ */
+ virtual void fieldEnd() = 0;
+
+ /**
+ * Called whenever an annotation starts while this handler is active. The
+ * function should return true if starting the annotation was successful,
+ * false otherwise.
+ *
+ * @param className is a string variant containing the name of the
+ * annotation class and the location of the name in the source code.
+ * @param args is a map from strings to variants (argument name and value).
+ * @return true if the mentioned annotation could be started here, false
+ * if an error occurred.
+ */
+ virtual bool annotationStart(const Variant &className,
+ const Variant::mapType &args) = 0;
+
+ /**
+ * Called whenever an annotation ends while this handler is active. The
+ * function should return true if ending the annotation was successful,
+ * false otherwise.
+ *
+ * @param className is a string variant containing the name of the
+ * annotation class and the location of the class name in the source code.
+ * @param elementName is a string variant containing the name of the
+ * annotation class and the location of the element name in the source code.
+ * @return true if the mentioned annotation could be started here, false if
+ * an error occurred.
+ */
+ virtual bool annotationEnd(const Variant &className,
+ const Variant &elementName) = 0;
+
+ /**
+ * Called whenever raw data (int the form of a string) is available for the
+ * Handler instance. Should return true if the data could be handled, false
+ * otherwise.
+ *
+ * @param data is a string variant containing the character data and its
+ * location.
+ * @return true if the data could be handled, false otherwise.
+ */
+ virtual bool data(const Variant &data) = 0;
+};
+
+/**
+ * HandlerConstructor is a function pointer type used to create concrete
+ * instances of the Handler class.
+ *
+ * @param handlerData is the data that should be passed to the new handler
+ * instance.
+ * @return a newly created handler instance.
+ */
+using HandlerConstructor = Handler *(*)(const HandlerData &handlerData);
+
+/**
+ * The EmptyHandler class is used in case no element handler is specified in
+ * the State descriptor. It just accepts all data and does nothing.
+ */
+class EmptyHandler : public Handler {
+protected:
+ using Handler::Handler;
+
+public:
+ bool start(const Variant::mapType &args) override;
+ void end() override;
+ bool fieldStart(bool &isDefault, size_t fieldIdx) override;
+ void fieldEnd() override;
+ bool annotationStart(const Variant &className,
+ const Variant::mapType &args) override;
+ bool annotationEnd(const Variant &className,
+ const Variant &elementName) override;
+ bool data(const Variant &data) override;
+
+ /**
+ * Creates an instance of the EmptyHandler class.
+ */
+ static Handler *create(const HandlerData &handlerData);
+};
+
+/**
+ * The StaticHandler class is used to handle predifined commands which do
+ * neither support annotations, nor multiple fields. Child classes can decide
+ * whether a single data field should be used.
+ */
+class StaticHandler : public Handler {
+protected:
+ using Handler::Handler;
+
+public:
+ bool start(const Variant::mapType &args) override;
+ void end() override;
+ bool fieldStart(bool &isDefault, size_t fieldIdx) override;
+ void fieldEnd() override;
+ bool annotationStart(const Variant &className,
+ const Variant::mapType &args) override;
+ bool annotationEnd(const Variant &className,
+ const Variant &elementName) override;
+ bool data(const Variant &data) override;
+};
+
+/**
+ * The StaticFieldHandler class is used to handle predifined commands which do
+ * neither support annotations, nor multiple fields. Additionally, it captures a
+ * data entry from a single default field.
+ */
+class StaticFieldHandler : public StaticHandler {
+private:
+ /**
+ * Set to the name of the data argument that should be used instead of the
+ * data field, if no data field is given.
+ */
+ std::string argName;
+
+ /**
+ * Set to true, once the "doHandle" function has been called.
+ */
+ bool handled;
+
+ /**
+ * Map containing the arguments given in the start function.
+ */
+ Variant::mapType args;
+
+protected:
+ /**
+ * Constructor of the StaticFieldHandler class.
+ *
+ * @param handlerData is a structure containing the internal data that
+ * should be stored inside the handler.
+ * @param name of the data argument that -- if present -- should be used
+ * instead of the data field. If empty, data is not captured from the
+ * arguments. If both, data in the data field and the argument, are given,
+ * this results in an error.
+ */
+ StaticFieldHandler(const HandlerData &handlerData,
+ const std::string &argName);
+
+ /**
+ * Function that should be overriden in order to handle the field data and
+ * the other arguments. This function is not called if no data was given.
+ *
+ * @param fieldData is the captured field data.
+ * @param args are the arguments that were given in the "start" function.
+ */
+ virtual void doHandle(const Variant &fieldData,
+ const Variant::mapType &args) = 0;
+
+public:
+ bool start(const Variant::mapType &args) override;
+ void end() override;
+ bool data(const Variant &data) override;
+};
+}
+}
+
+#endif /* _OUSIA_PARSER_STACK_HANDLER_HPP_ */
+
diff --git a/src/core/parser/stack/ImportIncludeHandler.hpp b/src/core/parser/stack/ImportIncludeHandler.hpp
index b0767be..f9abe55 100644
--- a/src/core/parser/stack/ImportIncludeHandler.hpp
+++ b/src/core/parser/stack/ImportIncludeHandler.hpp
@@ -19,6 +19,9 @@
/**
* @file ImportIncludeHandler.hpp
*
+ * Contains the conceptually similar handlers for the "include" and "import"
+ * commands.
+ *
* @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
*/
@@ -30,43 +33,54 @@
namespace ousia {
-class ImportIncludeHandler : public Handler {
-protected:
- bool srcInArgs = false;
- std::string rel;
- std::string type;
- std::string src;
-
-public:
- using Handler::Handler;
-
- void start(Variant::mapType &args) override;
-
- void data(const std::string &data, int field) override;
-};
-
-class ImportHandler : public ImportIncludeHandler {
+/**
+ * The ImportHandler is responsible for handling the "import" command. An import
+ * creates a reference to a specified file. The specified file is parsed (if
+ * this has not already been done) outside of the context of the current file.
+ * If the specified resource has already been parsed, a reference to the already
+ * parsed file is inserted. Imports are only possible before no other content
+ * has been parsed.
+ */
+class ImportHandler : public StaticFieldHandler {
public:
- using ImportIncludeHandler::ImportIncludeHandler;
-
- void start(Variant::mapType &args) override;
-
- void end() override;
-
+ using StaticFieldHandler::StaticFieldHandler;
+
+ void doHandle(const Variant &fieldData,
+ const Variant::mapType &args) override;
+
+ /**
+ * Creates a new instance of the ImportHandler.
+ *
+ * @param handlerData is the data that is passed to the constructor of the
+ * Handler base class and used there to e.g. access the ParserContext and
+ * the Callbacks instance.
+ */
static Handler *create(const HandlerData &handlerData)
{
return new ImportHandler{handlerData};
}
};
-class IncludeHandler : public ImportIncludeHandler {
+/**
+ * The IncludeHandler is responsible for handling the "include" command. The
+ * included file is parsed in the context of the current file and will change
+ * the content that is currently being parsed. Includes are possible at (almost)
+ * any position in the source file.
+ */
+class IncludeHandler : public StaticFieldHandler {
public:
- using ImportIncludeHandler::ImportIncludeHandler;
-
- void start(Variant::mapType &args) override;
-
- void end() override;
-
+ using StaticFieldHandler::StaticFieldHandler;
+
+ void doHandle(const Variant &fieldData,
+ const Variant::mapType &args) override;
+
+ /**
+ * Creates a new instance of the IncludeHandler.
+ *
+ * @param handlerData is the data that is passed to the constructor of the
+ * Handler base class and used there to e.g. access the ParserContext and
+ * the Callbacks instance.
+ */
static Handler *create(const HandlerData &handlerData)
{
return new IncludeHandler{handlerData};
diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp
new file mode 100644
index 0000000..d84a19c
--- /dev/null
+++ b/src/core/parser/stack/Stack.cpp
@@ -0,0 +1,554 @@
+/*
+ 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 <sstream>
+
+#include <core/common/Logger.hpp>
+#include <core/common/Utils.hpp>
+#include <core/common/Exceptions.hpp>
+#include <core/parser/ParserScope.hpp>
+#include <core/parser/ParserContext.hpp>
+
+#include "Handler.hpp"
+#include "Stack.hpp"
+#include "State.hpp"
+
+namespace ousia {
+namespace parser_stack {
+
+/* Class HandlerInfo */
+
+HandlerInfo::HandlerInfo() : HandlerInfo(nullptr) {}
+
+HandlerInfo::HandlerInfo(std::shared_ptr<Handler> handler)
+ : handler(handler),
+ fieldIdx(0),
+ valid(true),
+ implicit(false),
+ inField(false),
+ inDefaultField(false),
+ inImplicitDefaultField(false),
+ inValidField(false),
+ hadDefaultField(false)
+{
+}
+
+HandlerInfo::HandlerInfo(bool valid, bool implicit, bool inField,
+ bool inDefaultField, bool inImplicitDefaultField,
+ bool inValidField)
+ : handler(nullptr),
+ fieldIdx(0),
+ valid(valid),
+ implicit(implicit),
+ inField(inField),
+ inDefaultField(inDefaultField),
+ inImplicitDefaultField(inImplicitDefaultField),
+ inValidField(inValidField),
+ hadDefaultField(false)
+{
+}
+
+HandlerInfo::~HandlerInfo()
+{
+ // Do nothing
+}
+
+void HandlerInfo::fieldStart(bool isDefault, bool isImplicit, bool isValid)
+{
+ inField = true;
+ inDefaultField = isDefault || isImplicit;
+ inImplicitDefaultField = isImplicit;
+ inValidField = isValid;
+ hadDefaultField = hadDefaultField || inDefaultField;
+ fieldIdx++;
+}
+
+void HandlerInfo::fieldEnd()
+{
+ inField = false;
+ inDefaultField = false;
+ inImplicitDefaultField = false;
+ inValidField = false;
+}
+
+/**
+ * Stub instance of HandlerInfo containing no handler information.
+ */
+static HandlerInfo EmptyHandlerInfo{true, true, true, true, false, true};
+
+/* Helper functions */
+
+/**
+ * Returns an Exception that should be thrown when a currently invalid command
+ * is thrown.
+ *
+ * @param name is the name of the command for which no state transition is
+ * found.
+ * @param expected is a set containing the names of the expected commands.
+ */
+static LoggableException buildInvalidCommandException(
+ const std::string &name, const std::set<std::string> &expected)
+{
+ if (expected.empty()) {
+ return LoggableException{
+ std::string{"No nested elements allowed, but got \""} + name +
+ std::string{"\""}};
+ } else {
+ return LoggableException{
+ std::string{"Expected "} +
+ (expected.size() == 1 ? std::string{"\""}
+ : std::string{"one of \""}) +
+ Utils::join(expected, "\", \"") + std::string{"\", but got \""} +
+ name + std::string{"\""}};
+ }
+}
+
+/* Class Stack */
+
+Stack::Stack(ParserContext &ctx,
+ const std::multimap<std::string, const State *> &states)
+ : ctx(ctx), states(states)
+{
+ // If the scope instance is not empty we need to deduce the current parser
+ // state
+ if (!ctx.getScope().isEmpty()) {
+ deduceState();
+ }
+}
+
+Stack::~Stack()
+{
+ while (!stack.empty()) {
+ // Fetch the topmost stack element
+ HandlerInfo &info = currentInfo();
+
+ // It is an error if we're still in a field of an element while the
+ // Stack instance is destroyed. Log that
+ if (handlersValid()) {
+ if (info.inField && !info.implicit &&
+ !info.inImplicitDefaultField) {
+ logger().error(
+ std::string("Reached end of stream, but command \"") +
+ info.handler->getName() +
+ "\" has not ended yet. Command was started here:",
+ info.handler->getLocation());
+ }
+ }
+
+ // Remove the command from the stack
+ endCurrentHandler();
+ }
+}
+
+void Stack::deduceState()
+{
+ // Assemble all states
+ std::vector<const State *> states;
+ for (const auto &e : this->states) {
+ states.push_back(e.second);
+ }
+
+ // Fetch the type signature of the scope and derive all possible states,
+ // abort if no unique parser state was found
+ std::vector<const State *> possibleStates =
+ StateDeductor(ctx.getScope().getStackTypeSignature(), states).deduce();
+ if (possibleStates.size() != 1U) {
+ throw LoggableException(
+ "Error while including file: Cannot deduce parser state.");
+ }
+
+ // Switch to this state by creating a handler, but do not call its start
+ // function
+ const State &state = *possibleStates[0];
+ HandlerConstructor ctor =
+ state.elementHandler ? state.elementHandler : EmptyHandler::create;
+
+ std::shared_ptr<Handler> handler =
+ std::shared_ptr<Handler>{ctor({ctx, "", state, SourceLocation{}})};
+ stack.emplace_back(handler);
+
+ // Set the correct flags for this implicit handler
+ HandlerInfo &info = currentInfo();
+ info.implicit = true;
+ info.fieldStart(true, false, true);
+}
+
+std::set<std::string> Stack::expectedCommands()
+{
+ const State *currentState = &(this->currentState());
+ std::set<std::string> res;
+ for (const auto &v : states) {
+ if (v.second->parents.count(currentState)) {
+ res.insert(v.first);
+ }
+ }
+ return res;
+}
+
+const State &Stack::currentState()
+{
+ return stack.empty() ? States::None : stack.back().handler->getState();
+}
+
+std::string Stack::currentCommandName()
+{
+ return stack.empty() ? std::string{} : stack.back().handler->getName();
+}
+
+const State *Stack::findTargetState(const std::string &name)
+{
+ const State *currentState = &(this->currentState());
+ auto range = states.equal_range(name);
+ for (auto it = range.first; it != range.second; it++) {
+ const StateSet &parents = it->second->parents;
+ if (parents.count(currentState) || parents.count(&States::All)) {
+ return it->second;
+ }
+ }
+
+ return nullptr;
+}
+
+const State *Stack::findTargetStateOrWildcard(const std::string &name)
+{
+ // Try to find the target state with the given name, if none is found, try
+ // find a matching "*" state.
+ State const *targetState = findTargetState(name);
+ if (targetState == nullptr) {
+ return findTargetState("*");
+ }
+ return targetState;
+}
+
+HandlerInfo &Stack::currentInfo()
+{
+ return stack.empty() ? EmptyHandlerInfo : stack.back();
+}
+HandlerInfo &Stack::lastInfo()
+{
+ return stack.size() < 2U ? EmptyHandlerInfo : stack[stack.size() - 2];
+}
+
+void Stack::endCurrentHandler()
+{
+ if (!stack.empty()) {
+ // Fetch the handler info for the current top-level element
+ HandlerInfo &info = stack.back();
+
+ // Do not call any callback functions while the stack is marked as
+ // invalid or this is an elment marked as "implicit"
+ if (!info.implicit && handlersValid()) {
+ // Make sure the fieldEnd handler is called if the element still
+ // is in a field
+ if (info.inField) {
+ info.handler->fieldEnd();
+ info.fieldEnd();
+ }
+
+ // Call the "end" function of the corresponding Handler instance
+ info.handler->end();
+ }
+
+ // Remove the element from the stack
+ stack.pop_back();
+ }
+}
+
+bool Stack::ensureHandlerIsInField()
+{
+ // If the current handler is not in a field (and actually has a handler)
+ // try to start a default field
+ HandlerInfo &info = currentInfo();
+ if (!info.inField && info.handler != nullptr) {
+ // Abort if the element already had a default field
+ if (info.hadDefaultField) {
+ return false;
+ }
+
+ // Try to start a new default field, abort if this did not work
+ bool isDefault = true;
+ if (!info.handler->fieldStart(isDefault, info.fieldIdx)) {
+ info.handler->fieldEnd();
+ endCurrentHandler();
+ return false;
+ }
+
+ // Mark the field as started
+ info.fieldStart(true, true, true);
+ }
+ return true;
+}
+
+bool Stack::handlersValid()
+{
+ for (auto it = stack.crbegin(); it != stack.crend(); it++) {
+ if (!it->valid) {
+ return false;
+ }
+ }
+ return true;
+}
+
+Logger &Stack::logger() { return ctx.getLogger(); }
+
+void Stack::command(const Variant &name, const Variant::mapType &args)
+{
+ // Make sure the given identifier is valid (preventing "*" from being
+ // malicously passed to this function)
+ if (!Utils::isNamespacedIdentifier(name.asString())) {
+ throw LoggableException(std::string("Invalid identifier \"") +
+ name.asString() + std::string("\""),
+ name);
+ }
+
+ State const *lastTargetState = nullptr;
+ Variant::mapType canonicalArgs;
+ while (true) {
+ // Try to find a target state for the given command, if none can be
+ // found and the current command does not have an open field, then try
+ // to create an empty default field, otherwise this is an exception
+ const State *targetState = findTargetStateOrWildcard(name.asString());
+ if (targetState == nullptr) {
+ if (!currentInfo().inField) {
+ endCurrentHandler();
+ continue;
+ } else {
+ throw buildInvalidCommandException(name.asString(),
+ expectedCommands());
+ }
+ }
+
+ // Make sure we're currently inside a field
+ if (!ensureHandlerIsInField()) {
+ endCurrentHandler();
+ continue;
+ }
+
+ // Fork the logger. We do not want any validation errors to skip
+ LoggerFork loggerFork = logger().fork();
+
+ // Canonicalize the arguments (if this has not already been done), allow
+ // additional arguments
+ if (lastTargetState != targetState) {
+ canonicalArgs = args;
+ targetState->arguments.validateMap(canonicalArgs, loggerFork, true);
+ lastTargetState = targetState;
+ }
+
+ // Instantiate the handler and push it onto the stack
+ HandlerConstructor ctor = targetState->elementHandler
+ ? targetState->elementHandler
+ : EmptyHandler::create;
+ std::shared_ptr<Handler> handler{
+ ctor({ctx, name.asString(), *targetState, name.getLocation()})};
+ stack.emplace_back(handler);
+
+ // Fetch the HandlerInfo for the parent element and the current element
+ HandlerInfo &parentInfo = lastInfo();
+ HandlerInfo &info = currentInfo();
+
+ // Call the "start" method of the handler, store the result of the start
+ // method as the validity of the handler -- do not call the start method
+ // if the stack is currently invalid (as this may cause further,
+ // unwanted errors)
+ bool validStack = handlersValid();
+ info.valid = false;
+ if (validStack) {
+ handler->setLogger(loggerFork);
+ try {
+ info.valid = handler->start(canonicalArgs);
+ }
+ catch (LoggableException ex) {
+ loggerFork.log(ex);
+ }
+ handler->resetLogger();
+ }
+
+ // We started the command within an implicit default field and it is not
+ // valid -- remove both the new handler and the parent field from the
+ // stack
+ if (!info.valid && parentInfo.inImplicitDefaultField) {
+ endCurrentHandler();
+ endCurrentHandler();
+ continue;
+ }
+
+ // If we ended up here, starting the command may or may not have worked,
+ // but after all, we cannot unroll the stack any further. Update the
+ // "valid" flag, commit any potential error messages and return.
+ info.valid = parentInfo.valid && info.valid;
+ loggerFork.commit();
+ return;
+ }
+}
+
+void Stack::data(const Variant &data)
+{
+ while (true) {
+ // Check whether there is any command the data can be sent to
+ if (stack.empty()) {
+ throw LoggableException("No command here to receive data.");
+ }
+
+ // Fetch the current command handler information
+ HandlerInfo &info = currentInfo();
+
+ // Make sure the current handler has an open field
+ if (!ensureHandlerIsInField()) {
+ endCurrentHandler();
+ continue;
+ }
+
+ // If this field should not get any data, log an error and do not call
+ // the "data" handler
+ if (!info.inValidField) {
+ logger().error("Did not expect any data here", data);
+ }
+
+ if (handlersValid() && info.inValidField) {
+ // Fork the logger and set it as temporary logger for the "start"
+ // method. We only want to keep error messages if this was not a try
+ // to implicitly open a default field.
+ LoggerFork loggerFork = logger().fork();
+ info.handler->setLogger(loggerFork);
+
+ // Pass the data to the current Handler instance
+ bool valid = false;
+ try {
+ valid = info.handler->data(data);
+ }
+ catch (LoggableException ex) {
+ loggerFork.log(ex);
+ }
+
+ // Reset the logger instance as soon as possible
+ info.handler->resetLogger();
+
+ // If placing the data here failed and we're currently in an
+ // implicitly opened field, just unroll the stack to the next field
+ // and try again
+ if (!valid && info.inImplicitDefaultField) {
+ endCurrentHandler();
+ continue;
+ }
+
+ // Commit the content of the logger fork. Do not change the valid
+ // flag.
+ loggerFork.commit();
+ }
+
+ // There was no reason to unroll the stack any further, so continue
+ return;
+ }
+}
+
+void Stack::fieldStart(bool isDefault)
+{
+ // Make sure the current handler stack is not empty
+ if (stack.empty()) {
+ throw LoggableException(
+ "No command for which a field could be started");
+ }
+
+ // Fetch the information attached to the current handler
+ HandlerInfo &info = currentInfo();
+ if (info.inField) {
+ logger().error(
+ "Got field start, but there is no command for which to start the "
+ "field.");
+ return;
+ }
+
+ // Copy the isDefault flag to a local variable, the fieldStart method will
+ // write into this variable
+ bool defaultField = isDefault;
+
+ // Do not call the "fieldStart" function if we're in an invalid subtree
+ bool valid = false;
+ if (handlersValid()) {
+ try {
+ valid = info.handler->fieldStart(defaultField, info.fieldIdx);
+ }
+ catch (LoggableException ex) {
+ logger().log(ex);
+ }
+ if (!valid && !defaultField) {
+ logger().error(
+ std::string("Cannot start a new field here (index ") +
+ std::to_string(info.fieldIdx + 1) +
+ std::string("), field does not exist"));
+ }
+ }
+
+ // Mark the field as started
+ info.fieldStart(defaultField, false, valid);
+}
+
+void Stack::fieldEnd()
+{
+ // Make sure the current handler stack is not empty
+ if (stack.empty()) {
+ throw LoggableException("No command for which a field could be ended");
+ }
+
+ // Fetch the information attached to the current handler
+ HandlerInfo &info = currentInfo();
+ if (!info.inField) {
+ logger().error(
+ "Got field end, but there is no command for which to end the "
+ "field.");
+ return;
+ }
+
+ // Only continue if the current handler stack is in a valid state, do not
+ // call the fieldEnd function if something went wrong before
+ if (handlersValid()) {
+ try {
+ info.handler->fieldEnd();
+ }
+ catch (LoggableException ex) {
+ logger().log(ex);
+ }
+ }
+
+ // This command no longer is in a field
+ info.fieldEnd();
+
+ // As soon as this command had a default field, remove it from the stack
+ if (info.hadDefaultField) {
+ endCurrentHandler();
+ }
+}
+
+void Stack::annotationStart(const Variant &className, const Variant &args)
+{
+ // TODO
+}
+
+void Stack::annotationEnd(const Variant &className, const Variant &elementName)
+{
+ // TODO
+}
+
+void Stack::token(Variant token)
+{
+ // TODO
+}
+}
+}
+
diff --git a/src/core/parser/stack/Stack.hpp b/src/core/parser/stack/Stack.hpp
new file mode 100644
index 0000000..76eefd9
--- /dev/null
+++ b/src/core/parser/stack/Stack.hpp
@@ -0,0 +1,341 @@
+/*
+ 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 Stack.hpp
+ *
+ * Helper classes for document or description parsers. Contains the
+ * Stack class, which is an pushdown automaton responsible for
+ * accepting commands in the correct order and calling specified handlers.
+ *
+ * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
+ */
+
+#ifndef _OUSIA_PARSER_STACK_STACK_HPP_
+#define _OUSIA_PARSER_STACK_STACK_HPP_
+
+#include <cstdint>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <vector>
+
+#include <core/common/Variant.hpp>
+#include <core/parser/Parser.hpp>
+
+namespace ousia {
+
+// Forward declarations
+class ParserContext;
+class Logger;
+
+namespace parser_stack {
+
+// Forward declarations
+class Handler;
+class State;
+
+/**
+ * The HandlerInfo class is used internally by the stack to associate additional
+ * (mutable) data with a handler instance.
+ */
+class HandlerInfo {
+public:
+ /**
+ * Pointer pointing at the actual handler instance.
+ */
+ std::shared_ptr<Handler> handler;
+
+ /**
+ * Next field index to be passed to the "fieldStart" function of the Handler
+ * class.
+ */
+ size_t fieldIdx;
+
+ /**
+ * Set to true if the handler is valid (which is the case if the "start"
+ * method has returned true). If the handler is invalid, no more calls are
+ * directed at it until it can be removed from the stack.
+ */
+ bool valid : 1;
+
+ /**
+ * Set to true if this is an implicit handler, that was created when the
+ * current stack state was deduced.
+ */
+ bool implicit : 1;
+
+ /**
+ * Set to true if the handler currently is in a field.
+ */
+ bool inField : 1;
+
+ /**
+ * Set to true if the handler currently is in the default field.
+ */
+ bool inDefaultField : 1;
+
+ /**
+ * Set to true if the handler currently is in an implicitly started default
+ * field.
+ */
+ bool inImplicitDefaultField : 1;
+
+ /**
+ * Set to false if this field is only opened pro-forma and does not accept
+ * any data. Otherwise set to true.
+ */
+ bool inValidField : 1;
+
+ /**
+ * Set to true, if the default field was already started.
+ */
+ bool hadDefaultField : 1;
+
+ /**
+ * Default constructor of the HandlerInfo class.
+ */
+ HandlerInfo();
+ /**
+ * Constructor of the HandlerInfo class, allows to set all flags manually.
+ */
+ HandlerInfo(bool valid, bool implicit, bool inField, bool inDefaultField,
+ bool inImplicitDefaultField, bool inValidField);
+
+ /**
+ * Constructor of the HandlerInfo class, taking a shared_ptr to the handler
+ * to which additional information should be attached.
+ */
+ HandlerInfo(std::shared_ptr<Handler> handler);
+
+ /**
+ * Destructor of the HandlerInfo class (to allow Handler to be forward
+ * declared).
+ */
+ ~HandlerInfo();
+
+ /**
+ * Updates the "field" flags according to a "fieldStart" event.
+ */
+ void fieldStart(bool isDefault, bool isImplicit, bool isValid);
+
+ /**
+ * Updates the "fields" flags according to a "fieldEnd" event.
+ */
+ void fieldEnd();
+};
+
+/**
+ * The Stack class is a pushdown automaton responsible for turning a command
+ * stream into a tree of Node instances. It does so by following a state
+ * transition graph and creating a set of Handler instances, which are placed
+ * on the stack.
+ */
+class Stack {
+private:
+ /**
+ * Reference at the parser context.
+ */
+ ParserContext &ctx;
+
+ /**
+ * Map containing all registered command names and the corresponding
+ * state descriptors.
+ */
+ const std::multimap<std::string, const State *> &states;
+
+ /**
+ * Internal stack used for managing the currently active Handler instances.
+ */
+ std::vector<HandlerInfo> stack;
+
+ /**
+ * Return the reference in the Logger instance stored within the context.
+ */
+ Logger &logger();
+
+ /**
+ * Used internally to get all expected command names for the current state.
+ * This function is used to build error messages.
+ *
+ * @return a set of strings containing the names of the expected commands.
+ */
+ std::set<std::string> expectedCommands();
+
+ /**
+ * Returns the targetState for a command with the given name that can be
+ * reached from the current state.
+ *
+ * @param name is the name of the requested command.
+ * @return nullptr if no target state was found, a pointer at the target
+ * state otherwise.
+ */
+ const State *findTargetState(const std::string &name);
+
+ /**
+ * Returns the targetState for a command with the given name that can be
+ * reached from the current state, also including the wildcard "*" state.
+ * Throws an exception if the given target state is not a valid identifier.
+ *
+ * @param name is the name of the requested command.
+ * @return nullptr if no target state was found, a pointer at the target
+ * state otherwise.
+ */
+ const State *findTargetStateOrWildcard(const std::string &name);
+
+ /**
+ * Tries to reconstruct the parser state from the Scope instance of the
+ * ParserContext given in the constructor. This functionality is needed for
+ * including files,as the Parser of the included file needs to be brought to
+ * an equivalent state as the one in the including file.
+ */
+ void deduceState();
+
+ /**
+ * Returns a reference at the current HandlerInfo instance (or a stub
+ * HandlerInfo instance if the stack is empty).
+ */
+ HandlerInfo &currentInfo();
+
+ /**
+ * Returns a reference at the last HandlerInfo instance (or a stub
+ * HandlerInfo instance if the stack has only one element).
+ */
+ HandlerInfo &lastInfo();
+
+ /**
+ * Ends the current handler and removes the corresponding element from the
+ * stack.
+ */
+ void endCurrentHandler();
+
+ /**
+ * Tries to start a default field for the current handler, if currently the
+ * handler is not inside a field and did not have a default field yet.
+ *
+ * @return true if the handler is inside a field, false if no field could
+ * be started.
+ */
+ bool ensureHandlerIsInField();
+
+ /**
+ * Returns true if all handlers on the stack are currently valid, or false
+ * if at least one handler is invalid.
+ *
+ * @return true if all handlers on the stack are valid.
+ */
+ bool handlersValid();
+
+public:
+ /**
+ * Creates a new instance of the Stack class.
+ *
+ * @param ctx is the parser context the parser stack is working on.
+ * @param states is a map containing the command names and pointers at the
+ * corresponding State instances.
+ */
+ Stack(ParserContext &ctx,
+ const std::multimap<std::string, const State *> &states);
+
+ /**
+ * Destructor of the Stack class.
+ */
+ ~Stack();
+
+ /**
+ * Returns the state the Stack instance currently is in.
+ *
+ * @return the state of the currently active Handler instance or STATE_NONE
+ * if no handler is on the stack.
+ */
+ const State &currentState();
+
+ /**
+ * Returns the command name that is currently being handled.
+ *
+ * @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 currentCommandName();
+
+ /**
+ * Function that should be called whenever a new command is reached.
+ *
+ * @param name is the name of the command (including the namespace
+ * separator ':') and its corresponding location. Must be a string variant.
+ * @param args is a map containing the arguments that were passed to the
+ * command.
+ */
+ void command(const Variant &name, const Variant::mapType &args);
+
+ /**
+ * Function that shuold be called whenever character data is found in the
+ * input stream. May only be called if the currently is a command on the
+ * stack.
+ *
+ * @param data is a string variant containing the data that has been found.
+ */
+ void data(const Variant &data);
+
+ /**
+ * Function that should be called whenever a new field starts. Fields of the
+ * same command may not be separated by calls to data or annotations. Doing
+ * so will result in a LoggableException.
+ *
+ * @param isDefault should be set to true if the started field explicitly
+ * is the default field.
+ */
+ void fieldStart(bool isDefault);
+
+ /**
+ * Function that should be called whenever a field ends. Calling this
+ * function if there is no field to end will result in a LoggableException.
+ */
+ void fieldEnd();
+
+ /**
+ * Function that should be called whenever an annotation starts.
+ *
+ * @param name is the name of the annotation class.
+ * @param args is a map variant containing the arguments that were passed
+ * to the annotation.
+ */
+ void annotationStart(const Variant &className, const Variant &args);
+
+ /**
+ * Function that should be called whenever an annotation ends.
+ *
+ * @param name is the name of the annotation class that was ended.
+ * @param annotationName is the name of the annotation that was ended.
+ */
+ void annotationEnd(const Variant &className, const Variant &elementName);
+
+ /**
+ * Function that should be called whenever a previously registered token
+ * is found in the input stream.
+ *
+ * @param token is string variant containing the token that was encountered.
+ */
+ void token(Variant token);
+};
+}
+}
+
+#endif /* _OUSIA_STACK_HPP_ */
+
diff --git a/src/core/parser/stack/State.cpp b/src/core/parser/stack/State.cpp
new file mode 100644
index 0000000..d72f533
--- /dev/null
+++ b/src/core/parser/stack/State.cpp
@@ -0,0 +1,171 @@
+/*
+ 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 "State.hpp"
+
+namespace ousia {
+namespace parser_stack {
+
+/* Class State */
+
+State::State() : elementHandler(nullptr) {}
+
+State::State(StateSet parents, Arguments arguments,
+ RttiSet createdNodeTypes,
+ HandlerConstructor elementHandler,
+ bool supportsAnnotations)
+ : parents(parents),
+ arguments(arguments),
+ createdNodeTypes(createdNodeTypes),
+ elementHandler(elementHandler),
+ supportsAnnotations(supportsAnnotations)
+{
+}
+
+State::State(const StateBuilder &builder)
+ : State(builder.build())
+{
+}
+
+/* Class StateBuilder */
+
+StateBuilder &StateBuilder::copy(const State &state)
+{
+ this->state = state;
+ return *this;
+}
+
+StateBuilder &StateBuilder::parent(const State *parent)
+{
+ state.parents = StateSet{parent};
+ return *this;
+}
+
+StateBuilder &StateBuilder::parents(const StateSet &parents)
+{
+ state.parents = parents;
+ return *this;
+}
+
+StateBuilder &StateBuilder::arguments(const Arguments &arguments)
+{
+ state.arguments = arguments;
+ return *this;
+}
+
+StateBuilder &StateBuilder::createdNodeType(const Rtti *type)
+{
+ state.createdNodeTypes = RttiSet{type};
+ return *this;
+}
+
+StateBuilder &StateBuilder::createdNodeTypes(const RttiSet &types)
+{
+ state.createdNodeTypes = types;
+ return *this;
+}
+
+StateBuilder &StateBuilder::elementHandler(
+ HandlerConstructor elementHandler)
+{
+ state.elementHandler = elementHandler;
+ return *this;
+}
+
+StateBuilder &StateBuilder::supportsAnnotations(bool supportsAnnotations)
+{
+ state.supportsAnnotations = supportsAnnotations;
+ return *this;
+}
+
+const State &StateBuilder::build() const { return state; }
+
+/* Class StateDeductor */
+
+StateDeductor::StateDeductor(
+ std::vector<const Rtti *> signature,
+ std::vector<const State *> states)
+ : tbl(signature.size()),
+ signature(std::move(signature)),
+ states(std::move(states))
+{
+}
+
+bool StateDeductor::isActive(size_t d, const State *s)
+{
+ // Lookup the "active" state of (d, s), if it was not already set
+ // (e.second is true) we'll have to calculate it
+ auto e = tbl[d].emplace(s, false);
+ bool &res = e.first->second;
+ if (!e.second) {
+ return res;
+ }
+
+ // Check whether this node is generative (may have produced the Node
+ // described by the current Signature element)
+ bool isGenerative = signature[d]->isOneOf(s->createdNodeTypes);
+
+ if (isGenerative && d == 0) {
+ // End of recursion -- the last signature element is reached and the
+ // node was generative
+ res = true;
+ } else {
+ // Try repetition of this node
+ if (isGenerative && isActive(d - 1, s)) {
+ res = true;
+ } else {
+ // Check whether any of the parent nodes were active -- either for
+ // the previous element (if this one is generative) or for the
+ // current element (assuming this node was not generative)
+ for (const State *parent : s->parents) {
+ if ((isGenerative && isActive(d - 1, parent)) ||
+ isActive(d, parent)) {
+ res = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return res;
+}
+
+std::vector<const State *> StateDeductor::deduce()
+{
+ std::vector<const State *> res;
+ if (!signature.empty()) {
+ const size_t D = signature.size();
+ for (auto s : states) {
+ if (signature[D - 1]->isOneOf(s->createdNodeTypes) &&
+ isActive(D - 1, s)) {
+ res.push_back(s);
+ }
+ }
+ }
+ return res;
+}
+
+/* Constant initializations */
+
+namespace States {
+const State All;
+const State None;
+}
+}
+}
+
diff --git a/src/core/parser/stack/State.hpp b/src/core/parser/stack/State.hpp
new file mode 100644
index 0000000..4766235
--- /dev/null
+++ b/src/core/parser/stack/State.hpp
@@ -0,0 +1,308 @@
+/*
+ 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 State.hpp
+ *
+ * Defines the State class used within the ParserStack pushdown
+ * automaton and the StateBuilder 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>
+#include <core/common/Whitespace.hpp>
+
+namespace ousia {
+namespace parser_stack {
+
+// Forward declarations
+class StateBuilder;
+class State;
+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 StateSet = std::unordered_set<const State *>;
+
+/**
+ * Class used for the complete specification of a State. Stores possible
+ * parent states, state handlers and arguments to be passed to that state.
+ */
+struct State {
+ /**
+ * Vector containing all possible parent states.
+ */
+ StateSet parents;
+
+ /**
+ * Descriptor of the arguments that should be passed to the handler.
+ */
+ Arguments arguments;
+
+ /**
+ * Set containing the types of the nodes that may be created in this
+ * State. This information is needed for Parsers to reconstruct the
+ * current State from a given ParserScope when a file is included.
+ */
+ RttiSet createdNodeTypes;
+
+ /**
+ * 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;
+
+ /**
+ * Set to true if this handler does support annotations. This is almost
+ * always false (e.g. all description handlers), except for document
+ * element handlers.
+ */
+ bool supportsAnnotations;
+
+ /**
+ * Default constructor, initializes the handlers with nullptr.
+ */
+ State();
+
+ /**
+ * Constructor taking values for all fields. Use the StateBuilder
+ * class for a more convenient construction of State 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 createdNodeTypes is a set containing the types of the nodes tha
+ * may be created in this State. This information is needed for
+ * Parsers to reconstruct the current State from a given ParserScope
+ * when a file is included.
+ * @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 supportsAnnotations specifies whether annotations are supported
+ * here at all.
+ */
+ State(StateSet parents, Arguments arguments = Arguments{},
+ RttiSet createdNodeTypes = RttiSet{},
+ HandlerConstructor elementHandler = nullptr,
+ bool supportsAnnotations = false);
+
+ /**
+ * Creates this State from the given StateBuilder instance.
+ */
+ State(const StateBuilder &builder);
+};
+
+/**
+ * The StateBuilder class is a class used for conveniently building new
+ * State instances.
+ */
+class StateBuilder {
+private:
+ /**
+ * State instance that is currently being built by the
+ * StateBuilder.
+ */
+ State state;
+
+public:
+ /**
+ * Copies the State instance and uses it as internal state. Overrides
+ * all changes made by the StateBuilder.
+ *
+ * @param state is the state that should be copied.
+ * @return a reference at this StateBuilder instance for method
+ * chaining.
+ */
+ StateBuilder &copy(const State &state);
+
+ /**
+ * Sets the possible parent states to the single given parent element.
+ *
+ * @param parent is a pointer at the parent State instance that should
+ * be the possible parent state.
+ * @return a reference at this StateBuilder instance for method
+ * chaining.
+ */
+ StateBuilder &parent(const State *parent);
+
+ /**
+ * Sets the State instances in the given StateSet as the list of
+ * supported parent states.
+ *
+ * @param parents is a set of pointers at State instances that should
+ * be the possible parent states.
+ * @return a reference at this StateBuilder instance for method
+ * chaining.
+ */
+ StateBuilder &parents(const StateSet &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 State.
+ * @return a reference at this StateBuilder instance for method
+ * chaining.
+ */
+ StateBuilder &arguments(const Arguments &arguments);
+
+ /**
+ * Sets the Node types this state may produce to the given Rtti descriptor.
+ *
+ * @param type is the Rtti descriptor of the Type that may be produced by
+ * this state.
+ * @return a reference at this StateBuilder instance for method
+ * chaining.
+ */
+ StateBuilder &createdNodeType(const Rtti *type);
+
+ /**
+ * Sets the Node types this state may produce to the given Rtti descriptors.
+ *
+ * @param types is a set of Rtti descriptors of the Types that may be
+ * produced by this state.
+ * @return a reference at this StateBuilder instance for method
+ * chaining.
+ */
+ StateBuilder &createdNodeTypes(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 StateBuilder instance for method
+ * chaining.
+ */
+ StateBuilder &elementHandler(HandlerConstructor elementHandler);
+
+ /**
+ * Sets the state of the "supportsAnnotations" flags (default value is
+ * false)
+ *
+ * @param supportsAnnotations should be set to true, if annotations are
+ * supported for the handlers associated with this document.
+ * @return a reference at this StateBuilder instance for method
+ * chaining.
+ */
+ StateBuilder &supportsAnnotations(bool supportsAnnotations);
+
+ /**
+ * Returns a reference at the internal State instance that was built
+ * using the StateBuilder.
+ *
+ * @return the built State.
+ */
+ const State &build() const;
+};
+
+/**
+ * Class used to deduce the State a Parser is currently in based on the
+ * types of the Nodes that currently are on the ParserStack. Uses dynamic
+ * programming in order to solve this problem.
+ */
+class StateDeductor {
+public:
+ /**
+ * Type containing the dynamic programming table.
+ */
+ using Table = std::vector<std::unordered_map<const State *, bool>>;
+
+private:
+ /**
+ * Dynamic programming table.
+ */
+ Table tbl;
+
+ /**
+ * Signature given in the constructor.
+ */
+ const std::vector<const Rtti *> signature;
+
+ /**
+ * List of states that should be checked for being active.
+ */
+ const std::vector<const State *> states;
+
+ /**
+ * Used internally to check whether the given parser stack s may have been
+ * active for signature element d.
+ *
+ * @param d is the signature element.
+ * @param s is the parser state.
+ * @return true if the the given State may have been active.
+ */
+ bool isActive(size_t d, const State *s);
+
+public:
+ /**
+ * Constructor of the StateDeductor class.
+ *
+ * @param signature a Node type signature describing the types of the nodes
+ * which currently reside on e.g. the ParserScope stack.
+ * @param states is a list of states that should be checked.
+ */
+ StateDeductor(std::vector<const Rtti *> signature,
+ std::vector<const State *> states);
+
+ /**
+ * Selects all active states from the given states. Only considers those
+ * states that may have produced the last signature element.
+ *
+ * @return a list of states that may actually have been active.
+ */
+ std::vector<const State *> deduce();
+};
+
+/**
+ * The States namespace contains all the global state constants used
+ * in the ParserStack class.
+ */
+namespace States {
+/**
+ * State representing all states.
+ */
+extern const State All;
+
+/**
+ * State representing the initial state.
+ */
+extern const State None;
+}
+}
+}
+
+#endif /* _OUSIA_PARSER_STATE_HPP_ */
+