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 --- CMakeLists.txt | 110 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 55 insertions(+), 55 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ea5c3aa..225e63d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ ADD_LIBRARY(ousia_core src/core/common/Rtti src/core/common/RttiBuilder src/core/common/SourceContextReader + src/core/common/Token src/core/common/Utils src/core/common/Variant src/core/common/VariantConverter @@ -180,16 +181,15 @@ 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/Handler - src/core/parser/stack/ImportIncludeHandler +# 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/State - src/core/parser/stack/Stack - src/core/parser/stack/TypesystemHandler +# src/core/parser/stack/Stack +# src/core/parser/stack/TypesystemHandler src/core/parser/utils/SourceOffsetVector - src/core/parser/utils/Token src/core/parser/utils/TokenizedData src/core/parser/utils/Tokenizer src/core/parser/utils/TokenTrie @@ -212,19 +212,19 @@ ADD_LIBRARY(ousia_core # ousia_core #) -ADD_LIBRARY(ousia_osml - src/formats/osml/OsmlParser - src/formats/osml/OsmlStreamParser -) +#ADD_LIBRARY(ousia_osml +# src/formats/osml/OsmlParser +# src/formats/osml/OsmlStreamParser +#) -TARGET_LINK_LIBRARIES(ousia_osml - ousia_core -) +#TARGET_LINK_LIBRARIES(ousia_osml +# ousia_core +#) ADD_LIBRARY(ousia_osxml src/formats/osxml/OsxmlAttributeLocator src/formats/osxml/OsxmlEventParser - src/formats/osxml/OsxmlParser +# src/formats/osxml/OsxmlParser ) TARGET_LINK_LIBRARIES(ousia_osxml @@ -273,19 +273,19 @@ TARGET_LINK_LIBRARIES(ousia_xml # Command line interface -ADD_EXECUTABLE(ousia - src/cli/Main -) +#ADD_EXECUTABLE(ousia +# src/cli/Main +#) -TARGET_LINK_LIBRARIES(ousia - ousia_core - ousia_filesystem - ousia_html - ousia_xml - ousia_osml - ousia_osxml - ${Boost_LIBRARIES} -) +#TARGET_LINK_LIBRARIES(ousia +# ousia_core +# ousia_filesystem +# ousia_html +# ousia_xml +# ousia_osml +# ousia_osxml +# ${Boost_LIBRARIES} +#) # If testing is enabled, build the unit tests IF(TEST) @@ -323,11 +323,11 @@ 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/utils/SourceOffsetVectorTest test/core/parser/utils/TokenizedDataTest - test/core/parser/utils/TokenizerTest +# test/core/parser/utils/TokenizerTest test/core/parser/utils/TokenTrieTest test/core/resource/ResourceLocatorTest test/core/resource/ResourceRequestTest @@ -383,29 +383,29 @@ IF(TEST) # ousia_mozjs # ) - ADD_EXECUTABLE(ousia_test_osml - test/formats/osml/OsmlParserTest - test/formats/osml/OsmlStreamParserTest - ) +# ADD_EXECUTABLE(ousia_test_osml +# test/formats/osml/OsmlParserTest +# test/formats/osml/OsmlStreamParserTest +# ) - TARGET_LINK_LIBRARIES(ousia_test_osml - ${GTEST_LIBRARIES} - ousia_core - ousia_osml - ousia_filesystem - ) +# TARGET_LINK_LIBRARIES(ousia_test_osml +# ${GTEST_LIBRARIES} +# ousia_core +# ousia_osml +# ousia_filesystem +# ) - ADD_EXECUTABLE(ousia_test_osxml - test/formats/osxml/OsxmlEventParserTest - test/formats/osxml/OsxmlParserTest - ) +# ADD_EXECUTABLE(ousia_test_osxml +# test/formats/osxml/OsxmlEventParserTest +# test/formats/osxml/OsxmlParserTest +# ) - TARGET_LINK_LIBRARIES(ousia_test_osxml - ${GTEST_LIBRARIES} - ousia_core - ousia_osxml - ousia_filesystem - ) +# TARGET_LINK_LIBRARIES(ousia_test_osxml +# ${GTEST_LIBRARIES} +# ousia_core +# ousia_osxml +# ousia_filesystem +# ) ADD_EXECUTABLE(ousia_test_xml test/plugins/xml/XmlOutputTest @@ -423,8 +423,8 @@ IF(TEST) ADD_TEST(ousia_test_filesystem ousia_test_filesystem) ADD_TEST(ousia_test_html ousia_test_html) # ADD_TEST(ousia_test_mozjs ousia_test_mozjs) - ADD_TEST(ousia_test_osml ousia_test_osml) - ADD_TEST(ousia_test_osxml ousia_test_osxml) +# ADD_TEST(ousia_test_osml ousia_test_osml) +# ADD_TEST(ousia_test_osxml ousia_test_osxml) ADD_TEST(ousia_test_xml ousia_test_xml) ENDIF() @@ -442,9 +442,9 @@ INSTALL(DIRECTORY data/ DESTINATION share/ousia OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE ) -INSTALL(TARGETS ousia - RUNTIME DESTINATION bin -) +#INSTALL(TARGETS ousia +# RUNTIME DESTINATION bin +#) IF(INSTALL_GEDIT_HIGHLIGHTER) INSTALL(FILES contrib/gtksourceview-3.0/language-specs/ousia.lang -- cgit v1.2.3 From 19dd5946125e90dcbd61966896c9f6cfc4451d80 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Thu, 26 Feb 2015 00:22:23 +0100 Subject: Reactivated TokenizerTest --- CMakeLists.txt | 2 +- test/core/parser/utils/TokenizerTest.cpp | 94 ++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 13 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 225e63d..75909e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,7 +327,7 @@ IF(TEST) test/core/parser/stack/StateTest test/core/parser/utils/SourceOffsetVectorTest test/core/parser/utils/TokenizedDataTest -# test/core/parser/utils/TokenizerTest + test/core/parser/utils/TokenizerTest test/core/parser/utils/TokenTrieTest test/core/resource/ResourceLocatorTest test/core/resource/ResourceRequestTest diff --git a/test/core/parser/utils/TokenizerTest.cpp b/test/core/parser/utils/TokenizerTest.cpp index 0f2bfb7..785bd81 100644 --- a/test/core/parser/utils/TokenizerTest.cpp +++ b/test/core/parser/utils/TokenizerTest.cpp @@ -22,6 +22,8 @@ #include #include +#include "TokenizedDataTestUtils.hpp" + namespace ousia { TEST(Tokenizer, tokenRegistration) @@ -58,14 +60,16 @@ void expectData(const std::string &expected, SourceOffset tokenStart, { ASSERT_EQ(Tokens::Data, token.id); - Variant text = data.text(mode); - ASSERT_TRUE(text.isString()); + Token textToken; + TokenizedDataReader reader = data.reader(); + ASSERT_TRUE(reader.read(textToken, TokenSet{}, mode)); - EXPECT_EQ(expected, text.asString()); + EXPECT_EQ(expected, textToken.content); EXPECT_EQ(tokenStart, token.location.getStart()); EXPECT_EQ(tokenEnd, token.location.getEnd()); - EXPECT_EQ(textStart, text.getLocation().getStart()); - EXPECT_EQ(textEnd, text.getLocation().getEnd()); + EXPECT_EQ(textStart, textToken.getLocation().getStart()); + EXPECT_EQ(textEnd, textToken.getLocation().getEnd()); + EXPECT_TRUE(reader.atEnd()); } TEST(Tokenizer, textTokenPreserveWhitespace) @@ -97,8 +101,8 @@ TEST(Tokenizer, textTokenPreserveWhitespace) TokenizedData data; ASSERT_TRUE(tokenizer.read(reader, token, data)); - expectData("this \t is only a \n\n test text", 0, 32, 0, 32, - token, data, WhitespaceMode::PRESERVE); + expectData("this \t is only a \n\n test text", 0, 32, 0, 32, token, + data, WhitespaceMode::PRESERVE); data.clear(); ASSERT_FALSE(tokenizer.read(reader, token, data)); @@ -134,8 +138,8 @@ TEST(Tokenizer, textTokenTrimWhitespace) TokenizedData data; ASSERT_TRUE(tokenizer.read(reader, token, data)); - expectData("this \t is only a \n\n test text", 0, 32, 0, 32, - token, data, WhitespaceMode::TRIM); + expectData("this \t is only a \n\n test text", 0, 32, 0, 32, token, + data, WhitespaceMode::TRIM); data.clear(); ASSERT_FALSE(tokenizer.read(reader, token, data)); @@ -368,9 +372,12 @@ TEST(Tokenizer, commentTestWhitespacePreserve) if (te.id != Tokens::Data) { EXPECT_EQ(te.content, t.content); } else { - Variant text = data.text(WhitespaceMode::PRESERVE); - ASSERT_TRUE(text.isString()); - EXPECT_EQ(te.content, text.asString()); + TokenizedDataReader dataReader = data.reader(); + Token textToken; + ASSERT_TRUE(dataReader.read(textToken, TokenSet{}, + WhitespaceMode::PRESERVE)); + EXPECT_TRUE(dataReader.atEnd()); + EXPECT_EQ(te.content, textToken.content); } EXPECT_EQ(te.location.getSourceId(), t.location.getSourceId()); EXPECT_EQ(te.location.getStart(), t.location.getStart()); @@ -380,5 +387,68 @@ TEST(Tokenizer, commentTestWhitespacePreserve) TokenizedData data; ASSERT_FALSE(tokenizer.read(reader, t, data)); } + +TEST(Tokenizer, nonPrimaryTokens) +{ + CharReader reader{ + "<>"}; + // 012345678901234567890 12345678901234567890123456789012345678901234567 + // 0 1 2 3 4 5 6 + + Tokenizer tokenizer; + + TokenId tBackslash = tokenizer.registerToken("\\"); + TokenId tDollar = tokenizer.registerToken("$", false); + TokenId tSpeechStart = tokenizer.registerToken("<<", false); + TokenId tSpeechEnd = tokenizer.registerToken(">>", false); + + TokenSet tokens = TokenSet{tDollar, tSpeechStart, tSpeechEnd}; + + Token token, textToken; + { + TokenizedData data; + ASSERT_TRUE(tokenizer.read(reader, token, data)); + ASSERT_EQ(Tokens::Data, token.id); + + TokenizedDataReader dataReader = data.reader(); + assertToken(dataReader, tSpeechStart, "<<", tokens, + WhitespaceMode::TRIM, 0, 2); + assertText(dataReader, "switch to", tokens, WhitespaceMode::TRIM, 2, + 11); + assertToken(dataReader, tDollar, "$", tokens, WhitespaceMode::TRIM, 12, + 13); + assertText(dataReader, "inline", tokens, WhitespaceMode::TRIM, 13, 19); + assertEnd(dataReader); + } + + { + TokenizedData data; + ASSERT_TRUE(tokenizer.read(reader, token, data)); + ASSERT_EQ(tBackslash, token.id); + ASSERT_EQ(20U, token.location.getStart()); + ASSERT_EQ(21U, token.location.getEnd()); + } + + { + TokenizedData data; + ASSERT_TRUE(tokenizer.read(reader, token, data)); + ASSERT_EQ(Tokens::Data, token.id); + + TokenizedDataReader dataReader = data.reader(); + assertText(dataReader, "math mode", tokens, WhitespaceMode::TRIM, 21, + 30); + assertToken(dataReader, tDollar, "$", tokens, WhitespaceMode::TRIM, 30, + 31); + assertText(dataReader, "they said, see the world they said", tokens, + WhitespaceMode::TRIM, 32, 66); + assertToken(dataReader, tSpeechEnd, ">>", tokens, WhitespaceMode::TRIM, + 66, 68); + assertEnd(dataReader); + } + + TokenizedData data; + ASSERT_FALSE(tokenizer.read(reader, token, data)); +} } -- cgit v1.2.3 From 12e10d18810b7ea4ce142d76e846b4faf0c33488 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 27 Feb 2015 18:52:43 +0100 Subject: Made OsmlStreamParser ready for user defined tokens, started to adapt unit tests. --- CMakeLists.txt | 32 +- src/formats/osml/OsmlStreamParser.cpp | 701 +++++++++---- src/formats/osml/OsmlStreamParser.hpp | 298 ++---- test/formats/osml/OsmlStreamParserTest.cpp | 1542 ++++++++++++++-------------- 4 files changed, 1355 insertions(+), 1218 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 75909e9..4e2d7f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,14 +212,14 @@ ADD_LIBRARY(ousia_core # ousia_core #) -#ADD_LIBRARY(ousia_osml +ADD_LIBRARY(ousia_osml # src/formats/osml/OsmlParser -# src/formats/osml/OsmlStreamParser -#) + src/formats/osml/OsmlStreamParser +) -#TARGET_LINK_LIBRARIES(ousia_osml -# ousia_core -#) +TARGET_LINK_LIBRARIES(ousia_osml + ousia_core +) ADD_LIBRARY(ousia_osxml src/formats/osxml/OsxmlAttributeLocator @@ -383,17 +383,17 @@ IF(TEST) # ousia_mozjs # ) -# ADD_EXECUTABLE(ousia_test_osml + ADD_EXECUTABLE(ousia_test_osml # test/formats/osml/OsmlParserTest -# test/formats/osml/OsmlStreamParserTest -# ) + test/formats/osml/OsmlStreamParserTest + ) -# TARGET_LINK_LIBRARIES(ousia_test_osml -# ${GTEST_LIBRARIES} -# ousia_core -# ousia_osml -# ousia_filesystem -# ) + TARGET_LINK_LIBRARIES(ousia_test_osml + ${GTEST_LIBRARIES} + ousia_core + ousia_osml + ousia_filesystem + ) # ADD_EXECUTABLE(ousia_test_osxml # test/formats/osxml/OsxmlEventParserTest @@ -423,7 +423,7 @@ IF(TEST) ADD_TEST(ousia_test_filesystem ousia_test_filesystem) ADD_TEST(ousia_test_html ousia_test_html) # ADD_TEST(ousia_test_mozjs ousia_test_mozjs) -# ADD_TEST(ousia_test_osml ousia_test_osml) + ADD_TEST(ousia_test_osml ousia_test_osml) # ADD_TEST(ousia_test_osxml ousia_test_osxml) ADD_TEST(ousia_test_xml ousia_test_xml) ENDIF() diff --git a/src/formats/osml/OsmlStreamParser.cpp b/src/formats/osml/OsmlStreamParser.cpp index d4cdbf8..7e01a3c 100644 --- a/src/formats/osml/OsmlStreamParser.cpp +++ b/src/formats/osml/OsmlStreamParser.cpp @@ -19,92 +19,411 @@ #include #include #include +#include #include +#include +#include + #include "OsmlStreamParser.hpp" +#include +#include + namespace ousia { +namespace { /** - * Plain format default tokenizer. + * Osml format default tokenizer. Registers the primary tokens in its + * constructor. A single, static instance of this class is created as + * "OsmlTokens", which is copied to the Tokenizer instance of + * OsmlStreamParserImpl. */ -class PlainFormatTokens : public Tokenizer { +class OsmlFormatTokens : public Tokenizer { public: + TokenId Backslash; + TokenId LineComment; + TokenId BlockCommentStart; + TokenId BlockCommentEnd; + TokenId FieldStart; + TokenId FieldEnd; + TokenId DefaultFieldStart; + TokenId AnnotationStart; + TokenId AnnotationEnd; + /** - * Id of the backslash token. + * Registers the plain format tokens in the internal tokenizer. */ - TokenId Backslash; + OsmlFormatTokens() + { + Backslash = registerToken("\\"); + LineComment = registerToken("%"); + BlockCommentStart = registerToken("%{"); + BlockCommentEnd = registerToken("}%"); + FieldStart = registerToken("{"); + FieldEnd = registerToken("}"); + DefaultFieldStart = registerToken("{!"); + AnnotationStart = registerToken("<\\"); + AnnotationEnd = registerToken("\\>"); + } +}; + +/** + * Instance of OsmlFormatTokens used to initialize the internal tokenizer + * instance of OsmlStreamParserImpl. + */ +static const OsmlFormatTokens OsmlTokens; +/** + * Structure representing a field. + */ +struct Field { /** - * Id of the line comment token. + * Specifies whether this field was marked as default field. */ - TokenId LineComment; + bool defaultField; /** - * Id of the block comment start token. + * Location at which the field was started. */ - TokenId BlockCommentStart; + SourceLocation location; /** - * Id of the block comment end token. + * Constructor of the Field structure, initializes all member variables with + * the given values. + * + * @param defaultField is a flag specifying whether this field is a default + * field. + * @param location specifies the location at which the field was started. */ - TokenId BlockCommentEnd; + Field(bool defaultField = false, + const SourceLocation &location = SourceLocation{}) + : defaultField(defaultField), location(location) + { + } +}; +/** + * Entry used for the command stack. + */ +class Command { +private: /** - * Id of the field start token. + * Name and location of the current command. */ - TokenId FieldStart; + Variant name; /** - * Id of the field end token. + * Arguments that were passed to the command. */ - TokenId FieldEnd; + Variant arguments; /** - * Id of the default field start token. + * Vector used as stack for holding the number of opening/closing braces + * and the corresponding "isDefaultField" flag. */ - TokenId DefaultFieldStart; + std::vector fields; /** - * Id of the annotation start token. + * Set to true if this is a command with clear begin and end. */ - TokenId AnnotationStart; + bool hasRange; +public: /** - * Id of the annotation end token. + * Default constructor, marks this command as normal, non-range command. */ - TokenId AnnotationEnd; + Command() : hasRange(false) {} /** - * Registers the plain format tokens in the internal tokenizer. + * Constructor of the Command class. + * + * @param name is a string variant with name and location of the + * command. + * @param arguments is a map variant with the arguments given to the + * command. + * @param hasRange should be set to true if this is a command with + * explicit range. */ - PlainFormatTokens() + Command(Variant name, Variant arguments, bool hasRange) + : name(std::move(name)), + arguments(std::move(arguments)), + hasRange(hasRange) { - Backslash = registerToken("\\"); - LineComment = registerToken("%"); - BlockCommentStart = registerToken("%{"); - BlockCommentEnd = registerToken("}%"); - FieldStart = registerToken("{"); - FieldEnd = registerToken("}"); - DefaultFieldStart = registerToken("{!"); - AnnotationStart = registerToken("<\\"); - AnnotationEnd = registerToken("\\>"); + } + + /** + * Returns a reference at the variant representing name and location of the + * command. + * + * @return a variant containing name and location of the command. + */ + const Variant &getName() const { return name; } + + /** + * Returns a reference at the variant containing name, value and location of + * the arguments. + * + * @return the arguments stored for the command. + */ + const Variant &getArguments() const { return arguments; } + + /** + * Returns a reference at the internal field list. This list should be used + * for printing error messages when fields are still open although the outer + * range field closes. + * + * @return a const reference at the internal field vector. + */ + const std::vector &getFields() const { return fields; } + + /** + * Returns true if this command is currently in a default field. + * + * @return true if the current field on the field stack was explicitly + * marked as default field. If the field stack is empty, true is returned + * if this is a range command. + */ + bool inDefaultField() const + { + return (!fields.empty() && fields.back().defaultField) || + (fields.empty() && hasRange); + } + + /** + * Returns true if this command currently is in any field. + * + * @return true if a field is on the stack or this is a range commands. + * Range commands always are in a field. + */ + bool inField() const { return !fields.empty() || hasRange; } + + /** + * Returns true if this command currently is in a range field. + * + * @return true if the command has a range and no other ranges are on the + * stack. + */ + bool inRangeField() const { return fields.empty() && hasRange; } + + /** + * Returns true if this command currently is in a non-range field. + * + * @return true if the command is in a field, but the field is not the field + * constructed by the "range" + */ + bool inNonRangeField() const { return !fields.empty(); } + + /** + * Pushes another field onto the field stack of this command. + * + * @param defaultField if true, explicitly marks this field as default + * field. + * @param location is the source location at which the field was started. + * Used for error messages in which the user is notified about an error with + * too few closing fields. + */ + void pushField(bool defaultField = false, + const SourceLocation &location = SourceLocation{}) + { + fields.emplace_back(defaultField, location); + } + + /** + * Removes another field from the field stack of this command, returns true + * if the operation was successful. + * + * @return true if there was a field to pop on the stack, false otherwise. + */ + bool popField() + { + if (!fields.empty()) { + fields.pop_back(); + return true; + } + return false; } }; +} -static const PlainFormatTokens OsmlTokens; +/* Class OsmlStreamParserImpl */ -OsmlStreamParser::OsmlStreamParser(CharReader &reader, Logger &logger) - : reader(reader), - logger(logger), - tokenizer(OsmlTokens), - data(reader.getSourceId()) +/** + * Internal implementation of OsmlStreamParser. + */ +class OsmlStreamParserImpl { +public: + /** + * State enum compatible with OsmlStreamParserState but extended by two more + * entries (END and NONE). + */ + enum class State : uint8_t { + COMMAND_START = 0, + COMMAND_END = 1, + FIELD_START = 2, + FIELD_END = 3, + ANNOTATION_START = 4, + ANNOTATION_END = 5, + DATA = 6, + END = 7, + RECOVERABLE_ERROR = 8, + IRRECOVERABLE_ERROR = 9 + }; + +private: + /** + * Reference to the CharReader instance from which the incomming bytes are + * read. + */ + CharReader &reader; + + /** + * Reference at the logger instance to which all error messages are sent. + */ + Logger &logger; + + /** + * Tokenizer instance used to read individual tokens from the text. + */ + Tokenizer tokenizer; + + /** + * Stack containing the current commands. + */ + std::stack commands; + + /** + * Variant containing the tokenized data that was returned from the + * tokenizer as data. + */ + TokenizedData data; + + /** + * Variable containing the current location of the parser. + */ + SourceLocation location; + + /** + * Function used internally to parse an identifier. + * + * @param start is the start byte offset of the identifier (including the + * backslash). + * @param allowNSSep should be set to true if the namespace separator is + * allowed in the identifier name. Issues error if the namespace separator + * is placed incorrectly. + */ + Variant parseIdentifier(size_t start, bool allowNSSep = false); + + /** + * Function used internally to handle the special "\begin" command. + * + * @return an internal State specifying whether an error occured (return + * values State::REOVERABLE_ERROR or State::IRRECOVERABLE_ERROR) or a + * command was actually started (return value State::COMMAND_START). + */ + State parseBeginCommand(); + + /** + * Function used internally to handle the special "\end" command. + * + * @return an internal State specifying whether an error occured (return + * values State::REOVERABLE_ERROR or State::IRRECOVERABLE_ERROR) or a + * command was actually ended (return value State::COMMAND_END). + */ + State parseEndCommand(); + + /** + * Parses the command arguments. Handles errors if the name of the command + * was given using the hash notation and as a name field. + * + * @param commandArgName is the name argument that was given using the hash + * notation. + * @return a map variant containing the arguments. + */ + Variant parseCommandArguments(Variant commandArgName); + + /** + * Function used internally to parse a command. + * + * @param start is the start byte offset of the command (including the + * backslash) + * @param isAnnotation if true, the command is not returned as command, but + * as annotation start. + * @return true if a command was actuall parsed, false otherwise. + */ + State parseCommand(size_t start, bool isAnnotation); + + /** + * Function used internally to parse a block comment. + */ + void parseBlockComment(); + + /** + * Function used internally to parse a generic comment. + */ + void parseLineComment(); + + /** + * Pushes the parsed command onto the command stack. + */ + void pushCommand(Variant commandName, Variant commandArguments, + bool hasRange); + + /** + * Checks whether there is any data pending to be issued, if yes, resets the + * currently peeked characters and returns true. + * + * @return true if there was any data and DATA should be returned by the + * parse function, false otherwise. + */ + bool checkIssueData(); + + /** + * Returns a reference at the current command at the top of the command + * stack. + * + * @return a reference at the top command in the command stack. + */ + Command &cmd() { return commands.top(); } + + /** + * Returns a reference at the current command at the top of the command + * stack. + * + * @return a reference at the top command in the command stack. + */ + const Command &cmd() const { return commands.top(); } + +public: + /** + * Constructor of the OsmlStreamParserImpl class. Attaches the new + * OsmlStreamParserImpl to the given CharReader and Logger instances. + * + * @param reader is the reader instance from which incomming characters + * should be read. + * @param logger is the logger instance to which errors should be written. + */ + OsmlStreamParserImpl(CharReader &reader, Logger &logger); + + State parse(); + + const TokenizedData &getData() const { return data; } + const Variant &getCommandName() const { return cmd().getName(); } + const Variant &getCommandArguments() const { return cmd().getArguments(); } + const SourceLocation &getLocation() const { return location; } + bool inRangeCommand() const { return cmd().inRangeField(); }; + bool inDefaultField() const { return cmd().inDefaultField(); } +}; + +/* Class OsmlStreamParserImpl */ + +OsmlStreamParserImpl::OsmlStreamParserImpl(CharReader &reader, Logger &logger) + : reader(reader), logger(logger), tokenizer(OsmlTokens) { - // Place an intial command representing the complete file on the stack - commands.push(Command{"", Variant::mapType{}, true, true, true, false}); + commands.emplace("", Variant::mapType{}, true); } -Variant OsmlStreamParser::parseIdentifier(size_t start, bool allowNSSep) +Variant OsmlStreamParserImpl::parseIdentifier(size_t start, bool allowNSSep) { bool first = true; bool hasCharSinceNSSep = false; @@ -147,20 +466,20 @@ Variant OsmlStreamParser::parseIdentifier(size_t start, bool allowNSSep) return res; } -OsmlStreamParser::State OsmlStreamParser::parseBeginCommand() +OsmlStreamParserImpl::State OsmlStreamParserImpl::parseBeginCommand() { // Expect a '{' after the command reader.consumeWhitespace(); if (!reader.expect('{')) { logger.error("Expected \"{\" after \\begin", reader); - return State::NONE; + return State::RECOVERABLE_ERROR; } // Parse the name of the command that should be opened Variant commandName = parseIdentifier(reader.getOffset(), true); if (commandName.asString().empty()) { logger.error("Expected identifier", commandName); - return State::ERROR; + return State::IRRECOVERABLE_ERROR; } // Check whether the next character is a '#', indicating the start of the @@ -176,7 +495,7 @@ OsmlStreamParser::State OsmlStreamParser::parseBeginCommand() if (!reader.expect('}')) { logger.error("Expected \"}\"", reader); - return State::ERROR; + return State::IRRECOVERABLE_ERROR; } // Parse the arguments @@ -185,28 +504,15 @@ OsmlStreamParser::State OsmlStreamParser::parseBeginCommand() // Push the command onto the command stack pushCommand(std::move(commandName), std::move(commandArguments), true); - return State::COMMAND; -} - -static bool checkStillInField(const OsmlStreamParser::Command &cmd, - const Variant &endName, Logger &logger) -{ - if (cmd.inField && !cmd.inRangeField) { - logger.error(std::string("\\end in open field of command \"") + - cmd.name.asString() + std::string("\""), - endName); - logger.note(std::string("Open command started here:"), cmd.name); - return true; - } - return false; + return State::COMMAND_START; } -OsmlStreamParser::State OsmlStreamParser::parseEndCommand() +OsmlStreamParserImpl::State OsmlStreamParserImpl::parseEndCommand() { // Expect a '{' after the command if (!reader.expect('{')) { logger.error("Expected \"{\" after \\end", reader); - return State::NONE; + return State::RECOVERABLE_ERROR; } // Fetch the name of the command that should be ended here @@ -215,56 +521,58 @@ OsmlStreamParser::State OsmlStreamParser::parseEndCommand() // Make sure the given command name is not empty if (name.asString().empty()) { logger.error("Expected identifier", name); - return State::ERROR; + return State::IRRECOVERABLE_ERROR; } // Make sure the command name is terminated with a '}' if (!reader.expect('}')) { logger.error("Expected \"}\"", reader); - return State::ERROR; + return State::IRRECOVERABLE_ERROR; } - // Unroll the command stack up to the last range command - while (!commands.top().hasRange) { - if (checkStillInField(commands.top(), name, logger)) { - return State::ERROR; + // Unroll the command stack up to the last range command, make sure we do + // not intersect with any open field + while (!cmd().inRangeField()) { + if (cmd().inField()) { + logger.error(std::string("\\end in open field of command \"") + + cmd().getName().asString() + std::string("\""), + name); + const std::vector &fields = cmd().getFields(); + for (const Field &field : fields) { + logger.note(std::string("Still open field started here: "), + field.location); + } + return State::IRRECOVERABLE_ERROR; } commands.pop(); } - // Make sure we're not in an open field of this command - if (checkStillInField(commands.top(), name, logger)) { - return State::ERROR; - } - // Special error message if the top-level command is reached if (commands.size() == 1) { logger.error(std::string("Cannot end command \"") + name.asString() + std::string("\" here, no command open"), name); - return State::ERROR; + return State::IRRECOVERABLE_ERROR; } - // Inform the about command mismatches - const Command &cmd = commands.top(); - if (commands.top().name.asString() != name.asString()) { - logger.error(std::string("Trying to end command \"") + - cmd.name.asString() + + // Inform the user about command mismatches, copy the current command + // descriptor before popping it from the stack + if (getCommandName().asString() != name.asString()) { + logger.error(std::string("Trying to end command \"") + name.asString() + std::string("\", but open command is \"") + - name.asString() + std::string("\""), + getCommandName().asString() + std::string("\""), name); - logger.note("Last command was opened here:", cmd.name); - return State::ERROR; + logger.note("Open command started here:", getCommandName()); + return State::IRRECOVERABLE_ERROR; } - // Set the location to the location of the command that was ended, then end - // the current command + // End the current command location = name.getLocation(); commands.pop(); - return cmd.inRangeField ? State::FIELD_END : State::NONE; + return State::COMMAND_END; } -Variant OsmlStreamParser::parseCommandArguments(Variant commandArgName) +Variant OsmlStreamParserImpl::parseCommandArguments(Variant commandArgName) { // Parse the arguments using the universal VariantReader Variant commandArguments; @@ -290,29 +598,14 @@ Variant OsmlStreamParser::parseCommandArguments(Variant commandArgName) return commandArguments; } -void OsmlStreamParser::pushCommand(Variant commandName, - Variant commandArguments, bool hasRange) -{ - // Store the location on the stack - location = commandName.getLocation(); - - // Place the command on the command stack, remove the last commands if we're - // not currently inside a field of these commands - while (!commands.top().inField) { - commands.pop(); - } - commands.push(Command{std::move(commandName), std::move(commandArguments), - hasRange, false, false, false}); -} - -OsmlStreamParser::State OsmlStreamParser::parseCommand(size_t start, - bool isAnnotation) +OsmlStreamParserImpl::State OsmlStreamParserImpl::parseCommand( + size_t start, bool isAnnotation) { // Parse the commandName as a first identifier Variant commandName = parseIdentifier(start, true); if (commandName.asString().empty()) { logger.error("Empty command name", reader); - return State::NONE; + return State::RECOVERABLE_ERROR; } // Handle the special "begin" and "end" commands @@ -322,7 +615,7 @@ OsmlStreamParser::State OsmlStreamParser::parseCommand(size_t start, const bool isEnd = commandNameComponents[0] == "end"; // Parse the begin or end command - State res = State::COMMAND; + State res = State::COMMAND_START; if (isBegin || isEnd) { if (commandNameComponents.size() > 1) { logger.error( @@ -378,12 +671,13 @@ OsmlStreamParser::State OsmlStreamParser::parseCommand(size_t start, } else { // Make sure no arguments apart from the "name" argument are given // to an annotation end - Variant::mapType &map = commands.top().arguments.asMap(); + const Variant::mapType &map = getCommandArguments().asMap(); if (!map.empty()) { if (map.count("name") == 0 || map.size() > 1U) { logger.error( "An annotation end command may not have any arguments " - "other than \"name\""); + "other than \"name\"", + reader); return res; } } @@ -397,13 +691,13 @@ OsmlStreamParser::State OsmlStreamParser::parseCommand(size_t start, // If we're starting an annotation, return the command as annotation start // instead of command - if (isAnnotation && res == State::COMMAND) { + if (isAnnotation && res == State::COMMAND_START) { return State::ANNOTATION_START; } return res; } -void OsmlStreamParser::parseBlockComment() +void OsmlStreamParserImpl::parseBlockComment() { Token token; size_t depth = 1; @@ -426,7 +720,7 @@ void OsmlStreamParser::parseBlockComment() logger.error("File ended while being in a block comment", reader); } -void OsmlStreamParser::parseLineComment() +void OsmlStreamParserImpl::parseLineComment() { char c; while (reader.read(c)) { @@ -436,65 +730,34 @@ void OsmlStreamParser::parseLineComment() } } -bool OsmlStreamParser::checkIssueData() -{ - if (!data.empty()) { - location = data.getLocation(); - reader.resetPeek(); - return true; - } - return false; -} - -bool OsmlStreamParser::checkIssueFieldStart() +void OsmlStreamParserImpl::pushCommand(Variant commandName, + Variant commandArguments, bool hasRange) { - // Fetch the current command, and check whether we're currently inside a - // field of this command - Command &cmd = commands.top(); - if (!cmd.inField) { - // If this is a range command, we're now implicitly inside the field of - // this command -- we'll have to issue a field start command! - if (cmd.hasRange) { - cmd.inField = true; - cmd.inRangeField = true; - reader.resetPeek(); - return true; - } + // Store the location of the command + location = commandName.getLocation(); - // This was not a range command, so obviously we're now inside within - // a field of some command -- so unroll the commands stack until a - // command with open field is reached - while (!commands.top().inField) { - commands.pop(); - } + // Place the command on the command stack, remove the last commands if we're + // not currently inside a field of these commands + while (!cmd().inField()) { + commands.pop(); } - return false; + + // Push the new command onto the command stack + commands.emplace(std::move(commandName), std::move(commandArguments), + hasRange); } -bool OsmlStreamParser::closeField() +bool OsmlStreamParserImpl::checkIssueData() { - // Try to end an open field of the current command -- if the current command - // is not inside an open field, end this command and try to close the next - // one - for (int i = 0; i < 2 && commands.size() > 1; i++) { - Command &cmd = commands.top(); - if (!cmd.inRangeField) { - if (cmd.inField) { - cmd.inField = false; - if (cmd.inDefaultField) { - commands.pop(); - } - return true; - } - commands.pop(); - } else { - return false; - } + if (!data.empty()) { + location = data.getLocation(); + reader.resetPeek(); + return true; } return false; } -OsmlStreamParser::State OsmlStreamParser::parse() +OsmlStreamParserImpl::State OsmlStreamParserImpl::parse() { // Reset the data handler data.clear(); @@ -507,14 +770,6 @@ OsmlStreamParser::State OsmlStreamParser::parse() // Special handling for Backslash and Text if (type == OsmlTokens.Backslash || type == OsmlTokens.AnnotationStart) { - // Before appending anything to the output data or starting a new - // command, check whether FIELD_START has to be issued, as the - // current command is a command with range - if (checkIssueFieldStart()) { - location = token.location; - return State::FIELD_START; - } - // Check whether a command starts now, without advancing the peek // cursor char c; @@ -535,11 +790,11 @@ OsmlStreamParser::State OsmlStreamParser::parse() State res = parseCommand(token.location.getStart(), type == OsmlTokens.AnnotationStart); switch (res) { - case State::ERROR: + case State::IRRECOVERABLE_ERROR: throw LoggableException( "Last error was irrecoverable, ending parsing " "process"); - case State::NONE: + case State::RECOVERABLE_ERROR: continue; default: return res; @@ -558,15 +813,12 @@ OsmlStreamParser::State OsmlStreamParser::parse() token.location.getStart() + 1); } - data.append(c, token.location.getStart(), reader.getPeekOffset()); + // Append the character to the output data, mark it as protected + data.append(c, token.location.getStart(), reader.getPeekOffset(), + true); reader.consumePeek(); continue; } else if (type == Tokens::Data) { - // Check whether FIELD_START has to be issued before appending text - if (checkIssueFieldStart()) { - location = token.location; - return State::FIELD_START; - } reader.consumePeek(); continue; } @@ -580,7 +832,7 @@ OsmlStreamParser::State OsmlStreamParser::parse() // We will handle the token now, consume the peeked characters reader.consumePeek(); - // Update the location to the current token location + // Synchronize the location with the current token location location = token.location; if (token.id == OsmlTokens.LineComment) { @@ -588,39 +840,27 @@ OsmlStreamParser::State OsmlStreamParser::parse() } else if (token.id == OsmlTokens.BlockCommentStart) { parseBlockComment(); } else if (token.id == OsmlTokens.FieldStart) { - Command &cmd = commands.top(); - if (!cmd.inField) { - cmd.inField = true; - } + cmd().pushField(false, token.location); return State::FIELD_START; -/* logger.error( - "Got field start token \"{\", but no command for which to " - "start the field. Write \"\\{\" to insert this sequence as " - "text.", - token);*/ } else if (token.id == OsmlTokens.FieldEnd) { - closeField(); - return State::FIELD_END; -/* if (closeField()) { + // Remove all commands from the list that currently are not in any + // field + while (!cmd().inField()) { + commands.pop(); + } + + // If the remaining command is not in a range field, remove this + // command + if (cmd().inNonRangeField()) { + cmd().popField(); return State::FIELD_END; } logger.error( - "Got field end token \"}\", but there is no field to end. " - "Write \"\\}\" to insert this sequence as text.", - token);*/ + "Got field end token \"}\", but there is no field to end.", + token); } else if (token.id == OsmlTokens.DefaultFieldStart) { - // Try to start a default field the first time the token is reached - Command &topCmd = commands.top(); - if (!topCmd.inField) { - topCmd.inField = true; - topCmd.inDefaultField = true; - } + cmd().pushField(true, token.location); return State::FIELD_START; -/* logger.error( - "Got default field start token \"{!\", but no command for " - "which to start the field. Write \"\\{!\" to insert this " - "sequence as text", - token);*/ } else if (token.id == OsmlTokens.AnnotationEnd) { // We got a single annotation end token "\>" -- simply issue the // ANNOTATION_END event @@ -641,11 +881,25 @@ OsmlStreamParser::State OsmlStreamParser::parse() // Make sure all open commands and fields have been ended at the end of the // stream while (commands.size() > 1) { - Command &cmd = commands.top(); - if (cmd.inField || cmd.hasRange) { - logger.error("Reached end of stream, but command \"" + - cmd.name.asString() + "\" has not been ended", - cmd.name); + if (cmd().inField()) { + // If the stream ended with an open range field, issue information + // about the range field + if (cmd().inRangeField()) { + // Inform about the still open command itself + logger.error("Reached end of stream, but command \"" + + getCommandName().asString() + + "\" has not been ended", + getCommandName()); + } else { + // Issue information about still open fields + const std::vector &fields = cmd().getFields(); + if (!fields.empty()) { + logger.error( + std::string( + "Reached end of stream, but field is still open."), + fields.back().location); + } + } } commands.pop(); } @@ -654,26 +908,45 @@ OsmlStreamParser::State OsmlStreamParser::parse() return State::END; } -Variant OsmlStreamParser::getText(WhitespaceMode mode) +/* Class OsmlStreamParser */ + +OsmlStreamParser::OsmlStreamParser(CharReader &reader, Logger &logger) + : impl(new OsmlStreamParserImpl(reader, logger)) +{ +} + +OsmlStreamParser::~OsmlStreamParser() +{ + // Stub needed because OsmlStreamParserImpl is incomplete in header +} + +OsmlStreamParser::State OsmlStreamParser::parse() +{ + return static_cast(impl->parse()); +} + +const TokenizedData &OsmlStreamParser::getData() const { - TokenizedData dataFork = data; - Variant text = dataFork.text(mode); - location = text.getLocation(); - return text; + return impl->getData(); } const Variant &OsmlStreamParser::getCommandName() const { - return commands.top().name; + return impl->getCommandName(); } const Variant &OsmlStreamParser::getCommandArguments() const { - return commands.top().arguments; + return impl->getCommandArguments(); } -bool OsmlStreamParser::inDefaultField() const +const SourceLocation &OsmlStreamParser::getLocation() const { - return commands.top().inRangeField || commands.top().inDefaultField; + return impl->getLocation(); } + +bool OsmlStreamParser::inDefaultField() const { return impl->inDefaultField(); } + +bool OsmlStreamParser::inRangeCommand() const { return impl->inRangeCommand(); } + } diff --git a/src/formats/osml/OsmlStreamParser.hpp b/src/formats/osml/OsmlStreamParser.hpp index 453a2bb..1fee90b 100644 --- a/src/formats/osml/OsmlStreamParser.hpp +++ b/src/formats/osml/OsmlStreamParser.hpp @@ -29,30 +29,29 @@ #ifndef _OUSIA_OSML_STREAM_PARSER_HPP_ #define _OUSIA_OSML_STREAM_PARSER_HPP_ +#include #include -#include -#include -#include -#include - namespace ousia { // Forward declarations class CharReader; class Logger; class OsmlStreamParserImpl; +class TokenizedData; +class Variant; /** * The OsmlStreamParser class provides a low-level reader for the TeX-esque osml * format. The parser is constructed around a "parse" function, which reads data * from the underlying CharReader until a new state is reached and indicates * this state in a return value. The calling code then has to pull corresponding - * data from the stream reader. The reader makes sure the incommind file is + * data from the stream reader. The reader makes sure the incomming stream is * syntactically valid and tries to recorver from most errors. If an error is * irrecoverable (this is the case for errors with wrong nesting of commands or * fields, as this would lead to too many consecutive errors) a - * LoggableException is thrown. + * LoggableException is thrown. The OsmlStreamParser can be compared to a SAX + * parser for XML. */ class OsmlStreamParser { public: @@ -60,39 +59,21 @@ public: * Enum used to indicate which state the OsmlStreamParser class is in * after calling the "parse" function. */ - enum class State { - /** - * State returned if a fully featured command has been read. A command - * consists of the command name and its arguments (which optionally - * includes the name). - */ - COMMAND, - - /** - * State returned if data is given. The reader must decide which field - * or command this should be routed to. Trailing or leading whitespace - * has been removed. Only called if the data is non-empty. - */ - DATA, - + enum class State : uint8_t { /** - * A user-defined entity has been found. The entity sequence is stored - * in the command name. + * State returned if the start of a command has been read. Use the + * getCommandName(), getCommandArguments() and inRangeCommand() + * functions the retrieve more information about the command that was + * just started. */ - ENTITY, + COMMAND_START = 0, /** - * State returned if an annotation was started. An annotation consists - * of the command name and its arguments (which optionally include the - * name). + * State returned if a range command has just ended. This state is not + * returned for non-range commands (as the actual end of a command is + * context dependant). */ - ANNOTATION_START, - - /** - * State returned if an annotation ends. The reader indicates which - * annotation ends. - */ - ANNOTATION_END, + COMMAND_END = 1, /** * State returned if a new field started. The reader assures that the @@ -100,200 +81,47 @@ public: * is not started if data has been given outside of a field. The * field number is set to the current field index. */ - FIELD_START, + FIELD_START = 2, /** * State returned if the current field ends. The reader assures that a * field was actually open. */ - FIELD_END, + FIELD_END = 3, /** - * The end of the stream has been reached. + * State returned if an annotation was started. An annotation consists + * of the command name and its arguments (which optionally include the + * name). */ - END, + ANNOTATION_START = 4, /** - * Returned from internal functions if nothing should be done. + * State returned if an annotation ends. The reader indicates which + * annotation ends. */ - NONE, + ANNOTATION_END = 5, /** - * Returned from internal function to indicate irrecoverable errors. + * State returned if data is given. The reader must decide which field + * or command this should be routed to. Trailing or leading whitespace + * has been removed. Only called if the data is non-empty. */ - ERROR - }; - - /** - * Entry used for the command stack. - */ - struct Command { - /** - * Name and location of the current command. - */ - Variant name; - - /** - * Arguments that were passed to the command. - */ - Variant arguments; - - /** - * Vector used as stack for holding the number of opening/closing braces - * and the corresponding "isDefaultField" flag. - */ - std::vector fields; - - /** - * Set to true if this is a command with clear begin and end. - */ - bool hasRange; - - /** - * Default constructor. - */ - Command() - : hasRange(false), - inField(false), - inDefaultField() - { - } + DATA = 6, /** - * Constructor of the Command class. - * - * @param name is a string variant with name and location of the - * command. - * @param arguments is a map variant with the arguments given to the - * command. - * @param hasRange should be set to true if this is a command with - * explicit range. - * @param inDefaultField is set to true if we currently are in a - * specially marked default field. - */ - Command(Variant name, Variant arguments, bool hasRange) - : name(std::move(name)), - arguments(std::move(arguments)), - hasRange(hasRange), - inField(inField), - inRangeField(inRangeField), - inDefaultField(inDefaultField) - { - } + * The end of the stream has been reached. + */ + END = 7 }; private: /** - * Reference to the CharReader instance from which the incomming bytes are - * read. - */ - CharReader &reader; - - /** - * Reference at the logger instance to which all error messages are sent. - */ - Logger &logger; - - /** - * Tokenizer instance used to read individual tokens from the text. - */ - Tokenizer tokenizer; - - /** - * Variant containing the tokenized data that was returned from the - * tokenizer as data. - */ - TokenizedData data; - - /** - * Stack containing the current commands. - */ - std::stack commands; - - /** - * Pointer at + * Pointer at the class containing the internal implementation (according + * to the PIMPL idiom). */ std::unique_ptr impl; - /** - * Function used internall to parse an identifier. - * - * @param start is the start byte offset of the identifier (including the - * backslash). - * @param allowNSSep should be set to true if the namespace separator is - * allowed in the identifier name. Issues error if the namespace separator - * is placed incorrectly. - */ - Variant parseIdentifier(size_t start, bool allowNSSep = false); - - /** - * Function used internally to handle the special "\begin" command. - */ - State parseBeginCommand(); - - /** - * Function used internally to handle the special "\end" command. - */ - State parseEndCommand(); - - /** - * Pushes the parsed command onto the command stack. - */ - void pushCommand(Variant commandName, Variant commandArguments, - bool hasRange); - - /** - * Parses the command arguments. - */ - Variant parseCommandArguments(Variant commandArgName); - - /** - * Function used internally to parse a command. - * - * @param start is the start byte offset of the command (including the - * backslash) - * @param isAnnotation if true, the command is not returned as command, but - * as annotation start. - * @return true if a command was actuall parsed, false otherwise. - */ - State parseCommand(size_t start, bool isAnnotation); - - /** - * Function used internally to parse a block comment. - */ - void parseBlockComment(); - - /** - * Function used internally to parse a generic comment. - */ - void parseLineComment(); - - /** - * Checks whether there is any data pending to be issued, if yes, issues it. - * - * @return true if there was any data and DATA should be returned by the - * parse function, false otherwise. - */ - bool checkIssueData(); - - /** - * Called before any data is appended to the internal data handler. Checks - * whether a new field should be started or implicitly ended. - * - * @return true if FIELD_START should be returned by the parse function. - */ - bool checkIssueFieldStart(); - - /** - * Closes a currently open field. Note that the command will be removed from - * the internal command stack if the field that is being closed is a - * field marked as default field. - * - * @return true if the field could be closed, false if there was no field - * to close. - */ - bool closeField(); - public: /** * Constructor of the OsmlStreamParser class. Attaches the new @@ -321,30 +149,10 @@ public: */ State parse(); - /** - * Returns a reference at the internally stored data. Only valid if - * State::DATA was returned by the "parse" function. - * - * @return a reference at a variant containing the data parsed by the - * "parse" function. - */ - const TokenizedData &getData() const { return data; } - - /** - * Returns the complete content of the internal TokenizedData instance as - * a single string Variant. This method is mainly used in the unit tests for - * this class, it simply calls the text() method of TokenizedData. - * - * @param mode is the WhitespaceMode that should be used for returning the - * text. - * @return a string variant containing the text content of the internal - * TokenizedData instance or a nullptr variant if there is no text. - */ - Variant getText(WhitespaceMode mode = WhitespaceMode::COLLAPSE); - /** * Returns a reference at the internally stored command name. Only valid if - * State::COMMAND was returned by the "parse" function. + * State::COMMAND_START, State::ANNOTATION_START or State::ANNOTATION_END + * was returned by the "parse" function. * * @return a reference at a variant containing name and location of the * parsed command. @@ -353,18 +161,46 @@ public: /** * Returns a reference at the internally stored command name. Only valid if - * State::COMMAND was returned by the "parse" function. + * State::COMMAND_START, State::ANNOTATION_START or State::ANNOTATION_END + * was returned by the "parse" function. * * @return a reference at a variant containing arguments given to the * command. */ const Variant &getCommandArguments() const; + /** + * Returns a reference at the internally stored data. Only valid if + * State::DATA was returned by the "parse" function. + * + * @return a reference at a variant containing the data parsed by the + * "parse" function. + */ + const TokenizedData &getData() const; + + /** + * Returns the location of the current token. + */ + const SourceLocation &getLocation() const; + + /** + * Returns true if the currently started command is a range command, only + * valid if State::COMMAND_START was returned by the "parse" function. + * + * @return true if the command is started is a range command, false + * otherwise. + */ + bool inRangeCommand() const; + /** * Returns true if the current field is the "default" field. This is true if * the parser either is in the outer range of a range command or inside a - * field that has been especially marked as "default" field (using the "|" - * syntax). + * field that has been especially marked as "default" field (using the "{!" + * syntax). Only valid if State::FIELD_START was returned by the "parse" + * function. + * + * @return true if the current field was marked as default field (using the + * "{!" syntax). */ bool inDefaultField() const; }; diff --git a/test/formats/osml/OsmlStreamParserTest.cpp b/test/formats/osml/OsmlStreamParserTest.cpp index 3d01007..8b64e51 100644 --- a/test/formats/osml/OsmlStreamParserTest.cpp +++ b/test/formats/osml/OsmlStreamParserTest.cpp @@ -21,7 +21,9 @@ #include #include +#include #include +#include #include @@ -30,147 +32,196 @@ namespace ousia { static TerminalLogger logger(std::cerr, true); // static ConcreteLogger logger; -static OsmlStreamParser::State skipEmptyData(OsmlStreamParser &reader) +static void assertCommandStart(OsmlStreamParser &parser, + const std::string &name, + bool rangeCommand, + SourceOffset start = InvalidSourceOffset, + SourceOffset end = InvalidSourceOffset) { - OsmlStreamParser::State res = reader.parse(); - if (res == OsmlStreamParser::State::DATA) { - EXPECT_FALSE(reader.getData().hasNonWhitespaceText()); - res = reader.parse(); - } - return res; -} - -static void assertCommand(OsmlStreamParser &reader, const std::string &name, - SourceOffset start = InvalidSourceOffset, - SourceOffset end = InvalidSourceOffset) -{ - ASSERT_EQ(OsmlStreamParser::State::COMMAND, skipEmptyData(reader)); - EXPECT_EQ(name, reader.getCommandName().asString()); + ASSERT_EQ(OsmlStreamParser::State::COMMAND_START, parser.parse()); + EXPECT_EQ(name, parser.getCommandName().asString()); + EXPECT_EQ(rangeCommand, parser.inRangeCommand()); if (start != InvalidSourceOffset) { - EXPECT_EQ(start, reader.getCommandName().getLocation().getStart()); - EXPECT_EQ(start, reader.getLocation().getStart()); + EXPECT_EQ(start, parser.getCommandName().getLocation().getStart()); + EXPECT_EQ(start, parser.getLocation().getStart()); } if (end != InvalidSourceOffset) { - EXPECT_EQ(end, reader.getCommandName().getLocation().getEnd()); - EXPECT_EQ(end, reader.getLocation().getEnd()); + EXPECT_EQ(end, parser.getCommandName().getLocation().getEnd()); + EXPECT_EQ(end, parser.getLocation().getEnd()); } } -static void assertCommand(OsmlStreamParser &reader, const std::string &name, - const Variant::mapType &args, - SourceOffset start = InvalidSourceOffset, - SourceOffset end = InvalidSourceOffset) +static void assertCommandStart(OsmlStreamParser &parser, + const std::string &name, + bool rangeCommand, + const Variant::mapType &args, + SourceOffset start = InvalidSourceOffset, + SourceOffset end = InvalidSourceOffset) { - assertCommand(reader, name, start, end); - EXPECT_EQ(args, reader.getCommandArguments()); + assertCommandStart(parser, name, rangeCommand, start, end); + EXPECT_EQ(args, parser.getCommandArguments()); } -static void assertData(OsmlStreamParser &reader, const std::string &data, - SourceOffset start = InvalidSourceOffset, - SourceOffset end = InvalidSourceOffset, - WhitespaceMode mode = WhitespaceMode::COLLAPSE) +static void assertCommand(OsmlStreamParser &parser, + const std::string &name, + SourceOffset start = InvalidSourceOffset, + SourceOffset end = InvalidSourceOffset) { - ASSERT_EQ(OsmlStreamParser::State::DATA, reader.parse()); - Variant text = reader.getText(mode); - ASSERT_TRUE(text.isString()); - EXPECT_EQ(data, text.asString()); + assertCommandStart(parser, name, false, Variant::mapType{}, start, end); +} + +static void assertCommandEnd(OsmlStreamParser &parser, + SourceOffset start = InvalidSourceOffset, + SourceOffset end = InvalidSourceOffset) +{ + ASSERT_EQ(OsmlStreamParser::State::COMMAND_END, parser.parse()); if (start != InvalidSourceOffset) { - EXPECT_EQ(start, text.getLocation().getStart()); - EXPECT_EQ(start, reader.getLocation().getStart()); + EXPECT_EQ(start, parser.getLocation().getStart()); } if (end != InvalidSourceOffset) { - EXPECT_EQ(end, text.getLocation().getEnd()); - EXPECT_EQ(end, reader.getLocation().getEnd()); + EXPECT_EQ(end, parser.getLocation().getEnd()); + } +} + +static void assertTextData(OsmlStreamParser &parser, const std::string &text, + SourceOffset dataStart = InvalidSourceOffset, + SourceOffset dataEnd = InvalidSourceOffset, + SourceOffset textStart = InvalidSourceOffset, + SourceOffset textEnd = InvalidSourceOffset, + WhitespaceMode mode = WhitespaceMode::COLLAPSE) +{ + ASSERT_EQ(OsmlStreamParser::State::DATA, parser.parse()); + + const TokenizedData &data = parser.getData(); + TokenizedDataReader dataReader = data.reader(); + + Token token; + ASSERT_TRUE(dataReader.read(token, TokenSet{}, mode)); + EXPECT_EQ(Tokens::Data, token.id); + EXPECT_EQ(text, token.content); + if (dataStart != InvalidSourceOffset) { + EXPECT_EQ(dataStart, data.getLocation().getStart()); + EXPECT_EQ(dataStart, parser.getLocation().getStart()); + } + if (dataEnd != InvalidSourceOffset) { + EXPECT_EQ(dataEnd, data.getLocation().getEnd()); + EXPECT_EQ(dataEnd, parser.getLocation().getEnd()); + } + if (textStart != InvalidSourceOffset) { + EXPECT_EQ(textStart, token.getLocation().getStart()); } + if (textEnd != InvalidSourceOffset) { + EXPECT_EQ(textEnd, token.getLocation().getEnd()); + } +} + +static void assertData(OsmlStreamParser &parser, const std::string &text, + SourceOffset textStart = InvalidSourceOffset, + SourceOffset textEnd = InvalidSourceOffset, + WhitespaceMode mode = WhitespaceMode::COLLAPSE) +{ + assertTextData(parser, text, InvalidSourceOffset, InvalidSourceOffset, textStart, textEnd, mode); +} + +static void assertEmptyData(OsmlStreamParser &parser) +{ + ASSERT_EQ(OsmlStreamParser::State::DATA, parser.parse()); + + const TokenizedData &data = parser.getData(); + TokenizedDataReader dataReader = data.reader(); + + Token token; + EXPECT_FALSE(dataReader.read(token, TokenSet{}, WhitespaceMode::TRIM)); } -static void assertFieldStart(OsmlStreamParser &reader, bool defaultField, + +static void assertFieldStart(OsmlStreamParser &parser, bool defaultField, SourceOffset start = InvalidSourceOffset, SourceOffset end = InvalidSourceOffset) { - ASSERT_EQ(OsmlStreamParser::State::FIELD_START, skipEmptyData(reader)); - EXPECT_EQ(defaultField, reader.inDefaultField()); + ASSERT_EQ(OsmlStreamParser::State::FIELD_START, parser.parse()); + EXPECT_EQ(defaultField, parser.inDefaultField()); if (start != InvalidSourceOffset) { - EXPECT_EQ(start, reader.getLocation().getStart()); + EXPECT_EQ(start, parser.getLocation().getStart()); } if (end != InvalidSourceOffset) { - EXPECT_EQ(end, reader.getLocation().getEnd()); + EXPECT_EQ(end, parser.getLocation().getEnd()); } } -static void assertFieldEnd(OsmlStreamParser &reader, +static void assertFieldEnd(OsmlStreamParser &parser, SourceOffset start = InvalidSourceOffset, SourceOffset end = InvalidSourceOffset) { - ASSERT_EQ(OsmlStreamParser::State::FIELD_END, skipEmptyData(reader)); + ASSERT_EQ(OsmlStreamParser::State::FIELD_END, parser.parse()); if (start != InvalidSourceOffset) { - EXPECT_EQ(start, reader.getLocation().getStart()); + EXPECT_EQ(start, parser.getLocation().getStart()); } if (end != InvalidSourceOffset) { - EXPECT_EQ(end, reader.getLocation().getEnd()); + EXPECT_EQ(end, parser.getLocation().getEnd()); } } -static void assertAnnotationStart(OsmlStreamParser &reader, +static void assertAnnotationStart(OsmlStreamParser &parser, const std::string &name, SourceOffset start = InvalidSourceOffset, SourceOffset end = InvalidSourceOffset) { - ASSERT_EQ(OsmlStreamParser::State::ANNOTATION_START, skipEmptyData(reader)); - EXPECT_EQ(name, reader.getCommandName().asString()); + ASSERT_EQ(OsmlStreamParser::State::ANNOTATION_START, parser.parse()); + EXPECT_EQ(name, parser.getCommandName().asString()); if (start != InvalidSourceOffset) { - EXPECT_EQ(start, reader.getCommandName().getLocation().getStart()); - EXPECT_EQ(start, reader.getLocation().getStart()); + EXPECT_EQ(start, parser.getCommandName().getLocation().getStart()); + EXPECT_EQ(start, parser.getLocation().getStart()); } if (end != InvalidSourceOffset) { - EXPECT_EQ(end, reader.getCommandName().getLocation().getEnd()); - EXPECT_EQ(end, reader.getLocation().getEnd()); + EXPECT_EQ(end, parser.getCommandName().getLocation().getEnd()); + EXPECT_EQ(end, parser.getLocation().getEnd()); } } -static void assertAnnotationStart(OsmlStreamParser &reader, +static void assertAnnotationStart(OsmlStreamParser &parser, const std::string &name, const Variant::mapType &args, SourceOffset start = InvalidSourceOffset, SourceOffset end = InvalidSourceOffset) { - assertAnnotationStart(reader, name, start, end); - EXPECT_EQ(args, reader.getCommandArguments()); + assertAnnotationStart(parser, name, start, end); + EXPECT_EQ(args, parser.getCommandArguments()); } -static void assertAnnotationEnd(OsmlStreamParser &reader, +static void assertAnnotationEnd(OsmlStreamParser &parser, const std::string &name, const std::string &elementName, SourceOffset start = InvalidSourceOffset, SourceOffset end = InvalidSourceOffset) { - ASSERT_EQ(OsmlStreamParser::State::ANNOTATION_END, skipEmptyData(reader)); - ASSERT_EQ(name, reader.getCommandName().asString()); + ASSERT_EQ(OsmlStreamParser::State::ANNOTATION_END, parser.parse()); + ASSERT_EQ(name, parser.getCommandName().asString()); if (!elementName.empty()) { - ASSERT_EQ(1U, reader.getCommandArguments().asMap().size()); - ASSERT_EQ(1U, reader.getCommandArguments().asMap().count("name")); + ASSERT_EQ(1U, parser.getCommandArguments().asMap().size()); + ASSERT_EQ(1U, parser.getCommandArguments().asMap().count("name")); - auto it = reader.getCommandArguments().asMap().find("name"); + auto it = parser.getCommandArguments().asMap().find("name"); ASSERT_EQ(elementName, it->second.asString()); } if (start != InvalidSourceOffset) { - EXPECT_EQ(start, reader.getLocation().getStart()); + EXPECT_EQ(start, parser.getLocation().getStart()); } if (end != InvalidSourceOffset) { - EXPECT_EQ(end, reader.getLocation().getEnd()); + EXPECT_EQ(end, parser.getLocation().getEnd()); } } -static void assertEnd(OsmlStreamParser &reader, +static void assertEnd(OsmlStreamParser &parser, SourceOffset start = InvalidSourceOffset, SourceOffset end = InvalidSourceOffset) { - ASSERT_EQ(OsmlStreamParser::State::END, skipEmptyData(reader)); + ASSERT_EQ(OsmlStreamParser::State::END, parser.parse()); if (start != InvalidSourceOffset) { - EXPECT_EQ(start, reader.getLocation().getStart()); + EXPECT_EQ(start, parser.getLocation().getStart()); } if (end != InvalidSourceOffset) { - EXPECT_EQ(end, reader.getLocation().getEnd()); + EXPECT_EQ(end, parser.getLocation().getEnd()); } } @@ -179,9 +230,9 @@ TEST(OsmlStreamParser, empty) const char *testString = ""; CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - ASSERT_EQ(OsmlStreamParser::State::END, reader.parse()); + assertEnd(parser, 0, 0); } TEST(OsmlStreamParser, oneCharacter) @@ -189,45 +240,102 @@ TEST(OsmlStreamParser, oneCharacter) const char *testString = "a"; CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); + + assertTextData(parser, "a", 0, 1, 0, 1, WhitespaceMode::COLLAPSE); + assertEnd(parser, 1, 1); +} + +TEST(OsmlStreamParser, whitespacePreserve) +{ + const char *testString = " hello \t world "; + // 0123456 78901234 + // 0 1 + CharReader charReader(testString); + + OsmlStreamParser parser(charReader, logger); + + assertTextData(parser, " hello \t world ", 0, 15, 0, 15, + WhitespaceMode::PRESERVE); + assertEnd(parser, 15, 15); +} + +TEST(OsmlStreamParser, whitespaceTrim) +{ + const char *testString = " hello \t world "; + // 0123456 78901234 + // 0 1 + CharReader charReader(testString); + + OsmlStreamParser parser(charReader, logger); - assertData(reader, "a", 0, 1); + assertTextData(parser, "hello \t world", 0, 15, 1, 14, + WhitespaceMode::TRIM); + assertEnd(parser, 15, 15); } -TEST(OsmlStreamParser, whitespaceElimination) +TEST(OsmlStreamParser, whitespaceCollapse) { const char *testString = " hello \t world "; // 0123456 78901234 // 0 1 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - assertData(reader, "hello world", 1, 14); + assertTextData(parser, "hello world", 0, 15, 1, 14, + WhitespaceMode::COLLAPSE); + assertEnd(parser, 15, 15); } -TEST(OsmlStreamParser, whitespaceEliminationWithLinebreak) +TEST(OsmlStreamParser, whitespaceCollapseLinebreak) { const char *testString = " hello \n world "; // 0123456 78901234 // 0 1 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); + + assertTextData(parser, "hello world", 0, 15, 1, 14, + WhitespaceMode::COLLAPSE); + assertEnd(parser, 15, 15); +} + +TEST(OsmlStreamParser, whitespaceCollapseProtected) +{ + const char *testString = " hello\\ \\ world "; + // 012345 67 89012345 + // 0 1 + CharReader charReader(testString); + + OsmlStreamParser parser(charReader, logger); + + assertTextData(parser, "hello world", 0, 16, 1, 15, + WhitespaceMode::COLLAPSE); + assertEnd(parser, 16, 16); +} + +TEST(OsmlStreamParser, whitespaceCollapseProtected2) +{ + const char *testString = " hello \\ \\ world "; + // 012345 67 89012345 + // 0 1 + CharReader charReader(testString); + + OsmlStreamParser parser(charReader, logger); - assertData(reader, "hello world", 1, 14); + assertTextData(parser, "hello world", 0, 17, 1, 16, + WhitespaceMode::COLLAPSE); + assertEnd(parser, 17, 17); } static void testEscapeSpecialCharacter(const std::string &c) { CharReader charReader(std::string("\\") + c); - OsmlStreamParser reader(charReader, logger); - EXPECT_EQ(OsmlStreamParser::State::DATA, reader.parse()); - EXPECT_EQ(c, reader.getText().asString()); - - SourceLocation loc = reader.getText().getLocation(); - EXPECT_EQ(0U, loc.getStart()); - EXPECT_EQ(1U + c.size(), loc.getEnd()); + OsmlStreamParser parser(charReader, logger); + assertTextData(parser, c, 0, 2, 0, 2, WhitespaceMode::PRESERVE); + assertEnd(parser, 2, 2); } TEST(OsmlStreamParser, escapeSpecialCharacters) @@ -240,9 +348,11 @@ TEST(OsmlStreamParser, escapeSpecialCharacters) TEST(OsmlStreamParser, simpleSingleLineComment) { const char *testString = "% This is a single line comment"; + // 0123456789012345678901234567890 + // 0 1 2 3 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - ASSERT_EQ(OsmlStreamParser::State::END, reader.parse()); + OsmlStreamParser parser(charReader, logger); + assertEnd(parser, 31, 31); } TEST(OsmlStreamParser, singleLineComment) @@ -251,24 +361,11 @@ TEST(OsmlStreamParser, singleLineComment) // 01234567890123456789012345678901 23 // 0 1 2 3 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - { - ASSERT_EQ(OsmlStreamParser::State::DATA, reader.parse()); - ASSERT_EQ("a", reader.getText().asString()); - SourceLocation loc = reader.getText().getLocation(); - ASSERT_EQ(0U, loc.getStart()); - ASSERT_EQ(1U, loc.getEnd()); - } - - { - ASSERT_EQ(OsmlStreamParser::State::DATA, reader.parse()); - ASSERT_EQ("b", reader.getText().asString()); - SourceLocation loc = reader.getText().getLocation(); - ASSERT_EQ(33U, loc.getStart()); - ASSERT_EQ(34U, loc.getEnd()); - } + OsmlStreamParser parser(charReader, logger); - ASSERT_EQ(OsmlStreamParser::State::END, reader.parse()); + assertTextData(parser, "a", 0, 1, 0, 1, WhitespaceMode::PRESERVE); + assertTextData(parser, "b", 33, 34, 33, 34, WhitespaceMode::PRESERVE); + assertEnd(parser, 34, 34); } TEST(OsmlStreamParser, multilineComment) @@ -277,24 +374,27 @@ TEST(OsmlStreamParser, multilineComment) // 0123456789012 3 456789012345678901234567890 // 0 1 2 3 4 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - { - ASSERT_EQ(OsmlStreamParser::State::DATA, reader.parse()); - ASSERT_EQ("a", reader.getText().asString()); - SourceLocation loc = reader.getText().getLocation(); - ASSERT_EQ(0U, loc.getStart()); - ASSERT_EQ(1U, loc.getEnd()); - } + OsmlStreamParser parser(charReader, logger); - { - ASSERT_EQ(OsmlStreamParser::State::DATA, reader.parse()); - ASSERT_EQ("b", reader.getText().asString()); - SourceLocation loc = reader.getText().getLocation(); - ASSERT_EQ(40U, loc.getStart()); - ASSERT_EQ(41U, loc.getEnd()); - } + assertTextData(parser, "a", 0, 1, 0, 1, WhitespaceMode::PRESERVE); + assertTextData(parser, "b", 40, 41, 40, 41, WhitespaceMode::PRESERVE); + assertEnd(parser, 41, 41); +} - ASSERT_EQ(OsmlStreamParser::State::END, reader.parse()); +TEST(OsmlStreamParser, unfinishedMultilineComment) +{ + const char *testString = "a%{ This is a\n\n multiline line comment"; + // 0123456789012 3 456789012345678901234567 + // 0 1 2 3 + CharReader charReader(testString); + OsmlStreamParser parser(charReader, logger); + + logger.reset(); + + assertTextData(parser, "a", 0, 1, 0, 1, WhitespaceMode::PRESERVE); + ASSERT_FALSE(logger.hasError()); + assertEnd(parser, 38, 38); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, nestedMultilineComment) @@ -303,24 +403,11 @@ TEST(OsmlStreamParser, nestedMultilineComment) // 0123456789012 3 456789012345678901234567890 // 0 1 2 3 4 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - { - ASSERT_EQ(OsmlStreamParser::State::DATA, reader.parse()); - ASSERT_EQ("a", reader.getText().asString()); - SourceLocation loc = reader.getText().getLocation(); - ASSERT_EQ(0U, loc.getStart()); - ASSERT_EQ(1U, loc.getEnd()); - } - - { - ASSERT_EQ(OsmlStreamParser::State::DATA, reader.parse()); - ASSERT_EQ("b", reader.getText().asString()); - SourceLocation loc = reader.getText().getLocation(); - ASSERT_EQ(40U, loc.getStart()); - ASSERT_EQ(41U, loc.getEnd()); - } + OsmlStreamParser parser(charReader, logger); - ASSERT_EQ(OsmlStreamParser::State::END, reader.parse()); + assertTextData(parser, "a", 0, 1, 0, 1, WhitespaceMode::PRESERVE); + assertTextData(parser, "b", 40, 41, 40, 41, WhitespaceMode::PRESERVE); + assertEnd(parser, 41, 41); } TEST(OsmlStreamParser, simpleCommand) @@ -328,45 +415,27 @@ TEST(OsmlStreamParser, simpleCommand) const char *testString = "\\test"; // 0 12345 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - ASSERT_EQ(OsmlStreamParser::State::COMMAND, reader.parse()); + OsmlStreamParser parser(charReader, logger); - Variant commandName = reader.getCommandName(); - ASSERT_EQ("test", commandName.asString()); - - SourceLocation loc = commandName.getLocation(); - ASSERT_EQ(0U, loc.getStart()); - ASSERT_EQ(5U, loc.getEnd()); - - ASSERT_EQ(0U, reader.getCommandArguments().asMap().size()); - ASSERT_EQ(OsmlStreamParser::State::END, reader.parse()); + assertCommand(parser, "test", 0, 5); + assertEnd(parser); } TEST(OsmlStreamParser, simpleCommandWithName) { - const char *testString = "\\test#bla"; - // 0 12345678 + const char *testString = "\\test#foo"; + // 012345678 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - ASSERT_EQ(OsmlStreamParser::State::COMMAND, reader.parse()); - - Variant commandName = reader.getCommandName(); - ASSERT_EQ("test", commandName.asString()); - SourceLocation loc = commandName.getLocation(); - ASSERT_EQ(0U, loc.getStart()); - ASSERT_EQ(5U, loc.getEnd()); + OsmlStreamParser parser(charReader, logger); - Variant commandArguments = reader.getCommandArguments(); - ASSERT_TRUE(commandArguments.isMap()); - ASSERT_EQ(1U, commandArguments.asMap().size()); - ASSERT_EQ(1U, commandArguments.asMap().count("name")); - ASSERT_EQ("bla", commandArguments.asMap()["name"].asString()); + assertCommandStart(parser, "test", false, Variant::mapType{{"name", "foo"}}, + 0, 5); - loc = commandArguments.asMap()["name"].getLocation(); - ASSERT_EQ(5U, loc.getStart()); - ASSERT_EQ(9U, loc.getEnd()); + Variant::mapType args = parser.getCommandArguments().asMap(); + ASSERT_EQ(5U, args["name"].getLocation().getStart()); + ASSERT_EQ(9U, args["name"].getLocation().getEnd()); - ASSERT_EQ(OsmlStreamParser::State::END, reader.parse()); + assertEnd(parser); } TEST(OsmlStreamParser, simpleCommandWithArguments) @@ -375,38 +444,21 @@ TEST(OsmlStreamParser, simpleCommandWithArguments) // 0 123456789012345 678901 2 // 0 1 2 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - ASSERT_EQ(OsmlStreamParser::State::COMMAND, reader.parse()); + OsmlStreamParser parser(charReader, logger); - Variant commandName = reader.getCommandName(); - ASSERT_EQ("test", commandName.asString()); - SourceLocation loc = commandName.getLocation(); - ASSERT_EQ(0U, loc.getStart()); - ASSERT_EQ(5U, loc.getEnd()); + assertCommandStart(parser, "test", false, + Variant::mapType{{"a", 1}, {"b", 2}, {"c", "test"}}, 0, + 5); - Variant commandArguments = reader.getCommandArguments(); - ASSERT_TRUE(commandArguments.isMap()); - ASSERT_EQ(3U, commandArguments.asMap().size()); - ASSERT_EQ(1U, commandArguments.asMap().count("a")); - ASSERT_EQ(1U, commandArguments.asMap().count("b")); - ASSERT_EQ(1U, commandArguments.asMap().count("c")); - ASSERT_EQ(1, commandArguments.asMap()["a"].asInt()); - ASSERT_EQ(2, commandArguments.asMap()["b"].asInt()); - ASSERT_EQ("test", commandArguments.asMap()["c"].asString()); + Variant::mapType args = parser.getCommandArguments().asMap(); + ASSERT_EQ(8U, args["a"].getLocation().getStart()); + ASSERT_EQ(9U, args["a"].getLocation().getEnd()); + ASSERT_EQ(12U, args["b"].getLocation().getStart()); + ASSERT_EQ(13U, args["b"].getLocation().getEnd()); + ASSERT_EQ(16U, args["c"].getLocation().getStart()); + ASSERT_EQ(22U, args["c"].getLocation().getEnd()); - loc = commandArguments.asMap()["a"].getLocation(); - ASSERT_EQ(8U, loc.getStart()); - ASSERT_EQ(9U, loc.getEnd()); - - loc = commandArguments.asMap()["b"].getLocation(); - ASSERT_EQ(12U, loc.getStart()); - ASSERT_EQ(13U, loc.getEnd()); - - loc = commandArguments.asMap()["c"].getLocation(); - ASSERT_EQ(16U, loc.getStart()); - ASSERT_EQ(22U, loc.getEnd()); - - ASSERT_EQ(OsmlStreamParser::State::END, reader.parse()); + assertEnd(parser); } TEST(OsmlStreamParser, simpleCommandWithArgumentsAndName) @@ -415,44 +467,24 @@ TEST(OsmlStreamParser, simpleCommandWithArgumentsAndName) // 0 1234567890123456789 01234 56 // 0 1 2 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - ASSERT_EQ(OsmlStreamParser::State::COMMAND, reader.parse()); - - Variant commandName = reader.getCommandName(); - ASSERT_EQ("test", commandName.asString()); - SourceLocation loc = commandName.getLocation(); - ASSERT_EQ(0U, loc.getStart()); - ASSERT_EQ(5U, loc.getEnd()); - - Variant commandArguments = reader.getCommandArguments(); - ASSERT_TRUE(commandArguments.isMap()); - ASSERT_EQ(4U, commandArguments.asMap().size()); - ASSERT_EQ(1U, commandArguments.asMap().count("a")); - ASSERT_EQ(1U, commandArguments.asMap().count("b")); - ASSERT_EQ(1U, commandArguments.asMap().count("c")); - ASSERT_EQ(1U, commandArguments.asMap().count("name")); - ASSERT_EQ(1, commandArguments.asMap()["a"].asInt()); - ASSERT_EQ(2, commandArguments.asMap()["b"].asInt()); - ASSERT_EQ("test", commandArguments.asMap()["c"].asString()); - ASSERT_EQ("bla", commandArguments.asMap()["name"].asString()); - - loc = commandArguments.asMap()["a"].getLocation(); - ASSERT_EQ(12U, loc.getStart()); - ASSERT_EQ(13U, loc.getEnd()); - - loc = commandArguments.asMap()["b"].getLocation(); - ASSERT_EQ(16U, loc.getStart()); - ASSERT_EQ(17U, loc.getEnd()); - - loc = commandArguments.asMap()["c"].getLocation(); - ASSERT_EQ(20U, loc.getStart()); - ASSERT_EQ(26U, loc.getEnd()); - - loc = commandArguments.asMap()["name"].getLocation(); - ASSERT_EQ(5U, loc.getStart()); - ASSERT_EQ(9U, loc.getEnd()); - - ASSERT_EQ(OsmlStreamParser::State::END, reader.parse()); + OsmlStreamParser parser(charReader, logger); + + assertCommandStart( + parser, "test", false, + Variant::mapType{{"name", "bla"}, {"a", 1}, {"b", 2}, {"c", "test"}}, 0, + 5); + + Variant::mapType args = parser.getCommandArguments().asMap(); + ASSERT_EQ(5U, args["name"].getLocation().getStart()); + ASSERT_EQ(9U, args["name"].getLocation().getEnd()); + ASSERT_EQ(12U, args["a"].getLocation().getStart()); + ASSERT_EQ(13U, args["a"].getLocation().getEnd()); + ASSERT_EQ(16U, args["b"].getLocation().getStart()); + ASSERT_EQ(17U, args["b"].getLocation().getEnd()); + ASSERT_EQ(20U, args["c"].getLocation().getStart()); + ASSERT_EQ(26U, args["c"].getLocation().getEnd()); + + assertEnd(parser); } TEST(OsmlStreamParser, fields) @@ -461,21 +493,21 @@ TEST(OsmlStreamParser, fields) // 01234567890123 // 0 1 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - assertCommand(reader, "test", 0, 5); - assertFieldStart(reader, false, 5, 6); - assertData(reader, "a", 6, 7); - assertFieldEnd(reader, 7, 8); + assertCommand(parser, "test", 0, 5); + assertFieldStart(parser, false, 5, 6); + assertTextData(parser, "a", 6, 7, 6, 7, WhitespaceMode::PRESERVE); + assertFieldEnd(parser, 7, 8); - assertFieldStart(reader, false, 8, 9); - assertData(reader, "b", 9, 10); - assertFieldEnd(reader, 10, 11); + assertFieldStart(parser, false, 8, 9); + assertTextData(parser, "b", 9, 10, 9, 10, WhitespaceMode::PRESERVE); + assertFieldEnd(parser, 10, 11); - assertFieldStart(reader, false, 11, 12); - assertData(reader, "c", 12, 13); - assertFieldEnd(reader, 13, 14); - assertEnd(reader, 14, 14); + assertFieldStart(parser, false, 11, 12); + assertTextData(parser, "c", 12, 13, 12, 13, WhitespaceMode::PRESERVE); + assertFieldEnd(parser, 13, 14); + assertEnd(parser, 14, 14); } TEST(OsmlStreamParser, dataOutsideField) @@ -484,785 +516,781 @@ TEST(OsmlStreamParser, dataOutsideField) // 0123456789012 // 0 1 CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - assertCommand(reader, "test", 0, 5); - assertFieldStart(reader, false, 5, 6); - assertData(reader, "a", 6, 7); - assertFieldEnd(reader, 7, 8); + assertCommand(parser, "test", 0, 5); + assertFieldStart(parser, false, 5, 6); + assertTextData(parser, "a", 6, 7, 6, 7, WhitespaceMode::COLLAPSE); + assertFieldEnd(parser, 7, 8); - assertFieldStart(reader, false, 8, 9); - assertData(reader, "b", 9, 10); - assertFieldEnd(reader, 10, 11); + assertFieldStart(parser, false, 8, 9); + assertTextData(parser, "b", 9, 10, 9, 10, WhitespaceMode::COLLAPSE); + assertFieldEnd(parser, 10, 11); - assertData(reader, "c", 12, 13); - assertEnd(reader, 13, 13); + assertTextData(parser, "c", 11, 13, 12, 13, WhitespaceMode::COLLAPSE); + assertEnd(parser, 13, 13); } TEST(OsmlStreamParser, nestedCommand) { - const char *testString = "\\test{a}{\\test2{b} c} d"; - // 012345678 90123456789012 - // 0 1 2 - CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + const char *testString = "\\test{a}{\\test2{b} c} d"; + // 012345678 90123456789012 + // 0 1 2 + CharReader charReader(testString); + OsmlStreamParser parser(charReader, logger); - assertCommand(reader, "test", 0, 5); + assertCommand(parser, "test", 0, 5); + assertFieldStart(parser, false, 5, 6); + assertData(parser, "a", 6, 7); + assertFieldEnd(parser, 7, 8); - assertFieldStart(reader, false, 5, 6); - assertData(reader, "a", 6, 7); - assertFieldEnd(reader, 7, 8); - - assertFieldStart(reader, false, 8, 9); - { - assertCommand(reader, "test2", 9, 15); - assertFieldStart(reader, false, 15, 16); - assertData(reader, "b", 16, 17); - assertFieldEnd(reader, 17, 18); - } - assertData(reader, "c", 19, 20); - assertFieldEnd(reader, 20, 21); - assertData(reader, "d", 22, 23); - assertEnd(reader, 23, 23); + assertFieldStart(parser, false, 8, 9); + assertCommand(parser, "test2", 9, 15); + assertFieldStart(parser, false, 15, 16); + assertData(parser, "b", 16, 17); + assertFieldEnd(parser, 17, 18); + assertData(parser, "c", 19, 20); + assertFieldEnd(parser, 20, 21); + assertData(parser, "d", 22, 23); + assertEnd(parser, 23, 23); } + TEST(OsmlStreamParser, nestedCommandImmediateEnd) { - const char *testString = "\\test{\\test2{b}} d"; - // 012345 678901234567 - // 0 1 - CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - - assertCommand(reader, "test", 0, 5); - assertFieldStart(reader, false, 5, 6); - { - assertCommand(reader, "test2", 6, 12); - assertFieldStart(reader, false, 12, 13); - assertData(reader, "b", 13, 14); - assertFieldEnd(reader, 14, 15); - } - assertFieldEnd(reader, 15, 16); - assertData(reader, "d", 17, 18); - assertEnd(reader, 18, 18); + const char *testString = "\\test{\\test2{b}} d"; + // 012345 678901234567 + // 0 1 + CharReader charReader(testString); + OsmlStreamParser parser(charReader, logger); + + assertCommand(parser, "test", 0, 5); + assertFieldStart(parser, false, 5, 6); + { + assertCommand(parser, "test2", 6, 12); + assertFieldStart(parser, false, 12, 13); + assertData(parser, "b", 13, 14); + assertFieldEnd(parser, 14, 15); + } + assertFieldEnd(parser, 15, 16); + assertData(parser, "d", 17, 18); + assertEnd(parser, 18, 18); } TEST(OsmlStreamParser, nestedCommandNoData) { - const char *testString = "\\test{\\test2}"; - // 012345 6789012 - CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + const char *testString = "\\test{\\test2}"; + // 012345 6789012 + CharReader charReader(testString); + OsmlStreamParser parser(charReader, logger); - assertCommand(reader, "test", 0, 5); - assertFieldStart(reader, false, 5, 6); - assertCommand(reader, "test2", 6, 12); - assertFieldEnd(reader, 12, 13); - assertEnd(reader, 13, 13); + assertCommand(parser, "test", 0, 5); + assertFieldStart(parser, false, 5, 6); + assertCommand(parser, "test2", 6, 12); + assertFieldEnd(parser, 12, 13); + assertEnd(parser, 13, 13); } TEST(OsmlStreamParser, multipleCommands) { - const char *testString = "\\a \\b \\c \\d"; - // 012 345 678 90 - // 0 1 - CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + const char *testString = "\\a \\b \\c \\d"; + // 012 345 678 90 + // 0 1 + CharReader charReader(testString); + OsmlStreamParser parser(charReader, logger); - assertCommand(reader, "a", 0, 2); - assertData(reader, " ", 2, 3, WhitespaceMode::PRESERVE); - assertCommand(reader, "b", 3, 5); - assertData(reader, " ", 5, 6, WhitespaceMode::PRESERVE); - assertCommand(reader, "c", 6, 8); - assertData(reader, " ", 8, 9, WhitespaceMode::PRESERVE); - assertCommand(reader, "d", 9, 11); - assertEnd(reader, 11, 11); + assertCommand(parser, "a", 0, 2); + assertEmptyData(parser); + assertCommand(parser, "b", 3, 5); + assertEmptyData(parser); + assertCommand(parser, "c", 6, 8); + assertEmptyData(parser); + assertCommand(parser, "d", 9, 11); + assertEnd(parser, 11, 11); } TEST(OsmlStreamParser, fieldsWithSpaces) { - const char *testString = "\\a {\\b \\c} \n\n {\\d}"; - // 0123 456 789012 3 456 789 - // 0 1 - CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - - assertCommand(reader, "a", 0, 2); - assertData(reader, " ", 2, 3, WhitespaceMode::PRESERVE); - assertFieldStart(reader, false, 3, 4); - assertCommand(reader, "b", 4, 6); - assertData(reader, " ", 6, 7, WhitespaceMode::PRESERVE); - assertCommand(reader, "c", 7, 9); - assertFieldEnd(reader, 9, 10); - assertData(reader, " \n\n {", 10, 12, WhitespaceMode::PRESERVE); - assertFieldStart(reader, false, 16, 17); - assertCommand(reader, "d", 17, 19); - assertFieldEnd(reader, 19, 20); - assertEnd(reader, 20, 20); -} - -TEST(OsmlStreamParser, errorNoFieldToStart) -{ - const char *testString = "\\a b {"; - // 012345 - // 0 - CharReader charReader(testString); + const char *testString = "\\a {\\b \\c} \n\n {\\d}"; + // 0123 456 789012 3 456 789 + // 0 1 + CharReader charReader(testString); + OsmlStreamParser parser(charReader, logger); - OsmlStreamParser reader(charReader, logger); + assertCommand(parser, "a", 0, 2); + assertEmptyData(parser); + assertFieldStart(parser, false, 3, 4); + assertCommand(parser, "b", 4, 6); + assertEmptyData(parser); + assertCommand(parser, "c", 7, 9); + assertFieldEnd(parser, 9, 10); + assertEmptyData(parser); + assertFieldStart(parser, false, 16, 17); + assertCommand(parser, "d", 17, 19); + assertFieldEnd(parser, 19, 20); + assertEnd(parser, 20, 20); +} - logger.reset(); - assertCommand(reader, "a", 0, 2); - assertData(reader, "b", 3, 4); - ASSERT_FALSE(logger.hasError()); - assertEnd(reader, 6, 6); - ASSERT_TRUE(logger.hasError()); +TEST(OsmlStreamParser, errorEndButOpenField) +{ + const char *testString = "\\a b {"; + // 012345 + // 0 + CharReader charReader(testString); + + OsmlStreamParser parser(charReader, logger); + + logger.reset(); + assertCommand(parser, "a", 0, 2); + assertData(parser, "b", 3, 4); + assertFieldStart(parser, false, 5, 6); + ASSERT_FALSE(logger.hasError()); + assertEnd(parser, 6, 6); + ASSERT_TRUE(logger.hasError()); } + TEST(OsmlStreamParser, errorNoFieldToEnd) { - const char *testString = "\\a b }"; - // 012345 - // 0 - CharReader charReader(testString); + const char *testString = "\\a b }"; + // 012345 + // 0 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - logger.reset(); - assertCommand(reader, "a", 0, 2); - assertData(reader, "b", 3, 4); - ASSERT_FALSE(logger.hasError()); - assertEnd(reader, 6, 6); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + assertCommand(parser, "a", 0, 2); + assertData(parser, "b", 3, 4); + ASSERT_FALSE(logger.hasError()); + assertEnd(parser, 6, 6); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorNoFieldEndNested) { - const char *testString = "\\test{\\test2{}}}"; - // 012345 6789012345 - // 0 1 - CharReader charReader(testString); + const char *testString = "\\test{\\test2{}}}"; + // 012345 6789012345 + // 0 1 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - logger.reset(); - assertCommand(reader, "test", 0, 5); - assertFieldStart(reader, false, 5, 6); - assertCommand(reader, "test2", 6, 12); - assertFieldStart(reader, false, 12, 13); - assertFieldEnd(reader, 13, 14); - assertFieldEnd(reader, 14, 15); - ASSERT_FALSE(logger.hasError()); - assertEnd(reader, 16, 16); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + assertCommand(parser, "test", 0, 5); + assertFieldStart(parser, false, 5, 6); + assertCommand(parser, "test2", 6, 12); + assertFieldStart(parser, false, 12, 13); + assertFieldEnd(parser, 13, 14); + assertFieldEnd(parser, 14, 15); + ASSERT_FALSE(logger.hasError()); + assertEnd(parser, 16, 16); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorNoFieldEndNestedData) { - const char *testString = "\\test{\\test2{}}a}"; - // 012345 67890123456 - // 0 1 - CharReader charReader(testString); + const char *testString = "\\test{\\test2{}}a}"; + // 012345 67890123456 + // 0 1 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - logger.reset(); - assertCommand(reader, "test", 0, 5); - assertFieldStart(reader, false, 5, 6); - assertCommand(reader, "test2", 6, 12); - assertFieldStart(reader, false, 12, 13); - assertFieldEnd(reader, 13, 14); - assertFieldEnd(reader, 14, 15); - assertData(reader, "a", 15, 16); - ASSERT_FALSE(logger.hasError()); - assertEnd(reader, 17, 17); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + assertCommand(parser, "test", 0, 5); + assertFieldStart(parser, false, 5, 6); + assertCommand(parser, "test2", 6, 12); + assertFieldStart(parser, false, 12, 13); + assertFieldEnd(parser, 13, 14); + assertFieldEnd(parser, 14, 15); + assertData(parser, "a", 15, 16); + ASSERT_FALSE(logger.hasError()); + assertEnd(parser, 17, 17); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, beginEnd) { - const char *testString = "\\begin{book}\\end{book}"; - // 012345678901 2345678901 - // 0 1 2 - CharReader charReader(testString); + const char *testString = "\\begin{book}\\end{book}"; + // 012345678901 2345678901 + // 0 1 2 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - assertCommand(reader, "book", 7, 11); - assertFieldStart(reader, true, 12, 13); - assertFieldEnd(reader, 17, 21); - assertEnd(reader, 22, 22); + assertCommandStart(parser, "book", true, Variant::mapType{}, 7, 11); + assertCommandEnd(parser, 17, 21); + assertEnd(parser, 22, 22); } TEST(OsmlStreamParser, beginEndWithName) { - const char *testString = "\\begin{book#a}\\end{book}"; - // 01234567890123 4567890123 - // 0 1 2 - CharReader charReader(testString); + const char *testString = "\\begin{book#a}\\end{book}"; + // 01234567890123 4567890123 + // 0 1 2 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - assertCommand(reader, "book", {{"name", "a"}}, 7, 11); - assertFieldStart(reader, true, 14, 15); - assertFieldEnd(reader, 19, 23); - assertEnd(reader, 24, 24); + assertCommandStart(parser, "book", true, {{"name", "a"}}, 7, 11); + assertCommandEnd(parser, 19, 23); + assertEnd(parser, 24, 24); } TEST(OsmlStreamParser, beginEndWithNameAndArgs) { - const char *testString = "\\begin{book#a}[a=1,b=2,c=\"test\"]\\end{book}"; - // 0123456789012345678901234 56789 01 2345678901 - // 0 1 2 3 4 - CharReader charReader(testString); + const char *testString = "\\begin{book#a}[a=1,b=2,c=\"test\"]\\end{book}"; + // 0123456789012345678901234 56789 01 2345678901 + // 0 1 2 3 4 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - assertCommand(reader, "book", - {{"name", "a"}, {"a", 1}, {"b", 2}, {"c", "test"}}, 7, 11); - assertFieldStart(reader, true, 32, 33); - assertFieldEnd(reader, 37, 41); - assertEnd(reader, 42, 42); + assertCommandStart(parser, "book", true, + {{"name", "a"}, {"a", 1}, {"b", 2}, {"c", "test"}}, 7, 11); + assertCommandEnd(parser, 37, 41); + assertEnd(parser, 42, 42); } TEST(OsmlStreamParser, beginEndWithNameAndArgsMultipleFields) { - const char *testString = - "\\begin{book#a}[a=1,b=2,c=\"test\"]{a \\test}{b \\test{}}\\end{book}"; - // 0123456789012345678901234 56789 01234 567890123 45678901 2345678901 - // 0 1 2 3 4 5 6 - CharReader charReader(testString); - - OsmlStreamParser reader(charReader, logger); - - assertCommand(reader, "book", - {{"name", "a"}, {"a", 1}, {"b", 2}, {"c", "test"}}, 7, 11); - assertFieldStart(reader, false, 32, 33); - assertData(reader, "a", 33, 34); - assertCommand(reader, "test", Variant::mapType{}, 35, 40); - assertFieldEnd(reader, 40, 41); - assertFieldStart(reader, false, 41, 42); - assertData(reader, "b", 42, 43); - assertCommand(reader, "test", Variant::mapType{}, 44, 49); - assertFieldStart(reader, false, 49, 50); - assertFieldEnd(reader, 50, 51); - assertFieldEnd(reader, 51, 52); - assertFieldStart(reader, true, 52, 53); - assertFieldEnd(reader, 57, 61); - assertEnd(reader, 62, 62); + const char *testString = + "\\begin{book#a}[a=1,b=2,c=\"test\"]{a \\test}{b \\test{}}\\end{book}"; + // 0123456789012345678901234 56789 01234 567890123 45678901 2345678901 + // 0 1 2 3 4 5 6 + CharReader charReader(testString); + + OsmlStreamParser parser(charReader, logger); + + assertCommandStart(parser, "book", true, + {{"name", "a"}, {"a", 1}, {"b", 2}, {"c", "test"}}, 7, 11); + assertFieldStart(parser, false, 32, 33); + assertData(parser, "a", 33, 34); + assertCommand(parser, "test", 35, 40); + assertFieldEnd(parser, 40, 41); + assertFieldStart(parser, false, 41, 42); + assertData(parser, "b", 42, 43); + assertCommand(parser, "test", 44, 49); + assertFieldStart(parser, false, 49, 50); + assertFieldEnd(parser, 50, 51); + assertFieldEnd(parser, 51, 52); + assertCommandEnd(parser, 57, 61); + assertEnd(parser, 62, 62); } TEST(OsmlStreamParser, beginEndWithData) { - const char *testString = "\\begin{book}a\\end{book}"; - // 0123456789012 3456789012 - // 0 1 2 - CharReader charReader(testString); + const char *testString = "\\begin{book}a\\end{book}"; + // 0123456789012 3456789012 + // 0 1 2 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser parser(charReader, logger); - assertCommand(reader, "book", 7, 11); - assertFieldStart(reader, true, 12, 13); - assertData(reader, "a", 12, 13); - assertFieldEnd(reader, 18, 22); - assertEnd(reader, 23, 23); + assertCommandStart(parser, "book", true, Variant::mapType{}, 7, 11); + assertData(parser, "a", 12, 13); + assertCommandEnd(parser, 18, 22); + assertEnd(parser, 23, 23); } - +/* TEST(OsmlStreamParser, beginEndNested) { - const char *testString = - "\\begin{a}{b} c \\begin{d}{e}{f} \\g{h} \\end{d}\\end{a}"; - // 012345678901234 5678901234567890 123456 7890123 4567890 - // 0 1 2 3 4 5 - CharReader charReader(testString); - - OsmlStreamParser reader(charReader, logger); - - assertCommand(reader, "a", 7, 8); - assertFieldStart(reader, false, 9, 10); - assertData(reader, "b", 10, 11); - assertFieldEnd(reader, 11, 12); - assertFieldStart(reader, true, 13, 14); - assertData(reader, "c", 13, 14); - assertCommand(reader, "d", 22, 23); - assertFieldStart(reader, false, 24, 25); - assertData(reader, "e", 25, 26); - assertFieldEnd(reader, 26, 27); - assertFieldStart(reader, false, 27, 28); - assertData(reader, "f", 28, 29); - assertFieldEnd(reader, 29, 30); - assertFieldStart(reader, true, 31, 32); - assertCommand(reader, "g", 31, 33); - assertFieldStart(reader, false, 33, 34); - assertData(reader, "h", 34, 35); - assertFieldEnd(reader, 35, 36); - assertFieldEnd(reader, 42, 43); - assertFieldEnd(reader, 49, 50); - assertEnd(reader, 51, 51); + const char *testString = + "\\begin{a}{b} c \\begin{d}{e}{f} \\g{h} \\end{d}\\end{a}"; + // 012345678901234 5678901234567890 123456 7890123 4567890 + // 0 1 2 3 4 5 + CharReader charReader(testString); + + OsmlStreamParser reader(charReader, logger); + + assertCommand(reader, "a", 7, 8); + assertFieldStart(reader, false, 9, 10); + assertData(reader, "b", 10, 11); + assertFieldEnd(reader, 11, 12); + assertFieldStart(reader, true, 13, 14); + assertData(reader, "c", 13, 14); + assertCommand(reader, "d", 22, 23); + assertFieldStart(reader, false, 24, 25); + assertData(reader, "e", 25, 26); + assertFieldEnd(reader, 26, 27); + assertFieldStart(reader, false, 27, 28); + assertData(reader, "f", 28, 29); + assertFieldEnd(reader, 29, 30); + assertFieldStart(reader, true, 31, 32); + assertCommand(reader, "g", 31, 33); + assertFieldStart(reader, false, 33, 34); + assertData(reader, "h", 34, 35); + assertFieldEnd(reader, 35, 36); + assertFieldEnd(reader, 42, 43); + assertFieldEnd(reader, 49, 50); + assertEnd(reader, 51, 51); } TEST(OsmlStreamParser, beginEndWithCommand) { - const char *testString = "\\begin{book}\\a{test}\\end{book}"; - // 012345678901 23456789 0123456789 - // 0 1 2 - CharReader charReader(testString); + const char *testString = "\\begin{book}\\a{test}\\end{book}"; + // 012345678901 23456789 0123456789 + // 0 1 2 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertCommand(reader, "book", 7, 11); - assertFieldStart(reader, true, 12, 13); - assertCommand(reader, "a", 12, 14); - assertFieldStart(reader, false, 14, 15); - assertData(reader, "test", 15, 19); - assertFieldEnd(reader, 19, 20); - assertFieldEnd(reader, 25, 29); - assertEnd(reader, 30, 30); + assertCommand(reader, "book", 7, 11); + assertFieldStart(reader, true, 12, 13); + assertCommand(reader, "a", 12, 14); + assertFieldStart(reader, false, 14, 15); + assertData(reader, "test", 15, 19); + assertFieldEnd(reader, 19, 20); + assertFieldEnd(reader, 25, 29); + assertEnd(reader, 30, 30); } TEST(OsmlStreamParser, errorBeginNoBraceOpen) { - const char *testString = "\\begin a"; - // 01234567 - CharReader charReader(testString); + const char *testString = "\\begin a"; + // 01234567 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - assertData(reader, "a", 7, 8); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + assertData(reader, "a", 7, 8); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorBeginNoIdentifier) { - const char *testString = "\\begin{!"; - CharReader charReader(testString); + const char *testString = "\\begin{!"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - ASSERT_THROW(reader.parse(), LoggableException); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + ASSERT_THROW(parser.parse(), LoggableException); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorBeginNoBraceClose) { - const char *testString = "\\begin{a"; - CharReader charReader(testString); + const char *testString = "\\begin{a"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - ASSERT_THROW(reader.parse(), LoggableException); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + ASSERT_THROW(parser.parse(), LoggableException); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorBeginNoName) { - const char *testString = "\\begin{a#}"; - CharReader charReader(testString); + const char *testString = "\\begin{a#}"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - assertCommand(reader, "a"); - ASSERT_TRUE(logger.hasError()); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - assertEnd(reader); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + assertCommand(reader, "a"); + ASSERT_TRUE(logger.hasError()); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + assertEnd(reader); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorEndNoBraceOpen) { - const char *testString = "\\end a"; - // 012345 - CharReader charReader(testString); + const char *testString = "\\end a"; + // 012345 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - assertData(reader, "a", 5, 6); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + assertData(reader, "a", 5, 6); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorEndNoIdentifier) { - const char *testString = "\\end{!"; - CharReader charReader(testString); + const char *testString = "\\end{!"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - ASSERT_THROW(reader.parse(), LoggableException); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + ASSERT_THROW(parser.parse(), LoggableException); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorEndNoBraceClose) { - const char *testString = "\\end{a"; - CharReader charReader(testString); + const char *testString = "\\end{a"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - ASSERT_THROW(reader.parse(), LoggableException); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + ASSERT_THROW(parser.parse(), LoggableException); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorEndNoBegin) { - const char *testString = "\\end{a}"; - CharReader charReader(testString); + const char *testString = "\\end{a}"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - ASSERT_THROW(reader.parse(), LoggableException); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + ASSERT_THROW(parser.parse(), LoggableException); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, errorBeginEndMismatch) { - const char *testString = "\\begin{a} \\begin{b} test \\end{a}"; - // 0123456789 012345678901234 5678901 - // 0 1 2 3 - CharReader charReader(testString); + const char *testString = "\\begin{a} \\begin{b} test \\end{a}"; + // 0123456789 012345678901234 5678901 + // 0 1 2 3 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - assertCommand(reader, "a", 7, 8); - assertFieldStart(reader, true, 10, 11); - assertCommand(reader, "b", 17, 18); - assertFieldStart(reader, true, 20, 24); - assertData(reader, "test", 20, 24); - ASSERT_FALSE(logger.hasError()); - ASSERT_THROW(reader.parse(), LoggableException); - ASSERT_TRUE(logger.hasError()); + logger.reset(); + assertCommand(reader, "a", 7, 8); + assertFieldStart(reader, true, 10, 11); + assertCommand(reader, "b", 17, 18); + assertFieldStart(reader, true, 20, 24); + assertData(reader, "test", 20, 24); + ASSERT_FALSE(logger.hasError()); + ASSERT_THROW(parser.parse(), LoggableException); + ASSERT_TRUE(logger.hasError()); } TEST(OsmlStreamParser, commandWithNSSep) { - const char *testString = "\\test1:test2"; - // 012345678901 - CharReader charReader(testString); + const char *testString = "\\test1:test2"; + // 012345678901 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertCommand(reader, "test1:test2", 0, 12); - assertEnd(reader, 12, 12); + assertCommand(reader, "test1:test2", 0, 12); + assertEnd(reader, 12, 12); } TEST(OsmlStreamParser, beginEndWithNSSep) { - const char *testString = "\\begin{test1:test2}\\end{test1:test2}"; - // 0123456789012345678 90123456789012345 - // 0 1 2 3 - CharReader charReader(testString); + const char *testString = "\\begin{test1:test2}\\end{test1:test2}"; + // 0123456789012345678 90123456789012345 + // 0 1 2 3 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertCommand(reader, "test1:test2", 7, 18); - assertFieldStart(reader, true, 19, 20); - assertFieldEnd(reader, 24, 35); - assertEnd(reader, 36, 36); + assertCommand(reader, "test1:test2", 7, 18); + assertFieldStart(reader, true, 19, 20); + assertFieldEnd(reader, 24, 35); + assertEnd(reader, 36, 36); } TEST(OsmlStreamParser, errorBeginNSSep) { - const char *testString = "\\begin:test{blub}\\end{blub}"; - CharReader charReader(testString); + const char *testString = "\\begin:test{blub}\\end{blub}"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - assertCommand(reader, "blub"); - ASSERT_TRUE(logger.hasError()); - assertFieldStart(reader, true); - assertFieldEnd(reader); - assertEnd(reader); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + assertCommand(reader, "blub"); + ASSERT_TRUE(logger.hasError()); + assertFieldStart(reader, true); + assertFieldEnd(reader); + assertEnd(reader); } TEST(OsmlStreamParser, errorEndNSSep) { - const char *testString = "\\begin{blub}\\end:test{blub}"; - CharReader charReader(testString); + const char *testString = "\\begin{blub}\\end:test{blub}"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - assertCommand(reader, "blub"); - assertFieldStart(reader, true); - ASSERT_FALSE(logger.hasError()); - assertFieldEnd(reader); - ASSERT_TRUE(logger.hasError()); - assertEnd(reader); + logger.reset(); + assertCommand(reader, "blub"); + assertFieldStart(reader, true); + ASSERT_FALSE(logger.hasError()); + assertFieldEnd(reader); + ASSERT_TRUE(logger.hasError()); + assertEnd(reader); } TEST(OsmlStreamParser, errorEmptyNs) { - const char *testString = "\\test:"; - CharReader charReader(testString); + const char *testString = "\\test:"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - assertCommand(reader, "test"); - ASSERT_TRUE(logger.hasError()); - assertData(reader, ":"); - assertEnd(reader); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + assertCommand(reader, "test"); + ASSERT_TRUE(logger.hasError()); + assertData(reader, ":"); + assertEnd(reader); } TEST(OsmlStreamParser, errorRepeatedNs) { - const char *testString = "\\test::"; - CharReader charReader(testString); + const char *testString = "\\test::"; + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - assertCommand(reader, "test"); - ASSERT_TRUE(logger.hasError()); - assertData(reader, "::"); - assertEnd(reader); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + assertCommand(reader, "test"); + ASSERT_TRUE(logger.hasError()); + assertData(reader, "::"); + assertEnd(reader); } TEST(OsmlStreamParser, explicitDefaultField) { - const char *testString = "\\a{!b}c"; - // 01234567 - CharReader charReader(testString); + const char *testString = "\\a{!b}c"; + // 01234567 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertCommand(reader, "a", 0, 2); - assertFieldStart(reader, true, 2, 4); - assertData(reader, "b", 4, 5); - assertFieldEnd(reader, 5, 6); - assertData(reader, "c", 6, 7); - assertEnd(reader, 7, 7); + assertCommand(reader, "a", 0, 2); + assertFieldStart(reader, true, 2, 4); + assertData(reader, "b", 4, 5); + assertFieldEnd(reader, 5, 6); + assertData(reader, "c", 6, 7); + assertEnd(reader, 7, 7); } TEST(OsmlStreamParser, explicitDefaultFieldWithCommand) { - const char *testString = "\\a{!\\b}c"; - // 0123 4567 - CharReader charReader(testString); + const char *testString = "\\a{!\\b}c"; + // 0123 4567 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertCommand(reader, "a", 0, 2); - assertFieldStart(reader, true, 2, 4); - assertCommand(reader, "b", 4, 6); - assertFieldEnd(reader, 6, 7); - assertData(reader, "c", 7, 8); - assertEnd(reader, 8, 8); + assertCommand(reader, "a", 0, 2); + assertFieldStart(reader, true, 2, 4); + assertCommand(reader, "b", 4, 6); + assertFieldEnd(reader, 6, 7); + assertData(reader, "c", 7, 8); + assertEnd(reader, 8, 8); } TEST(OsmlStreamParser, errorFieldAfterExplicitDefaultField) { - const char *testString = "\\a{!\\b}{c}"; - // 0123 456789 - CharReader charReader(testString); + const char *testString = "\\a{!\\b}{c}"; + // 0123 456789 + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - assertCommand(reader, "a", 0, 2); - assertFieldStart(reader, true, 2, 4); - assertCommand(reader, "b", 4, 6); - assertFieldEnd(reader, 6, 7); - ASSERT_FALSE(logger.hasError()); - assertData(reader, "c", 8, 9); - ASSERT_TRUE(logger.hasError()); - assertEnd(reader, 10, 10); + logger.reset(); + assertCommand(reader, "a", 0, 2); + assertFieldStart(reader, true, 2, 4); + assertCommand(reader, "b", 4, 6); + assertFieldEnd(reader, 6, 7); + ASSERT_FALSE(logger.hasError()); + assertData(reader, "c", 8, 9); + ASSERT_TRUE(logger.hasError()); + assertEnd(reader, 10, 10); } TEST(OsmlStreamParser, annotationStart) { - const char *testString = "<\\a"; - // 0 12 + const char *testString = "<\\a"; + // 0 12 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertAnnotationStart(reader, "a", Variant::mapType{}, 0, 3); - assertEnd(reader, 3, 3); + assertAnnotationStart(reader, "a", Variant::mapType{}, 0, 3); + assertEnd(reader, 3, 3); } TEST(OsmlStreamParser, annotationStartWithName) { - const char *testString = "<\\annotationWithName#aName"; - // 0 1234567890123456789012345 - // 0 1 2 + const char *testString = "<\\annotationWithName#aName"; + // 0 1234567890123456789012345 + // 0 1 2 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertAnnotationStart(reader, "annotationWithName", - Variant::mapType{{"name", "aName"}}, 0, 20); - assertEnd(reader, 26, 26); + assertAnnotationStart(reader, "annotationWithName", + Variant::mapType{{"name", "aName"}}, 0, 20); + assertEnd(reader, 26, 26); } TEST(OsmlStreamParser, annotationStartWithArguments) { - const char *testString = "<\\annotationWithName#aName[a=1,b=2]"; - // 0 1234567890123456789012345678901234 - // 0 1 2 3 + const char *testString = "<\\annotationWithName#aName[a=1,b=2]"; + // 0 1234567890123456789012345678901234 + // 0 1 2 3 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertAnnotationStart( - reader, "annotationWithName", - Variant::mapType{{"name", "aName"}, {"a", 1}, {"b", 2}}, 0, 20); - assertEnd(reader, 35, 35); + assertAnnotationStart( + reader, "annotationWithName", + Variant::mapType{{"name", "aName"}, {"a", 1}, {"b", 2}}, 0, 20); + assertEnd(reader, 35, 35); } TEST(OsmlStreamParser, simpleAnnotationStartBeginEnd) { - const char *testString = "<\\begin{ab#name}[a=1,b=2] a \\end{ab}\\>"; - // 0 123456789012345678901234567 89012345 67 - // 0 1 2 3 + const char *testString = "<\\begin{ab#name}[a=1,b=2] a \\end{ab}\\>"; + // 0 123456789012345678901234567 89012345 67 + // 0 1 2 3 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertAnnotationStart( - reader, "ab", Variant::mapType{{"name", "name"}, {"a", 1}, {"b", 2}}, 8, - 10); - assertFieldStart(reader, true, 26, 27); - assertData(reader, "a", 26, 27); - assertFieldEnd(reader, 33, 35); - assertAnnotationEnd(reader, "", "", 36, 38); - assertEnd(reader, 38, 38); + assertAnnotationStart( + reader, "ab", Variant::mapType{{"name", "name"}, {"a", 1}, {"b", 2}}, 8, + 10); + assertFieldStart(reader, true, 26, 27); + assertData(reader, "a", 26, 27); + assertFieldEnd(reader, 33, 35); + assertAnnotationEnd(reader, "", "", 36, 38); + assertEnd(reader, 38, 38); } TEST(OsmlStreamParser, annotationEnd) { - const char *testString = "\\a>"; - // 012 + const char *testString = "\\a>"; + // 012 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertAnnotationEnd(reader, "a", "", 0, 2); - assertEnd(reader, 3, 3); + assertAnnotationEnd(reader, "a", "", 0, 2); + assertEnd(reader, 3, 3); } TEST(OsmlStreamParser, annotationEndWithName) { - const char *testString = "\\a#name>"; - // 01234567 + const char *testString = "\\a#name>"; + // 01234567 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertAnnotationEnd(reader, "a", "name", 0, 2); - assertEnd(reader, 8, 8); + assertAnnotationEnd(reader, "a", "name", 0, 2); + assertEnd(reader, 8, 8); } TEST(OsmlStreamParser, annotationEndWithNameAsArgs) { - const char *testString = "\\a[name=name]>"; - // 01234567890123 + const char *testString = "\\a[name=name]>"; + // 01234567890123 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertAnnotationEnd(reader, "a", "name", 0, 2); - assertEnd(reader, 14, 14); + assertAnnotationEnd(reader, "a", "name", 0, 2); + assertEnd(reader, 14, 14); } TEST(OsmlStreamParser, errorAnnotationEndWithArguments) { - const char *testString = "\\a[foo=bar]>"; - // 012345678901 - // 0 1 + const char *testString = "\\a[foo=bar]>"; + // 012345678901 + // 0 1 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - logger.reset(); - ASSERT_FALSE(logger.hasError()); - assertCommand(reader, "a", Variant::mapType{{"foo", "bar"}}, 0, 2); - ASSERT_TRUE(logger.hasError()); - assertData(reader, ">", 11, 12); - assertEnd(reader, 12, 12); + logger.reset(); + ASSERT_FALSE(logger.hasError()); + assertCommand(reader, "a", Variant::mapType{{"foo", "bar"}}, 0, 2); + ASSERT_TRUE(logger.hasError()); + assertData(reader, ">", 11, 12); + assertEnd(reader, 12, 12); } TEST(OsmlStreamParser, closingAnnotation) { - const char *testString = "<\\a>"; - // 0 123 + const char *testString = "<\\a>"; + // 0 123 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertAnnotationStart(reader, "a", Variant::mapType{}, 0, 3); - assertData(reader, ">", 3, 4); - assertEnd(reader, 4, 4); + assertAnnotationStart(reader, "a", Variant::mapType{}, 0, 3); + assertData(reader, ">", 3, 4); + assertEnd(reader, 4, 4); } TEST(OsmlStreamParser, annotationWithFields) { - const char *testString = "a <\\b{c}{d}{!e} f \\> g"; - // 012 345678901234567 8901 - // 0 1 2 + const char *testString = "a <\\b{c}{d}{!e} f \\> g"; + // 012 345678901234567 8901 + // 0 1 2 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); - - assertData(reader, "a", 0, 1); - assertAnnotationStart(reader, "b", Variant::mapType{}, 2, 5); - assertFieldStart(reader, false, 5, 6); - assertData(reader, "c", 6, 7); - assertFieldEnd(reader, 7, 8); - assertFieldStart(reader, false, 8, 9); - assertData(reader, "d", 9, 10); - assertFieldEnd(reader, 10, 11); - assertFieldStart(reader, true, 11, 13); - assertData(reader, "e", 13, 14); - assertFieldEnd(reader, 14, 15); - assertData(reader, "f", 16, 17); - assertAnnotationEnd(reader, "", "", 18, 20); - assertData(reader, "g", 21, 22); - assertEnd(reader, 22, 22); + OsmlStreamParser reader(charReader, logger); + + assertData(reader, "a", 0, 1); + assertAnnotationStart(reader, "b", Variant::mapType{}, 2, 5); + assertFieldStart(reader, false, 5, 6); + assertData(reader, "c", 6, 7); + assertFieldEnd(reader, 7, 8); + assertFieldStart(reader, false, 8, 9); + assertData(reader, "d", 9, 10); + assertFieldEnd(reader, 10, 11); + assertFieldStart(reader, true, 11, 13); + assertData(reader, "e", 13, 14); + assertFieldEnd(reader, 14, 15); + assertData(reader, "f", 16, 17); + assertAnnotationEnd(reader, "", "", 18, 20); + assertData(reader, "g", 21, 22); + assertEnd(reader, 22, 22); } TEST(OsmlStreamParser, annotationStartEscape) { - const char *testString = "<\\%test"; - // 0 123456 - // 0 + const char *testString = "<\\%test"; + // 0 123456 + // 0 - CharReader charReader(testString); + CharReader charReader(testString); - OsmlStreamParser reader(charReader, logger); + OsmlStreamParser reader(charReader, logger); - assertData(reader, "<%test", 0, 7); - assertEnd(reader, 7, 7); + assertData(reader, "<%test", 0, 7); + assertEnd(reader, 7, 7); } +*/ } -- 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 'CMakeLists.txt') 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 3bdc30e0798d6b356782da430e93b72b4303e963 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 00:32:34 +0100 Subject: Adapted OsxmlParser to new Stack interface, enabled Osxml code in CMakeLists again --- CMakeLists.txt | 22 ++++++++-------- src/formats/osxml/OsxmlEventParser.cpp | 6 ++--- src/formats/osxml/OsxmlEventParser.hpp | 11 +++----- src/formats/osxml/OsxmlParser.cpp | 13 ++++----- test/formats/osxml/OsxmlEventParserTest.cpp | 41 ++++++++++++++++------------- 5 files changed, 48 insertions(+), 45 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e021fd..2a09b54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,7 +225,7 @@ TARGET_LINK_LIBRARIES(ousia_osml ADD_LIBRARY(ousia_osxml src/formats/osxml/OsxmlAttributeLocator src/formats/osxml/OsxmlEventParser -# src/formats/osxml/OsxmlParser + src/formats/osxml/OsxmlParser ) TARGET_LINK_LIBRARIES(ousia_osxml @@ -397,17 +397,17 @@ IF(TEST) ousia_filesystem ) -# ADD_EXECUTABLE(ousia_test_osxml -# test/formats/osxml/OsxmlEventParserTest + ADD_EXECUTABLE(ousia_test_osxml + test/formats/osxml/OsxmlEventParserTest # test/formats/osxml/OsxmlParserTest -# ) + ) -# TARGET_LINK_LIBRARIES(ousia_test_osxml -# ${GTEST_LIBRARIES} -# ousia_core -# ousia_osxml -# ousia_filesystem -# ) + TARGET_LINK_LIBRARIES(ousia_test_osxml + ${GTEST_LIBRARIES} + ousia_core + ousia_osxml + ousia_filesystem + ) ADD_EXECUTABLE(ousia_test_xml test/plugins/xml/XmlOutputTest @@ -426,7 +426,7 @@ IF(TEST) ADD_TEST(ousia_test_html ousia_test_html) # ADD_TEST(ousia_test_mozjs ousia_test_mozjs) ADD_TEST(ousia_test_osml ousia_test_osml) -# ADD_TEST(ousia_test_osxml ousia_test_osxml) + ADD_TEST(ousia_test_osxml ousia_test_osxml) ADD_TEST(ousia_test_xml ousia_test_xml) ENDIF() diff --git a/src/formats/osxml/OsxmlEventParser.cpp b/src/formats/osxml/OsxmlEventParser.cpp index 855f80d..83c16f0 100644 --- a/src/formats/osxml/OsxmlEventParser.cpp +++ b/src/formats/osxml/OsxmlEventParser.cpp @@ -323,7 +323,7 @@ static void xmlStartElementHandler(void *ref, const XML_Char *name, // Just issue a "commandStart" event in any other case Variant nameVar = Variant::fromString(nameStr); nameVar.setLocation(nameLoc); - parser->getEvents().command(nameVar, args); + parser->getEvents().commandStart(nameVar, args); } } @@ -358,8 +358,8 @@ static void xmlEndElementHandler(void *ref, const XML_Char *name) return; } - // Issue the "fieldEnd" event - parser->getEvents().fieldEnd(); + // Issue the "rangeEnd" event + parser->getEvents().rangeEnd(); } static void xmlCharacterDataHandler(void *ref, const XML_Char *s, int len) diff --git a/src/formats/osxml/OsxmlEventParser.hpp b/src/formats/osxml/OsxmlEventParser.hpp index e3fd5d4..7a8c96d 100644 --- a/src/formats/osxml/OsxmlEventParser.hpp +++ b/src/formats/osxml/OsxmlEventParser.hpp @@ -59,7 +59,8 @@ public: * @param args is a map containing the arguments that were given to the * command. */ - virtual void command(const Variant &name, const Variant::mapType &args) = 0; + virtual void commandStart(const Variant &name, + const Variant::mapType &args) = 0; /** * Called whenever an annotation starts. Note that this implicitly always @@ -88,13 +89,9 @@ public: const Variant &elementName) = 0; /** - * Called whenever the default field which was implicitly started by - * commandStart or annotationStart ends. Note that this does not end the - * range of an annotation, but the default field of the annotation. To - * signal the end of the annotation this, the annotationEnd method will be - * invoked. + * Called whenever the command or annotation tags end. */ - virtual void fieldEnd() = 0; + virtual void rangeEnd() = 0; /** * Called whenever string data is found. diff --git a/src/formats/osxml/OsxmlParser.cpp b/src/formats/osxml/OsxmlParser.cpp index c216855..924d11b 100644 --- a/src/formats/osxml/OsxmlParser.cpp +++ b/src/formats/osxml/OsxmlParser.cpp @@ -16,6 +16,8 @@ along with this program. If not, see . */ +#include +#include #include #include #include @@ -63,17 +65,16 @@ public: */ void parse() { parser.parse(); } - void command(const Variant &name, const Variant::mapType &args) override + void commandStart(const Variant &name, + const Variant::mapType &args) override { - stack.command(name, args); - stack.fieldStart(true); + stack.commandStart(name, args, true); } void annotationStart(const Variant &name, const Variant::mapType &args) override { - stack.annotationStart(name, args); - stack.fieldStart(true); + stack.annotationStart(name, args, true); } void annotationEnd(const Variant &className, @@ -82,7 +83,7 @@ public: stack.annotationEnd(className, elementName); } - void fieldEnd() override { stack.fieldEnd(); } + void rangeEnd() override { stack.rangeEnd(); } void data(const Variant &data) override { stack.data(data); } }; diff --git a/test/formats/osxml/OsxmlEventParserTest.cpp b/test/formats/osxml/OsxmlEventParserTest.cpp index 6942166..b24a43d 100644 --- a/test/formats/osxml/OsxmlEventParserTest.cpp +++ b/test/formats/osxml/OsxmlEventParserTest.cpp @@ -32,10 +32,10 @@ static TerminalLogger logger(std::cerr, true); namespace { enum class OsxmlEvent { - COMMAND, + COMMAND_START, ANNOTATION_START, ANNOTATION_END, - FIELD_END, + RANGE_END, DATA }; @@ -43,9 +43,10 @@ class TestOsxmlEventListener : public OsxmlEvents { public: std::vector> events; - void command(const Variant &name, const Variant::mapType &args) override + void commandStart(const Variant &name, + const Variant::mapType &args) override { - events.emplace_back(OsxmlEvent::COMMAND, + events.emplace_back(OsxmlEvent::COMMAND_START, Variant::arrayType{name, args}); } @@ -63,9 +64,9 @@ public: Variant::arrayType{className, elementName}); } - void fieldEnd() override + void rangeEnd() override { - events.emplace_back(OsxmlEvent::FIELD_END, Variant::arrayType{}); + events.emplace_back(OsxmlEvent::RANGE_END, Variant::arrayType{}); } void data(const Variant &data) override @@ -92,11 +93,11 @@ TEST(OsxmlEventParser, simpleCommandWithArgs) // 0 1 2 3 std::vector> expectedEvents{ - {OsxmlEvent::COMMAND, + {OsxmlEvent::COMMAND_START, Variant::arrayType{ "a", Variant::mapType{ {"name", "test"}, {"a", 1}, {"b", 2}, {"c", "blub"}}}}, - {OsxmlEvent::FIELD_END, Variant::arrayType{}}}; + {OsxmlEvent::RANGE_END, Variant::arrayType{}}}; auto events = parseXml(testString); ASSERT_EQ(expectedEvents, events); @@ -132,10 +133,12 @@ TEST(OsxmlEventParser, magicTopLevelTag) const char *testString = ""; std::vector> expectedEvents{ - {OsxmlEvent::COMMAND, Variant::arrayType{{"a", Variant::mapType{}}}}, - {OsxmlEvent::FIELD_END, Variant::arrayType{}}, - {OsxmlEvent::COMMAND, Variant::arrayType{{"b", Variant::mapType{}}}}, - {OsxmlEvent::FIELD_END, Variant::arrayType{}}}; + {OsxmlEvent::COMMAND_START, + Variant::arrayType{{"a", Variant::mapType{}}}}, + {OsxmlEvent::RANGE_END, Variant::arrayType{}}, + {OsxmlEvent::COMMAND_START, + Variant::arrayType{{"b", Variant::mapType{}}}}, + {OsxmlEvent::RANGE_END, Variant::arrayType{}}}; auto events = parseXml(testString); ASSERT_EQ(expectedEvents, events); @@ -146,11 +149,12 @@ TEST(OsxmlEventParser, magicTopLevelTagInside) const char *testString = ""; std::vector> expectedEvents{ - {OsxmlEvent::COMMAND, Variant::arrayType{{"a", Variant::mapType{}}}}, - {OsxmlEvent::COMMAND, + {OsxmlEvent::COMMAND_START, + Variant::arrayType{{"a", Variant::mapType{}}}}, + {OsxmlEvent::COMMAND_START, Variant::arrayType{{"ousia", Variant::mapType{}}}}, - {OsxmlEvent::FIELD_END, Variant::arrayType{}}, - {OsxmlEvent::FIELD_END, Variant::arrayType{}}}; + {OsxmlEvent::RANGE_END, Variant::arrayType{}}, + {OsxmlEvent::RANGE_END, Variant::arrayType{}}}; auto events = parseXml(testString); ASSERT_EQ(expectedEvents, events); @@ -163,9 +167,10 @@ TEST(OsxmlEventParser, commandWithData) // 0 1 2 std::vector> expectedEvents{ - {OsxmlEvent::COMMAND, Variant::arrayType{"a", Variant::mapType{}}}, + {OsxmlEvent::COMMAND_START, + Variant::arrayType{"a", Variant::mapType{}}}, {OsxmlEvent::DATA, Variant::arrayType{" hello \n world "}}, - {OsxmlEvent::FIELD_END, Variant::arrayType{}}}; + {OsxmlEvent::RANGE_END, Variant::arrayType{}}}; auto events = parseXml(testString); ASSERT_EQ(expectedEvents, events); -- cgit v1.2.3 From 8197dc488926e8645efb47e60d0988a6a65fc15f Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Mon, 2 Mar 2015 00:33:32 +0100 Subject: Adapted OsmlParser to new Stack interface, reenabled OsmlParser code in CMakeLists --- CMakeLists.txt | 2 +- src/formats/osml/OsmlParser.cpp | 28 +++++++++++++--------------- src/formats/osml/OsmlStreamParser.cpp | 10 +++++----- 3 files changed, 19 insertions(+), 21 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a09b54..f6a7257 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,7 +214,7 @@ ADD_LIBRARY(ousia_core #) ADD_LIBRARY(ousia_osml -# src/formats/osml/OsmlParser + src/formats/osml/OsmlParser src/formats/osml/OsmlStreamParser ) diff --git a/src/formats/osml/OsmlParser.cpp b/src/formats/osml/OsmlParser.cpp index a24f091..c25974f 100644 --- a/src/formats/osml/OsmlParser.cpp +++ b/src/formats/osml/OsmlParser.cpp @@ -88,7 +88,7 @@ public: OsmlStreamParser::State state = parser.parse(); logger.setDefaultLocation(parser.getLocation()); switch (state) { - case OsmlStreamParser::State::COMMAND: { + case OsmlStreamParser::State::COMMAND_START: { // Implicitly create a "document" element if the first // command is not any other top-level command if (needsDocument) { @@ -96,23 +96,23 @@ public: parser.getCommandName().asString(); if (cmd != "typesystem" && cmd != "document" && cmd != "domain") { - stack.command("document", Variant::mapType{}); + stack.commandStart("document", Variant::mapType{}, + false); } needsDocument = false; } - stack.command(parser.getCommandName(), - parser.getCommandArguments().asMap()); + stack.commandStart(parser.getCommandName(), + parser.getCommandArguments().asMap(), + parser.inRangeCommand()); break; } - case OsmlStreamParser::State::DATA: - stack.data(parser.getData()); - break; - case OsmlStreamParser::State::ENTITY: - // TODO + case OsmlStreamParser::State::RANGE_END: + stack.rangeEnd(); break; case OsmlStreamParser::State::ANNOTATION_START: stack.annotationStart(parser.getCommandName(), - parser.getCommandArguments().asMap()); + parser.getCommandArguments().asMap(), + parser.inRangeCommand()); break; case OsmlStreamParser::State::ANNOTATION_END: { Variant elementName = Variant::fromString(std::string{}); @@ -130,11 +130,9 @@ public: case OsmlStreamParser::State::FIELD_END: stack.fieldEnd(); break; - case OsmlStreamParser::State::NONE: - case OsmlStreamParser::State::ERROR: - // Internally used in OsmlStreamParser, these states should - // never occur. Just contiunue. - continue; + case OsmlStreamParser::State::DATA: + stack.data(parser.getData()); + break; case OsmlStreamParser::State::END: return; } diff --git a/src/formats/osml/OsmlStreamParser.cpp b/src/formats/osml/OsmlStreamParser.cpp index 823075a..64a489d 100644 --- a/src/formats/osml/OsmlStreamParser.cpp +++ b/src/formats/osml/OsmlStreamParser.cpp @@ -409,7 +409,7 @@ public: State parse(); TokenId registerToken(const std::string &token); - void unregisterToken(TokenId token); + void unregisterToken(TokenId id); const TokenizedData &getData() const { return data; } const Variant &getCommandName() const { return cmd().getName(); } @@ -928,9 +928,9 @@ TokenId OsmlStreamParserImpl::registerToken(const std::string &token) return tokenizer.registerToken(token, false); } -void OsmlStreamParserImpl::unregisterToken(TokenId token) +void OsmlStreamParserImpl::unregisterToken(TokenId id) { - assert(tokenizer.unregisterToken(token)); + assert(tokenizer.unregisterToken(id)); } /* Class OsmlStreamParser */ @@ -979,8 +979,8 @@ TokenId OsmlStreamParser::registerToken(const std::string &token) return impl->registerToken(token); } -void OsmlStreamParser::unregisterToken(TokenId token) +void OsmlStreamParser::unregisterToken(TokenId id) { - impl->unregisterToken(token); + impl->unregisterToken(id); } } -- 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 'CMakeLists.txt') 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 'CMakeLists.txt') 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 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 'CMakeLists.txt') 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 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 'CMakeLists.txt') 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 'CMakeLists.txt') 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 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 'CMakeLists.txt') 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 21aa94db203c0b1bcab18bc4858edcdb2afc894d Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Tue, 3 Mar 2015 14:33:55 +0100 Subject: Reactivated main program --- CMakeLists.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index c7ad7a3..f6807f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -276,19 +276,19 @@ TARGET_LINK_LIBRARIES(ousia_xml # Command line interface -#ADD_EXECUTABLE(ousia -# src/cli/Main -#) +ADD_EXECUTABLE(ousia + src/cli/Main +) -#TARGET_LINK_LIBRARIES(ousia -# ousia_core -# ousia_filesystem -# ousia_html -# ousia_xml -# ousia_osml -# ousia_osxml -# ${Boost_LIBRARIES} -#) +TARGET_LINK_LIBRARIES(ousia + ousia_core + ousia_filesystem + ousia_html + ousia_xml + ousia_osml + ousia_osxml + ${Boost_LIBRARIES} +) # If testing is enabled, build the unit tests IF(TEST) -- cgit v1.2.3