From 84c9abc3e9762c4486ddc5ca0352a5d697a51987 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Wed, 25 Feb 2015 23:09:26 +0100 Subject: start of branch, commit log will be rewritten --- src/core/parser/stack/DocumentHandler.cpp | 24 ++++++---- src/core/parser/stack/DocumentHandler.hpp | 4 +- src/core/parser/stack/Handler.cpp | 25 ++++++++--- src/core/parser/stack/Handler.hpp | 74 ++++++++++++++++++++----------- src/core/parser/stack/Stack.cpp | 55 ++++++++++++++++++----- src/core/parser/stack/Stack.hpp | 18 ++++++-- 6 files changed, 144 insertions(+), 56 deletions(-) (limited to 'src/core/parser/stack') diff --git a/src/core/parser/stack/DocumentHandler.cpp b/src/core/parser/stack/DocumentHandler.cpp index bb04bd3..d44176a 100644 --- a/src/core/parser/stack/DocumentHandler.cpp +++ b/src/core/parser/stack/DocumentHandler.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -372,8 +373,15 @@ bool DocumentChildHandler::convertData(Handle field, return valid && scope().resolveValue(data, type, logger); } -bool DocumentChildHandler::data(Variant &data) +bool DocumentChildHandler::data(TokenizedData &data) { + // TODO: Handle this correctly + Variant text = data.text(WhitespaceMode::TRIM); + if (text == nullptr) { + // For now, except "no data" as success + return true; + } + // We're past the region in which explicit fields can be defined in the // parent structure element scope().setFlag(ParserFlag::POST_EXPLICIT_FIELDS, true); @@ -393,11 +401,11 @@ bool DocumentChildHandler::data(Variant &data) // If it is a primitive field directly, try to parse the content. if (field->isPrimitive()) { // Add it as primitive content. - if (!convertData(field, data, logger())) { + if (!convertData(field, text, logger())) { return false; } - parent->createChildDocumentPrimitive(data, fieldIdx); + parent->createChildDocumentPrimitive(text, fieldIdx); return true; } @@ -411,7 +419,7 @@ bool DocumentChildHandler::data(Variant &data) for (auto primitiveField : defaultFields) { // Then try to parse the content using the type specification. forks.emplace_back(logger().fork()); - if (!convertData(primitiveField, data, forks.back())) { + if (!convertData(primitiveField, text, forks.back())) { continue; } @@ -424,7 +432,7 @@ bool DocumentChildHandler::data(Variant &data) createPath(fieldIdx, path, parent); // Then create the primitive element - parent->createChildDocumentPrimitive(data); + parent->createChildDocumentPrimitive(text); return true; } @@ -434,10 +442,10 @@ bool DocumentChildHandler::data(Variant &data) if (defaultFields.empty()) { logger().error("Got data, but structure \"" + name() + "\" does not have any primitive field", - data); + text); } else { logger().error("Could not read data with any of the possible fields:", - data); + text); size_t f = 0; for (auto field : defaultFields) { logger().note(std::string("Field ") + @@ -471,4 +479,4 @@ namespace RttiTypes { const Rtti DocumentField = RttiBuilder( "DocumentField").parent(&Node); } -} \ No newline at end of file +} diff --git a/src/core/parser/stack/DocumentHandler.hpp b/src/core/parser/stack/DocumentHandler.hpp index 862081c..dda7d8b 100644 --- a/src/core/parser/stack/DocumentHandler.hpp +++ b/src/core/parser/stack/DocumentHandler.hpp @@ -167,7 +167,7 @@ public: bool start(Variant::mapType &args) override; void end() override; - bool data(Variant &data) override; + bool data(TokenizedData &data) override; bool fieldStart(bool &isDefault, size_t fieldIdx) override; @@ -213,4 +213,4 @@ extern const Rtti DocumentField; } } -#endif /* _OUSIA_PARSER_STACK_DOCUMENT_HANDLER_HPP_ */ \ No newline at end of file +#endif /* _OUSIA_PARSER_STACK_DOCUMENT_HANDLER_HPP_ */ diff --git a/src/core/parser/stack/Handler.cpp b/src/core/parser/stack/Handler.cpp index bf5d4ea..3d413e8 100644 --- a/src/core/parser/stack/Handler.cpp +++ b/src/core/parser/stack/Handler.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "Callbacks.hpp" @@ -130,7 +131,7 @@ bool EmptyHandler::annotationEnd(const Variant &className, return true; } -bool EmptyHandler::data(Variant &data) +bool EmptyHandler::data(TokenizedData &data) { // Support any data return true; @@ -184,10 +185,13 @@ bool StaticHandler::annotationEnd(const Variant &className, return false; } -bool StaticHandler::data(Variant &data) +bool StaticHandler::data(TokenizedData &data) { - logger().error("Did not expect any data here", data); - return false; + if (data.text(WhitespaceMode::TRIM) != nullptr) { + logger().error("Did not expect any data here", data); + return false; + } + return true; } /* Class StaticFieldHandler */ @@ -227,12 +231,19 @@ void StaticFieldHandler::end() } } -bool StaticFieldHandler::data(Variant &data) +bool StaticFieldHandler::data(TokenizedData &data) { + Variant text = data.text(WhitespaceMode::TRIM); + if (text == nullptr) { + // Providing no data here is ok as long as the "doHandle" callback + // function has already been called + return handled; + } + // Call the doHandle function if this has not been done before if (!handled) { handled = true; - doHandle(data, args); + doHandle(text, args); return true; } @@ -240,7 +251,7 @@ bool StaticFieldHandler::data(Variant &data) logger().error( std::string("Found data, but the corresponding argument \"") + argName + std::string("\" was already specified"), - data); + text); // Print the location at which the attribute was originally specified auto it = args.find(argName); diff --git a/src/core/parser/stack/Handler.hpp b/src/core/parser/stack/Handler.hpp index 7cda7a4..929466d 100644 --- a/src/core/parser/stack/Handler.hpp +++ b/src/core/parser/stack/Handler.hpp @@ -31,6 +31,7 @@ namespace ousia { class ParserScope; class ParserContext; class Logger; +class TokenizedData; namespace parser_stack { @@ -158,40 +159,63 @@ protected: */ const std::string &name() 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. + * class. This function should be called from the "fieldStart" callback and + * the "start" callback. If no whitespace mode is pushed in the "start" + * method the whitespace mode "TRIM" is implicitly assumed. * * @param whitespaceMode specifies one of the three WhitespaceMode constants * PRESERVE, TRIM or COLLAPSE. */ - void setWhitespaceMode(WhitespaceMode whitespaceMode); + void pushWhitespaceMode(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. + * Pops a previously pushed whitespace mode. Calls to this function should + * occur in the "end" callback and the "fieldEnd" callback. This function + * can only undo pushs that were performed by the pushWhitespaceMode() + * method of the same handler. */ - void registerToken(const std::string &token); + void popWhitespaceMode(); /** - * 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. + * 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. This function should be called from the "fieldStart" callback and + * the "start" callback. If no whitespace mode is pushed in the "start" + * method the whitespace mode "TRIM" is implicitly assumed. * - * @param token is the token string that should be unregistered. + * @param tokens is a list of tokens that should be reported to this handler + * instance via the "token" method. */ - void unregisterToken(const std::string &token); + void pushTokens(const std::vector &tokens); + + /** + * Pops a previously pushed whitespace mode. Calls to this function should + * occur in the "end" callback and the "fieldEnd" callback. This function + * can only undo pushs that were performed by the pushWhitespaceMode() + * method of the same handler. + */ + void popWhitespaceMode(); + + + /** + * Calls the corresponding function in the Callbacks instance. This method + * registers the given tokens as tokens that are generally available, tokens + * must be explicitly enabled using the "pushTokens" and "popTokens" method. + * Tokens that have not been registered are not guaranteed to be reported, + * even though they are + */ + void registerTokens(const std::vector &tokens); + +public: + /** + * Virtual destructor. + */ + virtual ~Handler(); /** * Returns the command name for which the handler was created. @@ -299,11 +323,11 @@ public: * 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. + * @param data is an instance of TokenizedData containing the segmented + * character data and its location. * @return true if the data could be handled, false otherwise. */ - virtual bool data(Variant &data) = 0; + virtual bool data(TokenizedData &data) = 0; }; /** @@ -333,7 +357,7 @@ public: Variant::mapType &args) override; bool annotationEnd(const Variant &className, const Variant &elementName) override; - bool data(Variant &data) override; + bool data(TokenizedData &data) override; /** * Creates an instance of the EmptyHandler class. @@ -359,7 +383,7 @@ public: Variant::mapType &args) override; bool annotationEnd(const Variant &className, const Variant &elementName) override; - bool data(Variant &data) override; + bool data(TokenizedData &data) override; }; /** @@ -412,7 +436,7 @@ protected: public: bool start(Variant::mapType &args) override; void end() override; - bool data(Variant &data) override; + bool data(TokenizedData &data) override; }; } } diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index 5b67248..309c9a0 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -413,16 +414,24 @@ void Stack::command(const Variant &name, const Variant::mapType &args) } } -void Stack::data(const Variant &data) +void Stack::data(TokenizedData data) { - // End handlers that already had a default field and are currently not - // active. - endOverdueHandlers(); + // TODO: Rewrite this function for token handling + // TODO: This loop needs to be refactored out + while (!data.atEnd()) { + // End handlers that already had a default field and are currently not + // active. + endOverdueHandlers(); - while (true) { - // Check whether there is any command the data can be sent to + const bool hasNonWhitespaceText = data.hasNonWhitespaceText(); + + // Check whether there is any command the data can be sent to -- if not, + // make sure the data actually is data if (stack.empty()) { - throw LoggableException("No command here to receive data.", data); + if (hasNonWhitespaceText) { + throw LoggableException("No command here to receive data.", data); + } + return; } // Fetch the current command handler information @@ -440,7 +449,10 @@ void Stack::data(const Variant &data) // If the "hadDefaultField" flag is set, we already issued an error // message if (!info.hadDefaultField) { - logger().error("Did not expect any data here", data); + if (hasNonWhitespaceText) { + logger().error("Did not expect any data here", data); + } + return; } } @@ -454,8 +466,16 @@ void Stack::data(const Variant &data) // Pass the data to the current Handler instance bool valid = false; try { - Variant dataCopy = data; - valid = info.handler->data(dataCopy); + // Create a fork of the TokenizedData and let the handler work + // on it + TokenizedData dataFork = data; + valid = info.handler->data(dataFork); + + // If the data was validly handled by the handler, commit the + // change + if (valid) { + data = dataFork; + } } catch (LoggableException ex) { loggerFork.log(ex); @@ -482,6 +502,19 @@ void Stack::data(const Variant &data) } } +void Stack::data(const Variant &stringData) +{ + // Fetch the SourceLocation of the given stringData variant + SourceLocation loc = stringData.getLocation(); + + // Create a TokenizedData instance and feed the given string data into it + TokenizedData tokenizedData(loc.getSourceId()); + tokenizedData.append(stringData.asString(), loc.getStart()); + + // Call the actual "data" method + data(tokenizedData); +} + void Stack::fieldStart(bool isDefault) { // Make sure the current handler stack is not empty @@ -584,4 +617,4 @@ void Stack::token(Variant token) // TODO } } -} \ No newline at end of file +} diff --git a/src/core/parser/stack/Stack.hpp b/src/core/parser/stack/Stack.hpp index b67ce82..cd29b28 100644 --- a/src/core/parser/stack/Stack.hpp +++ b/src/core/parser/stack/Stack.hpp @@ -44,6 +44,7 @@ namespace ousia { // Forward declarations class ParserContext; class Logger; +class TokenizedData; namespace parser_stack { @@ -292,13 +293,24 @@ public: void command(const Variant &name, const Variant::mapType &args); /** - * Function that shuold be called whenever character data is found in the + * Function that should 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. + * @param data is a TokenizedData instance containing the pre-segmented data + * that should be read. + */ + void data(TokenizedData data); + + /** + * Function that shuold be called whenever character data is found in the + * input stream. The given string variant is converted into a TokenizedData + * instance internally. + * + * @param stringData is a string variant containing the data that has been + * found. */ - void data(const Variant &data); + void data(const Variant &stringData); /** * Function that should be called whenever a new field starts. Fields of the -- cgit v1.2.3 From e2fd79ac8c85ac6191f6ed895fa5cdff091f7551 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Sun, 1 Mar 2015 16:28:35 +0100 Subject: Implemented TokenRegistry class and corresponding simple test case --- CMakeLists.txt | 2 + src/core/parser/stack/TokenRegistry.cpp | 72 +++++++++++++++++++++++ src/core/parser/stack/TokenRegistry.hpp | 87 ++++++++++++++++++++++++++++ test/core/parser/stack/TokenRegistryTest.cpp | 78 +++++++++++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 src/core/parser/stack/TokenRegistry.cpp create mode 100644 src/core/parser/stack/TokenRegistry.hpp create mode 100644 test/core/parser/stack/TokenRegistryTest.cpp (limited to 'src/core/parser/stack') diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e2d7f7..6e021fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,6 +188,7 @@ ADD_LIBRARY(ousia_core # src/core/parser/stack/ImportIncludeHandler src/core/parser/stack/State # src/core/parser/stack/Stack + src/core/parser/stack/TokenRegistry # src/core/parser/stack/TypesystemHandler src/core/parser/utils/SourceOffsetVector src/core/parser/utils/TokenizedData @@ -325,6 +326,7 @@ IF(TEST) test/core/parser/ParserScopeTest # test/core/parser/stack/StackTest test/core/parser/stack/StateTest + test/core/parser/stack/TokenRegistryTest test/core/parser/utils/SourceOffsetVectorTest test/core/parser/utils/TokenizedDataTest test/core/parser/utils/TokenizerTest diff --git a/src/core/parser/stack/TokenRegistry.cpp b/src/core/parser/stack/TokenRegistry.cpp new file mode 100644 index 0000000..21ae109 --- /dev/null +++ b/src/core/parser/stack/TokenRegistry.cpp @@ -0,0 +1,72 @@ +/* + 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 . +*/ + +#include "TokenRegistry.hpp" + +namespace ousia { +namespace parser_stack { + +TokenId TokenRegistry::registerToken(const std::string &token) +{ + // Check whether the given token is already registered + auto it = tokens.find(token); + if (it != tokens.end()) { + // Increment the reference count + size_t &refCount = it->second.second; + refCount++; + + // Return the token id + return it->second.first; + } + + // Register the token in the parser + TokenId id = parser.registerToken(token); + tokens[token] = std::pair(id, 1); + tokenIds[id] = token; + return id; +} + +void TokenRegistry::unregisterToken(TokenId id) +{ + // Lookup the token corresponding to the given token id + auto tokenIt = tokenIds.find(id); + if (tokenIt != tokenIds.end()) { + const std::string &token = tokenIt->second; + // Lookup the reference count for the corresponding token + auto idIt = tokens.find(token); + if (idIt != tokens.end()) { + // Decrement the reference count, abort if the refCount is larger + // than zero + size_t &refCount = idIt->second.second; + refCount--; + if (refCount > 0) { + return; + } + + // Unregister the token from the parser + parser.unregisterToken(id); + + // Unregister the token from the internal tokens map + tokens.erase(token); + } + // Unregister the token from the internal id map + tokenIds.erase(id); + } +} +} +} diff --git a/src/core/parser/stack/TokenRegistry.hpp b/src/core/parser/stack/TokenRegistry.hpp new file mode 100644 index 0000000..21c36b5 --- /dev/null +++ b/src/core/parser/stack/TokenRegistry.hpp @@ -0,0 +1,87 @@ +/* + 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 . +*/ + +/** + * @file TokenRegistry.hpp + * + * Contains the TokenRegistry class used for registering all possible tokens + * during the parsing process. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + +#ifndef _OUSIA_PARSER_STACK_TOKEN_REGISTRY_HPP_ +#define _OUSIA_PARSER_STACK_TOKEN_REGISTRY_HPP_ + +#include +#include + +#include "Callbacks.hpp" + +namespace ousia { +namespace parser_stack { + +/** + * The TokenRegistry class is used for registering all possible tokens during + * the Parsing process. The TokenRegistry class acts as an adapter between the + * parser which allocates TokenId for each unique token and the Handler classes + * which may register tokens multiple times and expect the same TokenId to be + * returned for the same token. + */ +class TokenRegistry : public ParserCallbacks { +private: + /** + * Reference at the ParserCallback instance the tokens are relayed to. + */ + ParserCallbacks &parser; + + /** + * Store containing all TokenId instances for all registered tokens. The map + * maps from the token strings to the corresponding TokenId and a reference + * count. + */ + std::unordered_map> tokens; + + /** + * Reverse map containing the string corresponding to a TokenId. + */ + std::unordered_map tokenIds; + +public: + /** + * Constructor of the TokenRegistry class. + * + * @param parser is the underlying parser implementing the ParserCallbacks + * interface to which all calls are relayed. + */ + TokenRegistry(ParserCallbacks &parser) : parser(parser) {} + + /* No copy construction */ + TokenRegistry(const TokenRegistry &) = delete; + + /* No assignment */ + TokenRegistry &operator=(const TokenRegistry &) = delete; + + TokenId registerToken(const std::string &token) override; + void unregisterToken(TokenId id) override; +}; +} +} + +#endif /* _OUSIA_PARSER_STACK_TOKEN_REGISTRY_HPP_ */ + diff --git a/test/core/parser/stack/TokenRegistryTest.cpp b/test/core/parser/stack/TokenRegistryTest.cpp new file mode 100644 index 0000000..390851e --- /dev/null +++ b/test/core/parser/stack/TokenRegistryTest.cpp @@ -0,0 +1,78 @@ +/* + 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 . +*/ + +#include + +#include + +namespace ousia { +namespace parser_stack { + +class ParserCallbacksProxy : public ParserCallbacks { +public: + size_t registerTokenCount = 0; + size_t unregisterTokenCount = 0; + + TokenId registerToken(const std::string &token) override + { + registerTokenCount++; + return registerTokenCount; + } + + void unregisterToken(TokenId id) override { unregisterTokenCount++; } +}; + +TEST(TokenRegistry, simple) +{ + ParserCallbacksProxy parser; + TokenRegistry registry(parser); + + ASSERT_EQ(0U, parser.registerTokenCount); + ASSERT_EQ(0U, parser.unregisterTokenCount); + + ASSERT_EQ(1U, registry.registerToken("test")); + ASSERT_EQ(1U, registry.registerToken("test")); + ASSERT_EQ(2U, registry.registerToken("test2")); + ASSERT_EQ(2U, registry.registerToken("test2")); + ASSERT_EQ(2U, parser.registerTokenCount); + ASSERT_EQ(0U, parser.unregisterTokenCount); + + registry.unregisterToken(1); + ASSERT_EQ(2U, parser.registerTokenCount); + ASSERT_EQ(0U, parser.unregisterTokenCount); + + registry.unregisterToken(1); + ASSERT_EQ(2U, parser.registerTokenCount); + ASSERT_EQ(1U, parser.unregisterTokenCount); + + registry.unregisterToken(1); + ASSERT_EQ(2U, parser.registerTokenCount); + ASSERT_EQ(1U, parser.unregisterTokenCount); + + registry.unregisterToken(2); + ASSERT_EQ(2U, parser.registerTokenCount); + ASSERT_EQ(1U, parser.unregisterTokenCount); + + registry.unregisterToken(2); + ASSERT_EQ(2U, parser.registerTokenCount); + ASSERT_EQ(2U, parser.unregisterTokenCount); +} + +} +} + -- cgit v1.2.3 From 231f426708babe0964495ac28a54f0f2835c084a Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 00:35:36 +0100 Subject: Unregistering all registered tokens when TokenRegistry is destroyed --- src/core/parser/stack/TokenRegistry.cpp | 8 ++++ src/core/parser/stack/TokenRegistry.hpp | 47 +++++++++++++++---- test/core/parser/stack/TokenRegistryTest.cpp | 68 +++++++++++++++------------- 3 files changed, 81 insertions(+), 42 deletions(-) (limited to 'src/core/parser/stack') diff --git a/src/core/parser/stack/TokenRegistry.cpp b/src/core/parser/stack/TokenRegistry.cpp index 21ae109..c135b98 100644 --- a/src/core/parser/stack/TokenRegistry.cpp +++ b/src/core/parser/stack/TokenRegistry.cpp @@ -16,11 +16,19 @@ along with this program. If not, see . */ +#include "Callbacks.hpp" #include "TokenRegistry.hpp" namespace ousia { namespace parser_stack { +TokenRegistry::~TokenRegistry() +{ + for (const auto &tid: tokenIds) { + parser.unregisterToken(tid.first); + } +} + TokenId TokenRegistry::registerToken(const std::string &token) { // Check whether the given token is already registered diff --git a/src/core/parser/stack/TokenRegistry.hpp b/src/core/parser/stack/TokenRegistry.hpp index 21c36b5..545db39 100644 --- a/src/core/parser/stack/TokenRegistry.hpp +++ b/src/core/parser/stack/TokenRegistry.hpp @@ -19,7 +19,7 @@ /** * @file TokenRegistry.hpp * - * Contains the TokenRegistry class used for registering all possible tokens + * Contains the TokenRegistry class used for registering all user defined tokens * during the parsing process. * * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) @@ -31,19 +31,22 @@ #include #include -#include "Callbacks.hpp" +#include namespace ousia { namespace parser_stack { +// Forward declarations +class ParserCallbacks; + /** - * The TokenRegistry class is used for registering all possible tokens during - * the Parsing process. The TokenRegistry class acts as an adapter between the - * parser which allocates TokenId for each unique token and the Handler classes - * which may register tokens multiple times and expect the same TokenId to be - * returned for the same token. + * The TokenRegistry class is used for registering all user defined tokens + * during the Parsing process. The TokenRegistry class acts as an adapter + * between the parser which allocates a TokenId for each unique token and the + * Handler classes which may register the same token multiple times and expect + * the same TokenId to be returned for the same token. */ -class TokenRegistry : public ParserCallbacks { +class TokenRegistry { private: /** * Reference at the ParserCallback instance the tokens are relayed to. @@ -71,14 +74,38 @@ public: */ TokenRegistry(ParserCallbacks &parser) : parser(parser) {} + /** + * Destructor of the TokenRegistry class, removes all registered tokens from + * the parser. + */ + ~TokenRegistry(); + /* No copy construction */ TokenRegistry(const TokenRegistry &) = delete; /* No assignment */ TokenRegistry &operator=(const TokenRegistry &) = delete; - TokenId registerToken(const std::string &token) override; - void unregisterToken(TokenId id) override; + /** + * Registers the given string token in the underlying parser and returns the + * TokenId of that token. If the same token string is given multiple times, + * the same TokenId is returned. The token is only registered once in the + * parser. + * + * @param token is the token that should be registered. + * @return the TokenId associated with this token. + */ + TokenId registerToken(const std::string &token); + + /** + * Unregisters the token with the given TokenId from the parser. Note that + * the token will only be unregistered if unregisterToken() has been called + * as many times as registerToken() for the same token. + * + * @param id is the id of the token returned by registerToken() that should + * be unregistered. + */ + void unregisterToken(TokenId id); }; } } diff --git a/test/core/parser/stack/TokenRegistryTest.cpp b/test/core/parser/stack/TokenRegistryTest.cpp index 390851e..20d6cd0 100644 --- a/test/core/parser/stack/TokenRegistryTest.cpp +++ b/test/core/parser/stack/TokenRegistryTest.cpp @@ -18,6 +18,7 @@ #include +#include #include namespace ousia { @@ -40,39 +41,42 @@ public: TEST(TokenRegistry, simple) { ParserCallbacksProxy parser; - TokenRegistry registry(parser); - - ASSERT_EQ(0U, parser.registerTokenCount); - ASSERT_EQ(0U, parser.unregisterTokenCount); - - ASSERT_EQ(1U, registry.registerToken("test")); - ASSERT_EQ(1U, registry.registerToken("test")); - ASSERT_EQ(2U, registry.registerToken("test2")); - ASSERT_EQ(2U, registry.registerToken("test2")); - ASSERT_EQ(2U, parser.registerTokenCount); - ASSERT_EQ(0U, parser.unregisterTokenCount); - - registry.unregisterToken(1); - ASSERT_EQ(2U, parser.registerTokenCount); - ASSERT_EQ(0U, parser.unregisterTokenCount); - - registry.unregisterToken(1); - ASSERT_EQ(2U, parser.registerTokenCount); - ASSERT_EQ(1U, parser.unregisterTokenCount); - - registry.unregisterToken(1); - ASSERT_EQ(2U, parser.registerTokenCount); - ASSERT_EQ(1U, parser.unregisterTokenCount); - - registry.unregisterToken(2); - ASSERT_EQ(2U, parser.registerTokenCount); - ASSERT_EQ(1U, parser.unregisterTokenCount); - - registry.unregisterToken(2); - ASSERT_EQ(2U, parser.registerTokenCount); - ASSERT_EQ(2U, parser.unregisterTokenCount); + { + TokenRegistry registry(parser); + + ASSERT_EQ(0U, parser.registerTokenCount); + ASSERT_EQ(0U, parser.unregisterTokenCount); + + ASSERT_EQ(1U, registry.registerToken("test")); + ASSERT_EQ(1U, registry.registerToken("test")); + ASSERT_EQ(2U, registry.registerToken("test2")); + ASSERT_EQ(2U, registry.registerToken("test2")); + ASSERT_EQ(3U, registry.registerToken("test3")); + ASSERT_EQ(3U, parser.registerTokenCount); + ASSERT_EQ(0U, parser.unregisterTokenCount); + + registry.unregisterToken(1); + ASSERT_EQ(3U, parser.registerTokenCount); + ASSERT_EQ(0U, parser.unregisterTokenCount); + + registry.unregisterToken(1); + ASSERT_EQ(3U, parser.registerTokenCount); + ASSERT_EQ(1U, parser.unregisterTokenCount); + + registry.unregisterToken(1); + ASSERT_EQ(3U, parser.registerTokenCount); + ASSERT_EQ(1U, parser.unregisterTokenCount); + + registry.unregisterToken(2); + ASSERT_EQ(3U, parser.registerTokenCount); + ASSERT_EQ(1U, parser.unregisterTokenCount); + + registry.unregisterToken(2); + ASSERT_EQ(3U, parser.registerTokenCount); + ASSERT_EQ(2U, parser.unregisterTokenCount); + } + ASSERT_EQ(3U, parser.unregisterTokenCount); } - } } -- cgit v1.2.3 From 7a8b4eb8b9d943959b919076596ec96ef0c4c03c Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 00:36:18 +0100 Subject: Adapted Callbacks interface and Handlers --- CMakeLists.txt | 2 +- src/core/parser/stack/Callbacks.cpp | 10 ++++ src/core/parser/stack/Callbacks.hpp | 68 ++++++++++++++++----------- src/core/parser/stack/Handler.cpp | 52 +++++++++++---------- src/core/parser/stack/Handler.hpp | 93 ++++++++++++++++++++++--------------- 5 files changed, 134 insertions(+), 91 deletions(-) (limited to 'src/core/parser/stack') diff --git a/CMakeLists.txt b/CMakeLists.txt index f6a7257..1e81822 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,7 +184,7 @@ ADD_LIBRARY(ousia_core # src/core/parser/stack/DocumentHandler # src/core/parser/stack/DomainHandler # src/core/parser/stack/GenericParserStates -# src/core/parser/stack/Handler + src/core/parser/stack/Handler # src/core/parser/stack/ImportIncludeHandler src/core/parser/stack/State # src/core/parser/stack/Stack diff --git a/src/core/parser/stack/Callbacks.cpp b/src/core/parser/stack/Callbacks.cpp index 6ebc549..44b31c6 100644 --- a/src/core/parser/stack/Callbacks.cpp +++ b/src/core/parser/stack/Callbacks.cpp @@ -19,5 +19,15 @@ #include "Callbacks.hpp" namespace ousia { +namespace parser_stack { + +/* Class ParserCallbacks */ + +ParserCallbacks::~ParserCallbacks() +{ + // Do nothing here +} + +} } diff --git a/src/core/parser/stack/Callbacks.hpp b/src/core/parser/stack/Callbacks.hpp index 9c61000..d7b2547 100644 --- a/src/core/parser/stack/Callbacks.hpp +++ b/src/core/parser/stack/Callbacks.hpp @@ -30,66 +30,78 @@ #define _OUSIA_PARSER_STACK_CALLBACKS_HPP_ #include +#include #include +#include namespace ousia { + +// Forward declarations +class Variant; + namespace parser_stack { /** - * Interface defining a set of callback functions that act as a basis for the - * StateStackCallbacks and the ParserCallbacks. + * Interface between the Stack class and the underlying parser used for + * registering and unregistering tokens. */ -class Callbacks { +class ParserCallbacks { 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; + virtual ~ParserCallbacks(); /** * 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. + * @return the token id with which the token will be reported. Should return + * Tokens::Empty if the given token could not be registered. */ - virtual void registerToken(const std::string &token) = 0; + virtual TokenId 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. + * @param id is the token id of the token that should be unregistered. */ - virtual void unregisterToken(const std::string &token) = 0; + virtual void unregisterToken(TokenId id) = 0; }; /** - * Interface defining the callback functions that can be passed from a - * StateStack to the underlying parser. + * Interface defining a set of callback functions that act as a basis for the + * StateStackCallbacks and the ParserCallbacks. */ -class ParserCallbacks : public Callbacks { +class HandlerCallbacks: public ParserCallbacks { +public: /** - * 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". + * Reads a string variant form the current input stream. This function must + * be called from the data() method. * - * @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. + * @return a string variant containing the current text data. The return + * value depends on the currently set whitespace mode and the tokens that + * were enabled using the enableTokens callback method. + */ + Variant readData(); + + /** + * Pushes a list of TokenSyntaxDescriptor instances onto the internal stack. + * The tokens described in the token list are the tokens that are currently + * enabled. + * + * @param tokens is a list of TokenSyntaxDescriptor instances that should be + * stored on the stack. + */ + void pushTokens(const std::vector &tokens); + + /** + * Removes the previously pushed list of tokens from the stack. */ - virtual bool supportsToken(const std::string &token) = 0; + void popTokens(); }; } diff --git a/src/core/parser/stack/Handler.cpp b/src/core/parser/stack/Handler.cpp index 3d413e8..734976a 100644 --- a/src/core/parser/stack/Handler.cpp +++ b/src/core/parser/stack/Handler.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -30,11 +31,11 @@ namespace parser_stack { /* Class HandlerData */ -HandlerData::HandlerData(ParserContext &ctx, /*Callbacks &callbacks,*/ +HandlerData::HandlerData(ParserContext &ctx, HandlerCallbacks &callbacks, const std::string &name, const State &state, const SourceLocation &location) : ctx(ctx), - /*callbacks(callbacks),*/ + callbacks(callbacks), name(name), state(state), location(location) @@ -68,19 +69,29 @@ const SourceLocation &Handler::location() const { return handlerData.location; } const std::string &Handler::name() const { return handlerData.name; } -void Handler::setWhitespaceMode(WhitespaceMode whitespaceMode) +Variant Handler::readData() { - /*handlerData.callbacks.setWhitespaceMode(whitespaceMode);*/ + return handlerData.callbacks.readData(); } -void Handler::registerToken(const std::string &token) +void Handler::pushTokens(const std::vector &tokens) { - /*handlerData.callbacks.registerToken(token);*/ + handlerData.callbacks.pushTokens(tokens); } -void Handler::unregisterToken(const std::string &token) +void Handler::popTokens() { - /*handlerData.callbacks.unregisterToken(token);*/ + handlerData.callbacks.popTokens(); +} + +TokenId Handler::registerToken(const std::string &token) +{ + return handlerData.callbacks.registerToken(token); +} + +void Handler::unregisterToken(TokenId id) +{ + handlerData.callbacks.unregisterToken(id); } const std::string &Handler::getName() const { return name(); } @@ -131,7 +142,7 @@ bool EmptyHandler::annotationEnd(const Variant &className, return true; } -bool EmptyHandler::data(TokenizedData &data) +bool EmptyHandler::data() { // Support any data return true; @@ -185,13 +196,10 @@ bool StaticHandler::annotationEnd(const Variant &className, return false; } -bool StaticHandler::data(TokenizedData &data) +bool StaticHandler::data() { - if (data.text(WhitespaceMode::TRIM) != nullptr) { - logger().error("Did not expect any data here", data); - return false; - } - return true; + logger().error("Did not expect any data here", readData()); + return false; } /* Class StaticFieldHandler */ @@ -231,19 +239,15 @@ void StaticFieldHandler::end() } } -bool StaticFieldHandler::data(TokenizedData &data) +bool StaticFieldHandler::data() { - Variant text = data.text(WhitespaceMode::TRIM); - if (text == nullptr) { - // Providing no data here is ok as long as the "doHandle" callback - // function has already been called - return handled; - } + // Fetch the actual text data + Variant stringData = readData(); // Call the doHandle function if this has not been done before if (!handled) { handled = true; - doHandle(text, args); + doHandle(stringData, args); return true; } @@ -251,7 +255,7 @@ bool StaticFieldHandler::data(TokenizedData &data) logger().error( std::string("Found data, but the corresponding argument \"") + argName + std::string("\" was already specified"), - text); + stringData); // Print the location at which the attribute was originally specified auto it = args.find(argName); diff --git a/src/core/parser/stack/Handler.hpp b/src/core/parser/stack/Handler.hpp index 929466d..848d395 100644 --- a/src/core/parser/stack/Handler.hpp +++ b/src/core/parser/stack/Handler.hpp @@ -32,6 +32,7 @@ class ParserScope; class ParserContext; class Logger; class TokenizedData; +class Variant; namespace parser_stack { @@ -52,11 +53,11 @@ public: 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). + * Reference at a class implementing the HandlerCallbacks interface, used + * for modifying the behaviour of the parser (like registering tokens, + * setting the data type or changing the whitespace handling mode). */ - // Callbacks &callbacks; + HandlerCallbacks &callbacks; /** * Contains the name of the command that is being handled. @@ -83,9 +84,9 @@ public: * @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); + HandlerData(ParserContext &ctx, HandlerCallbacks &callbacks, + const std::string &name, const State &state, + const SourceLocation &location); }; /** @@ -159,6 +160,17 @@ protected: */ const std::string &name() const; + /** + * Calls the corresponding method in the HandlerCallbacks instance. Reads a + * string variant form the current input stream. This function must be + * called from the data() method. + * + * @return a string variant containing the current text data. The return + * value depends on the currently set whitespace mode and the tokens that + * were enabled using the enableTokens callback method. + */ + Variant readData(); + /** * Calls the corresponding function in the Callbacks instance. Sets the * whitespace mode that specifies how string data should be processed. The @@ -170,7 +182,7 @@ protected: * @param whitespaceMode specifies one of the three WhitespaceMode constants * PRESERVE, TRIM or COLLAPSE. */ - void pushWhitespaceMode(WhitespaceMode whitespaceMode); + // void pushWhitespaceMode(WhitespaceMode whitespaceMode); /** * Pops a previously pushed whitespace mode. Calls to this function should @@ -178,38 +190,45 @@ protected: * can only undo pushs that were performed by the pushWhitespaceMode() * method of the same handler. */ - void popWhitespaceMode(); + // void popWhitespaceMode(); /** - * 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. This function should be called from the "fieldStart" callback and - * the "start" callback. If no whitespace mode is pushed in the "start" - * method the whitespace mode "TRIM" is implicitly assumed. + * Pushes a list of TokenSyntaxDescriptor instances onto the internal stack. + * The tokens described in the token list are the tokens that are currently + * enabled. * - * @param tokens is a list of tokens that should be reported to this handler - * instance via the "token" method. + * @param tokens is a list of TokenSyntaxDescriptor instances that should be + * stored on the stack. */ - void pushTokens(const std::vector &tokens); + void pushTokens(const std::vector &tokens); /** - * Pops a previously pushed whitespace mode. Calls to this function should - * occur in the "end" callback and the "fieldEnd" callback. This function - * can only undo pushs that were performed by the pushWhitespaceMode() - * method of the same handler. + * Calls the corresponding function in the HandlerCallbacks instance. + * Removes the previously pushed list of tokens from the stack. */ - void popWhitespaceMode(); + void popTokens(); + /** + * Calls the corresponding function in the HandlerCallbacks instance. This + * method registers the given tokens as tokens that are generally available, + * tokens must be explicitly enabled using the "pushTokens" and "popTokens" + * method. Tokens that have not been registered are not guaranteed to be + * reported (except for special tokens, these do not have to be registerd). + * + * @param token is the token string that should be made available. + * @return the TokenId that will be used to refer to the token. + */ + TokenId registerToken(const std::string &token); /** - * Calls the corresponding function in the Callbacks instance. This method - * registers the given tokens as tokens that are generally available, tokens - * must be explicitly enabled using the "pushTokens" and "popTokens" method. - * Tokens that have not been registered are not guaranteed to be reported, - * even though they are + * Calls the corresponding function in the HandlerCallbacks instance. This + * method unregisters the given token. Note that for a token to be no longer + * reported, this function has to be called as many times as registerToken() + * for the corresponding token. + * + * @param id is the id of the Token that should be unregistered. */ - void registerTokens(const std::vector &tokens); + void unregisterToken(TokenId id); public: /** @@ -321,13 +340,12 @@ public: /** * 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. + * otherwise. The actual data variant must be retrieved using the "text()" + * callback. * - * @param data is an instance of TokenizedData containing the segmented - * character data and its location. * @return true if the data could be handled, false otherwise. */ - virtual bool data(TokenizedData &data) = 0; + virtual bool data() = 0; }; /** @@ -357,7 +375,7 @@ public: Variant::mapType &args) override; bool annotationEnd(const Variant &className, const Variant &elementName) override; - bool data(TokenizedData &data) override; + bool data() override; /** * Creates an instance of the EmptyHandler class. @@ -383,7 +401,7 @@ public: Variant::mapType &args) override; bool annotationEnd(const Variant &className, const Variant &elementName) override; - bool data(TokenizedData &data) override; + bool data() override; }; /** @@ -430,13 +448,12 @@ protected: * @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, - Variant::mapType &args) = 0; + virtual void doHandle(const Variant &fieldData, Variant::mapType &args) = 0; public: bool start(Variant::mapType &args) override; void end() override; - bool data(TokenizedData &data) override; + bool data() override; }; } } -- cgit v1.2.3 From 2807dc44b0555c19944f2520852d242eacc30b20 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 00:36:44 +0100 Subject: Added first (not final) version of the TokenStack class which will be used internally by the Stack class --- CMakeLists.txt | 1 + src/core/parser/stack/TokenStack.cpp | 45 ++++++++++++++ src/core/parser/stack/TokenStack.hpp | 111 +++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 src/core/parser/stack/TokenStack.cpp create mode 100644 src/core/parser/stack/TokenStack.hpp (limited to 'src/core/parser/stack') diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e81822..b206458 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,7 @@ ADD_LIBRARY(ousia_core src/core/parser/stack/State # src/core/parser/stack/Stack src/core/parser/stack/TokenRegistry + src/core/parser/stack/TokenStack # src/core/parser/stack/TypesystemHandler src/core/parser/utils/SourceOffsetVector src/core/parser/utils/TokenizedData diff --git a/src/core/parser/stack/TokenStack.cpp b/src/core/parser/stack/TokenStack.cpp new file mode 100644 index 0000000..6afeaed --- /dev/null +++ b/src/core/parser/stack/TokenStack.cpp @@ -0,0 +1,45 @@ +/* + 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 . +*/ + +#include "TokenStack.hpp" + +namespace ousia { +namespace parser_stack { + +void TokenStack::pushTokens(const std::vector &tokens) +{ + stack.push_back(tokens); +} + +void TokenStack::popTokens() { stack.pop_back(); } + +TokenSet TokenStack::tokens() const +{ + if (stack.empty() && parentStack != nullptr) { + return parentStack->tokens(); + } + + TokenSet res; + for (const TokenSyntaxDescriptor &descr : stack.back()) { + descr.insertIntoTokenSet(res); + } + return res; +} +} +} + diff --git a/src/core/parser/stack/TokenStack.hpp b/src/core/parser/stack/TokenStack.hpp new file mode 100644 index 0000000..9669f50 --- /dev/null +++ b/src/core/parser/stack/TokenStack.hpp @@ -0,0 +1,111 @@ +/* + 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 . +*/ + +/** + * @file TokenStack.hpp + * + * Contains the TokenStack class used for collecting the currently enabled user + * defined tokens on a per-field basis. + * + * @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de) + */ + +#ifndef _OUSIA_PARSER_STACK_TOKEN_STACK_HPP_ +#define _OUSIA_PARSER_STACK_TOKEN_STACK_HPP_ + +#include +#include + +#include + +namespace ousia { +namespace parser_stack { + +/** + * The TokenStack class is used by the Stack class to collect all currently + * enabled user defined tokens. + */ +class TokenStack { +private: + /** + * Shared pointer at the parent TokenStack instance. May be nullptr, in + * which case no parent TokenStack instance exists. + */ + const TokenStack *parentStack; + + /** + * Stack containing vectors of TokenSyntaxDescriptor instances as given by + * the user. + */ + std::vector> stack; + + /** + * Constructor of the TokenStack class. + * + * @param parentStack is a pointer at the underlying parentStack instance + * to which calls should be forwarded if no data has been pushed onto this + * stack instance. + */ + TokenStack(const TokenStack *parentStack) : parentStack(parentStack) {} + +public: + /** + * Default constructor of the TokenStack class with no reference at a parent + * stack. + */ + TokenStack() : TokenStack(nullptr) {} + + /** + * Constructor of the TokenStack class with a reference at a parent + * TokenStack instance. + * + * @param parentStack is a reference at a parent TokenStack instance. If no + * data has yet been pushed onto this instance, calls will be forwarded to + * the parent stack. + */ + TokenStack(const TokenStack &parentStack) : TokenStack(&parentStack) {} + + /** + * Pushes a list of TokenSyntaxDescriptor instances onto the internal stack. + * + * @param tokens is a list of TokenSyntaxDescriptor instances that should be + * stored on the stack. + */ + void pushTokens(const std::vector &tokens); + + /** + * Removes the previously pushed list of tokens from the stack. + */ + void popTokens(); + + /** + * Returns a set containing all currently enabled tokens. The set of enabled + * tokens are those tokens that were pushed last onto the stack. This set + * has to be passed to the TokenizedData instance in order to gather all + * tokens that are currently possible. + * + * @return a set of tokens containing all the Tokens that are currently + * possible. + */ + TokenSet tokens() const; +}; +} +} + +#endif /* _OUSIA_PARSER_STACK_TOKEN_STACK_HPP_ */ + -- cgit v1.2.3 From 95f0ade7c19d7c6c451025e9a76d66ffb64e1f70 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 00:41:15 +0100 Subject: Adapted Stack interface (Stack.cpp is a mess right now and does not compile) --- src/core/parser/stack/Stack.cpp | 202 +++++++++++++++++++++++++++++- src/core/parser/stack/Stack.hpp | 270 ++++++---------------------------------- 2 files changed, 238 insertions(+), 234 deletions(-) (limited to 'src/core/parser/stack') diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index 309c9a0..292e7e2 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -32,6 +32,96 @@ namespace parser_stack { /* Class HandlerInfo */ +/** + * 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; + + /** + * 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); + + /** + * 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(); +}; + HandlerInfo::HandlerInfo() : HandlerInfo(nullptr) {} HandlerInfo::HandlerInfo(std::shared_ptr handler) @@ -117,6 +207,113 @@ static LoggableException buildInvalidCommandException( } } +/* Class StackImpl */ + +class StackImpl { + +private: + /** + * Reference at the parser context. + */ + ParserContext &ctx; + + /** + * Map containing all registered command names and the corresponding + * state descriptors. + */ + const std::multimap &states; + + /** + * Internal stack used for managing the currently active Handler instances. + */ + std::vector 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 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 ¤tInfo(); + + /** + * Returns a reference at the last HandlerInfo instance (or a stub + * HandlerInfo instance if the stack has only one element). + */ + HandlerInfo &lastInfo(); + + /** + * Ends all handlers that currently are not inside a field and already had + * a default field. This method is called whenever the data() and command() + * events are reached. + */ + void endOverdueHandlers(); + + /** + * 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(); +}; + + /* Class Stack */ Stack::Stack(ParserContext &ctx, @@ -611,10 +808,5 @@ 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 index cd29b28..e1173d0 100644 --- a/src/core/parser/stack/Stack.hpp +++ b/src/core/parser/stack/Stack.hpp @@ -29,226 +29,35 @@ #ifndef _OUSIA_PARSER_STACK_STACK_HPP_ #define _OUSIA_PARSER_STACK_STACK_HPP_ -#include - #include #include -#include -#include - -#include -#include namespace ousia { // Forward declarations class ParserContext; -class Logger; class TokenizedData; +class Variant; namespace parser_stack { // Forward declarations -class Handler; +class StackImpl; 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; - - /** - * 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); - - /** - * 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. + * on the stack. Additionally it is responsible for the normalization of + * Annotations and for handling tokens. */ class Stack { private: /** - * Reference at the parser context. - */ - ParserContext &ctx; - - /** - * Map containing all registered command names and the corresponding - * state descriptors. + * Pointer at the internal implementation */ - const std::multimap &states; - - /** - * Internal stack used for managing the currently active Handler instances. - */ - std::vector 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 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 ¤tInfo(); - - /** - * Returns a reference at the last HandlerInfo instance (or a stub - * HandlerInfo instance if the stack has only one element). - */ - HandlerInfo &lastInfo(); - - /** - * Ends all handlers that currently are not inside a field and already had - * a default field. This method is called whenever the data() and command() - * events are reached. - */ - void endOverdueHandlers(); - - /** - * 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(); + std::unique_ptr impl; public: /** @@ -269,8 +78,8 @@ public: /** * 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. + * @return the state of the currently active Handler instance or + * States::None if no handler is on the stack. */ const State ¤tState(); @@ -289,28 +98,36 @@ public: * separator ':') and its corresponding location. Must be a string variant. * @param args is a map containing the arguments that were passed to the * command. + * @param range if true, the started command has an explicit range. */ - void command(const Variant &name, const Variant::mapType &args); + void commandStart(const Variant &name, const Variant::mapType &args, + bool range); /** - * Function that should be called whenever character data is found in the - * input stream. May only be called if the currently is a command on the - * stack. + * Function that should be called whenever an annotation starts. * - * @param data is a TokenizedData instance containing the pre-segmented data - * that should be read. + * @param name is the name of the annotation class. + * @param args is a map variant containing the arguments that were passed + * to the annotation. + * @param range if true, the annotation fields have an explicit range. */ - void data(TokenizedData data); + void annotationStart(const Variant &className, const Variant &args, + bool range); /** - * Function that shuold be called whenever character data is found in the - * input stream. The given string variant is converted into a TokenizedData - * instance internally. + * Function that should be called whenever an annotation ends. * - * @param stringData is a string variant containing the data that has been - * found. + * @param name is the name of the annotation class that was ended. + * @param annotationName is the name of the annotation that was ended. */ - void data(const Variant &stringData); + void annotationEnd(const Variant &className, const Variant &elementName); + + /** + * Function the should be called whenever a ranged command or annotation + * ends. Must be called if the range parameter range was set to true when + * annotationStart() or commandStart() were called. + */ + void rangeEnd(); /** * Function that should be called whenever a new field starts. Fields of the @@ -329,29 +146,24 @@ public: 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. + * Function that should 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 name is the name of the annotation class that was ended. - * @param annotationName is the name of the annotation that was ended. + * @param data is a TokenizedData instance containing the pre-segmented data + * that should be read. */ - void annotationEnd(const Variant &className, const Variant &elementName); + void data(const TokenizedData &data); /** - * Function that should be called whenever a previously registered token - * is found in the input stream. + * Function that shuold be called whenever character data is found in the + * input stream. The given string variant is converted into a TokenizedData + * instance internally. * - * @param token is string variant containing the token that was encountered. + * @param stringData is a string variant containing the data that has been + * found. */ - void token(Variant token); + void data(const Variant &stringData); }; } } -- cgit v1.2.3 From f65e7af0dd0028ec481360eeaa16c4ff95ce253b Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 10:59:50 +0100 Subject: Got all handlers compling again --- CMakeLists.txt | 12 +-- src/core/parser/stack/DocumentHandler.cpp | 18 ++--- src/core/parser/stack/DocumentHandler.hpp | 4 +- src/core/parser/stack/Handler.hpp | 3 +- src/core/parser/stack/Stack.cpp | 123 +++++++++++++++++++++++------- src/core/parser/stack/Stack.hpp | 14 +--- 6 files changed, 112 insertions(+), 62 deletions(-) (limited to 'src/core/parser/stack') diff --git a/CMakeLists.txt b/CMakeLists.txt index b206458..cef1e31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,16 +181,16 @@ ADD_LIBRARY(ousia_core src/core/parser/ParserContext src/core/parser/ParserScope src/core/parser/stack/Callbacks -# src/core/parser/stack/DocumentHandler -# src/core/parser/stack/DomainHandler -# src/core/parser/stack/GenericParserStates + src/core/parser/stack/DocumentHandler + src/core/parser/stack/DomainHandler + src/core/parser/stack/GenericParserStates src/core/parser/stack/Handler -# src/core/parser/stack/ImportIncludeHandler + src/core/parser/stack/ImportIncludeHandler src/core/parser/stack/State # src/core/parser/stack/Stack src/core/parser/stack/TokenRegistry src/core/parser/stack/TokenStack -# src/core/parser/stack/TypesystemHandler + src/core/parser/stack/TypesystemHandler src/core/parser/utils/SourceOffsetVector src/core/parser/utils/TokenizedData src/core/parser/utils/Tokenizer @@ -215,8 +215,8 @@ ADD_LIBRARY(ousia_core #) ADD_LIBRARY(ousia_osml - src/formats/osml/OsmlParser src/formats/osml/OsmlStreamParser + src/formats/osml/OsmlParser ) TARGET_LINK_LIBRARIES(ousia_osml diff --git a/src/core/parser/stack/DocumentHandler.cpp b/src/core/parser/stack/DocumentHandler.cpp index d44176a..714ab1b 100644 --- a/src/core/parser/stack/DocumentHandler.cpp +++ b/src/core/parser/stack/DocumentHandler.cpp @@ -246,8 +246,6 @@ bool DocumentChildHandler::start(Variant::mapType &args) parent->getDescriptor()->getFieldDescriptorIndex(); } // create the entity for the new element at last. - // TODO: REMOVE - strct_name = strct->getName(); entity = parent->createChildStructuredEntity(strct, lastFieldIdx, args, nameAttr); } @@ -373,15 +371,8 @@ bool DocumentChildHandler::convertData(Handle field, return valid && scope().resolveValue(data, type, logger); } -bool DocumentChildHandler::data(TokenizedData &data) +bool DocumentChildHandler::data() { - // TODO: Handle this correctly - Variant text = data.text(WhitespaceMode::TRIM); - if (text == nullptr) { - // For now, except "no data" as success - return true; - } - // We're past the region in which explicit fields can be defined in the // parent structure element scope().setFlag(ParserFlag::POST_EXPLICIT_FIELDS, true); @@ -401,6 +392,7 @@ bool DocumentChildHandler::data(TokenizedData &data) // If it is a primitive field directly, try to parse the content. if (field->isPrimitive()) { // Add it as primitive content. + Variant text = readData(); if (!convertData(field, text, logger())) { return false; } @@ -419,6 +411,10 @@ bool DocumentChildHandler::data(TokenizedData &data) for (auto primitiveField : defaultFields) { // Then try to parse the content using the type specification. forks.emplace_back(logger().fork()); + + // TODO: Actually the data has to be read after the path has been + // created (as createPath may push more tokens onto the stack) + Variant text = readData(); if (!convertData(primitiveField, text, forks.back())) { continue; } @@ -428,7 +424,6 @@ bool DocumentChildHandler::data(TokenizedData &data) // Construct the necessary path NodeVector path = field->pathTo(primitiveField, logger()); - // TODO: Create methods with indices instead of names. createPath(fieldIdx, path, parent); // Then create the primitive element @@ -439,6 +434,7 @@ bool DocumentChildHandler::data(TokenizedData &data) // No field was found that might take the data -- dump the error messages // from the loggers -- or, if there were no primitive fields, clearly state // this fact + Variant text = readData(); if (defaultFields.empty()) { logger().error("Got data, but structure \"" + name() + "\" does not have any primitive field", diff --git a/src/core/parser/stack/DocumentHandler.hpp b/src/core/parser/stack/DocumentHandler.hpp index dda7d8b..c51c188 100644 --- a/src/core/parser/stack/DocumentHandler.hpp +++ b/src/core/parser/stack/DocumentHandler.hpp @@ -93,8 +93,6 @@ public: class DocumentChildHandler : public Handler { private: bool isExplicitField = false; - //TODO: REMOVE - std::string strct_name; /** * Code shared by both the start(), fieldStart() and the data() method. @@ -167,7 +165,7 @@ public: bool start(Variant::mapType &args) override; void end() override; - bool data(TokenizedData &data) override; + bool data() override; bool fieldStart(bool &isDefault, size_t fieldIdx) override; diff --git a/src/core/parser/stack/Handler.hpp b/src/core/parser/stack/Handler.hpp index 848d395..377a214 100644 --- a/src/core/parser/stack/Handler.hpp +++ b/src/core/parser/stack/Handler.hpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace ousia { @@ -37,7 +38,7 @@ class Variant; namespace parser_stack { // More forward declarations -class Callbacks; +class HandlerCallbacks; class State; /** diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index 292e7e2..ff03a6b 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -210,7 +210,6 @@ static LoggableException buildInvalidCommandException( /* Class StackImpl */ class StackImpl { - private: /** * Reference at the parser context. @@ -231,7 +230,7 @@ private: /** * Return the reference in the Logger instance stored within the context. */ - Logger &logger(); + Logger &logger() {return ctx.getLogger();} /** * Used internally to get all expected command names for the current state. @@ -311,12 +310,28 @@ private: * @return true if all handlers on the stack are valid. */ bool handlersValid(); -}; +public: + StackImpl(ParserContext &ctx, + const std::multimap &states); + + ~StackImpl(); -/* Class Stack */ + const State ¤tState() const; + std::string currentCommandName() const; -Stack::Stack(ParserContext &ctx, + void commandStart(const Variant &name, const Variant::mapType &args, + bool range); + void annotationStart(const Variant &className, const Variant &args, + bool range); + void annotationEnd(const Variant &className, const Variant &elementName); + void rangeEnd(); + void fieldStart(bool isDefault); + void fieldEnd(); + void data(const TokenizedData &data); +}; + +StackImpl::StackImpl(ParserContext &ctx, const std::multimap &states) : ctx(ctx), states(states) { @@ -327,7 +342,7 @@ Stack::Stack(ParserContext &ctx, } } -Stack::~Stack() +StackImpl::~StackImpl() { while (!stack.empty()) { // Fetch the topmost stack element @@ -351,7 +366,7 @@ Stack::~Stack() } } -void Stack::deduceState() +void StackImpl::deduceState() { // Assemble all states std::vector states; @@ -384,7 +399,7 @@ void Stack::deduceState() info.fieldStart(true, false, true); } -std::set Stack::expectedCommands() +std::set StackImpl::expectedCommands() { const State *currentState = &(this->currentState()); std::set res; @@ -396,17 +411,17 @@ std::set Stack::expectedCommands() return res; } -const State &Stack::currentState() +const State &StackImpl::currentState() { return stack.empty() ? States::None : stack.back().handler->getState(); } -std::string Stack::currentCommandName() +std::string StackImpl::currentCommandName() { return stack.empty() ? std::string{} : stack.back().handler->getName(); } -const State *Stack::findTargetState(const std::string &name) +const State *StackImpl::findTargetState(const std::string &name) { const State *currentState = &(this->currentState()); auto range = states.equal_range(name); @@ -420,7 +435,7 @@ const State *Stack::findTargetState(const std::string &name) return nullptr; } -const State *Stack::findTargetStateOrWildcard(const std::string &name) +const State *StackImpl::findTargetStateOrWildcard(const std::string &name) { // Try to find the target state with the given name, if none is found, try // find a matching "*" state. @@ -431,16 +446,16 @@ const State *Stack::findTargetStateOrWildcard(const std::string &name) return targetState; } -HandlerInfo &Stack::currentInfo() +HandlerInfo &StackImpl::currentInfo() { return stack.empty() ? EmptyHandlerInfo : stack.back(); } -HandlerInfo &Stack::lastInfo() +HandlerInfo &StackImpl::lastInfo() { return stack.size() < 2U ? EmptyHandlerInfo : stack[stack.size() - 2]; } -void Stack::endCurrentHandler() +void StackImpl::endCurrentHandler() { if (!stack.empty()) { // Fetch the handler info for the current top-level element @@ -467,7 +482,7 @@ void Stack::endCurrentHandler() } } -void Stack::endOverdueHandlers() +void StackImpl::endOverdueHandlers() { if (!stack.empty()) { // Fetch the handler info for the current top-level element @@ -483,7 +498,7 @@ void Stack::endOverdueHandlers() } } -bool Stack::ensureHandlerIsInField() +bool StackImpl::ensureHandlerIsInField() { // If the current handler is not in a field (and actually has a handler) // try to start a default field @@ -507,7 +522,7 @@ bool Stack::ensureHandlerIsInField() return true; } -bool Stack::handlersValid() +bool StackImpl::handlersValid() { for (auto it = stack.crbegin(); it != stack.crend(); it++) { if (!it->valid) { @@ -517,9 +532,7 @@ bool Stack::handlersValid() return true; } -Logger &Stack::logger() { return ctx.getLogger(); } - -void Stack::command(const Variant &name, const Variant::mapType &args) +void StackImpl::commandStart(const Variant &name, const Variant::mapType &args) { // End handlers that already had a default field and are currently not // active. @@ -611,7 +624,22 @@ void Stack::command(const Variant &name, const Variant::mapType &args) } } -void Stack::data(TokenizedData data) +void StackImpl::annotationStart(const Variant &className, const Variant &args) +{ + // TODO +} + +void StackImpl::annotationEnd(const Variant &className, const Variant &elementName) +{ + // TODO +} + +void StackImpl::rangeEnd() +{ + // TODO +} + +void StackImpl::data(TokenizedData data) { // TODO: Rewrite this function for token handling // TODO: This loop needs to be refactored out @@ -626,7 +654,8 @@ void Stack::data(TokenizedData data) // make sure the data actually is data if (stack.empty()) { if (hasNonWhitespaceText) { - throw LoggableException("No command here to receive data.", data); + throw LoggableException("No command here to receive data.", + data); } return; } @@ -699,7 +728,7 @@ void Stack::data(TokenizedData data) } } -void Stack::data(const Variant &stringData) +void StackImpl::data(const Variant &stringData) { // Fetch the SourceLocation of the given stringData variant SourceLocation loc = stringData.getLocation(); @@ -712,7 +741,7 @@ void Stack::data(const Variant &stringData) data(tokenizedData); } -void Stack::fieldStart(bool isDefault) +void StackImpl::fieldStart(bool isDefault) { // Make sure the current handler stack is not empty if (stack.empty()) { @@ -764,7 +793,7 @@ void Stack::fieldStart(bool isDefault) info.fieldStart(defaultField, false, valid); } -void Stack::fieldEnd() +void StackImpl::fieldEnd() { // Unroll the stack until the next explicitly open field while (!stack.empty()) { @@ -799,14 +828,50 @@ void Stack::fieldEnd() info.fieldEnd(); } -void Stack::annotationStart(const Variant &className, const Variant &args) +/* Class Stack */ + +Stack::Stack(ParserContext &ctx, + const std::multimap &states) + : impl(new StackImpl(ctx, states)) +{ +} + +Stack::~Stack() { - // TODO + // Do nothing here, stub needed because StackImpl is incomplete in hpp +} + +const State &Stack::currentState() const { return impl->currentState(); } + +std::string Stack::currentCommandName() const +{ + return impl->currentCommandName(); +} + +void Stack::commandStart(const Variant &name, const Variant::mapType &args, + bool range) +{ + impl->commandStart(name, args, range); +} + +void Stack::annotationStart(const Variant &className, const Variant &args, + bool range) +{ + impl->annotationStart(className, args, range); } void Stack::annotationEnd(const Variant &className, const Variant &elementName) { - // TODO + impl->annotationEnd(className, elementName); } + +void Stack::rangeEnd() { impl->rangeEnd(); } + +void Stack::fieldStart(bool isDefault) { impl->fieldStart(isDefault); } + +void Stack::fieldEnd() { impl->fieldEnd(); } + +void Stack::data(const TokenizedData &data) { impl->data(data); } +}; } } diff --git a/src/core/parser/stack/Stack.hpp b/src/core/parser/stack/Stack.hpp index e1173d0..1d87b9c 100644 --- a/src/core/parser/stack/Stack.hpp +++ b/src/core/parser/stack/Stack.hpp @@ -81,7 +81,7 @@ public: * @return the state of the currently active Handler instance or * States::None if no handler is on the stack. */ - const State ¤tState(); + const State ¤tState() const; /** * Returns the command name that is currently being handled. @@ -89,7 +89,7 @@ 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 currentCommandName(); + std::string currentCommandName() const; /** * Function that should be called whenever a new command is reached. @@ -154,16 +154,6 @@ public: * that should be read. */ void data(const TokenizedData &data); - - /** - * Function that shuold be called whenever character data is found in the - * input stream. The given string variant is converted into a TokenizedData - * instance internally. - * - * @param stringData is a string variant containing the data that has been - * found. - */ - void data(const Variant &stringData); }; } } -- cgit v1.2.3 From 5b81f755a5303c3eab05c605711ecca32c071b6d Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 11:46:47 +0100 Subject: Got Stack compiling again --- src/core/parser/stack/Callbacks.hpp | 28 ++-- src/core/parser/stack/Handler.hpp | 75 ++++----- src/core/parser/stack/Stack.cpp | 297 ++++++++++++++++++++++-------------- src/core/parser/stack/Stack.hpp | 5 +- src/formats/osml/OsmlParser.cpp | 2 +- src/formats/osxml/OsxmlParser.cpp | 15 +- 6 files changed, 251 insertions(+), 171 deletions(-) (limited to 'src/core/parser/stack') diff --git a/src/core/parser/stack/Callbacks.hpp b/src/core/parser/stack/Callbacks.hpp index d7b2547..8acc02d 100644 --- a/src/core/parser/stack/Callbacks.hpp +++ b/src/core/parser/stack/Callbacks.hpp @@ -76,18 +76,8 @@ public: * Interface defining a set of callback functions that act as a basis for the * StateStackCallbacks and the ParserCallbacks. */ -class HandlerCallbacks: public ParserCallbacks { +class HandlerCallbacks : public ParserCallbacks { public: - /** - * Reads a string variant form the current input stream. This function must - * be called from the data() method. - * - * @return a string variant containing the current text data. The return - * value depends on the currently set whitespace mode and the tokens that - * were enabled using the enableTokens callback method. - */ - Variant readData(); - /** * Pushes a list of TokenSyntaxDescriptor instances onto the internal stack. * The tokens described in the token list are the tokens that are currently @@ -96,14 +86,24 @@ public: * @param tokens is a list of TokenSyntaxDescriptor instances that should be * stored on the stack. */ - void pushTokens(const std::vector &tokens); + virtual void pushTokens( + const std::vector &tokens) = 0; /** * Removes the previously pushed list of tokens from the stack. */ - void popTokens(); -}; + virtual void popTokens() = 0; + /** + * Reads a string variant form the current input stream. This function must + * be called from the data() method. + * + * @return a string variant containing the current text data. The return + * value depends on the currently set whitespace mode and the tokens that + * were enabled using the enableTokens callback method. + */ + virtual Variant readData() = 0; +}; } } diff --git a/src/core/parser/stack/Handler.hpp b/src/core/parser/stack/Handler.hpp index 377a214..19c3d65 100644 --- a/src/core/parser/stack/Handler.hpp +++ b/src/core/parser/stack/Handler.hpp @@ -161,6 +161,44 @@ protected: */ const std::string &name() const; + /** + * Calls the corresponding function in the HandlerCallbacks instance. This + * method registers the given tokens as tokens that are generally available, + * tokens must be explicitly enabled using the "pushTokens" and "popTokens" + * method. Tokens that have not been registered are not guaranteed to be + * reported (except for special tokens, these do not have to be registerd). + * + * @param token is the token string that should be made available. + * @return the TokenId that will be used to refer to the token. + */ + TokenId registerToken(const std::string &token); + + /** + * Calls the corresponding function in the HandlerCallbacks instance. This + * method unregisters the given token. Note that for a token to be no longer + * reported, this function has to be called as many times as registerToken() + * for the corresponding token. + * + * @param id is the id of the Token that should be unregistered. + */ + void unregisterToken(TokenId id); + + /** + * Pushes a list of TokenSyntaxDescriptor instances onto the internal stack. + * The tokens described in the token list are the tokens that are currently + * enabled. + * + * @param tokens is a list of TokenSyntaxDescriptor instances that should be + * stored on the stack. + */ + void pushTokens(const std::vector &tokens); + + /** + * Calls the corresponding function in the HandlerCallbacks instance. + * Removes the previously pushed list of tokens from the stack. + */ + void popTokens(); + /** * Calls the corresponding method in the HandlerCallbacks instance. Reads a * string variant form the current input stream. This function must be @@ -193,43 +231,6 @@ protected: */ // void popWhitespaceMode(); - /** - * Pushes a list of TokenSyntaxDescriptor instances onto the internal stack. - * The tokens described in the token list are the tokens that are currently - * enabled. - * - * @param tokens is a list of TokenSyntaxDescriptor instances that should be - * stored on the stack. - */ - void pushTokens(const std::vector &tokens); - - /** - * Calls the corresponding function in the HandlerCallbacks instance. - * Removes the previously pushed list of tokens from the stack. - */ - void popTokens(); - - /** - * Calls the corresponding function in the HandlerCallbacks instance. This - * method registers the given tokens as tokens that are generally available, - * tokens must be explicitly enabled using the "pushTokens" and "popTokens" - * method. Tokens that have not been registered are not guaranteed to be - * reported (except for special tokens, these do not have to be registerd). - * - * @param token is the token string that should be made available. - * @return the TokenId that will be used to refer to the token. - */ - TokenId registerToken(const std::string &token); - - /** - * Calls the corresponding function in the HandlerCallbacks instance. This - * method unregisters the given token. Note that for a token to be no longer - * reported, this function has to be called as many times as registerToken() - * for the corresponding token. - * - * @param id is the id of the Token that should be unregistered. - */ - void unregisterToken(TokenId id); public: /** diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index ff03a6b..a556999 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -23,9 +23,12 @@ #include #include +#include "Callbacks.hpp" #include "Handler.hpp" #include "Stack.hpp" #include "State.hpp" +#include "TokenRegistry.hpp" +#include "TokenStack.hpp" namespace ousia { namespace parser_stack { @@ -209,8 +212,14 @@ static LoggableException buildInvalidCommandException( /* Class StackImpl */ -class StackImpl { +class StackImpl : public HandlerCallbacks { private: + /** + * Reference at an implementation of the ParserCallbacks instance to which + * certain handler callbacks are directed. + */ + ParserCallbacks &parser; + /** * Reference at the parser context. */ @@ -222,6 +231,18 @@ private: */ const std::multimap &states; + /** + * Registry responsible for registering the tokens proposed by the + * Handlers in the parser. + */ + TokenRegistry tokenRegistry; + + /** + * Pointer at a TokenizedDataReader instance from which the data should + * currently be read. + */ + TokenizedDataReader *dataReader; + /** * Internal stack used for managing the currently active Handler instances. */ @@ -230,7 +251,7 @@ private: /** * Return the reference in the Logger instance stored within the context. */ - Logger &logger() {return ctx.getLogger();} + Logger &logger() { return ctx.getLogger(); } /** * Used internally to get all expected command names for the current state. @@ -312,8 +333,8 @@ private: bool handlersValid(); public: - StackImpl(ParserContext &ctx, - const std::multimap &states); + StackImpl(ParserCallbacks &parser, ParserContext &ctx, + const std::multimap &states); ~StackImpl(); @@ -329,11 +350,22 @@ public: void fieldStart(bool isDefault); void fieldEnd(); void data(const TokenizedData &data); + + TokenId registerToken(const std::string &token) override; + void unregisterToken(TokenId id) override; + Variant readData() override; + bool hasData(); + void pushTokens(const std::vector &tokens) override; + void popTokens() override; }; -StackImpl::StackImpl(ParserContext &ctx, - const std::multimap &states) - : ctx(ctx), states(states) +StackImpl::StackImpl(ParserCallbacks &parser, ParserContext &ctx, + const std::multimap &states) + : parser(parser), + ctx(ctx), + states(states), + tokenRegistry(parser), + dataReader(nullptr) { // If the scope instance is not empty we need to deduce the current parser // state @@ -389,8 +421,8 @@ void StackImpl::deduceState() HandlerConstructor ctor = state.elementHandler ? state.elementHandler : EmptyHandler::create; - std::shared_ptr handler = - std::shared_ptr{ctor({ctx, "", state, SourceLocation{}})}; + std::shared_ptr handler = std::shared_ptr{ + ctor({ctx, *this, "", state, SourceLocation{}})}; stack.emplace_back(handler); // Set the correct flags for this implicit handler @@ -411,12 +443,12 @@ std::set StackImpl::expectedCommands() return res; } -const State &StackImpl::currentState() +const State &StackImpl::currentState() const { return stack.empty() ? States::None : stack.back().handler->getState(); } -std::string StackImpl::currentCommandName() +std::string StackImpl::currentCommandName() const { return stack.empty() ? std::string{} : stack.back().handler->getName(); } @@ -532,7 +564,8 @@ bool StackImpl::handlersValid() return true; } -void StackImpl::commandStart(const Variant &name, const Variant::mapType &args) +void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, + bool range) { // End handlers that already had a default field and are currently not // active. @@ -575,8 +608,8 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args) HandlerConstructor ctor = targetState->elementHandler ? targetState->elementHandler : EmptyHandler::create; - std::shared_ptr handler{ - ctor({ctx, name.asString(), *targetState, name.getLocation()})}; + std::shared_ptr handler{ctor( + {ctx, *this, name.asString(), *targetState, name.getLocation()})}; stack.emplace_back(handler); // Fetch the HandlerInfo for the parent element and the current element @@ -624,12 +657,14 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args) } } -void StackImpl::annotationStart(const Variant &className, const Variant &args) +void StackImpl::annotationStart(const Variant &className, const Variant &args, + bool range) { // TODO } -void StackImpl::annotationEnd(const Variant &className, const Variant &elementName) +void StackImpl::annotationEnd(const Variant &className, + const Variant &elementName) { // TODO } @@ -639,106 +674,93 @@ void StackImpl::rangeEnd() // TODO } -void StackImpl::data(TokenizedData data) +void StackImpl::data(const TokenizedData &data) { // TODO: Rewrite this function for token handling // TODO: This loop needs to be refactored out - while (!data.atEnd()) { - // End handlers that already had a default field and are currently not - // active. - endOverdueHandlers(); - - const bool hasNonWhitespaceText = data.hasNonWhitespaceText(); - - // Check whether there is any command the data can be sent to -- if not, - // make sure the data actually is data - if (stack.empty()) { - if (hasNonWhitespaceText) { - throw LoggableException("No command here to receive data.", - data); - } - return; - } - - // 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) { - // If the "hadDefaultField" flag is set, we already issued an error - // message - if (!info.hadDefaultField) { - if (hasNonWhitespaceText) { - logger().error("Did not expect any data here", data); - } - return; - } - } - - 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 { - // Create a fork of the TokenizedData and let the handler work - // on it - TokenizedData dataFork = data; - valid = info.handler->data(dataFork); - - // If the data was validly handled by the handler, commit the - // change - if (valid) { - data = dataFork; - } - } - 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 StackImpl::data(const Variant &stringData) -{ - // Fetch the SourceLocation of the given stringData variant - SourceLocation loc = stringData.getLocation(); - - // Create a TokenizedData instance and feed the given string data into it - TokenizedData tokenizedData(loc.getSourceId()); - tokenizedData.append(stringData.asString(), loc.getStart()); - - // Call the actual "data" method - data(tokenizedData); + /*while (!data.atEnd()) { + // End handlers that already had a default field and are currently not + // active. + endOverdueHandlers(); + + const bool hasNonWhitespaceText = data.hasNonWhitespaceText(); + + // Check whether there is any command the data can be sent to -- if not, + // make sure the data actually is data + if (stack.empty()) { + if (hasNonWhitespaceText) { + throw LoggableException("No command here to receive data.", + data); + } + return; + } + + // 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) { + // If the "hadDefaultField" flag is set, we already issued an error + // message + if (!info.hadDefaultField) { + if (hasNonWhitespaceText) { + logger().error("Did not expect any data here", data); + } + return; + } + } + + 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 { + // Create a fork of the TokenizedData and let the handler work + // on it + TokenizedData dataFork = data; + valid = info.handler->data(dataFork); + + // If the data was validly handled by the handler, commit the + // change + if (valid) { + data = dataFork; + } + } + 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 StackImpl::fieldStart(bool isDefault) @@ -828,11 +850,55 @@ void StackImpl::fieldEnd() info.fieldEnd(); } +TokenId StackImpl::registerToken(const std::string &token) +{ + return tokenRegistry.registerToken(token); +} + +void StackImpl::unregisterToken(TokenId id) +{ + tokenRegistry.unregisterToken(id); +} + +void StackImpl::pushTokens(const std::vector &tokens) +{ + // TODO +} + +void StackImpl::popTokens() +{ + // TODO +} + +Variant StackImpl::readData() +{ + if (dataReader != nullptr) { + TokenizedDataReaderFork dataReaderFork = dataReader->fork(); + Token token; + + // TODO: Use correct token set + TokenSet tokens; + + // TODO: Use correct whitespace mode + WhitespaceMode mode = WhitespaceMode::COLLAPSE; + + dataReaderFork.read(token, tokens, mode); + if (token.id == Tokens::Data) { + Variant res = Variant::fromString(token.content); + res.setLocation(token.getLocation()); + return res; + } + } + return Variant{}; +} + +bool StackImpl::hasData() { return readData() != nullptr; } + /* Class Stack */ -Stack::Stack(ParserContext &ctx, +Stack::Stack(ParserCallbacks &parser, ParserContext &ctx, const std::multimap &states) - : impl(new StackImpl(ctx, states)) + : impl(new StackImpl(parser, ctx, states)) { } @@ -872,6 +938,5 @@ void Stack::fieldStart(bool isDefault) { impl->fieldStart(isDefault); } void Stack::fieldEnd() { impl->fieldEnd(); } void Stack::data(const TokenizedData &data) { impl->data(data); } -}; } } diff --git a/src/core/parser/stack/Stack.hpp b/src/core/parser/stack/Stack.hpp index 1d87b9c..de281d4 100644 --- a/src/core/parser/stack/Stack.hpp +++ b/src/core/parser/stack/Stack.hpp @@ -42,6 +42,7 @@ class Variant; namespace parser_stack { // Forward declarations +class ParserCallbacks; class StackImpl; class State; @@ -63,11 +64,13 @@ public: /** * Creates a new instance of the Stack class. * + * @param parser is an implementation of the ParserCallbacks instance to + * which certain calls are directed. * @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, + Stack(ParserCallbacks &parser, ParserContext &ctx, const std::multimap &states); /** diff --git a/src/formats/osml/OsmlParser.cpp b/src/formats/osml/OsmlParser.cpp index c25974f..36ef2b6 100644 --- a/src/formats/osml/OsmlParser.cpp +++ b/src/formats/osml/OsmlParser.cpp @@ -73,7 +73,7 @@ public: : logger(ctx.getLogger()), ctx(ctx), parser(reader, logger), - stack(ctx, GenericParserStates) + stack(parser, ctx, GenericParserStates) { } diff --git a/src/formats/osxml/OsxmlParser.cpp b/src/formats/osxml/OsxmlParser.cpp index afe0dc6..10cc77a 100644 --- a/src/formats/osxml/OsxmlParser.cpp +++ b/src/formats/osxml/OsxmlParser.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -32,7 +33,7 @@ using namespace parser_stack; /** * Class containing the actual OsxmlParser implementation. */ -class OsxmlParserImplementation : public OsxmlEvents { +class OsxmlParserImplementation : public OsxmlEvents, ParserCallbacks { private: /** * Actual xml parser -- converts the xml stream into a set of events. @@ -56,7 +57,7 @@ public: */ OsxmlParserImplementation(CharReader &reader, ParserContext &ctx) : parser(reader, *this, ctx.getLogger()), - stack(ctx, GenericParserStates) + stack(*this, ctx, GenericParserStates) { } @@ -86,6 +87,16 @@ public: void rangeEnd() override { stack.rangeEnd(); } void data(const TokenizedData &data) override { stack.data(data); } + + TokenId registerToken(const std::string &token) override + { + return Tokens::Empty; + } + + void unregisterToken(TokenId id) override + { + // Do nothing here + } }; /* Class OsxmlParser */ -- cgit v1.2.3 From 072992a634d816fc7061b7eee5fd0cabe4242de4 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 13:45:24 +0100 Subject: Added supportsTokens method to StateBuilder --- src/core/parser/stack/State.cpp | 15 ++++++++++++--- src/core/parser/stack/State.hpp | 33 ++++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 10 deletions(-) (limited to 'src/core/parser/stack') diff --git a/src/core/parser/stack/State.cpp b/src/core/parser/stack/State.cpp index d72f533..0feeed6 100644 --- a/src/core/parser/stack/State.cpp +++ b/src/core/parser/stack/State.cpp @@ -23,17 +23,19 @@ namespace parser_stack { /* Class State */ -State::State() : elementHandler(nullptr) {} +State::State() : elementHandler(nullptr), supportsAnnotations(false), supportsTokens(false) {} State::State(StateSet parents, Arguments arguments, RttiSet createdNodeTypes, HandlerConstructor elementHandler, - bool supportsAnnotations) + bool supportsAnnotations, + bool supportsTokens) : parents(parents), arguments(arguments), createdNodeTypes(createdNodeTypes), elementHandler(elementHandler), - supportsAnnotations(supportsAnnotations) + supportsAnnotations(supportsAnnotations), + supportsTokens(supportsTokens) { } @@ -93,6 +95,13 @@ StateBuilder &StateBuilder::supportsAnnotations(bool supportsAnnotations) return *this; } +StateBuilder &StateBuilder::supportsTokens(bool supportsTokens) +{ + state.supportsTokens = supportsTokens; + return *this; +} + + const State &StateBuilder::build() const { return state; } /* Class StateDeductor */ diff --git a/src/core/parser/stack/State.hpp b/src/core/parser/stack/State.hpp index 4766235..011ccd6 100644 --- a/src/core/parser/stack/State.hpp +++ b/src/core/parser/stack/State.hpp @@ -82,13 +82,21 @@ struct State { /** * Set to true if this handler does support annotations. This is almost - * always false (e.g. all description handlers), except for document + * always false (e.g. all description handlers), except for document * element handlers. */ - bool supportsAnnotations; + bool supportsAnnotations : 1; /** - * Default constructor, initializes the handlers with nullptr. + * Set to true if this handler does support tokens. This is almost + * always false (e.g. all description handlers), except for document + * element handlers. + */ + bool supportsTokens : 1; + + /** + * Default constructor, initializes the handlers with nullptr and the + * supportsAnnotations and supportsTokens flags with false. */ State(); @@ -108,11 +116,12 @@ struct State { * be nullptr in which case no handler instance is created. * @param supportsAnnotations specifies whether annotations are supported * here at all. + * @param supportsTokens specified whether tokens are supported here at all. */ State(StateSet parents, Arguments arguments = Arguments{}, - RttiSet createdNodeTypes = RttiSet{}, - HandlerConstructor elementHandler = nullptr, - bool supportsAnnotations = false); + RttiSet createdNodeTypes = RttiSet{}, + HandlerConstructor elementHandler = nullptr, + bool supportsAnnotations = false, bool supportsTokens = false); /** * Creates this State from the given StateBuilder instance. @@ -219,6 +228,16 @@ public: */ StateBuilder &supportsAnnotations(bool supportsAnnotations); + /** + * Sets the state of the "supportsTokens" flag (default value is false). + * + * @param supportsTokens should be set to true, if the elementHandler + * registered for this state is capable of handling tokens. + * @return a reference at this StateBuilder instance for method + * chaining. + */ + StateBuilder &supportsTokens(bool supportsTokens); + /** * Returns a reference at the internal State instance that was built * using the StateBuilder. @@ -275,7 +294,7 @@ public: * @param states is a list of states that should be checked. */ StateDeductor(std::vector signature, - std::vector states); + std::vector states); /** * Selects all active states from the given states. Only considers those -- cgit v1.2.3 From 5d6ee07995c7f59e66e0df558c8ebe7d2a8d1f68 Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Mon, 2 Mar 2015 15:52:13 +0100 Subject: refactored SyntaxDescriptor to Token.hpp and added TokenDescriptor class. --- CMakeLists.txt | 1 + src/core/common/Token.cpp | 14 --- src/core/common/Token.hpp | 67 +----------- src/core/model/Syntax.cpp | 58 +++++++++++ src/core/model/Syntax.hpp | 196 +++++++++++++++++++++++++++++++++++ src/core/parser/stack/Callbacks.hpp | 3 +- src/core/parser/stack/Handler.cpp | 2 +- src/core/parser/stack/Handler.hpp | 3 +- src/core/parser/stack/TokenStack.cpp | 4 +- src/core/parser/stack/TokenStack.hpp | 5 +- 10 files changed, 266 insertions(+), 87 deletions(-) create mode 100644 src/core/model/Syntax.cpp create mode 100644 src/core/model/Syntax.hpp (limited to 'src/core/parser/stack') diff --git a/CMakeLists.txt b/CMakeLists.txt index b206458..13de9ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,7 @@ ADD_LIBRARY(ousia_core src/core/model/Project src/core/model/RootNode src/core/model/Style + src/core/model/Syntax src/core/model/Typesystem src/core/parser/Parser src/core/parser/ParserContext diff --git a/src/core/common/Token.cpp b/src/core/common/Token.cpp index e454ae4..17ce03e 100644 --- a/src/core/common/Token.cpp +++ b/src/core/common/Token.cpp @@ -20,19 +20,5 @@ namespace ousia { -/* Class TokenSyntaxDescriptor */ - -void TokenSyntaxDescriptor::insertIntoTokenSet(TokenSet &set) const -{ - if (start != Tokens::Empty) { - set.insert(start); - } - if (end != Tokens::Empty) { - set.insert(end); - } - if (shortForm != Tokens::Empty) { - set.insert(shortForm); - } -} } diff --git a/src/core/common/Token.hpp b/src/core/common/Token.hpp index f89a0ce..f37151f 100644 --- a/src/core/common/Token.hpp +++ b/src/core/common/Token.hpp @@ -173,71 +173,6 @@ struct Token { const SourceLocation &getLocation() const { return location; } }; -/** - * Class describing the user defined syntax for a single field or annotation. - */ -struct TokenSyntaxDescriptor { - /** - * Possible start token or Tokens::Empty if no token is set. - */ - TokenId start; - - /** - * Possible end token or Tokens::Empty if no token is set. - */ - TokenId end; - - /** - * Possible representation token or Tokens::Empty if no token is set. - */ - TokenId shortForm; - - /** - * Flag specifying whether this TokenSyntaxDescriptor describes an - * annotation. - */ - bool isAnnotation; - - /** - * Default constructor, sets all token ids to Tokens::Empty and isAnnotation - * to false. - */ - TokenSyntaxDescriptor() - : start(Tokens::Empty), - end(Tokens::Empty), - shortForm(Tokens::Empty), - isAnnotation(false) - { - } - - /** - * Member initializer constructor. - * - * @param start is a possible start token. - * @param end is a possible end token. - * @param shortForm is a possible short form token. - * @param isAnnotation is set to true if this syntax descriptor describes an - * annotation. - */ - TokenSyntaxDescriptor(TokenId start, TokenId end, TokenId shortForm, - bool isAnnotation) - : start(start), - end(end), - shortForm(shortForm), - isAnnotation(isAnnotation) - { - } - - /** - * Inserts all tokens referenced in this TokenSyntaxDescriptor into the - * given TokenSet. Skips token ids set to Tokens::Empty. - * - * @param set is the TokenSet instance into which the Tokens should be - * inserted. - */ - void insertIntoTokenSet(TokenSet &set) const; -}; } -#endif /* _OUSIA_TOKENS_HPP_ */ - +#endif /* _OUSIA_TOKENS_HPP_ */ \ No newline at end of file diff --git a/src/core/model/Syntax.cpp b/src/core/model/Syntax.cpp new file mode 100644 index 0000000..9dbaccc --- /dev/null +++ b/src/core/model/Syntax.cpp @@ -0,0 +1,58 @@ +/* + 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 . +*/ + +#include "Syntax.hpp" + +#include "Domain.hpp" + +namespace ousia { + +/* Class TokenSyntaxDescriptor */ + +bool SyntaxDescriptor::isAnnotation() const +{ + return descriptor->isa(&RttiTypes::AnnotationClass); +} +bool SyntaxDescriptor::isFieldDescriptor() const +{ + return descriptor->isa(&RttiTypes::FieldDescriptor); +} +bool SyntaxDescriptor::isStruct() const +{ + return descriptor->isa(&RttiTypes::StructuredClass); +} + +void SyntaxDescriptor::insertIntoTokenSet(TokenSet &set) const +{ + if (start != Tokens::Empty) { + set.insert(start); + } + if (end != Tokens::Empty) { + set.insert(end); + } + if (shortForm != Tokens::Empty) { + set.insert(shortForm); + } +} + +bool SyntaxDescriptor::isEmpty() const +{ + return start == Tokens::Empty && end == Tokens::Empty && + shortForm == Tokens::Empty; +} +} \ No newline at end of file diff --git a/src/core/model/Syntax.hpp b/src/core/model/Syntax.hpp new file mode 100644 index 0000000..4da3408 --- /dev/null +++ b/src/core/model/Syntax.hpp @@ -0,0 +1,196 @@ +/* + 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 . +*/ + +/** + * @file Syntax.hpp + * + * This header contains the Descriptor classes for user definable syntax for + * Document entities or fields. These classes are referenced in Ontology.hpp. + */ + +#ifndef _OUSIA_MODEL_SYNTAX_HPP_ +#define _OUSIA_MODEL_SYNTAX_HPP_ + +#include +#include "Node.hpp" + +namespace ousia { + +/** + * Class to describe a single token that shall be used as user-defined syntax. + */ +struct TokenDescriptor { + /** + * The string content of this token, if it is not a special one. + */ + std::string token; + /** + * A flag to be set true if this TokenDescriptor uses a special token. + */ + bool special; + /** + * An id to uniquely identify this token. + */ + TokenId id; + + /** + * Constructor for non-special tokens. The special flag is set to false and + * the id to Tokens::Empty. + * + * @param token The string content of this token, if it is not a special + * one. + */ + TokenDescriptor(std::string token = std::string()) + : token(std::move(token)), special(false), id(Tokens::Empty) + { + } + + /** + * Constructor for special tokens. The token is set to an empty string and + * the special flag to true. + * + * @param id the id of the special token. + */ + TokenDescriptor(TokenId id) : special(true), id(id) {} + + /** + * Returns true if and only if neither a string nor an ID is given. + * + * @return true if and only if neither a string nor an ID is given. + */ + bool isEmpty() const { return token.empty() && id == Tokens::Empty; } +}; + +/** + * Class describing the user defined syntax for a StructuredClass, + * AnnotationClass or FieldDescriptor. + * + * This class is used during parsing of a Document. It is used to describe + * the tokens relevant for one Descriptor that could be created at this point + * during parsing. + */ +struct SyntaxDescriptor { + /** + * Possible start token or Tokens::Empty if no token is set. + */ + TokenId start; + + /** + * Possible end token or Tokens::Empty if no token is set. + */ + TokenId end; + + /** + * Possible representation token or Tokens::Empty if no token is set. + */ + TokenId shortForm; + + /* + * The Descriptor this SyntaxDescriptor belongs to. As this may be + * a FieldDescriptor as well as a class Descriptor (StructuredClass or + * AnnotationClass) we can only use the class Node as inner argument here. + */ + Rooted descriptor; + /* + * Given the current leaf in the parsed document the depth of a + * SyntaxDescriptor is defined as the number of transparent elements that + * would be needed to construct an instance of the referenced descriptor. + */ + ssize_t depth; + + /** + * Default constructor, sets all token ids to Tokens::Empty and the + * descriptor handle to nullptr. + */ + SyntaxDescriptor() + : start(Tokens::Empty), + end(Tokens::Empty), + shortForm(Tokens::Empty), + descriptor(nullptr), + depth(-1) + { + } + + /** + * Member initializer constructor. + * + * @param start is a possible start token. + * @param end is a possible end token. + * @param shortForm is a possible short form token. + * @param descriptor The Descriptor this SyntaxDescriptor belongs to. + * @param depth Given the current leaf in the parsed document the depth of a + * SyntaxDescriptor is defined as the number of transparent elements that + * would be needed to construct an instance of the referenced descriptor. + */ + SyntaxDescriptor(TokenId start, TokenId end, TokenId shortForm, + Handle descriptor, ssize_t depth) + : start(start), + end(end), + shortForm(shortForm), + descriptor(descriptor), + depth(depth) + { + } + + /** + * Inserts all tokens referenced in this SyntaxDescriptor into the + * given TokenSet. Skips token ids set to Tokens::Empty. + * + * @param set is the TokenSet instance into which the Tokens should be + * inserted. + */ + void insertIntoTokenSet(TokenSet &set) const; + + /** + * Returns true if and only if this SyntaxDescriptor belongs to an + * AnnotationClass. + * + * @return true if and only if this SyntaxDescriptor belongs to an + * AnnotationClass. + */ + bool isAnnotation() const; + + /** + * Returns true if and only if this SyntaxDescriptor belongs to a + * StrcturedClass. + * + * @return true if and only if this SyntaxDescriptor belongs to a + * StrcturedClass. + */ + bool isStruct() const; + + /** + * Returns true if and only if this SyntaxDescriptor belongs to a + * FieldDescriptor. + * + * @return true if and only if this SyntaxDescriptor belongs to a + * FieldDescriptor. + */ + bool isFieldDescriptor() const; + + /** + * Returns true if and only if this SyntaxDescriptor has only empty + * entries in start, end and short. + * + * @return true if and only if this SyntaxDescriptor has only empty + * entries in start, end and short. + */ + bool isEmpty() const; +}; +} +#endif \ No newline at end of file diff --git a/src/core/parser/stack/Callbacks.hpp b/src/core/parser/stack/Callbacks.hpp index d7b2547..e471881 100644 --- a/src/core/parser/stack/Callbacks.hpp +++ b/src/core/parser/stack/Callbacks.hpp @@ -34,6 +34,7 @@ #include #include +#include namespace ousia { @@ -96,7 +97,7 @@ public: * @param tokens is a list of TokenSyntaxDescriptor instances that should be * stored on the stack. */ - void pushTokens(const std::vector &tokens); + void pushTokens(const std::vector &tokens); /** * Removes the previously pushed list of tokens from the stack. diff --git a/src/core/parser/stack/Handler.cpp b/src/core/parser/stack/Handler.cpp index 734976a..12df0fd 100644 --- a/src/core/parser/stack/Handler.cpp +++ b/src/core/parser/stack/Handler.cpp @@ -74,7 +74,7 @@ Variant Handler::readData() return handlerData.callbacks.readData(); } -void Handler::pushTokens(const std::vector &tokens) +void Handler::pushTokens(const std::vector &tokens) { handlerData.callbacks.pushTokens(tokens); } diff --git a/src/core/parser/stack/Handler.hpp b/src/core/parser/stack/Handler.hpp index 848d395..19660d0 100644 --- a/src/core/parser/stack/Handler.hpp +++ b/src/core/parser/stack/Handler.hpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace ousia { @@ -200,7 +201,7 @@ protected: * @param tokens is a list of TokenSyntaxDescriptor instances that should be * stored on the stack. */ - void pushTokens(const std::vector &tokens); + void pushTokens(const std::vector &tokens); /** * Calls the corresponding function in the HandlerCallbacks instance. diff --git a/src/core/parser/stack/TokenStack.cpp b/src/core/parser/stack/TokenStack.cpp index 6afeaed..ac1d94e 100644 --- a/src/core/parser/stack/TokenStack.cpp +++ b/src/core/parser/stack/TokenStack.cpp @@ -21,7 +21,7 @@ namespace ousia { namespace parser_stack { -void TokenStack::pushTokens(const std::vector &tokens) +void TokenStack::pushTokens(const std::vector &tokens) { stack.push_back(tokens); } @@ -35,7 +35,7 @@ TokenSet TokenStack::tokens() const } TokenSet res; - for (const TokenSyntaxDescriptor &descr : stack.back()) { + for (const SyntaxDescriptor &descr : stack.back()) { descr.insertIntoTokenSet(res); } return res; diff --git a/src/core/parser/stack/TokenStack.hpp b/src/core/parser/stack/TokenStack.hpp index 9669f50..af734bb 100644 --- a/src/core/parser/stack/TokenStack.hpp +++ b/src/core/parser/stack/TokenStack.hpp @@ -32,6 +32,7 @@ #include #include +#include namespace ousia { namespace parser_stack { @@ -52,7 +53,7 @@ private: * Stack containing vectors of TokenSyntaxDescriptor instances as given by * the user. */ - std::vector> stack; + std::vector> stack; /** * Constructor of the TokenStack class. @@ -86,7 +87,7 @@ public: * @param tokens is a list of TokenSyntaxDescriptor instances that should be * stored on the stack. */ - void pushTokens(const std::vector &tokens); + void pushTokens(const std::vector &tokens); /** * Removes the previously pushed list of tokens from the stack. -- cgit v1.2.3 From 3cc6ebf406c53b0c82a52f0daf1ce14c62f7b521 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 16:30:51 +0100 Subject: Implemented new "start" methods in the Handler instances --- CMakeLists.txt | 6 +- src/core/parser/stack/DocumentHandler.cpp | 133 +++++++++++++++--------- src/core/parser/stack/DocumentHandler.hpp | 69 ++++++++++--- src/core/parser/stack/DomainHandler.cpp | 76 ++++++++------ src/core/parser/stack/DomainHandler.hpp | 22 ++-- src/core/parser/stack/Handler.cpp | 102 +++++++++---------- src/core/parser/stack/Handler.hpp | 150 +++++++++++++++------------- src/core/parser/stack/Stack.cpp | 24 +++-- src/core/parser/stack/TypesystemHandler.cpp | 29 +++--- src/core/parser/stack/TypesystemHandler.hpp | 15 ++- 10 files changed, 375 insertions(+), 251 deletions(-) (limited to 'src/core/parser/stack') diff --git a/CMakeLists.txt b/CMakeLists.txt index cef1e31..45310a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,7 +187,7 @@ ADD_LIBRARY(ousia_core src/core/parser/stack/Handler src/core/parser/stack/ImportIncludeHandler src/core/parser/stack/State -# src/core/parser/stack/Stack + src/core/parser/stack/Stack src/core/parser/stack/TokenRegistry src/core/parser/stack/TokenStack src/core/parser/stack/TypesystemHandler @@ -387,7 +387,7 @@ IF(TEST) # ) ADD_EXECUTABLE(ousia_test_osml -# test/formats/osml/OsmlParserTest + test/formats/osml/OsmlParserTest test/formats/osml/OsmlStreamParserTest ) @@ -400,7 +400,7 @@ IF(TEST) ADD_EXECUTABLE(ousia_test_osxml test/formats/osxml/OsxmlEventParserTest -# test/formats/osxml/OsxmlParserTest + test/formats/osxml/OsxmlParserTest ) TARGET_LINK_LIBRARIES(ousia_test_osxml diff --git a/src/core/parser/stack/DocumentHandler.cpp b/src/core/parser/stack/DocumentHandler.cpp index 714ab1b..de6e367 100644 --- a/src/core/parser/stack/DocumentHandler.cpp +++ b/src/core/parser/stack/DocumentHandler.cpp @@ -37,7 +37,8 @@ namespace parser_stack { /* DocumentHandler */ -bool DocumentHandler::start(Variant::mapType &args) +bool DocumentHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { Rooted document = context().getProject()->createDocument(args["name"].asString()); @@ -52,6 +53,25 @@ void DocumentHandler::end() { scope().pop(logger()); } /* DocumentChildHandler */ +DocumentChildHandler::DocumentChildHandler(const HandlerData &handlerData) + : Handler(handlerData), mode(Mode::STRUCT) +{ +} + +void DocumentChildHandler::setMode(Mode mode, const std::string &name) +{ + this->mode = mode; + this->name = name; + this->token = Token(); +} + +void DocumentChildHandler::setMode(Mode mode, const Token &token) +{ + this->mode = mode; + this->name = token.content; + this->token = token; +} + void DocumentChildHandler::preamble(Rooted &parentNode, size_t &fieldIdx, DocumentEntity *&parent) { @@ -122,10 +142,14 @@ void DocumentChildHandler::createPath(const size_t &firstFieldIdx, scope().setFlag(ParserFlag::POST_EXPLICIT_FIELDS, false); } -bool DocumentChildHandler::start(Variant::mapType &args) +bool DocumentChildHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { - // extract the special "name" attribute from the input arguments. - // the remaining attributes will be forwarded to the newly constructed + // Set the internal mode to STRUCT and copy the name + setMode(Mode::STRUCT, name); + + // Extract the special "name" attribute from the input arguments. + // The remaining attributes will be forwarded to the newly constructed // element. std::string nameAttr; { @@ -152,11 +176,11 @@ bool DocumentChildHandler::start(Variant::mapType &args) return false; } Rooted strct = scope().resolve( - Utils::split(name(), ':'), logger()); + Utils::split(name, ':'), logger()); if (strct == nullptr) { // if we could not resolve the name, throw an exception. throw LoggableException( - std::string("\"") + name() + "\" could not be resolved.", + std::string("\"") + name + "\" could not be resolved.", location()); } entity = parentNode.cast()->createRootStructuredEntity( @@ -169,13 +193,6 @@ bool DocumentChildHandler::start(Variant::mapType &args) preamble(parentNode, fieldIdx, parent); - // TODO: REMOVE - std::string thisName = name(); - std::string parentClassName; - if (parent != nullptr) { - parentClassName = parent->getDescriptor()->getName(); - } - /* * Try to find a FieldDescriptor for the given tag if we are not in * a field already. This does _not_ try to construct transparent @@ -183,7 +200,7 @@ bool DocumentChildHandler::start(Variant::mapType &args) */ { ssize_t newFieldIdx = - parent->getDescriptor()->getFieldDescriptorIndex(name()); + parent->getDescriptor()->getFieldDescriptorIndex(name); if (newFieldIdx != -1) { // Check whether explicit fields are allowed here, if not if (scope().getFlag(ParserFlag::POST_EXPLICIT_FIELDS)) { @@ -191,17 +208,17 @@ bool DocumentChildHandler::start(Variant::mapType &args) std::string( "Data or structure commands have already been " "given, command \"") + - name() + std::string( - "\" is not interpreted explicit " - "field. Move explicit field " - "references to the beginning."), + name + std::string( + "\" is not interpreted explicit " + "field. Move explicit field " + "references to the beginning."), location()); } else { Rooted field{new DocumentField( manager(), parentNode, newFieldIdx, false)}; field->setLocation(location()); scope().push(field); - isExplicitField = true; + setMode(Mode::EXPLICIT_FIELD, name); return true; } } @@ -210,11 +227,11 @@ bool DocumentChildHandler::start(Variant::mapType &args) // Otherwise create a new StructuredEntity // TODO: Consider Anchors and AnnotationEntities Rooted strct = scope().resolve( - Utils::split(name(), ':'), logger()); + Utils::split(name, ':'), logger()); if (strct == nullptr) { // if we could not resolve the name, throw an exception. throw LoggableException( - std::string("\"") + name() + "\" could not be resolved.", + std::string("\"") + name + "\" could not be resolved.", location()); } @@ -261,24 +278,56 @@ bool DocumentChildHandler::start(Variant::mapType &args) } } +bool DocumentChildHandler::startAnnotation(const std::string &name, + Variant::mapType &args, + AnnotationType annotationType) +{ + // Set the internal mode and name correctly + if (annotationType == AnnotationType::START) { + setMode(Mode::ANNOTATION_START, name); + } else { + setMode(Mode::ANNOTATION_END, name); + } + + // TODO: Handle annotation + return false; +} + +bool DocumentChildHandler::startToken(const Token &token, Handle node) +{ + // Set the internal mode correctly + setMode(Mode::TOKEN, token); + + // TODO: Handle token start + return false; +} + +DocumentChildHandler::EndTokenResult DocumentChildHandler::endToken( + const Token &token, Handle node) +{ + // TODO: Handle token end + return EndTokenResult::ENDED_NONE; +} + void DocumentChildHandler::end() { - // in case of explicit fields we do not want to pop something from the + // In case of explicit fields we do not want to pop something from the // stack. - if (isExplicitField) { - return; + if (mode == Mode::STRUCT) { + // pop the "main" element. + scope().pop(logger()); } - // pop the "main" element. - scope().pop(logger()); } bool DocumentChildHandler::fieldStart(bool &isDefault, size_t fieldIdx) { - if (isExplicitField) { + // TODO: Handle other cases + if (mode == Mode::EXPLICIT_FIELD) { // In case of explicit fields we do not want to create another field. isDefault = true; return fieldIdx == 0; } + Rooted parentNode = scope().getLeaf(); assert(parentNode->isa(&RttiTypes::StructuredEntity) || parentNode->isa(&RttiTypes::AnnotationEntity)); @@ -291,7 +340,7 @@ bool DocumentChildHandler::fieldStart(bool &isDefault, size_t fieldIdx) parent->getDescriptor()->getFieldDescriptors(); if (isDefault) { - if(fields.empty()){ + if (fields.empty()) { return false; } fieldIdx = fields.size() - 1; @@ -317,33 +366,19 @@ void DocumentChildHandler::fieldEnd() { assert(scope().getLeaf()->isa(&RttiTypes::DocumentField)); - // pop the field from the stack. + // Pop the field from the stack. scope().pop(logger()); - // pop all remaining transparent elements. + // Pop all remaining transparent elements. while (scope().getLeaf()->isa(&RttiTypes::StructuredEntity) && scope().getLeaf().cast()->isTransparent()) { - // pop the transparent element. + // Pop the transparent element. scope().pop(logger()); - // pop the transparent field. + // Pop the transparent field. scope().pop(logger()); } } -bool DocumentChildHandler::annotationStart(const Variant &className, - Variant::mapType &args) -{ - // TODO: Implement - return false; -} - -bool DocumentChildHandler::annotationEnd(const Variant &className, - const Variant &elementName) -{ - // TODO: Implement - return false; -} - bool DocumentChildHandler::convertData(Handle field, Variant &data, Logger &logger) { @@ -436,7 +471,7 @@ bool DocumentChildHandler::data() // this fact Variant text = readData(); if (defaultFields.empty()) { - logger().error("Got data, but structure \"" + name() + + logger().error("Got data, but structure \"" + name + "\" does not have any primitive field", text); } else { @@ -467,7 +502,9 @@ const State DocumentChild = StateBuilder() .createdNodeTypes({&RttiTypes::StructureNode, &RttiTypes::AnnotationEntity, &RttiTypes::DocumentField}) - .elementHandler(DocumentChildHandler::create); + .elementHandler(DocumentChildHandler::create) + .supportsAnnotations(true) + .supportsTokens(true); } } diff --git a/src/core/parser/stack/DocumentHandler.hpp b/src/core/parser/stack/DocumentHandler.hpp index c51c188..9a41508 100644 --- a/src/core/parser/stack/DocumentHandler.hpp +++ b/src/core/parser/stack/DocumentHandler.hpp @@ -53,7 +53,8 @@ class DocumentHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; void end() override; /** @@ -91,8 +92,55 @@ public: * defined elements in an Ousía document. */ class DocumentChildHandler : public Handler { +public: + /** + * Enum type used to represent the mode of the DocumentChildHandler. + * TODO: Having to have such a type is actually quite stupid, it would be + * nicer to have separate handler classes for each of these cases. But this + * is a story for a different day. + */ + enum class Mode { + STRUCT, + EXPLICIT_FIELD, + ANNOTATION_START, + ANNOTATION_END, + TOKEN + }; + private: - bool isExplicitField = false; + /** + * Internal Mode of the DocumentChildHandler. + */ + Mode mode; + + /** + * Contains the name of the command or the annotation that is represented + * by this DocumentChildHandler. + */ + std::string name; + + /** + * Token represented by the document child handler. + */ + Token token; + + /** + * Switches the mode to the given mode and copies the given name. Resets the + * token. + * + * @param mode is the new mode. + * @param name is the new name. + */ + void setMode(Mode mode, const std::string &name); + + /** + * Switches the mode to the given mode and copies the given token, sets the + * name to the content of the token. + * + * @param mode is the new mode. + * @param token is the new token. + */ + void setMode(Mode mode, const Token &token); /** * Code shared by both the start(), fieldStart() and the data() method. @@ -161,22 +209,19 @@ private: Logger &logger); public: - using Handler::Handler; + DocumentChildHandler(const HandlerData &handlerData); - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; + bool startAnnotation(const std::string &name, Variant::mapType &args, + AnnotationType annotationType) override; + bool startToken(const Token &token, Handle node) override; + EndTokenResult endToken(const Token &token, Handle node) override; void end() override; bool data() override; - bool fieldStart(bool &isDefault, size_t fieldIdx) override; - void fieldEnd() override; - bool annotationStart(const Variant &className, - Variant::mapType &args) override; - - bool annotationEnd(const Variant &className, - const Variant &elementName) override; - /** * Creates a new instance of the DocumentChildHandler. * diff --git a/src/core/parser/stack/DomainHandler.cpp b/src/core/parser/stack/DomainHandler.cpp index aa18faa..5ca4f5b 100644 --- a/src/core/parser/stack/DomainHandler.cpp +++ b/src/core/parser/stack/DomainHandler.cpp @@ -33,7 +33,8 @@ namespace parser_stack { /* DomainHandler */ -bool DomainHandler::start(Variant::mapType &args) +bool DomainHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { // Create the Domain node Rooted domain = @@ -57,7 +58,8 @@ void DomainHandler::end() { scope().pop(logger()); } /* DomainStructHandler */ -bool DomainStructHandler::start(Variant::mapType &args) +bool DomainStructHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); @@ -88,7 +90,8 @@ bool DomainStructHandler::start(Variant::mapType &args) void DomainStructHandler::end() { scope().pop(logger()); } /* DomainAnnotationHandler */ -bool DomainAnnotationHandler::start(Variant::mapType &args) +bool DomainAnnotationHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); @@ -106,7 +109,8 @@ void DomainAnnotationHandler::end() { scope().pop(logger()); } /* DomainAttributesHandler */ -bool DomainAttributesHandler::start(Variant::mapType &args) +bool DomainAttributesHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { // Fetch the current typesystem and create the struct node Rooted parent = scope().selectOrThrow(); @@ -122,7 +126,8 @@ void DomainAttributesHandler::end() { scope().pop(logger()); } /* DomainFieldHandler */ -bool DomainFieldHandler::start(Variant::mapType &args) +bool DomainFieldHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { FieldDescriptor::FieldType type; if (args["isSubtree"].asBool()) { @@ -152,15 +157,16 @@ void DomainFieldHandler::end() { scope().pop(logger()); } /* DomainFieldRefHandler */ -bool DomainFieldRefHandler::start(Variant::mapType &args) +bool DomainFieldRefHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { Rooted parent = scope().selectOrThrow(); - const std::string &name = args["ref"].asString(); + const std::string &ref = args["ref"].asString(); auto loc = location(); - scope().resolveFieldDescriptor(name, parent, logger(), + scope().resolveFieldDescriptor(ref, parent, logger(), [loc](Handle field, Handle parent, Logger &logger) { if (field != nullptr) { @@ -182,7 +188,8 @@ void DomainFieldRefHandler::end() {} /* DomainPrimitiveHandler */ -bool DomainPrimitiveHandler::start(Variant::mapType &args) +bool DomainPrimitiveHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { Rooted parent = scope().selectOrThrow(); @@ -222,7 +229,8 @@ void DomainPrimitiveHandler::end() { scope().pop(logger()); } /* DomainChildHandler */ -bool DomainChildHandler::start(Variant::mapType &args) +bool DomainChildHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { Rooted field = scope().selectOrThrow(); @@ -240,7 +248,8 @@ bool DomainChildHandler::start(Variant::mapType &args) /* DomainParentHandler */ -bool DomainParentHandler::start(Variant::mapType &args) +bool DomainParentHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { Rooted strct = scope().selectOrThrow(); @@ -255,7 +264,8 @@ void DomainParentHandler::end() { scope().pop(logger()); } /* DomainParentFieldHandler */ -bool DomainParentFieldHandler::start(Variant::mapType &args) +bool DomainParentFieldHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { Rooted parentNameNode = scope().selectOrThrow(); FieldDescriptor::FieldType type; @@ -265,7 +275,7 @@ bool DomainParentFieldHandler::start(Variant::mapType &args) type = FieldDescriptor::FieldType::TREE; } - const std::string &name = args["name"].asString(); + const std::string &fieldName = args["name"].asString(); const bool optional = args["optional"].asBool(); Rooted strct = parentNameNode->getParent().cast(); @@ -274,12 +284,12 @@ bool DomainParentFieldHandler::start(Variant::mapType &args) // StructuredClass as child to it. scope().resolve( parentNameNode->getName(), strct, logger(), - [type, name, optional](Handle parent, Handle strct, - Logger &logger) { + [type, fieldName, optional](Handle parent, Handle strct, + Logger &logger) { if (parent != nullptr) { Rooted field = (parent.cast()->createFieldDescriptor( - logger, type, name, optional)).first; + logger, type, fieldName, optional)).first; field->addChild(strct.cast()); } }); @@ -288,32 +298,32 @@ bool DomainParentFieldHandler::start(Variant::mapType &args) /* DomainParentFieldRefHandler */ -bool DomainParentFieldRefHandler::start(Variant::mapType &args) +bool DomainParentFieldRefHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { Rooted parentNameNode = scope().selectOrThrow(); - const std::string &name = args["ref"].asString(); + const std::string &ref = args["ref"].asString(); Rooted strct = parentNameNode->getParent().cast(); auto loc = location(); // resolve the parent, get the referenced field and add the declared // StructuredClass as child to it. - scope().resolve( - parentNameNode->getName(), strct, logger(), - [name, loc](Handle parent, Handle strct, Logger &logger) { - if (parent != nullptr) { - Rooted field = - parent.cast()->getFieldDescriptor(name); - if (field == nullptr) { - logger.error( - std::string("Could not find referenced field ") + name, - loc); - return; - } - field->addChild(strct.cast()); - } - }); + scope().resolve(parentNameNode->getName(), strct, logger(), + [ref, loc](Handle parent, + Handle strct, Logger &logger) { + if (parent != nullptr) { + Rooted field = + parent.cast()->getFieldDescriptor(ref); + if (field == nullptr) { + logger.error( + std::string("Could not find referenced field ") + ref, loc); + return; + } + field->addChild(strct.cast()); + } + }); return true; } diff --git a/src/core/parser/stack/DomainHandler.hpp b/src/core/parser/stack/DomainHandler.hpp index 76172d6..4116919 100644 --- a/src/core/parser/stack/DomainHandler.hpp +++ b/src/core/parser/stack/DomainHandler.hpp @@ -46,7 +46,7 @@ class DomainHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -59,7 +59,7 @@ class DomainStructHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -72,7 +72,7 @@ class DomainAnnotationHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -85,7 +85,7 @@ class DomainAttributesHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -98,7 +98,7 @@ class DomainFieldHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -111,7 +111,7 @@ class DomainFieldRefHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -124,7 +124,7 @@ class DomainPrimitiveHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -137,7 +137,7 @@ class DomainChildHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; static Handler *create(const HandlerData &handlerData) { @@ -154,7 +154,7 @@ class DomainParentHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -167,7 +167,7 @@ class DomainParentFieldHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; static Handler *create(const HandlerData &handlerData) { @@ -179,7 +179,7 @@ class DomainParentFieldRefHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &name, Variant::mapType &args) override; static Handler *create(const HandlerData &handlerData) { diff --git a/src/core/parser/stack/Handler.cpp b/src/core/parser/stack/Handler.cpp index 734976a..f9cefc2 100644 --- a/src/core/parser/stack/Handler.cpp +++ b/src/core/parser/stack/Handler.cpp @@ -32,13 +32,8 @@ namespace parser_stack { /* Class HandlerData */ HandlerData::HandlerData(ParserContext &ctx, HandlerCallbacks &callbacks, - const std::string &name, const State &state, - const SourceLocation &location) - : ctx(ctx), - callbacks(callbacks), - name(name), - state(state), - location(location) + const State &state, const SourceLocation &location) + : ctx(ctx), callbacks(callbacks), state(state), location(location) { } @@ -67,22 +62,14 @@ Logger &Handler::logger() const SourceLocation &Handler::location() const { return handlerData.location; } -const std::string &Handler::name() const { return handlerData.name; } - -Variant Handler::readData() -{ - return handlerData.callbacks.readData(); -} +Variant Handler::readData() { return handlerData.callbacks.readData(); } void Handler::pushTokens(const std::vector &tokens) { handlerData.callbacks.pushTokens(tokens); } -void Handler::popTokens() -{ - handlerData.callbacks.popTokens(); -} +void Handler::popTokens() { handlerData.callbacks.popTokens(); } TokenId Handler::registerToken(const std::string &token) { @@ -94,8 +81,6 @@ void Handler::unregisterToken(TokenId id) handlerData.callbacks.unregisterToken(id); } -const std::string &Handler::getName() const { return name(); } - const State &Handler::getState() const { return handlerData.state; } void Handler::setLogger(Logger &logger) { internalLogger = &logger; } @@ -106,42 +91,51 @@ const SourceLocation &Handler::getLocation() const { return location(); } /* Class EmptyHandler */ -bool EmptyHandler::start(Variant::mapType &args) +bool EmptyHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { - // Just accept anything + // Well, we'll support any command we get, don't we? return true; } -void EmptyHandler::end() +bool EmptyHandler::startAnnotation(const std::string &name, + Variant::mapType &args, + Handler::AnnotationType annotationType) { - // Do nothing if a command ends + // Do not support annotations. Annotations are too complicated for poor + // EmptyHandler. + return false; } -bool EmptyHandler::fieldStart(bool &isDefaultField, size_t fieldIndex) +bool EmptyHandler::startToken(const Token &token, Handle node) { - // Accept any field - return true; + // EmptyHandler does not support tokens. + return false; } -void EmptyHandler::fieldEnd() +Handler::EndTokenResult EmptyHandler::endToken(const Token &token, + Handle node) { - // Do not handle fields + // There are no tokens to end here. + return EndTokenResult::ENDED_NONE; } -bool EmptyHandler::annotationStart(const Variant &className, - Variant::mapType &args) +void EmptyHandler::end() { - // Accept any data - return true; + // Do nothing if a command ends } -bool EmptyHandler::annotationEnd(const Variant &className, - const Variant &elementName) +bool EmptyHandler::fieldStart(bool &isDefaultField, size_t fieldIndex) { - // Accept any annotation + // Accept any field return true; } +void EmptyHandler::fieldEnd() +{ + // Do not handle field ends +} + bool EmptyHandler::data() { // Support any data @@ -155,12 +149,31 @@ Handler *EmptyHandler::create(const HandlerData &handlerData) /* Class StaticHandler */ -bool StaticHandler::start(Variant::mapType &args) +bool StaticHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { // Do nothing in the default implementation, accept anything return true; } +bool StaticHandler::startAnnotation(const std::string &name, + Variant::mapType &args, + Handler::AnnotationType annotationType) +{ + return false; +} + +bool StaticHandler::startToken(const Token &token, Handle node) +{ + return false; +} + +Handler::EndTokenResult StaticHandler::endToken(const Token &token, + Handle node) +{ + return EndTokenResult::ENDED_NONE; +} + void StaticHandler::end() { // Do nothing here @@ -182,20 +195,6 @@ void StaticHandler::fieldEnd() // Do nothing here } -bool StaticHandler::annotationStart(const Variant &className, - 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() { logger().error("Did not expect any data here", readData()); @@ -210,7 +209,8 @@ StaticFieldHandler::StaticFieldHandler(const HandlerData &handlerData, { } -bool StaticFieldHandler::start(Variant::mapType &args) +bool StaticFieldHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { if (!argName.empty()) { auto it = args.find(argName); diff --git a/src/core/parser/stack/Handler.hpp b/src/core/parser/stack/Handler.hpp index 19c3d65..f0968e7 100644 --- a/src/core/parser/stack/Handler.hpp +++ b/src/core/parser/stack/Handler.hpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace ousia { @@ -60,11 +61,6 @@ public: */ HandlerCallbacks &callbacks; - /** - * Contains the name of the command that is being handled. - */ - std::string name; - /** * Contains the current state of the state machine. */ @@ -81,13 +77,11 @@ public: * @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, HandlerCallbacks &callbacks, - const std::string &name, const State &state, - const SourceLocation &location); + const State &state, const SourceLocation &location); }; /** @@ -154,13 +148,6 @@ protected: */ const SourceLocation &location() const; - /** - * Returns the command name for which the handler was created. - * - * @return a const reference at the command name. - */ - const std::string &name() const; - /** * Calls the corresponding function in the HandlerCallbacks instance. This * method registers the given tokens as tokens that are generally available, @@ -231,19 +218,23 @@ protected: */ // void popWhitespaceMode(); - public: /** - * Virtual destructor. + * Enum representing the type of the annotation a Handle instance handles. + * It may either handle the start of an annotation or the end of an + * annotation. */ - virtual ~Handler(); + enum class AnnotationType { START, END }; /** - * Returns the command name for which the handler was created. - * - * @return a const reference at the command name. + * Enum type representing the possible outcomes of the endToken() method. */ - const std::string &getName() const; + enum class EndTokenResult { ENDED_THIS, ENDED_HIDDEN, ENDED_NONE }; + + /** + * Virtual destructor. + */ + virtual ~Handler(); /** * Reference at the State descriptor for which this Handler was created. @@ -274,14 +265,63 @@ public: const SourceLocation &getLocation() const; /** - * Called when the command that was specified in the constructor is - * instanciated. + * Called whenever the handler should handle the start of a command. This + * method (or any other of the "start" methods) is called exactly once, + * after the constructor. * + * @param name is the name of the command that is started here. * @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. + * @return true if the handler was successful in starting an element with + * the given name represents, false otherwise. */ - virtual bool start(Variant::mapType &args) = 0; + virtual bool startCommand(const std::string &commandName, + Variant::mapType &args) = 0; + + /** + * Called whenever the handler should handle the start of an annotation. + * This method (or any other of the "start" methods) is called exactly once, + * after the constructor. This method is only called if the + * "supportsAnnotations" flag of the State instance referencing this Handler + * is set to true. + * + * @param name is the name of the annotation that is started here. + * @param args is a map from strings to variants (argument name and value). + * @param type specifies whether this handler should handle the start of an + * annotation or the end of an annotation. + */ + virtual bool startAnnotation(const std::string &name, + Variant::mapType &args, + AnnotationType annotationType) = 0; + + /** + * Called whenever the handler should handle the start of a token. This + * method (or any other of the "start" methods) is called exactly once, + * after the constructor. This method is only called if the "supportsTokens" + * flag of the State instance referencing this Handler is set to true. + * + * @param token is the Token for which the handler should be started. + * @param node is the node for which this token was registered. + */ + virtual bool startToken(const Token &token, Handle node) = 0; + + /** + * Called whenever a token is marked as "end" token and this handler happens + * to be the currently active handler. This operation may have three + * outcomes: + *
    + *
  1. The token marks the end of the complete handler and the calling + * code should call the "end" method.
  2. + *
  3. The token marks the end of some element that is unknown the calling + * code. So the operation itself was a success, but the calling code + * should not call the "end" method. + *
  4. The token did not anything in this context. Basically this shuold + * never happen, but who knows.
  5. + *
+ * + * @param id is the Token for which the handler should be started. + * @param node is the node for which this token was registered. + */ + virtual EndTokenResult endToken(const Token &token, Handle node) = 0; /** * Called before the command for which this handler is defined ends (is @@ -310,35 +350,6 @@ public: */ 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, - 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 @@ -369,14 +380,15 @@ protected: using Handler::Handler; public: - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; + bool startAnnotation(const std::string &name, Variant::mapType &args, + AnnotationType annotationType) override; + bool startToken(const Token &token, Handle node) override; + EndTokenResult endToken(const Token &token, Handle node) override; void end() override; bool fieldStart(bool &isDefault, size_t fieldIdx) override; void fieldEnd() override; - bool annotationStart(const Variant &className, - Variant::mapType &args) override; - bool annotationEnd(const Variant &className, - const Variant &elementName) override; bool data() override; /** @@ -395,14 +407,15 @@ protected: using Handler::Handler; public: - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; + bool startAnnotation(const std::string &name, Variant::mapType &args, + AnnotationType annotationType) override; + bool startToken(const Token &token, Handle node) override; + EndTokenResult endToken(const Token &token, Handle node) override; void end() override; bool fieldStart(bool &isDefault, size_t fieldIdx) override; void fieldEnd() override; - bool annotationStart(const Variant &className, - Variant::mapType &args) override; - bool annotationEnd(const Variant &className, - const Variant &elementName) override; bool data() override; }; @@ -453,9 +466,10 @@ protected: virtual void doHandle(const Variant &fieldData, Variant::mapType &args) = 0; public: - bool start(Variant::mapType &args) override; - void end() override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; bool data() override; + void end() override; }; } } diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index a556999..3545c37 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -32,7 +32,7 @@ namespace ousia { namespace parser_stack { - +namespace { /* Class HandlerInfo */ /** @@ -41,6 +41,11 @@ namespace parser_stack { */ class HandlerInfo { public: + /** + * Name of the command or the token sequence. + */ + std::string name; + /** * Pointer pointing at the actual handler instance. */ @@ -96,6 +101,7 @@ public: * Default constructor of the HandlerInfo class. */ HandlerInfo(); + /** * Constructor of the HandlerInfo class, allows to set all flags manually. */ @@ -182,6 +188,7 @@ void HandlerInfo::fieldEnd() * Stub instance of HandlerInfo containing no handler information. */ static HandlerInfo EmptyHandlerInfo{true, true, true, true, false, true}; +} /* Helper functions */ @@ -387,7 +394,7 @@ StackImpl::~StackImpl() !info.inImplicitDefaultField) { logger().error( std::string("Reached end of stream, but command \"") + - info.handler->getName() + + info.name + "\" has not ended yet. Command was started here:", info.handler->getLocation()); } @@ -421,8 +428,8 @@ void StackImpl::deduceState() HandlerConstructor ctor = state.elementHandler ? state.elementHandler : EmptyHandler::create; - std::shared_ptr handler = std::shared_ptr{ - ctor({ctx, *this, "", state, SourceLocation{}})}; + std::shared_ptr handler = + std::shared_ptr{ctor({ctx, *this, state, SourceLocation{}})}; stack.emplace_back(handler); // Set the correct flags for this implicit handler @@ -450,7 +457,7 @@ const State &StackImpl::currentState() const std::string StackImpl::currentCommandName() const { - return stack.empty() ? std::string{} : stack.back().handler->getName(); + return stack.empty() ? std::string{} : stack.back().name; } const State *StackImpl::findTargetState(const std::string &name) @@ -608,8 +615,8 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, HandlerConstructor ctor = targetState->elementHandler ? targetState->elementHandler : EmptyHandler::create; - std::shared_ptr handler{ctor( - {ctx, *this, name.asString(), *targetState, name.getLocation()})}; + std::shared_ptr handler{ + ctor({ctx, *this, *targetState, name.getLocation()})}; stack.emplace_back(handler); // Fetch the HandlerInfo for the parent element and the current element @@ -631,7 +638,8 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, handler->setLogger(loggerFork); try { - info.valid = handler->start(canonicalArgs); + info.valid = + handler->startCommand(name.asString(), canonicalArgs); } catch (LoggableException ex) { loggerFork.log(ex); diff --git a/src/core/parser/stack/TypesystemHandler.cpp b/src/core/parser/stack/TypesystemHandler.cpp index de8ee49..110c56f 100644 --- a/src/core/parser/stack/TypesystemHandler.cpp +++ b/src/core/parser/stack/TypesystemHandler.cpp @@ -32,7 +32,8 @@ namespace parser_stack { /* TypesystemHandler */ -bool TypesystemHandler::start(Variant::mapType &args) +bool TypesystemHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { // Create the typesystem instance Rooted typesystem = @@ -63,7 +64,8 @@ void TypesystemHandler::end() { scope().pop(logger()); } /* TypesystemEnumHandler */ -bool TypesystemEnumHandler::start(Variant::mapType &args) +bool TypesystemEnumHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); @@ -91,17 +93,18 @@ void TypesystemEnumEntryHandler::doHandle(const Variant &fieldData, /* TypesystemStructHandler */ -bool TypesystemStructHandler::start(Variant::mapType &args) +bool TypesystemStructHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); // Fetch the arguments used for creating this type - const std::string &name = args["name"].asString(); + const std::string &structNmae = args["name"].asString(); const std::string &parent = args["parent"].asString(); // Fetch the current typesystem and create the struct node Rooted typesystem = scope().selectOrThrow(); - Rooted structType = typesystem->createStructType(name); + Rooted structType = typesystem->createStructType(structNmae); structType->setLocation(location()); // Try to resolve the parent type and set it as parent structure @@ -124,18 +127,19 @@ void TypesystemStructHandler::end() { scope().pop(logger()); } /* TypesystemStructFieldHandler */ -bool TypesystemStructFieldHandler::start(Variant::mapType &args) +bool TypesystemStructFieldHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { // Read the argument values - const std::string &name = args["name"].asString(); + const std::string &fieldName = args["name"].asString(); const std::string &type = args["type"].asString(); const Variant &defaultValue = args["default"]; const bool optional = !(defaultValue.isObject() && defaultValue.asObject() == nullptr); Rooted structType = scope().selectOrThrow(); - Rooted attribute = - structType->createAttribute(name, defaultValue, optional, logger()); + Rooted attribute = structType->createAttribute( + fieldName, defaultValue, optional, logger()); attribute->setLocation(location()); // Try to resolve the type and default value @@ -163,17 +167,18 @@ bool TypesystemStructFieldHandler::start(Variant::mapType &args) /* TypesystemConstantHandler */ -bool TypesystemConstantHandler::start(Variant::mapType &args) +bool TypesystemConstantHandler::startCommand(const std::string &commandName, + Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); // Read the argument values - const std::string &name = args["name"].asString(); + const std::string &constantName = args["name"].asString(); const std::string &type = args["type"].asString(); const Variant &value = args["value"]; Rooted typesystem = scope().selectOrThrow(); - Rooted constant = typesystem->createConstant(name, value); + Rooted constant = typesystem->createConstant(constantName, value); constant->setLocation(location()); // Try to resolve the type diff --git a/src/core/parser/stack/TypesystemHandler.hpp b/src/core/parser/stack/TypesystemHandler.hpp index 85494f1..75cba01 100644 --- a/src/core/parser/stack/TypesystemHandler.hpp +++ b/src/core/parser/stack/TypesystemHandler.hpp @@ -43,7 +43,8 @@ class TypesystemHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; void end() override; /** @@ -67,7 +68,8 @@ class TypesystemEnumHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; void end() override; /** @@ -114,7 +116,8 @@ class TypesystemStructHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; void end() override; /** @@ -139,7 +142,8 @@ class TypesystemStructFieldHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; /** * Creates a new instance of the TypesystemStructFieldHandler. @@ -162,7 +166,8 @@ class TypesystemConstantHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool start(Variant::mapType &args) override; + bool startCommand(const std::string &commandName, + Variant::mapType &args) override; /** * Creates a new instance of the TypesystemConstantHandler. -- cgit v1.2.3 From e0b9f6ef6692ee8c37386c23f721dc6a57f69ae6 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 18:10:28 +0100 Subject: Storing type and name in the HandlerData once again, using a Token --- src/core/parser/stack/Callbacks.hpp | 3 +- src/core/parser/stack/DocumentHandler.cpp | 61 +++++-------------- src/core/parser/stack/DocumentHandler.hpp | 59 +++---------------- src/core/parser/stack/DomainHandler.cpp | 55 +++++++---------- src/core/parser/stack/DomainHandler.hpp | 22 +++---- src/core/parser/stack/Handler.cpp | 44 +++++++------- src/core/parser/stack/Stack.cpp | 91 +++++++++++++++++------------ src/core/parser/stack/TokenStack.hpp | 4 +- src/core/parser/stack/TypesystemHandler.cpp | 15 ++--- src/core/parser/stack/TypesystemHandler.hpp | 15 ++--- 10 files changed, 148 insertions(+), 221 deletions(-) (limited to 'src/core/parser/stack') diff --git a/src/core/parser/stack/Callbacks.hpp b/src/core/parser/stack/Callbacks.hpp index 092664a..dfe41fc 100644 --- a/src/core/parser/stack/Callbacks.hpp +++ b/src/core/parser/stack/Callbacks.hpp @@ -87,7 +87,8 @@ public: * @param tokens is a list of TokenSyntaxDescriptor instances that should be * stored on the stack. */ - void pushTokens(const std::vector &tokens); + virtual void pushTokens(const std::vector &tokens) = 0; + /** * Removes the previously pushed list of tokens from the stack. */ diff --git a/src/core/parser/stack/DocumentHandler.cpp b/src/core/parser/stack/DocumentHandler.cpp index de6e367..e931d8d 100644 --- a/src/core/parser/stack/DocumentHandler.cpp +++ b/src/core/parser/stack/DocumentHandler.cpp @@ -37,8 +37,7 @@ namespace parser_stack { /* DocumentHandler */ -bool DocumentHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DocumentHandler::startCommand(Variant::mapType &args) { Rooted document = context().getProject()->createDocument(args["name"].asString()); @@ -54,24 +53,10 @@ void DocumentHandler::end() { scope().pop(logger()); } /* DocumentChildHandler */ DocumentChildHandler::DocumentChildHandler(const HandlerData &handlerData) - : Handler(handlerData), mode(Mode::STRUCT) + : Handler(handlerData), isExplicitField(false) { } -void DocumentChildHandler::setMode(Mode mode, const std::string &name) -{ - this->mode = mode; - this->name = name; - this->token = Token(); -} - -void DocumentChildHandler::setMode(Mode mode, const Token &token) -{ - this->mode = mode; - this->name = token.content; - this->token = token; -} - void DocumentChildHandler::preamble(Rooted &parentNode, size_t &fieldIdx, DocumentEntity *&parent) { @@ -142,12 +127,8 @@ void DocumentChildHandler::createPath(const size_t &firstFieldIdx, scope().setFlag(ParserFlag::POST_EXPLICIT_FIELDS, false); } -bool DocumentChildHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DocumentChildHandler::startCommand(Variant::mapType &args) { - // Set the internal mode to STRUCT and copy the name - setMode(Mode::STRUCT, name); - // Extract the special "name" attribute from the input arguments. // The remaining attributes will be forwarded to the newly constructed // element. @@ -176,11 +157,11 @@ bool DocumentChildHandler::startCommand(const std::string &commandName, return false; } Rooted strct = scope().resolve( - Utils::split(name, ':'), logger()); + Utils::split(name(), ':'), logger()); if (strct == nullptr) { // if we could not resolve the name, throw an exception. throw LoggableException( - std::string("\"") + name + "\" could not be resolved.", + std::string("\"") + name() + "\" could not be resolved.", location()); } entity = parentNode.cast()->createRootStructuredEntity( @@ -200,7 +181,7 @@ bool DocumentChildHandler::startCommand(const std::string &commandName, */ { ssize_t newFieldIdx = - parent->getDescriptor()->getFieldDescriptorIndex(name); + parent->getDescriptor()->getFieldDescriptorIndex(name()); if (newFieldIdx != -1) { // Check whether explicit fields are allowed here, if not if (scope().getFlag(ParserFlag::POST_EXPLICIT_FIELDS)) { @@ -208,7 +189,7 @@ bool DocumentChildHandler::startCommand(const std::string &commandName, std::string( "Data or structure commands have already been " "given, command \"") + - name + std::string( + name() + std::string( "\" is not interpreted explicit " "field. Move explicit field " "references to the beginning."), @@ -218,7 +199,7 @@ bool DocumentChildHandler::startCommand(const std::string &commandName, manager(), parentNode, newFieldIdx, false)}; field->setLocation(location()); scope().push(field); - setMode(Mode::EXPLICIT_FIELD, name); + isExplicitField = true; return true; } } @@ -227,11 +208,11 @@ bool DocumentChildHandler::startCommand(const std::string &commandName, // Otherwise create a new StructuredEntity // TODO: Consider Anchors and AnnotationEntities Rooted strct = scope().resolve( - Utils::split(name, ':'), logger()); + Utils::split(name(), ':'), logger()); if (strct == nullptr) { // if we could not resolve the name, throw an exception. throw LoggableException( - std::string("\"") + name + "\" could not be resolved.", + std::string("\"") + name() + "\" could not be resolved.", location()); } @@ -278,26 +259,15 @@ bool DocumentChildHandler::startCommand(const std::string &commandName, } } -bool DocumentChildHandler::startAnnotation(const std::string &name, - Variant::mapType &args, +bool DocumentChildHandler::startAnnotation(Variant::mapType &args, AnnotationType annotationType) { - // Set the internal mode and name correctly - if (annotationType == AnnotationType::START) { - setMode(Mode::ANNOTATION_START, name); - } else { - setMode(Mode::ANNOTATION_END, name); - } - // TODO: Handle annotation return false; } -bool DocumentChildHandler::startToken(const Token &token, Handle node) +bool DocumentChildHandler::startToken(Handle node) { - // Set the internal mode correctly - setMode(Mode::TOKEN, token); - // TODO: Handle token start return false; } @@ -313,7 +283,7 @@ void DocumentChildHandler::end() { // In case of explicit fields we do not want to pop something from the // stack. - if (mode == Mode::STRUCT) { + if (!isExplicitField) { // pop the "main" element. scope().pop(logger()); } @@ -321,8 +291,7 @@ void DocumentChildHandler::end() bool DocumentChildHandler::fieldStart(bool &isDefault, size_t fieldIdx) { - // TODO: Handle other cases - if (mode == Mode::EXPLICIT_FIELD) { + if (isExplicitField) { // In case of explicit fields we do not want to create another field. isDefault = true; return fieldIdx == 0; @@ -471,7 +440,7 @@ bool DocumentChildHandler::data() // this fact Variant text = readData(); if (defaultFields.empty()) { - logger().error("Got data, but structure \"" + name + + logger().error("Got data, but structure \"" + name() + "\" does not have any primitive field", text); } else { diff --git a/src/core/parser/stack/DocumentHandler.hpp b/src/core/parser/stack/DocumentHandler.hpp index 9a41508..d34c020 100644 --- a/src/core/parser/stack/DocumentHandler.hpp +++ b/src/core/parser/stack/DocumentHandler.hpp @@ -53,8 +53,7 @@ class DocumentHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &commandName, - Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; /** @@ -92,55 +91,11 @@ public: * defined elements in an Ousía document. */ class DocumentChildHandler : public Handler { -public: - /** - * Enum type used to represent the mode of the DocumentChildHandler. - * TODO: Having to have such a type is actually quite stupid, it would be - * nicer to have separate handler classes for each of these cases. But this - * is a story for a different day. - */ - enum class Mode { - STRUCT, - EXPLICIT_FIELD, - ANNOTATION_START, - ANNOTATION_END, - TOKEN - }; - private: /** - * Internal Mode of the DocumentChildHandler. - */ - Mode mode; - - /** - * Contains the name of the command or the annotation that is represented - * by this DocumentChildHandler. - */ - std::string name; - - /** - * Token represented by the document child handler. + * If set to true, this handler represents an explicit field. */ - Token token; - - /** - * Switches the mode to the given mode and copies the given name. Resets the - * token. - * - * @param mode is the new mode. - * @param name is the new name. - */ - void setMode(Mode mode, const std::string &name); - - /** - * Switches the mode to the given mode and copies the given token, sets the - * name to the content of the token. - * - * @param mode is the new mode. - * @param token is the new token. - */ - void setMode(Mode mode, const Token &token); + bool isExplicitField; /** * Code shared by both the start(), fieldStart() and the data() method. @@ -211,11 +166,10 @@ private: public: DocumentChildHandler(const HandlerData &handlerData); - bool startCommand(const std::string &commandName, - Variant::mapType &args) override; - bool startAnnotation(const std::string &name, Variant::mapType &args, + bool startCommand(Variant::mapType &args) override; + bool startAnnotation(Variant::mapType &args, AnnotationType annotationType) override; - bool startToken(const Token &token, Handle node) override; + bool startToken(Handle node) override; EndTokenResult endToken(const Token &token, Handle node) override; void end() override; bool data() override; @@ -257,3 +211,4 @@ extern const Rtti DocumentField; } #endif /* _OUSIA_PARSER_STACK_DOCUMENT_HANDLER_HPP_ */ + diff --git a/src/core/parser/stack/DomainHandler.cpp b/src/core/parser/stack/DomainHandler.cpp index 5ca4f5b..aef5b47 100644 --- a/src/core/parser/stack/DomainHandler.cpp +++ b/src/core/parser/stack/DomainHandler.cpp @@ -33,8 +33,7 @@ namespace parser_stack { /* DomainHandler */ -bool DomainHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainHandler::startCommand(Variant::mapType &args) { // Create the Domain node Rooted domain = @@ -58,8 +57,7 @@ void DomainHandler::end() { scope().pop(logger()); } /* DomainStructHandler */ -bool DomainStructHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainStructHandler::startCommand(Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); @@ -90,8 +88,7 @@ bool DomainStructHandler::startCommand(const std::string &commandName, void DomainStructHandler::end() { scope().pop(logger()); } /* DomainAnnotationHandler */ -bool DomainAnnotationHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainAnnotationHandler::startCommand(Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); @@ -109,8 +106,7 @@ void DomainAnnotationHandler::end() { scope().pop(logger()); } /* DomainAttributesHandler */ -bool DomainAttributesHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainAttributesHandler::startCommand(Variant::mapType &args) { // Fetch the current typesystem and create the struct node Rooted parent = scope().selectOrThrow(); @@ -126,8 +122,7 @@ void DomainAttributesHandler::end() { scope().pop(logger()); } /* DomainFieldHandler */ -bool DomainFieldHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainFieldHandler::startCommand(Variant::mapType &args) { FieldDescriptor::FieldType type; if (args["isSubtree"].asBool()) { @@ -157,16 +152,15 @@ void DomainFieldHandler::end() { scope().pop(logger()); } /* DomainFieldRefHandler */ -bool DomainFieldRefHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainFieldRefHandler::startCommand(Variant::mapType &args) { Rooted parent = scope().selectOrThrow(); - const std::string &ref = args["ref"].asString(); + const std::string &name = args["ref"].asString(); auto loc = location(); - scope().resolveFieldDescriptor(ref, parent, logger(), + scope().resolveFieldDescriptor(name, parent, logger(), [loc](Handle field, Handle parent, Logger &logger) { if (field != nullptr) { @@ -188,8 +182,7 @@ void DomainFieldRefHandler::end() {} /* DomainPrimitiveHandler */ -bool DomainPrimitiveHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainPrimitiveHandler::startCommand(Variant::mapType &args) { Rooted parent = scope().selectOrThrow(); @@ -229,14 +222,13 @@ void DomainPrimitiveHandler::end() { scope().pop(logger()); } /* DomainChildHandler */ -bool DomainChildHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainChildHandler::startCommand(Variant::mapType &args) { Rooted field = scope().selectOrThrow(); - const std::string &ref = args["ref"].asString(); + const std::string &name = args["ref"].asString(); scope().resolve( - ref, field, logger(), + name, field, logger(), [](Handle child, Handle field, Logger &logger) { if (child != nullptr) { field.cast()->addChild( @@ -248,8 +240,7 @@ bool DomainChildHandler::startCommand(const std::string &commandName, /* DomainParentHandler */ -bool DomainParentHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainParentHandler::startCommand(Variant::mapType &args) { Rooted strct = scope().selectOrThrow(); @@ -264,8 +255,7 @@ void DomainParentHandler::end() { scope().pop(logger()); } /* DomainParentFieldHandler */ -bool DomainParentFieldHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainParentFieldHandler::startCommand(Variant::mapType &args) { Rooted parentNameNode = scope().selectOrThrow(); FieldDescriptor::FieldType type; @@ -275,7 +265,7 @@ bool DomainParentFieldHandler::startCommand(const std::string &commandName, type = FieldDescriptor::FieldType::TREE; } - const std::string &fieldName = args["name"].asString(); + const std::string &name = args["name"].asString(); const bool optional = args["optional"].asBool(); Rooted strct = parentNameNode->getParent().cast(); @@ -284,12 +274,12 @@ bool DomainParentFieldHandler::startCommand(const std::string &commandName, // StructuredClass as child to it. scope().resolve( parentNameNode->getName(), strct, logger(), - [type, fieldName, optional](Handle parent, Handle strct, + [type, name, optional](Handle parent, Handle strct, Logger &logger) { if (parent != nullptr) { Rooted field = (parent.cast()->createFieldDescriptor( - logger, type, fieldName, optional)).first; + logger, type, name, optional)).first; field->addChild(strct.cast()); } }); @@ -298,12 +288,11 @@ bool DomainParentFieldHandler::startCommand(const std::string &commandName, /* DomainParentFieldRefHandler */ -bool DomainParentFieldRefHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool DomainParentFieldRefHandler::startCommand(Variant::mapType &args) { Rooted parentNameNode = scope().selectOrThrow(); - const std::string &ref = args["ref"].asString(); + const std::string &name = args["ref"].asString(); Rooted strct = parentNameNode->getParent().cast(); auto loc = location(); @@ -311,14 +300,14 @@ bool DomainParentFieldRefHandler::startCommand(const std::string &commandName, // resolve the parent, get the referenced field and add the declared // StructuredClass as child to it. scope().resolve(parentNameNode->getName(), strct, logger(), - [ref, loc](Handle parent, + [name, loc](Handle parent, Handle strct, Logger &logger) { if (parent != nullptr) { Rooted field = - parent.cast()->getFieldDescriptor(ref); + parent.cast()->getFieldDescriptor(name); if (field == nullptr) { logger.error( - std::string("Could not find referenced field ") + ref, loc); + std::string("Could not find referenced field ") + name, loc); return; } field->addChild(strct.cast()); diff --git a/src/core/parser/stack/DomainHandler.hpp b/src/core/parser/stack/DomainHandler.hpp index 4116919..f12d863 100644 --- a/src/core/parser/stack/DomainHandler.hpp +++ b/src/core/parser/stack/DomainHandler.hpp @@ -46,7 +46,7 @@ class DomainHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -59,7 +59,7 @@ class DomainStructHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -72,7 +72,7 @@ class DomainAnnotationHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -85,7 +85,7 @@ class DomainAttributesHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -98,7 +98,7 @@ class DomainFieldHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -111,7 +111,7 @@ class DomainFieldRefHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -124,7 +124,7 @@ class DomainPrimitiveHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -137,7 +137,7 @@ class DomainChildHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; static Handler *create(const HandlerData &handlerData) { @@ -154,7 +154,7 @@ class DomainParentHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; static Handler *create(const HandlerData &handlerData) @@ -167,7 +167,7 @@ class DomainParentFieldHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; static Handler *create(const HandlerData &handlerData) { @@ -179,7 +179,7 @@ class DomainParentFieldRefHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &name, Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; static Handler *create(const HandlerData &handlerData) { diff --git a/src/core/parser/stack/Handler.cpp b/src/core/parser/stack/Handler.cpp index 006e521..c01e74c 100644 --- a/src/core/parser/stack/Handler.cpp +++ b/src/core/parser/stack/Handler.cpp @@ -32,8 +32,9 @@ namespace parser_stack { /* Class HandlerData */ HandlerData::HandlerData(ParserContext &ctx, HandlerCallbacks &callbacks, - const State &state, const SourceLocation &location) - : ctx(ctx), callbacks(callbacks), state(state), location(location) + const State &state, const Token &token, + HandlerType type) + : ctx(ctx), callbacks(callbacks), state(state), token(token), type(type) { } @@ -60,7 +61,20 @@ Logger &Handler::logger() return handlerData.ctx.getLogger(); } -const SourceLocation &Handler::location() const { return handlerData.location; } +const std::string &Handler::name() const { return handlerData.token.content; } + +TokenId Handler::tokenId() const { return handlerData.token.id; } + +const Token &Handler::token() const { return handlerData.token; } + +const SourceLocation &Handler::location() const +{ + return handlerData.token.location; +} + +HandlerType Handler::type() const { return handlerData.type; } + +const State &Handler::state() const { return handlerData.state; } Variant Handler::readData() { return handlerData.callbacks.readData(); } @@ -81,8 +95,6 @@ void Handler::unregisterToken(TokenId id) handlerData.callbacks.unregisterToken(id); } -const State &Handler::getState() const { return handlerData.state; } - void Handler::setLogger(Logger &logger) { internalLogger = &logger; } void Handler::resetLogger() { internalLogger = nullptr; } @@ -91,15 +103,13 @@ const SourceLocation &Handler::getLocation() const { return location(); } /* Class EmptyHandler */ -bool EmptyHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool EmptyHandler::startCommand(Variant::mapType &args) { // Well, we'll support any command we get, don't we? return true; } -bool EmptyHandler::startAnnotation(const std::string &name, - Variant::mapType &args, +bool EmptyHandler::startAnnotation(Variant::mapType &args, Handler::AnnotationType annotationType) { // Do not support annotations. Annotations are too complicated for poor @@ -107,7 +117,7 @@ bool EmptyHandler::startAnnotation(const std::string &name, return false; } -bool EmptyHandler::startToken(const Token &token, Handle node) +bool EmptyHandler::startToken(Handle node) { // EmptyHandler does not support tokens. return false; @@ -149,24 +159,19 @@ Handler *EmptyHandler::create(const HandlerData &handlerData) /* Class StaticHandler */ -bool StaticHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool StaticHandler::startCommand(Variant::mapType &args) { // Do nothing in the default implementation, accept anything return true; } -bool StaticHandler::startAnnotation(const std::string &name, - Variant::mapType &args, +bool StaticHandler::startAnnotation(Variant::mapType &args, Handler::AnnotationType annotationType) { return false; } -bool StaticHandler::startToken(const Token &token, Handle node) -{ - return false; -} +bool StaticHandler::startToken(Handle node) { return false; } Handler::EndTokenResult StaticHandler::endToken(const Token &token, Handle node) @@ -209,8 +214,7 @@ StaticFieldHandler::StaticFieldHandler(const HandlerData &handlerData, { } -bool StaticFieldHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool StaticFieldHandler::startCommand(Variant::mapType &args) { if (!argName.empty()) { auto it = args.find(argName); diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index 3545c37..cad4078 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -41,11 +41,6 @@ namespace { */ class HandlerInfo { public: - /** - * Name of the command or the token sequence. - */ - std::string name; - /** * Pointer pointing at the actual handler instance. */ @@ -362,7 +357,7 @@ public: void unregisterToken(TokenId id) override; Variant readData() override; bool hasData(); - void pushTokens(const std::vector &tokens) override; + void pushTokens(const std::vector &tokens) override; void popTokens() override; }; @@ -394,7 +389,7 @@ StackImpl::~StackImpl() !info.inImplicitDefaultField) { logger().error( std::string("Reached end of stream, but command \"") + - info.name + + currentCommandName() + "\" has not ended yet. Command was started here:", info.handler->getLocation()); } @@ -428,8 +423,8 @@ void StackImpl::deduceState() HandlerConstructor ctor = state.elementHandler ? state.elementHandler : EmptyHandler::create; - std::shared_ptr handler = - std::shared_ptr{ctor({ctx, *this, state, SourceLocation{}})}; + std::shared_ptr handler = std::shared_ptr{ + ctor({ctx, *this, state, SourceLocation{}, HandlerType::COMMAND})}; stack.emplace_back(handler); // Set the correct flags for this implicit handler @@ -452,12 +447,12 @@ std::set StackImpl::expectedCommands() const State &StackImpl::currentState() const { - return stack.empty() ? States::None : stack.back().handler->getState(); + return stack.empty() ? States::None : stack.back().handler->state(); } std::string StackImpl::currentCommandName() const { - return stack.empty() ? std::string{} : stack.back().name; + return stack.empty() ? std::string{} : stack.back().handler->name(); } const State *StackImpl::findTargetState(const std::string &name) @@ -616,21 +611,29 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, ? targetState->elementHandler : EmptyHandler::create; std::shared_ptr handler{ - ctor({ctx, *this, *targetState, name.getLocation()})}; + ctor({ctx, + *this, + *targetState, + {name.asString(), name.getLocation()}, + HandlerType::COMMAND})}; stack.emplace_back(handler); - // Fetch the HandlerInfo for the parent element and the current element + // 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 + // 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) { - // Canonicalize the arguments (if this has not already been done), + // Canonicalize the arguments (if this has not already been + // done), // allow additional arguments and numeric indices Variant::mapType canonicalArgs = args; targetState->arguments.validateMap(canonicalArgs, loggerFork, true, @@ -638,8 +641,7 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, handler->setLogger(loggerFork); try { - info.valid = - handler->startCommand(name.asString(), canonicalArgs); + info.valid = handler->startCommand(canonicalArgs); } catch (LoggableException ex) { loggerFork.log(ex); @@ -647,8 +649,10 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, 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 + // 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(); @@ -656,7 +660,8 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, continue; } - // If we ended up here, starting the command may or may not have worked, + // 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; @@ -687,13 +692,15 @@ void StackImpl::data(const TokenizedData &data) // TODO: Rewrite this function for token handling // TODO: This loop needs to be refactored out /*while (!data.atEnd()) { - // End handlers that already had a default field and are currently not + // End handlers that already had a default field and are currently + not // active. endOverdueHandlers(); const bool hasNonWhitespaceText = data.hasNonWhitespaceText(); - // Check whether there is any command the data can be sent to -- if not, + // Check whether there is any command the data can be sent to -- if + not, // make sure the data actually is data if (stack.empty()) { if (hasNonWhitespaceText) { @@ -712,10 +719,12 @@ void StackImpl::data(const TokenizedData &data) continue; } - // If this field should not get any data, log an error and do not call + // If this field should not get any data, log an error and do not + call // the "data" handler if (!info.inValidField) { - // If the "hadDefaultField" flag is set, we already issued an error + // If the "hadDefaultField" flag is set, we already issued an + error // message if (!info.hadDefaultField) { if (hasNonWhitespaceText) { @@ -726,8 +735,10 @@ void StackImpl::data(const TokenizedData &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 + // 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); @@ -735,12 +746,14 @@ void StackImpl::data(const TokenizedData &data) // Pass the data to the current Handler instance bool valid = false; try { - // Create a fork of the TokenizedData and let the handler work + // Create a fork of the TokenizedData and let the handler + work // on it TokenizedData dataFork = data; valid = info.handler->data(dataFork); - // If the data was validly handled by the handler, commit the + // If the data was validly handled by the handler, commit + the // change if (valid) { data = dataFork; @@ -754,14 +767,16 @@ void StackImpl::data(const TokenizedData &data) 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 + // 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 + // Commit the content of the logger fork. Do not change the + valid // flag. loggerFork.commit(); } @@ -783,12 +798,14 @@ void StackImpl::fieldStart(bool isDefault) HandlerInfo &info = currentInfo(); if (info.inField) { logger().error( - "Got field start, but there is no command for which to start the " + "Got field start, but there is no command for which to start " + "the " "field."); return; } - // If the handler already had a default field we cannot start a new field + // If the handler already had a default field we cannot start a new + // field // (the default field always is the last field) -- mark the command as // invalid if (info.hadDefaultField) { @@ -797,7 +814,8 @@ void StackImpl::fieldStart(bool isDefault) std::string("\" does not have any more fields")); } - // Copy the isDefault flag to a local variable, the fieldStart method will + // Copy the isDefault flag to a local variable, the fieldStart method + // will // write into this variable bool defaultField = isDefault; @@ -843,7 +861,8 @@ void StackImpl::fieldEnd() return; } - // Only continue if the current handler stack is in a valid state, do not + // 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() && !info.hadDefaultField && info.inValidField) { try { @@ -868,7 +887,7 @@ void StackImpl::unregisterToken(TokenId id) tokenRegistry.unregisterToken(id); } -void StackImpl::pushTokens(const std::vector &tokens) +void StackImpl::pushTokens(const std::vector &tokens) { // TODO } diff --git a/src/core/parser/stack/TokenStack.hpp b/src/core/parser/stack/TokenStack.hpp index af734bb..f2e7edc 100644 --- a/src/core/parser/stack/TokenStack.hpp +++ b/src/core/parser/stack/TokenStack.hpp @@ -82,9 +82,9 @@ public: TokenStack(const TokenStack &parentStack) : TokenStack(&parentStack) {} /** - * Pushes a list of TokenSyntaxDescriptor instances onto the internal stack. + * Pushes a list of SyntaxDescriptor instances onto the internal stack. * - * @param tokens is a list of TokenSyntaxDescriptor instances that should be + * @param tokens is a list of SyntaxDescriptor instances that should be * stored on the stack. */ void pushTokens(const std::vector &tokens); diff --git a/src/core/parser/stack/TypesystemHandler.cpp b/src/core/parser/stack/TypesystemHandler.cpp index 110c56f..3fa641a 100644 --- a/src/core/parser/stack/TypesystemHandler.cpp +++ b/src/core/parser/stack/TypesystemHandler.cpp @@ -32,8 +32,7 @@ namespace parser_stack { /* TypesystemHandler */ -bool TypesystemHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool TypesystemHandler::startCommand(Variant::mapType &args) { // Create the typesystem instance Rooted typesystem = @@ -64,8 +63,7 @@ void TypesystemHandler::end() { scope().pop(logger()); } /* TypesystemEnumHandler */ -bool TypesystemEnumHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool TypesystemEnumHandler::startCommand(Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); @@ -93,8 +91,7 @@ void TypesystemEnumEntryHandler::doHandle(const Variant &fieldData, /* TypesystemStructHandler */ -bool TypesystemStructHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool TypesystemStructHandler::startCommand(Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); @@ -127,8 +124,7 @@ void TypesystemStructHandler::end() { scope().pop(logger()); } /* TypesystemStructFieldHandler */ -bool TypesystemStructFieldHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool TypesystemStructFieldHandler::startCommand(Variant::mapType &args) { // Read the argument values const std::string &fieldName = args["name"].asString(); @@ -167,8 +163,7 @@ bool TypesystemStructFieldHandler::startCommand(const std::string &commandName, /* TypesystemConstantHandler */ -bool TypesystemConstantHandler::startCommand(const std::string &commandName, - Variant::mapType &args) +bool TypesystemConstantHandler::startCommand(Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); diff --git a/src/core/parser/stack/TypesystemHandler.hpp b/src/core/parser/stack/TypesystemHandler.hpp index 75cba01..0773a3a 100644 --- a/src/core/parser/stack/TypesystemHandler.hpp +++ b/src/core/parser/stack/TypesystemHandler.hpp @@ -43,8 +43,7 @@ class TypesystemHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &commandName, - Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; /** @@ -68,8 +67,7 @@ class TypesystemEnumHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &commandName, - Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; /** @@ -116,8 +114,7 @@ class TypesystemStructHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &commandName, - Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; void end() override; /** @@ -142,8 +139,7 @@ class TypesystemStructFieldHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &commandName, - Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; /** * Creates a new instance of the TypesystemStructFieldHandler. @@ -166,8 +162,7 @@ class TypesystemConstantHandler : public StaticHandler { public: using StaticHandler::StaticHandler; - bool startCommand(const std::string &commandName, - Variant::mapType &args) override; + bool startCommand(Variant::mapType &args) override; /** * Creates a new instance of the TypesystemConstantHandler. -- cgit v1.2.3 From cdae062d0cbc19ce605df24b2fff5e3808f21ca6 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 18:33:46 +0100 Subject: Added range flag to HandlerInfo --- src/core/parser/stack/Stack.cpp | 60 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) (limited to 'src/core/parser/stack') diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index cad4078..e5bd224 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -65,6 +65,11 @@ public: */ bool implicit : 1; + /** + * Set to true if the handled command or annotation has a range. + */ + bool range : 1; + /** * Set to true if the handler currently is in a field. */ @@ -100,8 +105,9 @@ public: /** * Constructor of the HandlerInfo class, allows to set all flags manually. */ - HandlerInfo(bool valid, bool implicit, bool inField, bool inDefaultField, - bool inImplicitDefaultField, bool inValidField); + HandlerInfo(bool valid, bool implicit, bool range, bool inField, + bool inDefaultField, bool inImplicitDefaultField, + bool inValidField); /** * Constructor of the HandlerInfo class, taking a shared_ptr to the handler @@ -124,6 +130,30 @@ public: * Updates the "fields" flags according to a "fieldEnd" event. */ void fieldEnd(); + + /** + * Returns the name of the referenced handler or an empty string if no + * handler is present. + * + * @return the current handler name. + */ + std::string name() const; + + /** + * Returns the type of the referenced handler or COMMAND if no handler is + * present. + * + * @return the current handler type. + */ + HandlerType type() const; + + /** + * Returns the current state the handler is on or States::None if no handler + * is present. + * + * @return the current state machine state. + */ + const State &state() const; }; HandlerInfo::HandlerInfo() : HandlerInfo(nullptr) {} @@ -133,6 +163,7 @@ HandlerInfo::HandlerInfo(std::shared_ptr handler) fieldIdx(0), valid(true), implicit(false), + range(false), inField(false), inDefaultField(false), inImplicitDefaultField(false), @@ -141,13 +172,14 @@ HandlerInfo::HandlerInfo(std::shared_ptr handler) { } -HandlerInfo::HandlerInfo(bool valid, bool implicit, bool inField, +HandlerInfo::HandlerInfo(bool valid, bool implicit, bool range, bool inField, bool inDefaultField, bool inImplicitDefaultField, bool inValidField) : handler(nullptr), fieldIdx(0), valid(valid), implicit(implicit), + range(range), inField(inField), inDefaultField(inDefaultField), inImplicitDefaultField(inImplicitDefaultField), @@ -156,6 +188,21 @@ HandlerInfo::HandlerInfo(bool valid, bool implicit, bool inField, { } +std::string HandlerInfo::name() const +{ + return handler == nullptr ? std::string{} : handler->name(); +} + +HandlerType HandlerInfo::type() const +{ + return handler == nullptr ? HandlerType::COMMAND : handler->type(); +} + +const State &HandlerInfo::state() const +{ + return handler == nullptr ? States::None : handler->state(); +} + HandlerInfo::~HandlerInfo() { // Do nothing @@ -182,7 +229,7 @@ void HandlerInfo::fieldEnd() /** * Stub instance of HandlerInfo containing no handler information. */ -static HandlerInfo EmptyHandlerInfo{true, true, true, true, false, true}; +static HandlerInfo EmptyHandlerInfo{true, true, false, true, true, false, true}; } /* Helper functions */ @@ -447,12 +494,12 @@ std::set StackImpl::expectedCommands() const State &StackImpl::currentState() const { - return stack.empty() ? States::None : stack.back().handler->state(); + return stack.empty() ? States::None : stack.back().state(); } std::string StackImpl::currentCommandName() const { - return stack.empty() ? std::string{} : stack.back().handler->name(); + return stack.empty() ? std::string{} : stack.back().name(); } const State *StackImpl::findTargetState(const std::string &name) @@ -665,6 +712,7 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, // 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; + info.range = range; loggerFork.commit(); return; } -- cgit v1.2.3 From 1c33913ebb5d9202575d3ca99bd17366d30f2261 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 3 Mar 2015 00:30:38 +0100 Subject: Started restructuring and adapting Stack class, reenabled unit tests (does not compile right now) --- CMakeLists.txt | 2 +- src/core/parser/stack/Stack.cpp | 447 ++++++++------- src/core/parser/stack/Stack.hpp | 13 +- src/core/parser/utils/TokenizedData.cpp | 10 +- src/core/parser/utils/TokenizedData.hpp | 12 + test/core/parser/stack/StackTest.cpp | 959 ++++++++++++++++---------------- 6 files changed, 744 insertions(+), 699 deletions(-) (limited to 'src/core/parser/stack') diff --git a/CMakeLists.txt b/CMakeLists.txt index f99c212..c7ad7a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -326,7 +326,7 @@ IF(TEST) test/core/model/StyleTest test/core/model/TypesystemTest test/core/parser/ParserScopeTest -# test/core/parser/stack/StackTest + test/core/parser/stack/StackTest test/core/parser/stack/StateTest test/core/parser/stack/TokenRegistryTest test/core/parser/utils/SourceOffsetVectorTest diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index e5bd224..89217ea 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -86,6 +86,12 @@ public: */ bool inImplicitDefaultField : 1; + /** + * Set to true if the handler current is in an implicitly started range + * field. + */ + bool inImplicitRangeField: 1; + /** * Set to false if this field is only opened pro-forma and does not accept * any data. Otherwise set to true. @@ -230,6 +236,18 @@ void HandlerInfo::fieldEnd() * Stub instance of HandlerInfo containing no handler information. */ static HandlerInfo EmptyHandlerInfo{true, true, false, true, true, false, true}; + +/** + * Small helper class makeing sure the reference at some variable is reset once + * the scope is left. + */ +template +struct GuardedTemporaryPointer { + T **ptr; + GuardedTemporaryPointer(T *ref, T **ptr) : ptr(ptr) { *ptr = ref; } + + ~GuardedTemporaryPointer() { *ptr = nullptr; } +}; } /* Helper functions */ @@ -352,11 +370,18 @@ private: HandlerInfo &lastInfo(); /** - * Ends all handlers that currently are not inside a field and already had - * a default field. This method is called whenever the data() and command() - * events are reached. + * Returns a set containing the tokens that should currently be processed + * by the TokenizedData instance. + * + * @return a TokenSet instance containing all tokens that should currently + * be processed. */ - void endOverdueHandlers(); + TokenSet currentTokens() const; + + /** + * Returns the whitespace mode defined by the current command. + */ + WhitespaceMode currentWhitespaceMode() const; /** * Ends the current handler and removes the corresponding element from the @@ -365,13 +390,14 @@ private: 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. + * Ends all handlers that currently are not inside a field and already had + * a default field. 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. This method is called whenever the data(), startAnnotation(), + * startToken(), startCommand(), annotationStart() or annotationEnd() events + * are reached. */ - bool ensureHandlerIsInField(); + void prepareCurrentHandler(); /** * Returns true if all handlers on the stack are currently valid, or false @@ -381,6 +407,30 @@ private: */ bool handlersValid(); + /** + * Called whenever there is an actual data pending on the current + * TokenizedDataReader. Tries to feed this data to the current handler. + */ + void handleData(); + + /** + * Called whenever there is a token waiting to be processed. If possible + * tries to end a current handler with this token or to start a new handler + * with the token. + * + * @param token is the token that should be handled. + */ + void handleToken(const Token &token); + + /** + * Called by the rangeEnd() and fieldEnd() methods to end the current ranged + * command. + * + * @param rangeCommand specifies whether this should end the range of a + * command with range. + */ + void handleFieldEnd(bool rangeCommand); + public: StackImpl(ParserCallbacks &parser, ParserContext &ctx, const std::multimap &states); @@ -403,7 +453,6 @@ public: TokenId registerToken(const std::string &token) override; void unregisterToken(TokenId id) override; Variant readData() override; - bool hasData(); void pushTokens(const std::vector &tokens) override; void popTokens() override; }; @@ -492,16 +541,6 @@ std::set StackImpl::expectedCommands() return res; } -const State &StackImpl::currentState() const -{ - return stack.empty() ? States::None : stack.back().state(); -} - -std::string StackImpl::currentCommandName() const -{ - return stack.empty() ? std::string{} : stack.back().name(); -} - const State *StackImpl::findTargetState(const std::string &name) { const State *currentState = &(this->currentState()); @@ -527,6 +566,28 @@ const State *StackImpl::findTargetStateOrWildcard(const std::string &name) return targetState; } +const State &StackImpl::currentState() const +{ + return stack.empty() ? States::None : stack.back().state(); +} + +std::string StackImpl::currentCommandName() const +{ + return stack.empty() ? std::string{} : stack.back().name(); +} + +TokenSet StackImpl::currentTokens() const +{ + // TODO: Implement + return Tokens{}; +} + +WhitespaceMode currentWhitespaceMode() const +{ + // TODO: Implement + return WhitespaceMode::COLLAPSE; +} + HandlerInfo &StackImpl::currentInfo() { return stack.empty() ? EmptyHandlerInfo : stack.back(); @@ -536,6 +597,8 @@ HandlerInfo &StackImpl::lastInfo() return stack.size() < 2U ? EmptyHandlerInfo : stack[stack.size() - 2]; } +/* Stack helper functions */ + void StackImpl::endCurrentHandler() { if (!stack.empty()) { @@ -563,44 +626,37 @@ void StackImpl::endCurrentHandler() } } -void StackImpl::endOverdueHandlers() +void StackImpl::prepareCurrentHandler() { - if (!stack.empty()) { - // Fetch the handler info for the current top-level element - HandlerInfo &info = stack.back(); + // Repeat until a valid handler is found on the stack + while (true) { + // Fetch the handler for the current top-level element + HandlerInfo &info = currentInfo(); - // Abort if this handler currently is inside a field - if (info.inField || (!info.hadDefaultField && info.valid)) { + // If the current Handler is in a field, there is nothing to be done, + // abort + if (info.inField) { return; } - // Otherwise end the current handler - endCurrentHandler(); - } -} - -bool StackImpl::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 or the handler is - // not valid + // If the current field already had a default field or is not valid, + // end it and repeat if (info.hadDefaultField || !info.valid) { - return false; + endCurrentHandler(); + continue; } // Try to start a new default field, abort if this did not work bool isDefault = true; if (!info.handler->fieldStart(isDefault, info.fieldIdx)) { - return false; + endCurrentHandler(); + continue; } - // Mark the field as started - info.fieldStart(true, true, true); + // Mark the field as started and return -- the field should be marked + // is implicit if this is not a field with range + info.fieldStart(true, !info.range, true, info.range); } - return true; } bool StackImpl::handlersValid() @@ -613,13 +669,105 @@ bool StackImpl::handlersValid() return true; } +void StackImpl::handleData() +{ + // Repeat until we found some handle willingly consuming the data + while (true) { + // Prepare the stack -- make sure all overdue handlers are ended and + // we currently are in an open field + prepareCurrentHandler(); + + // Fetch the current handler information + HandlerInfo &info = currentInfo(); + + // If this field should not get any data, log an error and do not + // call the "data" handler + if (!info.inValidField) { + if (!info.hadDefaultField) { + logger().error("Did not expect any data here", data); + } + return; + } + + // If we're currently in an invalid subtree, just eat the data and abort + if (!handlersValid()) { + return; + } + + // Fork the logger and set it as temporary logger for the "data" + // 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(); + } + catch (LoggableException ex) { + loggerFork.log(ex); + } + + // Reset the logger instance of the handler 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(); + } +} + +void StackImpl::handleToken(const Token &token) { + // TODO: Implement + // Just eat them for now +} + +void StackImpl::handleFieldEnd(bool rangedCommand) +{ + // Throw away all overdue handlers, start the default field at least once + // if this has not been done yet (this is important for range commands) + prepareStack(); + + // Close all implicit default fields + while (!stack.empty()) { + HandlerInfo &info = currentInfo(); + if (!info.inImplicitDefaultField) { + break; + } + endCurrentHandler(); + } + + // Fetch the information attached to the current handler + HandlerInfo &info = currentInfo(); + if (!info.inField || stack.empty()) { + logger().error("Got field end, but there is no field here to end"); + 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()) { + if (info.range && info.inDefaultField) + info.handler->fieldEnd(); + } + + // This command no longer is in a field + info.fieldEnd(); +} + +/* Class StackImpl public functions */ + void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, bool range) { - // End handlers that already had a default field and are currently not - // active. - endOverdueHandlers(); - // Make sure the given identifier is valid (preventing "*" from being // malicously passed to this function) if (!Utils::isNamespacedIdentifier(name.asString())) { @@ -629,6 +777,10 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, } while (true) { + // Prepare the stack -- make sure all overdue handlers are ended and + // we currently are in an open field + prepareCurrentHandler(); + // 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 @@ -644,12 +796,6 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, } } - // 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(); @@ -670,17 +816,14 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, 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 + // 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) { - // Canonicalize the arguments (if this has not already been - // done), + // Canonicalize the arguments (if this has not already been done), // allow additional arguments and numeric indices Variant::mapType canonicalArgs = args; targetState->arguments.validateMap(canonicalArgs, loggerFork, true, @@ -697,10 +840,8 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, } // 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 + // not valid -- remove both the new handler and the parent field from + // the stack if (!info.valid && parentInfo.inImplicitDefaultField) { endCurrentHandler(); endCurrentHandler(); @@ -708,9 +849,8 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, } // 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. + // 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; info.range = range; loggerFork.commit(); @@ -732,106 +872,31 @@ void StackImpl::annotationEnd(const Variant &className, void StackImpl::rangeEnd() { - // TODO + handleFieldEnd(true); } void StackImpl::data(const TokenizedData &data) { - // TODO: Rewrite this function for token handling - // TODO: This loop needs to be refactored out - /*while (!data.atEnd()) { - // End handlers that already had a default field and are currently - not - // active. - endOverdueHandlers(); - - const bool hasNonWhitespaceText = data.hasNonWhitespaceText(); - - // Check whether there is any command the data can be sent to -- if - not, - // make sure the data actually is data - if (stack.empty()) { - if (hasNonWhitespaceText) { - throw LoggableException("No command here to receive data.", - data); - } - return; - } - - // 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) { - // If the "hadDefaultField" flag is set, we already issued an - error - // message - if (!info.hadDefaultField) { - if (hasNonWhitespaceText) { - logger().error("Did not expect any data here", data); - } - return; - } - } - - 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 { - // Create a fork of the TokenizedData and let the handler - work - // on it - TokenizedData dataFork = data; - valid = info.handler->data(dataFork); - - // If the data was validly handled by the handler, commit - the - // change - if (valid) { - data = dataFork; - } - } - 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; - }*/ + // Fetch a reader for the given tokenized data instance. + TokenizedDataReader reader = data.reader(); + + // Use the GuardedTemporaryPointer to make sure that the member variable + // dataReader is resetted to nullptr once this scope is left. + GuardedTemporaryPointer ptr(&reader, &dataReader); + + // Peek a token from the reader, repeat until all tokens have been read + Token token; + while (reader.peek(token, currentTokens(), currentWhitespaceMode())) { + // Handle the token as text data or as actual token + if (token.id == Tokens::Data) { + handleData(); + } else { + handleToken(token); + } + + // Consume the peeked token + reader.consumePeek(); + } } void StackImpl::fieldStart(bool isDefault) @@ -853,8 +918,7 @@ void StackImpl::fieldStart(bool isDefault) } // If the handler already had a default field we cannot start a new - // field - // (the default field always is the last field) -- mark the command as + // field (the default field always is the last field) -- mark the command as // invalid if (info.hadDefaultField) { logger().error(std::string("Got field start, but command \"") + @@ -862,8 +926,7 @@ void StackImpl::fieldStart(bool isDefault) std::string("\" does not have any more fields")); } - // Copy the isDefault flag to a local variable, the fieldStart method - // will + // Copy the isDefault flag to a local variable, the fieldStart method will // write into this variable bool defaultField = isDefault; @@ -891,40 +954,11 @@ void StackImpl::fieldStart(bool isDefault) void StackImpl::fieldEnd() { - // Unroll the stack until the next explicitly open field - while (!stack.empty()) { - HandlerInfo &info = currentInfo(); - if (info.inField && !info.inImplicitDefaultField) { - break; - } - endCurrentHandler(); - } - - // Fetch the information attached to the current handler - HandlerInfo &info = currentInfo(); - if (!info.inField || info.inImplicitDefaultField || stack.empty()) { - 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() && !info.hadDefaultField && info.inValidField) { - try { - info.handler->fieldEnd(); - } - catch (LoggableException ex) { - logger().log(ex); - } - } - - // This command no longer is in a field - info.fieldEnd(); + handleFieldEnd(false); } +/* Class StackImpl HandlerCallbacks */ + TokenId StackImpl::registerToken(const std::string &token) { return tokenRegistry.registerToken(token); @@ -950,14 +984,7 @@ Variant StackImpl::readData() if (dataReader != nullptr) { TokenizedDataReaderFork dataReaderFork = dataReader->fork(); Token token; - - // TODO: Use correct token set - TokenSet tokens; - - // TODO: Use correct whitespace mode - WhitespaceMode mode = WhitespaceMode::COLLAPSE; - - dataReaderFork.read(token, tokens, mode); + dataReaderFork.read(token, currentTokens(), currentWhitespaceMode()); if (token.id == Tokens::Data) { Variant res = Variant::fromString(token.content); res.setLocation(token.getLocation()); @@ -967,8 +994,6 @@ Variant StackImpl::readData() return Variant{}; } -bool StackImpl::hasData() { return readData() != nullptr; } - /* Class Stack */ Stack::Stack(ParserCallbacks &parser, ParserContext &ctx, @@ -1013,5 +1038,7 @@ void Stack::fieldStart(bool isDefault) { impl->fieldStart(isDefault); } void Stack::fieldEnd() { impl->fieldEnd(); } void Stack::data(const TokenizedData &data) { impl->data(data); } + +void Stack::data(const std::string &str) { data(TokenizedData(str)); } } } diff --git a/src/core/parser/stack/Stack.hpp b/src/core/parser/stack/Stack.hpp index de281d4..1de7cff 100644 --- a/src/core/parser/stack/Stack.hpp +++ b/src/core/parser/stack/Stack.hpp @@ -150,13 +150,24 @@ public: /** * Function that should be called whenever character data is found in the - * input stream. May only be called if the currently is a command on the + * input stream. May only be called if there currently is a command on the * stack. * * @param data is a TokenizedData instance containing the pre-segmented data * that should be read. */ void data(const TokenizedData &data); + + /** + * Function that may be called whenever character data is found in the + * input stream. May only be called if the currently is a command on the + * stack. This method is mainly intended for unit testing. Pass a + * TokenizedData instance to the + * + * @param str is a string containing the data that should be passed to the + * tokenizer. + */ + void data(const std::string &str); }; } } diff --git a/src/core/parser/utils/TokenizedData.cpp b/src/core/parser/utils/TokenizedData.cpp index c3c4f98..d8a8b37 100644 --- a/src/core/parser/utils/TokenizedData.cpp +++ b/src/core/parser/utils/TokenizedData.cpp @@ -29,8 +29,7 @@ namespace ousia { /** * Maximum token length. */ -constexpr TokenLength MaxTokenLength = - std::numeric_limits::max(); +constexpr TokenLength MaxTokenLength = std::numeric_limits::max(); namespace { /** @@ -510,6 +509,13 @@ TokenizedData::TokenizedData(SourceId sourceId) { } +TokenizedData::TokenizedData(const std::string &data, SourceOffset offsStart, + SourceId sourceId) + : TokenizedData(sourceId) +{ + append(data, offsStart); +} + TokenizedData::~TokenizedData() {} size_t TokenizedData::append(const std::string &data, SourceOffset offsStart, diff --git a/src/core/parser/utils/TokenizedData.hpp b/src/core/parser/utils/TokenizedData.hpp index b72ca02..bc937f2 100644 --- a/src/core/parser/utils/TokenizedData.hpp +++ b/src/core/parser/utils/TokenizedData.hpp @@ -95,6 +95,18 @@ public: */ TokenizedData(SourceId sourceId); + /** + * Creates a new instance of TokenizedData, takes a SourceId and an initial + * string buffer. + * + * @param data is the string that should be appended to the buffer. + * @param offsStart is the start offset in bytes in the input file. + * @param sourceId is the source identifier that should be used for + * constructing the location when returning tokens. + */ + TokenizedData(const std::string &data, SourceOffset offsStart = 0, + SourceId sourceId = InvalidSourceId); + /** * Destructor. Needs to be defined explicitly for freeing a shared pointer * of the incomplete TokenizedDataImpl type. diff --git a/test/core/parser/stack/StackTest.cpp b/test/core/parser/stack/StackTest.cpp index 83966d5..8f6c4df 100644 --- a/test/core/parser/stack/StackTest.cpp +++ b/test/core/parser/stack/StackTest.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -38,70 +39,69 @@ static StandaloneEnvironment env(logger); namespace { +class Parser : public ParserCallbacks { + TokenId registerToken(const std::string &token) override + { + return Tokens::Empty; + } + + void unregisterToken(TokenId id) override + { + // Do nothing here + } +}; + +static Parser parser; + struct Tracker { - int startCount; + int startCommandCount; + int startAnnotationCount; + int startTokenCount; + int endTokenCount; int endCount; int fieldStartCount; int fieldEndCount; - int annotationStartCount; - int annotationEndCount; int dataCount; - Variant::mapType startArgs; - bool fieldStartIsDefault; - size_t fieldStartIdx; - Variant annotationStartClassName; - Variant::mapType annotationStartArgs; - Variant annotationEndClassName; - Variant annotationEndElementName; - TokenizedData dataData; - - bool startResult; - bool fieldStartSetIsDefault; + bool startCommandResult; + bool startAnnotationResult; + bool startTokenResult; + Handler::EndTokenResult endTokenResult; bool fieldStartResult; - bool annotationStartResult; - bool annotationEndResult; bool dataResult; Tracker() { reset(); } void reset() { - startCount = 0; + startCommandCount = 0; + startAnnotationCount = 0; + startTokenCount = 0; + endTokenCount = 0; endCount = 0; fieldStartCount = 0; fieldEndCount = 0; - annotationStartCount = 0; - annotationEndCount = 0; dataCount = 0; - startArgs = Variant::mapType{}; - fieldStartIsDefault = false; - fieldStartIdx = 0; - annotationStartClassName = Variant::fromString(std::string{}); - annotationStartArgs = Variant::mapType{}; - annotationEndClassName = Variant::fromString(std::string{}); - annotationEndElementName = Variant::fromString(std::string{}); - dataData = TokenizedData(); - - startResult = true; - fieldStartSetIsDefault = false; + startCommandResult = true; + startAnnotationResult = true; + startTokenResult = true; + endTokenResult = Handler::EndTokenResult::ENDED_THIS; fieldStartResult = true; - annotationStartResult = true; - annotationEndResult = true; dataResult = true; } - void expect(int startCount, int endCount, int fieldStartCount, - int fieldEndCount, int annotationStartCount, - int annotationEndCount, int dataCount) + void expect(int startCommandCount, int endCount, int fieldStartCount, + int fieldEndCount, int dataCount, int startAnnotationCount = 0, + int startTokenCount = 0, int endTokenCount = 0) { - EXPECT_EQ(startCount, this->startCount); + EXPECT_EQ(startCommandCount, this->startCommandCount); + EXPECT_EQ(startAnnotationCount, this->startAnnotationCount); + EXPECT_EQ(startTokenCount, this->startTokenCount); + EXPECT_EQ(endTokenCount, this->endTokenCount); EXPECT_EQ(endCount, this->endCount); EXPECT_EQ(fieldStartCount, this->fieldStartCount); EXPECT_EQ(fieldEndCount, this->fieldEndCount); - EXPECT_EQ(annotationStartCount, this->annotationStartCount); - EXPECT_EQ(annotationEndCount, this->annotationEndCount); EXPECT_EQ(dataCount, this->dataCount); } }; @@ -113,55 +113,44 @@ private: TestHandler(const HandlerData &handlerData) : Handler(handlerData) {} public: - bool start(Variant::mapType &args) override + bool startCommand(Variant::mapType &args) override { - tracker.startCount++; - tracker.startArgs = args; - if (!tracker.startResult) { - logger().error( - "The TestHandler was told not to allow a field start. So it " - "doesn't. The TestHandler always obeys its master."); - } - return tracker.startResult; + tracker.startCommandCount++; + return tracker.startCommandResult; } - void end() override { tracker.endCount++; } - - bool fieldStart(bool &isDefault, size_t fieldIdx) override + bool startAnnotation(Variant::mapType &args, + AnnotationType annotationType) override { - tracker.fieldStartCount++; - tracker.fieldStartIsDefault = isDefault; - tracker.fieldStartIdx = fieldIdx; - if (tracker.fieldStartSetIsDefault) { - isDefault = true; - } - return tracker.fieldStartResult; + tracker.startAnnotationCount++; + return tracker.startAnnotationResult; } - void fieldEnd() override { tracker.fieldEndCount++; } + bool startToken(Handle node) override + { + tracker.startTokenCount++; + return tracker.startTokenResult; + } - bool annotationStart(const Variant &className, - Variant::mapType &args) override + EndTokenResult endToken(const Token &token, Handle node) override { - tracker.annotationStartCount++; - tracker.annotationStartClassName = className; - tracker.annotationStartArgs = args; - return tracker.annotationStartResult; + tracker.endTokenCount++; + return tracker.endTokenResult; } - bool annotationEnd(const Variant &className, - const Variant &elementName) override + void end() override { tracker.endCount++; } + + bool fieldStart(bool &isDefault, size_t fieldIdx) override { - tracker.annotationEndCount++; - tracker.annotationEndClassName = className; - tracker.annotationEndElementName = elementName; - return tracker.annotationEndResult; + tracker.fieldStartCount++; + return tracker.fieldStartResult; } - bool data(TokenizedData &data) override + void fieldEnd() override { tracker.fieldEndCount++; } + + bool data() override { tracker.dataCount++; - tracker.dataData = data; return tracker.dataResult; } @@ -205,544 +194,544 @@ TEST(Stack, basicTest) tracker.reset(); logger.reset(); { - Stack s{env.context, States::TestHandlers}; + Stack s{parser, env.context, States::TestHandlers}; EXPECT_EQ("", s.currentCommandName()); EXPECT_EQ(&States::None, &s.currentState()); - s.command("document", {}); + s.commandStart("document", {}, true); s.fieldStart(true); s.data("test1"); EXPECT_EQ("document", s.currentCommandName()); EXPECT_EQ(&States::Document, &s.currentState()); - tracker.expect(1, 0, 1, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(1, 0, 1, 0, 1); // scc, ec, fsc, fse, dc, sac, stc, etc - s.command("body", {}); + s.commandStart("body", {}, true); s.fieldStart(true); s.data("test2"); EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); - tracker.expect(2, 0, 2, 0, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 2, 0, 2); // scc, ec, fsc, fse, dc, sac, stc, etc - s.command("inner", {}); + s.commandStart("inner", {}, true); s.fieldStart(true); EXPECT_EQ("inner", s.currentCommandName()); EXPECT_EQ(&States::BodyChildren, &s.currentState()); s.fieldEnd(); - tracker.expect(3, 0, 3, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(3, 0, 3, 1, 2); // scc, ec, fsc, fse, dc, sac, stc, etc s.fieldEnd(); EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); - tracker.expect(3, 1, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(3, 1, 3, 2, 2); // scc, ec, fsc, fse, dc, sac, stc, etc - s.command("body", {}); + s.commandStart("body", {}, true); EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); - tracker.expect(4, 2, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(4, 2, 3, 2, 2); // scc, ec, fsc, fse, dc, sac, stc, etc s.fieldStart(true); s.data("test3"); EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); s.fieldEnd(); - tracker.expect(4, 2, 4, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(4, 2, 4, 3, 3); // scc, ec, fsc, fse, dc, sac, stc, etc EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); s.fieldEnd(); - tracker.expect(4, 3, 4, 4, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(4, 3, 4, 4, 3); // scc, ec, fsc, fse, dc, sac, stc, etc EXPECT_EQ("document", s.currentCommandName()); EXPECT_EQ(&States::Document, &s.currentState()); } - tracker.expect(4, 4, 4, 4, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(4, 4, 4, 4, 3); // scc, ec, fsc, fse, dc, sac, stc, etc ASSERT_FALSE(logger.hasError()); } - +/* TEST(Stack, errorInvalidCommands) { - Stack s{env.context, States::TestHandlers}; - tracker.reset(); - EXPECT_THROW(s.command("body", {}), LoggableException); - s.command("document", {}); - s.fieldStart(true); - EXPECT_THROW(s.command("document", {}), LoggableException); - s.command("empty", {}); - s.fieldStart(true); - EXPECT_THROW(s.command("body", {}), LoggableException); - s.command("special", {}); - s.fieldStart(true); - s.fieldEnd(); - s.fieldEnd(); - s.fieldEnd(); - - logger.reset(); - s.fieldEnd(); - ASSERT_TRUE(logger.hasError()); - - EXPECT_THROW(s.data("test"), LoggableException); - EXPECT_EQ(&States::None, &s.currentState()); + Stack s{env.context, States::TestHandlers}; + tracker.reset(); + EXPECT_THROW(s.command("body", {}), LoggableException); + s.command("document", {}); + s.fieldStart(true); + EXPECT_THROW(s.command("document", {}), LoggableException); + s.command("empty", {}); + s.fieldStart(true); + EXPECT_THROW(s.command("body", {}), LoggableException); + s.command("special", {}); + s.fieldStart(true); + s.fieldEnd(); + s.fieldEnd(); + s.fieldEnd(); + + logger.reset(); + s.fieldEnd(); + ASSERT_TRUE(logger.hasError()); + + EXPECT_THROW(s.data("test"), LoggableException); + EXPECT_EQ(&States::None, &s.currentState()); } TEST(Stack, validation) { - Stack s{env.context, States::TestHandlers}; - tracker.reset(); - logger.reset(); - - s.command("arguments", {}); - EXPECT_TRUE(logger.hasError()); - s.fieldStart(true); - s.fieldEnd(); - - logger.reset(); - s.command("arguments", {{"a", 5}}); - EXPECT_TRUE(logger.hasError()); - s.fieldStart(true); - s.fieldEnd(); - - logger.reset(); - s.command("arguments", {{"a", 5}, {"b", "test"}}); - EXPECT_FALSE(logger.hasError()); - s.fieldStart(true); - s.fieldEnd(); + Stack s{env.context, States::TestHandlers}; + tracker.reset(); + logger.reset(); + + s.command("arguments", {}); + EXPECT_TRUE(logger.hasError()); + s.fieldStart(true); + s.fieldEnd(); + + logger.reset(); + s.command("arguments", {{"a", 5}}); + EXPECT_TRUE(logger.hasError()); + s.fieldStart(true); + s.fieldEnd(); + + logger.reset(); + s.command("arguments", {{"a", 5}, {"b", "test"}}); + EXPECT_FALSE(logger.hasError()); + s.fieldStart(true); + s.fieldEnd(); } TEST(Stack, invalidCommandName) { - tracker.reset(); - logger.reset(); - - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.command("a_", {}); - tracker.expect(2, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(2, 1, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.command("a_:b", {}); - tracker.expect(3, 2, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(3, 2, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - ASSERT_THROW(s.command("_a", {}), LoggableException); - tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - ASSERT_THROW(s.command("a:", {}), LoggableException); - tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - ASSERT_THROW(s.command("a:_b", {}), LoggableException); - tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.reset(); + logger.reset(); + + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.command("a_", {}); + tracker.expect(2, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(2, 1, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.command("a_:b", {}); + tracker.expect(3, 2, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(3, 2, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + ASSERT_THROW(s.command("_a", {}), LoggableException); + tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + ASSERT_THROW(s.command("a:", {}), LoggableException); + tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + ASSERT_THROW(s.command("a:_b", {}), LoggableException); + tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } TEST(Stack, multipleFields) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - - s.command("a", {{"a", false}}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("a", s.currentCommandName()); - EXPECT_EQ(Variant::mapType({{"a", false}}), tracker.startArgs); - - s.fieldStart(false); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_FALSE(tracker.fieldStartIsDefault); - EXPECT_EQ(0U, tracker.fieldStartIdx); - - s.data("test"); - tracker.expect(1, 0, 1, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("test", tracker.dataData.text().asString()); - - s.fieldEnd(); - tracker.expect(1, 0, 1, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - - s.fieldStart(false); - tracker.expect(1, 0, 2, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_FALSE(tracker.fieldStartIsDefault); - EXPECT_EQ(1U, tracker.fieldStartIdx); - - s.data("test2"); - tracker.expect(1, 0, 2, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("test2", tracker.dataData.text().asString()); - - s.fieldEnd(); - tracker.expect(1, 0, 2, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - - s.fieldStart(true); - tracker.expect(1, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_TRUE(tracker.fieldStartIsDefault); - EXPECT_EQ(2U, tracker.fieldStartIdx); - - s.data("test3"); - tracker.expect(1, 0, 3, 2, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("test3", tracker.dataData.text().asString()); - - s.fieldEnd(); - tracker.expect(1, 0, 3, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 3, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + { + Stack s{env.context, States::AnyHandlers}; + + s.command("a", {{"a", false}}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + EXPECT_EQ("a", s.currentCommandName()); + EXPECT_EQ(Variant::mapType({{"a", false}}), tracker.startArgs); + + s.fieldStart(false); + tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + EXPECT_FALSE(tracker.fieldStartIsDefault); + EXPECT_EQ(0U, tracker.fieldStartIdx); + + s.data("test"); + tracker.expect(1, 0, 1, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + EXPECT_EQ("test", tracker.dataData.text().asString()); + + s.fieldEnd(); + tracker.expect(1, 0, 1, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldStart(false); + tracker.expect(1, 0, 2, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + EXPECT_FALSE(tracker.fieldStartIsDefault); + EXPECT_EQ(1U, tracker.fieldStartIdx); + + s.data("test2"); + tracker.expect(1, 0, 2, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + EXPECT_EQ("test2", tracker.dataData.text().asString()); + + s.fieldEnd(); + tracker.expect(1, 0, 2, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldStart(true); + tracker.expect(1, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + EXPECT_TRUE(tracker.fieldStartIsDefault); + EXPECT_EQ(2U, tracker.fieldStartIdx); + + s.data("test3"); + tracker.expect(1, 0, 3, 2, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc + EXPECT_EQ("test3", tracker.dataData.text().asString()); + + s.fieldEnd(); + tracker.expect(1, 0, 3, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(1, 1, 3, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, implicitDefaultFieldOnNewCommand) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.command("b", {}); - tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(2, 2, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + { + Stack s{env.context, States::AnyHandlers}; + + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.command("b", {}); + tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(2, 2, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, implicitDefaultFieldOnNewCommandWithExplicitDefaultField) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); - - s.command("b", {}); - tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(2, 0, 2, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - } - tracker.expect(2, 2, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + { + Stack s{env.context, States::AnyHandlers}; + + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("a", s.currentCommandName()); + + s.command("b", {}); + tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("b", s.currentCommandName()); + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(2, 0, 2, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("b", s.currentCommandName()); + } + tracker.expect(2, 2, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, noImplicitDefaultFieldOnIncompatibleCommand) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); - - tracker.fieldStartResult = false; - s.command("b", {}); - tracker.expect(2, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - } - tracker.expect(2, 2, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + { + Stack s{env.context, States::AnyHandlers}; + + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("a", s.currentCommandName()); + + tracker.fieldStartResult = false; + s.command("b", {}); + tracker.expect(2, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("b", s.currentCommandName()); + } + tracker.expect(2, 2, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, noImplicitDefaultFieldIfDefaultFieldGiven) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); - s.fieldStart(true); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); - s.fieldEnd(); - tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); - - s.command("b", {}); - tracker.expect(2, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - } - tracker.expect(2, 2, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + { + Stack s{env.context, States::AnyHandlers}; + + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("a", s.currentCommandName()); + s.fieldStart(true); + tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("a", s.currentCommandName()); + s.fieldEnd(); + tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("a", s.currentCommandName()); + + s.command("b", {}); + tracker.expect(2, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("b", s.currentCommandName()); + } + tracker.expect(2, 2, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, noEndIfStartFails) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); - - tracker.startResult = false; - s.command("b", {}); - tracker.expect(3, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - } - tracker.expect(3, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_TRUE(logger.hasError()); + tracker.reset(); + logger.reset(); + { + Stack s{env.context, States::AnyHandlers}; + + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("a", s.currentCommandName()); + + tracker.startResult = false; + s.command("b", {}); + tracker.expect(3, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_EQ("b", s.currentCommandName()); + } + tracker.expect(3, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_TRUE(logger.hasError()); } TEST(Stack, implicitDefaultFieldOnData) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.data("test"); - tracker.expect(1, 0, 1, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + { + Stack s{env.context, States::AnyHandlers}; + + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.data("test"); + tracker.expect(1, 0, 1, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(1, 1, 1, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, autoFieldEnd) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(1, 1, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, autoImplicitFieldEnd) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - s.command("b", {}); - s.command("c", {}); - s.command("d", {}); - s.command("e", {}); - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(5, 0, 5, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(5, 5, 5, 5, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + s.command("b", {}); + s.command("c", {}); + s.command("d", {}); + s.command("e", {}); + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(5, 0, 5, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(5, 5, 5, 5, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, invalidDefaultField) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.fieldStartResult = false; - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.fieldStartResult = false; + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(1, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, errorInvalidDefaultFieldData) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.fieldStartResult = false; - s.fieldStart(true); - ASSERT_FALSE(logger.hasError()); - s.data("test"); - ASSERT_TRUE(logger.hasError()); - s.fieldEnd(); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.fieldStartResult = false; + s.fieldStart(true); + ASSERT_FALSE(logger.hasError()); + s.data("test"); + ASSERT_TRUE(logger.hasError()); + s.fieldEnd(); + tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(1, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } TEST(Stack, errorInvalidFieldData) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.fieldStartResult = false; - ASSERT_FALSE(logger.hasError()); - s.fieldStart(false); - ASSERT_TRUE(logger.hasError()); - s.data("test"); - s.fieldEnd(); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.fieldStartResult = false; + ASSERT_FALSE(logger.hasError()); + s.fieldStart(false); + ASSERT_TRUE(logger.hasError()); + s.data("test"); + s.fieldEnd(); + tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(1, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } TEST(Stack, errorFieldStartNoCommand) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - Stack s{env.context, States::AnyHandlers}; - ASSERT_THROW(s.fieldStart(false), LoggableException); - ASSERT_THROW(s.fieldStart(true), LoggableException); - tracker.expect(0, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + Stack s{env.context, States::AnyHandlers}; + ASSERT_THROW(s.fieldStart(false), LoggableException); + ASSERT_THROW(s.fieldStart(true), LoggableException); + tracker.expect(0, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } TEST(Stack, errorMultipleFieldStarts) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.fieldStart(false); - ASSERT_FALSE(logger.hasError()); - s.fieldStart(false); - ASSERT_TRUE(logger.hasError()); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.fieldEnd(); - tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldStart(false); + ASSERT_FALSE(logger.hasError()); + s.fieldStart(false); + ASSERT_TRUE(logger.hasError()); + tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldEnd(); + tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } TEST(Stack, errorMultipleFieldEnds) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.fieldStart(false); - s.fieldEnd(); - ASSERT_FALSE(logger.hasError()); - tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.fieldEnd(); - ASSERT_TRUE(logger.hasError()); - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldStart(false); + s.fieldEnd(); + ASSERT_FALSE(logger.hasError()); + tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + ASSERT_TRUE(logger.hasError()); + tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } TEST(Stack, errorOpenField) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.fieldStart(false); - ASSERT_FALSE(logger.hasError()); - } - ASSERT_TRUE(logger.hasError()); - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldStart(false); + ASSERT_FALSE(logger.hasError()); + } + ASSERT_TRUE(logger.hasError()); + tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc } TEST(Stack, fieldEndWhenImplicitDefaultFieldOpen) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - s.fieldStart(true); - s.command("b", {}); - s.data("test"); - s.fieldEnd(); - tracker.expect(2, 1, 2, 2, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(2, 2, 2, 2, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + s.fieldStart(true); + s.command("b", {}); + s.data("test"); + s.fieldEnd(); + tracker.expect(2, 1, 2, 2, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(2, 2, 2, 2, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, fieldAfterDefaultField) { - tracker.reset(); - logger.reset(); - - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.fieldStart(true); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.command("b", {}); - tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.fieldStart(false); - tracker.expect(2, 0, 2, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.data("f1"); - tracker.expect(2, 0, 2, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - s.fieldEnd(); - tracker.expect(2, 0, 2, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - tracker.fieldStartSetIsDefault = true; - - s.fieldStart(false); - tracker.fieldStartSetIsDefault = false; - tracker.expect(2, 0, 3, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - s.data("f2"); - tracker.expect(2, 0, 3, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - s.fieldEnd(); - tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - - ASSERT_FALSE(logger.hasError()); - s.fieldStart(false); - ASSERT_TRUE(logger.hasError()); - logger.reset(); - tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - s.data("f3"); - tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - s.fieldEnd(); - tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - - s.fieldEnd(); - tracker.expect(2, 1, 3, 3, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(2, 2, 3, 3, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); -} + tracker.reset(); + logger.reset(); + + { + Stack s{env.context, States::AnyHandlers}; + s.command("a", {}); + tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.fieldStart(true); + tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.command("b", {}); + tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldStart(false); + tracker.expect(2, 0, 2, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.data("f1"); + tracker.expect(2, 0, 2, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + tracker.expect(2, 0, 2, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + tracker.fieldStartSetIsDefault = true; + + s.fieldStart(false); + tracker.fieldStartSetIsDefault = false; + tracker.expect(2, 0, 3, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + s.data("f2"); + tracker.expect(2, 0, 3, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + + ASSERT_FALSE(logger.hasError()); + s.fieldStart(false); + ASSERT_TRUE(logger.hasError()); + logger.reset(); + tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + s.data("f3"); + tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + + s.fieldEnd(); + tracker.expect(2, 1, 3, 3, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + } + tracker.expect(2, 2, 3, 3, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + ASSERT_FALSE(logger.hasError()); +}*/ } } -- cgit v1.2.3 From fb8d4cdf01909b61e4e5d0806ec6de178ff0058c Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 3 Mar 2015 14:34:14 +0100 Subject: Finished stack and adapted all unit tests --- src/core/parser/stack/Stack.cpp | 218 +++++++--- src/core/parser/stack/Stack.hpp | 4 +- test/core/parser/stack/StackTest.cpp | 772 ++++++++++++++++++++--------------- 3 files changed, 595 insertions(+), 399 deletions(-) (limited to 'src/core/parser/stack') diff --git a/src/core/parser/stack/Stack.cpp b/src/core/parser/stack/Stack.cpp index 89217ea..f341f1d 100644 --- a/src/core/parser/stack/Stack.cpp +++ b/src/core/parser/stack/Stack.cpp @@ -30,9 +30,15 @@ #include "TokenRegistry.hpp" #include "TokenStack.hpp" +#define STACK_DEBUG_OUTPUT 0 +#if STACK_DEBUG_OUTPUT +#include +#endif + namespace ousia { namespace parser_stack { namespace { + /* Class HandlerInfo */ /** @@ -86,12 +92,6 @@ public: */ bool inImplicitDefaultField : 1; - /** - * Set to true if the handler current is in an implicitly started range - * field. - */ - bool inImplicitRangeField: 1; - /** * Set to false if this field is only opened pro-forma and does not accept * any data. Otherwise set to true. @@ -109,11 +109,10 @@ public: HandlerInfo(); /** - * Constructor of the HandlerInfo class, allows to set all flags manually. + * Constructor of the HandlerInfo class, allows to set some flags manually. */ - HandlerInfo(bool valid, bool implicit, bool range, bool inField, - bool inDefaultField, bool inImplicitDefaultField, - bool inValidField); + HandlerInfo(bool implicit, bool inField, bool inDefaultField, + bool inImplicitDefaultField); /** * Constructor of the HandlerInfo class, taking a shared_ptr to the handler @@ -178,18 +177,17 @@ HandlerInfo::HandlerInfo(std::shared_ptr handler) { } -HandlerInfo::HandlerInfo(bool valid, bool implicit, bool range, bool inField, - bool inDefaultField, bool inImplicitDefaultField, - bool inValidField) +HandlerInfo::HandlerInfo(bool implicit, bool inField, bool inDefaultField, + bool inImplicitDefaultField) : handler(nullptr), fieldIdx(0), - valid(valid), + valid(true), implicit(implicit), - range(range), + range(false), inField(inField), inDefaultField(inDefaultField), inImplicitDefaultField(inImplicitDefaultField), - inValidField(inValidField), + inValidField(true), hadDefaultField(false) { } @@ -235,7 +233,7 @@ void HandlerInfo::fieldEnd() /** * Stub instance of HandlerInfo containing no handler information. */ -static HandlerInfo EmptyHandlerInfo{true, true, false, true, true, false, true}; +static HandlerInfo EmptyHandlerInfo{true, true, true, true}; /** * Small helper class makeing sure the reference at some variable is reset once @@ -386,8 +384,10 @@ private: /** * Ends the current handler and removes the corresponding element from the * stack. + * + * @return true if a command was ended, false otherwise. */ - void endCurrentHandler(); + bool endCurrentHandler(); /** * Ends all handlers that currently are not inside a field and already had @@ -396,8 +396,10 @@ private: * field yet. This method is called whenever the data(), startAnnotation(), * startToken(), startCommand(), annotationStart() or annotationEnd() events * are reached. + * + * @return true if the current command is in a valid field. */ - void prepareCurrentHandler(); + bool prepareCurrentHandler(bool startImplicitDefaultField = true); /** * Returns true if all handlers on the stack are currently valid, or false @@ -413,23 +415,23 @@ private: */ void handleData(); - /** - * Called whenever there is a token waiting to be processed. If possible - * tries to end a current handler with this token or to start a new handler - * with the token. - * - * @param token is the token that should be handled. - */ - void handleToken(const Token &token); + /** + * Called whenever there is a token waiting to be processed. If possible + * tries to end a current handler with this token or to start a new handler + * with the token. + * + * @param token is the token that should be handled. + */ + void handleToken(const Token &token); /** * Called by the rangeEnd() and fieldEnd() methods to end the current ranged * command. * - * @param rangeCommand specifies whether this should end the range of a + * @param endRange specifies whether this should end the range of a * command with range. */ - void handleFieldEnd(bool rangeCommand); + void handleFieldEnd(bool endRange); public: StackImpl(ParserCallbacks &parser, ParserContext &ctx, @@ -579,10 +581,10 @@ std::string StackImpl::currentCommandName() const TokenSet StackImpl::currentTokens() const { // TODO: Implement - return Tokens{}; + return TokenSet{}; } -WhitespaceMode currentWhitespaceMode() const +WhitespaceMode StackImpl::currentWhitespaceMode() const { // TODO: Implement return WhitespaceMode::COLLAPSE; @@ -599,7 +601,7 @@ HandlerInfo &StackImpl::lastInfo() /* Stack helper functions */ -void StackImpl::endCurrentHandler() +bool StackImpl::endCurrentHandler() { if (!stack.empty()) { // Fetch the handler info for the current top-level element @@ -623,29 +625,43 @@ void StackImpl::endCurrentHandler() // Remove the element from the stack stack.pop_back(); + return true; } + return false; } -void StackImpl::prepareCurrentHandler() +bool StackImpl::prepareCurrentHandler(bool startImplicitDefaultField) { // Repeat until a valid handler is found on the stack - while (true) { + while (!stack.empty()) { // Fetch the handler for the current top-level element HandlerInfo &info = currentInfo(); // If the current Handler is in a field, there is nothing to be done, // abort if (info.inField) { - return; + return true; } // If the current field already had a default field or is not valid, // end it and repeat - if (info.hadDefaultField || !info.valid) { + if ((info.hadDefaultField || !startImplicitDefaultField) || + !info.valid) { + // We cannot end the command if it is marked as "range" command + if (info.range) { + return false; + } + + // End the current handler endCurrentHandler(); continue; } + // Abort if starting new default fields is not allowed here + if (!startImplicitDefaultField) { + 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)) { @@ -655,8 +671,10 @@ void StackImpl::prepareCurrentHandler() // Mark the field as started and return -- the field should be marked // is implicit if this is not a field with range - info.fieldStart(true, !info.range, true, info.range); + info.fieldStart(true, !info.range, true); + return true; } + return false; } bool StackImpl::handlersValid() @@ -675,7 +693,9 @@ void StackImpl::handleData() while (true) { // Prepare the stack -- make sure all overdue handlers are ended and // we currently are in an open field - prepareCurrentHandler(); + if (stack.empty() || !prepareCurrentHandler()) { + throw LoggableException("Did not expect any data here"); + } // Fetch the current handler information HandlerInfo &info = currentInfo(); @@ -684,7 +704,7 @@ void StackImpl::handleData() // call the "data" handler if (!info.inValidField) { if (!info.hadDefaultField) { - logger().error("Did not expect any data here", data); + logger().error("Did not expect any data here"); } return; } @@ -722,24 +742,25 @@ void StackImpl::handleData() // Commit the content of the logger fork. Do not change the valid flag. loggerFork.commit(); + return; } } -void StackImpl::handleToken(const Token &token) { +void StackImpl::handleToken(const Token &token) +{ // TODO: Implement // Just eat them for now } -void StackImpl::handleFieldEnd(bool rangedCommand) +void StackImpl::handleFieldEnd(bool endRange) { - // Throw away all overdue handlers, start the default field at least once - // if this has not been done yet (this is important for range commands) - prepareStack(); + // Throw away all overdue handlers + prepareCurrentHandler(false); // Close all implicit default fields while (!stack.empty()) { HandlerInfo &info = currentInfo(); - if (!info.inImplicitDefaultField) { + if (!info.inImplicitDefaultField || info.range) { break; } endCurrentHandler(); @@ -747,16 +768,37 @@ void StackImpl::handleFieldEnd(bool rangedCommand) // Fetch the information attached to the current handler HandlerInfo &info = currentInfo(); - if (!info.inField || stack.empty()) { - logger().error("Got field end, but there is no field here to end"); + if (stack.empty() || (!info.inField && !endRange) || + (!info.range && endRange)) { + if (endRange) { + logger().error( + "Got end of range, but there is no command here to end"); + } else { + logger().error("Got field end, but there is no field here to end"); + } 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()) { - if (info.range && info.inDefaultField) - info.handler->fieldEnd(); + // End the current field if it is valid + if (info.inValidField) { + info.handler->fieldEnd(); + info.fieldEnd(); + } + + // End the complete command if this is a range command, start the + // default field for once if range command did not have a default field + if (info.range && endRange) { + if (!info.hadDefaultField) { + bool isDefault = true; + info.handler->fieldStart(isDefault, true); + info.fieldStart(true, true, true); + } + endCurrentHandler(); + return; + } } // This command no longer is in a field @@ -768,6 +810,9 @@ void StackImpl::handleFieldEnd(bool rangedCommand) void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, bool range) { + // Call prepareCurrentHandler once to end all overdue commands + prepareCurrentHandler(); + // Make sure the given identifier is valid (preventing "*" from being // malicously passed to this function) if (!Utils::isNamespacedIdentifier(name.asString())) { @@ -787,8 +832,8 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, const State *targetState = findTargetStateOrWildcard(name.asString()); if (targetState == nullptr) { HandlerInfo &info = currentInfo(); - if (info.inImplicitDefaultField || !info.inField) { - endCurrentHandler(); + if ((info.inImplicitDefaultField || !info.inField) && + endCurrentHandler()) { continue; } else { throw buildInvalidCommandException(name.asString(), @@ -843,9 +888,10 @@ void StackImpl::commandStart(const Variant &name, const Variant::mapType &args, // not valid -- remove both the new handler and the parent field from // the stack if (!info.valid && parentInfo.inImplicitDefaultField) { - endCurrentHandler(); - endCurrentHandler(); - continue; + // Only continue if the parent handler could actually be removed + if (endCurrentHandler() && endCurrentHandler()) { + continue; + } } // If we ended up here, starting the command may or may not have @@ -870,10 +916,7 @@ void StackImpl::annotationEnd(const Variant &className, // TODO } -void StackImpl::rangeEnd() -{ - handleFieldEnd(true); -} +void StackImpl::rangeEnd() { handleFieldEnd(true); } void StackImpl::data(const TokenizedData &data) { @@ -882,7 +925,7 @@ void StackImpl::data(const TokenizedData &data) // Use the GuardedTemporaryPointer to make sure that the member variable // dataReader is resetted to nullptr once this scope is left. - GuardedTemporaryPointer ptr(&reader, &dataReader); + GuardedTemporaryPointer ptr(&reader, &dataReader); // Peek a token from the reader, repeat until all tokens have been read Token token; @@ -952,10 +995,7 @@ void StackImpl::fieldStart(bool isDefault) info.fieldStart(defaultField, false, valid); } -void StackImpl::fieldEnd() -{ - handleFieldEnd(false); -} +void StackImpl::fieldEnd() { handleFieldEnd(false); } /* Class StackImpl HandlerCallbacks */ @@ -1017,28 +1057,70 @@ std::string Stack::currentCommandName() const void Stack::commandStart(const Variant &name, const Variant::mapType &args, bool range) { +#if STACK_DEBUG_OUTPUT + std::cout << "STACK: commandStart " << name << " " << args << " " << range + << std::endl; +#endif impl->commandStart(name, args, range); } void Stack::annotationStart(const Variant &className, const Variant &args, bool range) { +#if STACK_DEBUG_OUTPUT + std::cout << "STACK: annotationStart " << className << " " << args << " " + << range << std::endl; +#endif impl->annotationStart(className, args, range); } void Stack::annotationEnd(const Variant &className, const Variant &elementName) { +#if STACK_DEBUG_OUTPUT + std::cout << "STACK: annotationEnd " << className << " " << elementName + << std::endl; +#endif impl->annotationEnd(className, elementName); } -void Stack::rangeEnd() { impl->rangeEnd(); } +void Stack::rangeEnd() +{ +#if STACK_DEBUG_OUTPUT + std::cout << "STACK: rangeEnd" << std::endl; +#endif + impl->rangeEnd(); +} -void Stack::fieldStart(bool isDefault) { impl->fieldStart(isDefault); } +void Stack::fieldStart(bool isDefault) +{ +#if STACK_DEBUG_OUTPUT + std::cout << "STACK: fieldStart " << isDefault << std::endl; +#endif + impl->fieldStart(isDefault); +} -void Stack::fieldEnd() { impl->fieldEnd(); } +void Stack::fieldEnd() +{ +#if STACK_DEBUG_OUTPUT + std::cout << "STACK: fieldEnd" << std::endl; +#endif + impl->fieldEnd(); +} -void Stack::data(const TokenizedData &data) { impl->data(data); } +void Stack::data(const TokenizedData &data) +{ +#if STACK_DEBUG_OUTPUT + std::cout << "STACK: data" << std::endl; +#endif + impl->data(data); +} -void Stack::data(const std::string &str) { data(TokenizedData(str)); } +void Stack::data(const std::string &str) +{ +#if STACK_DEBUG_OUTPUT + std::cout << "STACK: data (string) " << str << std::endl; +#endif + data(TokenizedData(str)); +} } } diff --git a/src/core/parser/stack/Stack.hpp b/src/core/parser/stack/Stack.hpp index 1de7cff..6d42f10 100644 --- a/src/core/parser/stack/Stack.hpp +++ b/src/core/parser/stack/Stack.hpp @@ -104,7 +104,7 @@ public: * @param range if true, the started command has an explicit range. */ void commandStart(const Variant &name, const Variant::mapType &args, - bool range); + bool range = false); /** * Function that should be called whenever an annotation starts. @@ -115,7 +115,7 @@ public: * @param range if true, the annotation fields have an explicit range. */ void annotationStart(const Variant &className, const Variant &args, - bool range); + bool range = false); /** * Function that should be called whenever an annotation ends. diff --git a/test/core/parser/stack/StackTest.cpp b/test/core/parser/stack/StackTest.cpp index 8f6c4df..a831c32 100644 --- a/test/core/parser/stack/StackTest.cpp +++ b/test/core/parser/stack/StackTest.cpp @@ -70,6 +70,16 @@ struct Tracker { bool fieldStartResult; bool dataResult; + Variant::mapType startCommandArgs; + Variant::mapType startAnnotationArgs; + + bool fieldStartReturnValue; + size_t fieldStartIdx; + bool fieldStartIsDefault; + bool fieldStartSetIsDefault; + + Variant dataData; + Tracker() { reset(); } void reset() @@ -89,6 +99,15 @@ struct Tracker { endTokenResult = Handler::EndTokenResult::ENDED_THIS; fieldStartResult = true; dataResult = true; + + startCommandArgs = Variant::mapType{}; + startAnnotationArgs = Variant::mapType{}; + + fieldStartIdx = 0; + fieldStartIsDefault = false; + fieldStartSetIsDefault = false; + + dataData = Variant{}; } void expect(int startCommandCount, int endCount, int fieldStartCount, @@ -115,13 +134,20 @@ private: public: bool startCommand(Variant::mapType &args) override { + tracker.startCommandArgs = args; tracker.startCommandCount++; + if (!tracker.startCommandResult) { + logger().error( + "TestHandler was told not to allow a command start. " + "TestHandler always obeys its master."); + } return tracker.startCommandResult; } bool startAnnotation(Variant::mapType &args, AnnotationType annotationType) override { + tracker.startAnnotationArgs = args; tracker.startAnnotationCount++; return tracker.startAnnotationResult; } @@ -142,6 +168,11 @@ public: bool fieldStart(bool &isDefault, size_t fieldIdx) override { + tracker.fieldStartIsDefault = isDefault; + tracker.fieldStartIdx = fieldIdx; + if (tracker.fieldStartSetIsDefault) { + isDefault = true; + } tracker.fieldStartCount++; return tracker.fieldStartResult; } @@ -150,6 +181,7 @@ public: bool data() override { + tracker.dataData = readData(); tracker.dataCount++; return tracker.dataResult; } @@ -199,456 +231,518 @@ TEST(Stack, basicTest) EXPECT_EQ("", s.currentCommandName()); EXPECT_EQ(&States::None, &s.currentState()); - s.commandStart("document", {}, true); + s.commandStart("document", {}); s.fieldStart(true); s.data("test1"); EXPECT_EQ("document", s.currentCommandName()); EXPECT_EQ(&States::Document, &s.currentState()); - tracker.expect(1, 0, 1, 0, 1); // scc, ec, fsc, fse, dc, sac, stc, etc + tracker.expect(1, 0, 1, 0, 1); // scc, ec, fsc, fec, dc, sac, stc, etc - s.commandStart("body", {}, true); + s.commandStart("body", {}); s.fieldStart(true); s.data("test2"); EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); - tracker.expect(2, 0, 2, 0, 2); // scc, ec, fsc, fse, dc, sac, stc, etc + tracker.expect(2, 0, 2, 0, 2); // scc, ec, fsc, fec, dc, sac, stc, etc - s.commandStart("inner", {}, true); + s.commandStart("inner", {}); s.fieldStart(true); EXPECT_EQ("inner", s.currentCommandName()); EXPECT_EQ(&States::BodyChildren, &s.currentState()); s.fieldEnd(); - tracker.expect(3, 0, 3, 1, 2); // scc, ec, fsc, fse, dc, sac, stc, etc + tracker.expect(3, 0, 3, 1, 2); // scc, ec, fsc, fec, dc, sac, stc, etc s.fieldEnd(); EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); - tracker.expect(3, 1, 3, 2, 2); // scc, ec, fsc, fse, dc, sac, stc, etc + tracker.expect(3, 1, 3, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc - s.commandStart("body", {}, true); + s.commandStart("body", {}); EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); - tracker.expect(4, 2, 3, 2, 2); // scc, ec, fsc, fse, dc, sac, stc, etc + tracker.expect(4, 2, 3, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc s.fieldStart(true); s.data("test3"); EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); s.fieldEnd(); - tracker.expect(4, 2, 4, 3, 3); // scc, ec, fsc, fse, dc, sac, stc, etc + tracker.expect(4, 2, 4, 3, 3); // scc, ec, fsc, fec, dc, sac, stc, etc EXPECT_EQ("body", s.currentCommandName()); EXPECT_EQ(&States::Body, &s.currentState()); s.fieldEnd(); - tracker.expect(4, 3, 4, 4, 3); // scc, ec, fsc, fse, dc, sac, stc, etc + tracker.expect(4, 3, 4, 4, 3); // scc, ec, fsc, fec, dc, sac, stc, etc EXPECT_EQ("document", s.currentCommandName()); EXPECT_EQ(&States::Document, &s.currentState()); } - tracker.expect(4, 4, 4, 4, 3); // scc, ec, fsc, fse, dc, sac, stc, etc + tracker.expect(4, 4, 4, 4, 3); // scc, ec, fsc, fec, dc, sac, stc, etc ASSERT_FALSE(logger.hasError()); } -/* + +TEST(Stack, basicTestRangeCommands) +{ + tracker.reset(); + logger.reset(); + { + Stack s{parser, env.context, States::TestHandlers}; + + EXPECT_EQ("", s.currentCommandName()); + EXPECT_EQ(&States::None, &s.currentState()); + + s.commandStart("document", {}, true); + EXPECT_EQ("document", s.currentCommandName()); + EXPECT_EQ(&States::Document, &s.currentState()); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + + s.data("test1"); + tracker.expect(1, 0, 1, 0, 1); // scc, ec, fsc, fec, dc, sac, stc, etc + + s.commandStart("body", {}, true); + tracker.expect(2, 0, 1, 0, 1); // scc, ec, fsc, fec, dc, sac, stc, etc + s.data("test2"); + tracker.expect(2, 0, 2, 0, 2); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ("body", s.currentCommandName()); + EXPECT_EQ(&States::Body, &s.currentState()); + + s.commandStart("inner", {}, true); + tracker.expect(3, 0, 2, 0, 2); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ("inner", s.currentCommandName()); + EXPECT_EQ(&States::BodyChildren, &s.currentState()); + s.rangeEnd(); + tracker.expect(3, 1, 3, 1, 2); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ("body", s.currentCommandName()); + EXPECT_EQ(&States::Body, &s.currentState()); + s.rangeEnd(); + tracker.expect(3, 2, 3, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc + + s.commandStart("body", {}, true); + EXPECT_EQ("body", s.currentCommandName()); + EXPECT_EQ(&States::Body, &s.currentState()); + tracker.expect(4, 2, 3, 2, 2); // scc, ec, fsc, fse, dc, sac, stc, etc + s.fieldStart(true); + tracker.expect(4, 2, 4, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc + s.data("test3"); + tracker.expect(4, 2, 4, 2, 3); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ("body", s.currentCommandName()); + EXPECT_EQ(&States::Body, &s.currentState()); + s.fieldEnd(); + tracker.expect(4, 2, 4, 3, 3); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ("body", s.currentCommandName()); + EXPECT_EQ(&States::Body, &s.currentState()); + s.rangeEnd(); + tracker.expect(4, 3, 4, 3, 3); // scc, ec, fsc, fec, dc, sac, stc, etc + + EXPECT_EQ("document", s.currentCommandName()); + EXPECT_EQ(&States::Document, &s.currentState()); + s.rangeEnd(); + tracker.expect(4, 4, 4, 4, 3); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(4, 4, 4, 4, 3); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); +} + TEST(Stack, errorInvalidCommands) { - Stack s{env.context, States::TestHandlers}; - tracker.reset(); - EXPECT_THROW(s.command("body", {}), LoggableException); - s.command("document", {}); - s.fieldStart(true); - EXPECT_THROW(s.command("document", {}), LoggableException); - s.command("empty", {}); - s.fieldStart(true); - EXPECT_THROW(s.command("body", {}), LoggableException); - s.command("special", {}); - s.fieldStart(true); - s.fieldEnd(); - s.fieldEnd(); - s.fieldEnd(); + Stack s{parser, env.context, States::TestHandlers}; + tracker.reset(); + EXPECT_THROW(s.commandStart("body", {}), LoggableException); + s.commandStart("document", {}); + s.fieldStart(true); + EXPECT_THROW(s.commandStart("document", {}), LoggableException); + s.commandStart("empty", {}); + s.fieldStart(true); + EXPECT_THROW(s.commandStart("body", {}), LoggableException); + s.commandStart("special", {}); + s.fieldStart(true); + s.fieldEnd(); + s.fieldEnd(); + s.fieldEnd(); - logger.reset(); - s.fieldEnd(); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + s.fieldEnd(); + ASSERT_TRUE(logger.hasError()); - EXPECT_THROW(s.data("test"), LoggableException); - EXPECT_EQ(&States::None, &s.currentState()); + EXPECT_THROW(s.data("test"), LoggableException); + EXPECT_EQ(&States::None, &s.currentState()); } TEST(Stack, validation) { - Stack s{env.context, States::TestHandlers}; - tracker.reset(); - logger.reset(); + Stack s{parser, env.context, States::TestHandlers}; + tracker.reset(); + logger.reset(); - s.command("arguments", {}); - EXPECT_TRUE(logger.hasError()); - s.fieldStart(true); - s.fieldEnd(); + s.commandStart("arguments", {}); + EXPECT_TRUE(logger.hasError()); + s.fieldStart(true); + s.fieldEnd(); - logger.reset(); - s.command("arguments", {{"a", 5}}); - EXPECT_TRUE(logger.hasError()); - s.fieldStart(true); - s.fieldEnd(); + logger.reset(); + s.commandStart("arguments", {{"a", 5}}, false); + EXPECT_TRUE(logger.hasError()); + s.fieldStart(true); + s.fieldEnd(); - logger.reset(); - s.command("arguments", {{"a", 5}, {"b", "test"}}); - EXPECT_FALSE(logger.hasError()); - s.fieldStart(true); - s.fieldEnd(); + logger.reset(); + s.commandStart("arguments", {{"a", 5}, {"b", "test"}}, false); + EXPECT_FALSE(logger.hasError()); + s.fieldStart(true); + s.fieldEnd(); } TEST(Stack, invalidCommandName) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.command("a_", {}); - tracker.expect(2, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(2, 1, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - s.command("a_:b", {}); - tracker.expect(3, 2, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(3, 2, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - ASSERT_THROW(s.command("_a", {}), LoggableException); - tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - ASSERT_THROW(s.command("a:", {}), LoggableException); - tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - - ASSERT_THROW(s.command("a:_b", {}), LoggableException); - tracker.expect(3, 3, 3, 3, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(1, 0, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + + s.commandStart("a_", {}); + tracker.expect(2, 1, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(2, 1, 2, 2, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + + s.commandStart("a_:b", {}); + tracker.expect(3, 2, 2, 2, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(3, 2, 3, 3, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + + ASSERT_THROW(s.commandStart("_a", {}), LoggableException); + tracker.expect(3, 3, 3, 3, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + + ASSERT_THROW(s.commandStart("a:", {}), LoggableException); + tracker.expect(3, 3, 3, 3, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + + ASSERT_THROW(s.commandStart("a:_b", {}), LoggableException); + tracker.expect(3, 3, 3, 3, 0); // scc, ec, fsc, fec, dc, sac, stc, etc } TEST(Stack, multipleFields) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; + tracker.reset(); + logger.reset(); + { + Stack s{parser, env.context, States::AnyHandlers}; - s.command("a", {{"a", false}}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("a", s.currentCommandName()); - EXPECT_EQ(Variant::mapType({{"a", false}}), tracker.startArgs); + s.commandStart("a", {{"a", false}}, false); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ("a", s.currentCommandName()); + EXPECT_EQ(Variant::mapType({{"a", false}}), tracker.startCommandArgs); - s.fieldStart(false); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_FALSE(tracker.fieldStartIsDefault); - EXPECT_EQ(0U, tracker.fieldStartIdx); + s.fieldStart(false); + tracker.expect(1, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_FALSE(tracker.fieldStartIsDefault); + EXPECT_EQ(0U, tracker.fieldStartIdx); - s.data("test"); - tracker.expect(1, 0, 1, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("test", tracker.dataData.text().asString()); + s.data("test"); + tracker.expect(1, 0, 1, 0, 1); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ("test", tracker.dataData.asString()); - s.fieldEnd(); - tracker.expect(1, 0, 1, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + tracker.expect(1, 0, 1, 1, 1); // scc, ec, fsc, fec, dc, sac, stc, etc - s.fieldStart(false); - tracker.expect(1, 0, 2, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_FALSE(tracker.fieldStartIsDefault); - EXPECT_EQ(1U, tracker.fieldStartIdx); + s.fieldStart(false); + tracker.expect(1, 0, 2, 1, 1); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_FALSE(tracker.fieldStartIsDefault); + EXPECT_EQ(1U, tracker.fieldStartIdx); - s.data("test2"); - tracker.expect(1, 0, 2, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("test2", tracker.dataData.text().asString()); + s.data("test2"); + tracker.expect(1, 0, 2, 1, 2); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ("test2", tracker.dataData.asString()); - s.fieldEnd(); - tracker.expect(1, 0, 2, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + tracker.expect(1, 0, 2, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc - s.fieldStart(true); - tracker.expect(1, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_TRUE(tracker.fieldStartIsDefault); - EXPECT_EQ(2U, tracker.fieldStartIdx); + s.fieldStart(true); + tracker.expect(1, 0, 3, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_TRUE(tracker.fieldStartIsDefault); + EXPECT_EQ(2U, tracker.fieldStartIdx); - s.data("test3"); - tracker.expect(1, 0, 3, 2, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc - EXPECT_EQ("test3", tracker.dataData.text().asString()); + s.data("test3"); + tracker.expect(1, 0, 3, 2, 3); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ("test3", tracker.dataData.asString()); - s.fieldEnd(); - tracker.expect(1, 0, 3, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 3, 3, 0, 0, 3); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + s.fieldEnd(); + tracker.expect(1, 0, 3, 3, 3); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(1, 1, 3, 3, 3); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, implicitDefaultFieldOnNewCommand) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; + tracker.reset(); + logger.reset(); + { + Stack s{parser, env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc - s.command("b", {}); - tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(2, 2, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + s.commandStart("b", {}); + tracker.expect(2, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(2, 2, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, implicitDefaultFieldOnNewCommandWithExplicitDefaultField) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; + tracker.reset(); + logger.reset(); + { + Stack s{parser, env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("a", s.currentCommandName()); - s.command("b", {}); - tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(2, 0, 2, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - } - tracker.expect(2, 2, 2, 2, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + s.commandStart("b", {}); + tracker.expect(2, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("b", s.currentCommandName()); + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(2, 0, 2, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("b", s.currentCommandName()); + } + tracker.expect(2, 2, 2, 2, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, noImplicitDefaultFieldOnIncompatibleCommand) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; + tracker.reset(); + logger.reset(); + { + Stack s{parser, env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("a", s.currentCommandName()); - tracker.fieldStartResult = false; - s.command("b", {}); - tracker.expect(2, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - } - tracker.expect(2, 2, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + tracker.fieldStartResult = false; + s.commandStart("b", {}); + tracker.expect(2, 1, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("b", s.currentCommandName()); + } + tracker.expect(2, 2, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, noImplicitDefaultFieldIfDefaultFieldGiven) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; + tracker.reset(); + logger.reset(); + { + Stack s{parser, env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); - s.fieldStart(true); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); - s.fieldEnd(); - tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("a", s.currentCommandName()); + s.fieldStart(true); + tracker.expect(1, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("a", s.currentCommandName()); + s.fieldEnd(); + tracker.expect(1, 0, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("a", s.currentCommandName()); - s.command("b", {}); - tracker.expect(2, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - } - tracker.expect(2, 2, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + s.commandStart("b", {}); + tracker.expect(2, 1, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("b", s.currentCommandName()); + } + tracker.expect(2, 2, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, noEndIfStartFails) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; + tracker.reset(); + logger.reset(); + { + Stack s{parser, env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("a", s.currentCommandName()); + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_EQ("a", s.currentCommandName()); - tracker.startResult = false; - s.command("b", {}); - tracker.expect(3, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_EQ("b", s.currentCommandName()); - } - tracker.expect(3, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_TRUE(logger.hasError()); + tracker.startCommandResult = false; + s.commandStart("b", {}); + tracker.expect(3, 1, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + EXPECT_EQ(&States::None, &s.currentState()); + } + tracker.expect(3, 1, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_TRUE(logger.hasError()); } TEST(Stack, implicitDefaultFieldOnData) { - tracker.reset(); - logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; + tracker.reset(); + logger.reset(); + { + Stack s{parser, env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc - s.data("test"); - tracker.expect(1, 0, 1, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + s.data("test"); + tracker.expect(1, 0, 1, 0, 1); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(1, 1, 1, 1, 1); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, autoFieldEnd) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + { + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(1, 1, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, autoImplicitFieldEnd) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - s.command("b", {}); - s.command("c", {}); - s.command("d", {}); - s.command("e", {}); - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(5, 0, 5, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(5, 5, 5, 5, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + { + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + s.commandStart("b", {}); + s.commandStart("c", {}); + s.commandStart("d", {}); + s.commandStart("e", {}); + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(5, 0, 5, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(5, 5, 5, 5, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, invalidDefaultField) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.fieldStartResult = false; - s.fieldStart(true); - s.fieldEnd(); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - ASSERT_FALSE(logger.hasError()); + { + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + tracker.fieldStartResult = false; + s.fieldStart(true); + s.fieldEnd(); + tracker.expect(1, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(1, 1, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); } TEST(Stack, errorInvalidDefaultFieldData) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.fieldStartResult = false; - s.fieldStart(true); - ASSERT_FALSE(logger.hasError()); - s.data("test"); - ASSERT_TRUE(logger.hasError()); - s.fieldEnd(); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + { + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + tracker.fieldStartResult = false; + s.fieldStart(true); + ASSERT_FALSE(logger.hasError()); + s.data("test"); + ASSERT_TRUE(logger.hasError()); + s.fieldEnd(); + tracker.expect(1, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(1, 1, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc } TEST(Stack, errorInvalidFieldData) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.fieldStartResult = false; - ASSERT_FALSE(logger.hasError()); - s.fieldStart(false); - ASSERT_TRUE(logger.hasError()); - s.data("test"); - s.fieldEnd(); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + { + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + tracker.fieldStartResult = false; + ASSERT_FALSE(logger.hasError()); + s.fieldStart(false); + ASSERT_TRUE(logger.hasError()); + s.data("test"); + s.fieldEnd(); + tracker.expect(1, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(1, 1, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc } TEST(Stack, errorFieldStartNoCommand) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - Stack s{env.context, States::AnyHandlers}; - ASSERT_THROW(s.fieldStart(false), LoggableException); - ASSERT_THROW(s.fieldStart(true), LoggableException); - tracker.expect(0, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + Stack s{parser, env.context, States::AnyHandlers}; + ASSERT_THROW(s.fieldStart(false), LoggableException); + ASSERT_THROW(s.fieldStart(true), LoggableException); + tracker.expect(0, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc } TEST(Stack, errorMultipleFieldStarts) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + { + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc - s.fieldStart(false); - ASSERT_FALSE(logger.hasError()); - s.fieldStart(false); - ASSERT_TRUE(logger.hasError()); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.fieldStart(false); + ASSERT_FALSE(logger.hasError()); + s.fieldStart(false); + ASSERT_TRUE(logger.hasError()); + tracker.expect(1, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc - s.fieldEnd(); - tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.fieldEnd(); + tracker.expect(1, 0, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(1, 1, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc } TEST(Stack, errorMultipleFieldEnds) { - tracker.reset(); - logger.reset(); + tracker.reset(); + logger.reset(); - { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + { + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc - s.fieldStart(false); - s.fieldEnd(); - ASSERT_FALSE(logger.hasError()); - tracker.expect(1, 0, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - s.fieldEnd(); - ASSERT_TRUE(logger.hasError()); - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc - } - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.fieldStart(false); + s.fieldEnd(); + ASSERT_FALSE(logger.hasError()); + tracker.expect(1, 0, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + s.fieldEnd(); + ASSERT_TRUE(logger.hasError()); + tracker.expect(1, 1, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(1, 1, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc } TEST(Stack, errorOpenField) @@ -657,15 +751,15 @@ TEST(Stack, errorOpenField) logger.reset(); { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc s.fieldStart(false); ASSERT_FALSE(logger.hasError()); } ASSERT_TRUE(logger.hasError()); - tracker.expect(1, 1, 1, 1, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(1, 1, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc } TEST(Stack, fieldEndWhenImplicitDefaultFieldOpen) @@ -674,15 +768,15 @@ TEST(Stack, fieldEndWhenImplicitDefaultFieldOpen) logger.reset(); { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); s.fieldStart(true); - s.command("b", {}); + s.commandStart("b", {}); s.data("test"); s.fieldEnd(); - tracker.expect(2, 1, 2, 2, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 1, 2, 2, 1); // scc, ec, fsc, fec, dc, sac, stc, etc } - tracker.expect(2, 2, 2, 2, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 2, 2, 2, 1); // scc, ec, fsc, fec, dc, sac, stc, etc ASSERT_FALSE(logger.hasError()); } @@ -692,46 +786,66 @@ TEST(Stack, fieldAfterDefaultField) logger.reset(); { - Stack s{env.context, States::AnyHandlers}; - s.command("a", {}); - tracker.expect(1, 0, 0, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + Stack s{parser, env.context, States::AnyHandlers}; + s.commandStart("a", {}); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc s.fieldStart(true); - tracker.expect(1, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(1, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc - s.command("b", {}); - tracker.expect(2, 0, 1, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + s.commandStart("b", {}); + tracker.expect(2, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc s.fieldStart(false); - tracker.expect(2, 0, 2, 0, 0, 0, 0); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 2, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc s.data("f1"); - tracker.expect(2, 0, 2, 0, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 2, 0, 1); // scc, ec, fsc, fec, dc, sac, stc, etc s.fieldEnd(); - tracker.expect(2, 0, 2, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 2, 1, 1); // scc, ec, fsc, fec, dc, sac, stc, etc tracker.fieldStartSetIsDefault = true; s.fieldStart(false); tracker.fieldStartSetIsDefault = false; - tracker.expect(2, 0, 3, 1, 0, 0, 1); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 3, 1, 1); // scc, ec, fsc, fec, dc, sac, stc, etc s.data("f2"); - tracker.expect(2, 0, 3, 1, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 3, 1, 2); // scc, ec, fsc, fec, dc, sac, stc, etc s.fieldEnd(); - tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 3, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc ASSERT_FALSE(logger.hasError()); s.fieldStart(false); ASSERT_TRUE(logger.hasError()); logger.reset(); - tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 3, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc s.data("f3"); - tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 3, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc s.fieldEnd(); - tracker.expect(2, 0, 3, 2, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 0, 3, 2, 2); // scc, ec, fsc, fec, dc, sac, stc, etc s.fieldEnd(); - tracker.expect(2, 1, 3, 3, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 1, 3, 3, 2); // scc, ec, fsc, fec, dc, sac, stc, etc } - tracker.expect(2, 2, 3, 3, 0, 0, 2); // sc, ec, fsc, fse, asc, aec, dc + tracker.expect(2, 2, 3, 3, 2); // scc, ec, fsc, fec, dc, sac, stc, etc ASSERT_FALSE(logger.hasError()); -}*/ +} + +TEST(Stack, rangeCommandUnranged) +{ + tracker.reset(); + logger.reset(); + + { + Stack s{parser, env.context, States::AnyHandlers}; + tracker.expect(0, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + s.commandStart("a", {}, true); + tracker.expect(1, 0, 0, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + s.commandStart("b", {}); + tracker.expect(2, 0, 1, 0, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + s.rangeEnd(); + tracker.expect(2, 2, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + } + tracker.expect(2, 2, 1, 1, 0); // scc, ec, fsc, fec, dc, sac, stc, etc + ASSERT_FALSE(logger.hasError()); +} + } } -- cgit v1.2.3