diff --git a/src/ZoneCodeGeneratorLib/Parsing/Commands/Impl/CommandsLexer.h b/src/ZoneCodeGeneratorLib/Parsing/Commands/Impl/CommandsLexer.h index 971ecf37..19c0b700 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Commands/Impl/CommandsLexer.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Commands/Impl/CommandsLexer.h @@ -1,7 +1,7 @@ #pragma once #include "CommandsParserValue.h" -#include "Parsing/AbstractLexer.h" +#include "Parsing/Impl/AbstractLexer.h" class CommandsLexer final : public AbstractLexer { diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderLexer.h b/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderLexer.h index 7b4633b1..ab9640b7 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderLexer.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderLexer.h @@ -3,7 +3,7 @@ #include #include "HeaderParserValue.h" -#include "Parsing/AbstractLexer.h" +#include "Parsing/Impl/AbstractLexer.h" class HeaderLexer final : public AbstractLexer { diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderParser.h b/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderParser.h index 17cb189a..f7bbc5f1 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderParser.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Impl/HeaderParser.h @@ -2,7 +2,7 @@ #include "HeaderLexer.h" #include "HeaderParserState.h" -#include "Parsing/AbstractParser.h" +#include "Parsing/Impl/AbstractParser.h" #include "Persistence/IDataRepository.h" class HeaderParser final : public AbstractParser diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherCharacter.cpp b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherCharacter.cpp index 62cbb30a..8305a27c 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherCharacter.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherCharacter.cpp @@ -5,7 +5,7 @@ HeaderMatcherCharacter::HeaderMatcherCharacter(const char c) { } -MatcherResult HeaderMatcherCharacter::CanMatch(AbstractLexer* lexer, const unsigned tokenOffset) +MatcherResult HeaderMatcherCharacter::CanMatch(ILexer* lexer, const unsigned tokenOffset) { const auto& token = lexer->GetToken(tokenOffset); return token.m_type == HeaderParserValueType::CHARACTER && token.CharacterValue() == m_char diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherCharacter.h b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherCharacter.h index 756533c1..7f195e4d 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherCharacter.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherCharacter.h @@ -8,7 +8,7 @@ class HeaderMatcherCharacter final : public AbstractMatcher char m_char; protected: - MatcherResult CanMatch(AbstractLexer* lexer, unsigned tokenOffset) override; + MatcherResult CanMatch(ILexer* lexer, unsigned tokenOffset) override; public: explicit HeaderMatcherCharacter(char c); diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherFactory.cpp b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherFactory.cpp index 928de2f8..f1da3ae5 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherFactory.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherFactory.cpp @@ -13,6 +13,11 @@ MatcherFactoryWrapper HeaderMatcherFactory::Type(HeaderParser return MatcherFactoryWrapper(std::make_unique(type)); } +MatcherFactoryWrapper HeaderMatcherFactory::Identifier() const +{ + return MatcherFactoryWrapper(std::make_unique(HeaderParserValueType::IDENTIFIER)); +} + MatcherFactoryWrapper HeaderMatcherFactory::Char(char c) const { return MatcherFactoryWrapper(std::make_unique(c)); diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherFactory.h b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherFactory.h index 606c31ed..5e5b57b8 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherFactory.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherFactory.h @@ -9,5 +9,6 @@ public: explicit HeaderMatcherFactory(const IMatcherForLabelSupplier* labelSupplier); _NODISCARD MatcherFactoryWrapper Type(HeaderParserValueType type) const; + _NODISCARD MatcherFactoryWrapper Identifier() const; _NODISCARD MatcherFactoryWrapper Char(char c) const; }; diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherValueType.cpp b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherValueType.cpp index 305912ed..a37c8eb2 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherValueType.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherValueType.cpp @@ -5,7 +5,7 @@ HeaderMatcherValueType::HeaderMatcherValueType(HeaderParserValueType type) { } -MatcherResult HeaderMatcherValueType::CanMatch(AbstractLexer* lexer, const unsigned tokenOffset) +MatcherResult HeaderMatcherValueType::CanMatch(ILexer* lexer, const unsigned tokenOffset) { return lexer->GetToken(tokenOffset).m_type == m_type ? MatcherResult::Match(1) diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherValueType.h b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherValueType.h index c8e7c6f4..5b9571f9 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherValueType.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Matcher/HeaderMatcherValueType.h @@ -8,7 +8,7 @@ class HeaderMatcherValueType final : public AbstractMatcher HeaderParserValueType m_type; protected: - MatcherResult CanMatch(AbstractLexer* lexer, unsigned tokenOffset) override; + MatcherResult CanMatch(ILexer* lexer, unsigned tokenOffset) override; public: explicit HeaderMatcherValueType(HeaderParserValueType type); diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Sequence/SequenceNamespace.cpp b/src/ZoneCodeGeneratorLib/Parsing/Header/Sequence/SequenceNamespace.cpp index 25679f55..8eacd68f 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Sequence/SequenceNamespace.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Sequence/SequenceNamespace.cpp @@ -13,6 +13,6 @@ SequenceNamespace::SequenceNamespace() }); } -void SequenceNamespace::ProcessMatch(HeaderParserState* state, const SequenceResult& result) const +void SequenceNamespace::ProcessMatch(HeaderParserState* state, SequenceResult& result) const { } diff --git a/src/ZoneCodeGeneratorLib/Parsing/Header/Sequence/SequenceNamespace.h b/src/ZoneCodeGeneratorLib/Parsing/Header/Sequence/SequenceNamespace.h index 54e5d04c..a3d7793a 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Header/Sequence/SequenceNamespace.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Header/Sequence/SequenceNamespace.h @@ -1,15 +1,16 @@ #pragma once +#include "Parsing/Header/Impl/HeaderParser.h" #include "Parsing/Sequence/AbstractSequence.h" #include "Parsing/Header/Impl/HeaderParserState.h" #include "Parsing/Header/Impl/HeaderParserValue.h" -class SequenceNamespace final : public AbstractSequence +class SequenceNamespace final : public HeaderParser::sequence_t { static constexpr int CAPTURE_NAME = 0; protected: - void ProcessMatch(HeaderParserState* state, const SequenceResult& result) const override; + void ProcessMatch(HeaderParserState* state, SequenceResult& result) const override; public: SequenceNamespace(); diff --git a/src/ZoneCodeGeneratorLib/Parsing/ILexer.h b/src/ZoneCodeGeneratorLib/Parsing/ILexer.h index c4b98c3b..da94842d 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/ILexer.h +++ b/src/ZoneCodeGeneratorLib/Parsing/ILexer.h @@ -1,15 +1,24 @@ #pragma once +#include "Parsing/IParserValue.h" + +template class ILexer { -public: + // TokenType must inherit IParserValue + static_assert(std::is_base_of::value); +public: ILexer() = default; virtual ~ILexer() = default; ILexer(const ILexer& other) = default; ILexer(ILexer&& other) noexcept = default; ILexer& operator=(const ILexer& other) = default; ILexer& operator=(ILexer&& other) noexcept = default; - + + virtual const TokenType& GetToken(unsigned index) = 0; virtual void PopTokens(int amount) = 0; + + _NODISCARD virtual bool IsEof() = 0; + _NODISCARD virtual const TokenPos& GetPos() = 0; }; diff --git a/src/ZoneCodeGeneratorLib/Parsing/IParser.h b/src/ZoneCodeGeneratorLib/Parsing/IParser.h new file mode 100644 index 00000000..b2c3ac03 --- /dev/null +++ b/src/ZoneCodeGeneratorLib/Parsing/IParser.h @@ -0,0 +1,14 @@ +#pragma once + +class IParser +{ +public: + IParser() = default; + virtual ~IParser() = default; + IParser(const IParser& other) = default; + IParser(IParser&& other) noexcept = default; + IParser& operator=(const IParser& other) = default; + IParser& operator=(IParser&& other) noexcept = default; + + virtual bool Parse() = 0; +}; diff --git a/src/ZoneCodeGeneratorLib/Parsing/AbstractLexer.h b/src/ZoneCodeGeneratorLib/Parsing/Impl/AbstractLexer.h similarity index 96% rename from src/ZoneCodeGeneratorLib/Parsing/AbstractLexer.h rename to src/ZoneCodeGeneratorLib/Parsing/Impl/AbstractLexer.h index 23e37c1c..a0c6eba8 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/AbstractLexer.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Impl/AbstractLexer.h @@ -4,13 +4,12 @@ #include #include "Utils/ClassUtils.h" -#include "ILexer.h" -#include "IParserValue.h" -#include "IParserLineStream.h" -#include "ParsingException.h" +#include "Parsing/ILexer.h" +#include "Parsing/IParserLineStream.h" +#include "Parsing/ParsingException.h" template -class AbstractLexer : public ILexer +class AbstractLexer : public ILexer { // TokenType must inherit IParserValue static_assert(std::is_base_of::value); @@ -278,7 +277,7 @@ protected: } public: - const TokenType& GetToken(unsigned index) + const TokenType& GetToken(unsigned index) override { assert(index >= 0); while (index >= m_token_cache.size()) @@ -292,12 +291,12 @@ public: m_token_cache.erase(m_token_cache.begin(), m_token_cache.begin() + amount); } - _NODISCARD bool IsEof() + _NODISCARD bool IsEof() override { return GetToken(0).IsEof(); } - _NODISCARD const TokenPos& GetPos() + _NODISCARD const TokenPos& GetPos() override { return GetToken(0).GetPos(); } diff --git a/src/ZoneCodeGeneratorLib/Parsing/AbstractParser.h b/src/ZoneCodeGeneratorLib/Parsing/Impl/AbstractParser.h similarity index 85% rename from src/ZoneCodeGeneratorLib/Parsing/AbstractParser.h rename to src/ZoneCodeGeneratorLib/Parsing/Impl/AbstractParser.h index 5907ef22..7fa3ff65 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/AbstractParser.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Impl/AbstractParser.h @@ -3,11 +3,13 @@ #include #include -#include "AbstractLexer.h" -#include "Sequence/AbstractSequence.h" +#include "Parsing/IParser.h" +#include "Parsing/ILexer.h" +#include "Parsing/Sequence/AbstractSequence.h" +#include "Parsing/ParsingException.h" template -class AbstractParser +class AbstractParser : public IParser { // TokenType must inherit IParserValue static_assert(std::is_base_of::value); @@ -16,10 +18,10 @@ public: typedef AbstractSequence sequence_t; protected: - AbstractLexer* m_lexer; + ILexer* m_lexer; std::unique_ptr m_state; - explicit AbstractParser(AbstractLexer* lexer, std::unique_ptr state) + explicit AbstractParser(ILexer* lexer, std::unique_ptr state) : m_lexer(lexer), m_state(std::move(state)) { @@ -34,7 +36,7 @@ public: AbstractParser& operator=(const AbstractParser& other) = default; AbstractParser& operator=(AbstractParser&& other) noexcept = default; - bool Parse() + bool Parse() override { try { diff --git a/src/ZoneCodeGeneratorLib/Parsing/Matcher/AbstractMatcher.h b/src/ZoneCodeGeneratorLib/Parsing/Matcher/AbstractMatcher.h index 2b7d1be2..5a6a16bc 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Matcher/AbstractMatcher.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Matcher/AbstractMatcher.h @@ -3,7 +3,7 @@ #include #include "Parsing/IParserValue.h" -#include "Parsing/AbstractLexer.h" +#include "Parsing/ILexer.h" #include "Parsing/Matcher/MatcherResult.h" template @@ -19,7 +19,7 @@ private: int m_tag_id; int m_capture_id; bool m_no_consume; - std::function>)> m_transform_func; + std::function>&)> m_transform_func; protected: AbstractMatcher() @@ -29,7 +29,7 @@ protected: { } - virtual MatcherResult CanMatch(AbstractLexer* lexer, unsigned tokenOffset) = 0; + virtual MatcherResult CanMatch(ILexer* lexer, unsigned tokenOffset) = 0; public: virtual ~AbstractMatcher() = default; @@ -53,12 +53,12 @@ public: m_no_consume = !value; } - void SetTransform(std::function>)> transform) + void SetTransform(std::function>&)> transform) { m_transform_func = std::move(transform); } - MatcherResult Match(AbstractLexer* lexer, const unsigned tokenOffset) + MatcherResult Match(ILexer* lexer, const unsigned tokenOffset) { MatcherResult result = CanMatch(lexer, tokenOffset); diff --git a/src/ZoneCodeGeneratorLib/Parsing/Matcher/AbstractMatcherFactory.h b/src/ZoneCodeGeneratorLib/Parsing/Matcher/AbstractMatcherFactory.h index 8312dcf6..068392bd 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Matcher/AbstractMatcherFactory.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Matcher/AbstractMatcherFactory.h @@ -43,7 +43,7 @@ public: return *this; } - MatcherFactoryWrapper& Transform(std::function>)> transform) + MatcherFactoryWrapper& Transform(std::function>&)> transform) { m_matcher->SetTransform(std::move(transform)); return *this; @@ -81,14 +81,14 @@ public: { } - _NODISCARD MatcherFactoryWrapper And(std::initializer_list>> matchers) const + _NODISCARD MatcherFactoryWrapper And(std::initializer_list>>> matchers) const { - return MatcherFactoryWrapper(std::make_unique>(std::move(matchers))); + return MatcherFactoryWrapper(std::make_unique>(matchers)); } - _NODISCARD MatcherFactoryWrapper Or(std::initializer_list>> matchers) const + _NODISCARD MatcherFactoryWrapper Or(std::initializer_list>>> matchers) const { - return MatcherFactoryWrapper(std::make_unique>(std::move(matchers))); + return MatcherFactoryWrapper(std::make_unique>(matchers)); } _NODISCARD MatcherFactoryWrapper Loop(std::unique_ptr> matcher) const @@ -108,6 +108,6 @@ public: _NODISCARD MatcherFactoryWrapper Label(const int label) const { - return MatcherFactoryWrapper(std::make_unique>(label)); + return MatcherFactoryWrapper(std::make_unique>(m_label_supplier, label)); } }; diff --git a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherAnd.h b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherAnd.h index c9461b03..712ee0b2 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherAnd.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherAnd.h @@ -15,7 +15,7 @@ class MatcherAnd final : public AbstractMatcher std::vector>> m_matchers; protected: - MatcherResult CanMatch(AbstractLexer* lexer, const unsigned tokenOffset) override + MatcherResult CanMatch(ILexer* lexer, const unsigned tokenOffset) override { auto matchResult = MatcherResult::Match(0); diff --git a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherLabel.h b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherLabel.h index d23efdb8..80eb5159 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherLabel.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherLabel.h @@ -31,7 +31,7 @@ class MatcherLabel final : public AbstractMatcher int m_label; protected: - MatcherResult CanMatch(AbstractLexer* lexer, unsigned tokenOffset) override + MatcherResult CanMatch(ILexer* lexer, unsigned tokenOffset) override { AbstractMatcher* matcher = m_supplier->GetMatcherForLabel(m_label); diff --git a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherLoop.h b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherLoop.h index aea6f046..2ef41507 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherLoop.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherLoop.h @@ -14,7 +14,7 @@ class MatcherLoop final : public AbstractMatcher std::unique_ptr> m_matcher; protected: - MatcherResult CanMatch(AbstractLexer* lexer, const unsigned tokenOffset) override + MatcherResult CanMatch(ILexer* lexer, const unsigned tokenOffset) override { auto matchResult = MatcherResult::Match(0); auto loopedAtLeastOnce = false; diff --git a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherOptional.h b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherOptional.h index 5b456f49..1360fd6b 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherOptional.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherOptional.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include "Parsing/IParserValue.h" @@ -15,7 +14,7 @@ class MatcherOptional final : public AbstractMatcher std::unique_ptr> m_matcher; protected: - MatcherResult CanMatch(AbstractLexer* lexer, unsigned tokenOffset) override + MatcherResult CanMatch(ILexer* lexer, unsigned tokenOffset) override { auto result = m_matcher->Match(lexer, tokenOffset); diff --git a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherOr.h b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherOr.h index 159b8b77..ee773d54 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherOr.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Matcher/MatcherOr.h @@ -15,11 +15,11 @@ class MatcherOr final : public AbstractMatcher std::vector>> m_matchers; protected: - MatcherResult CanMatch(AbstractLexer* lexer, unsigned tokenOffset) override + MatcherResult CanMatch(ILexer* lexer, unsigned tokenOffset) override { - for (const auto& matcher : m_matchers) + for (const std::unique_ptr>& matcher : m_matchers) { - const auto result = matcher->Match(lexer, tokenOffset); + MatcherResult result = matcher->Match(lexer, tokenOffset); if (!result.m_matches) continue; diff --git a/src/ZoneCodeGeneratorLib/Parsing/Sequence/AbstractSequence.h b/src/ZoneCodeGeneratorLib/Parsing/Sequence/AbstractSequence.h index df58c251..d82fa475 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Sequence/AbstractSequence.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Sequence/AbstractSequence.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "SequenceResult.h" #include "Utils/ClassUtils.h" @@ -27,7 +28,7 @@ protected: AbstractSequence() = default; - virtual void ProcessMatch(ParserState* state, const SequenceResult& result) const = 0; + virtual void ProcessMatch(ParserState* state, SequenceResult& result) const = 0; void AddMatchers(std::initializer_list>> matchers) { @@ -35,7 +36,7 @@ protected: m_entry = std::make_unique>(matchers); } - void AddLabeledMatchers(int label, std::initializer_list>> matchers) + void AddLabeledMatchers(std::initializer_list>> matchers, const int label) { assert(m_matchers.find(label) == m_matchers.end()); m_matchers.emplace(label, std::make_unique>(matchers)); @@ -61,7 +62,7 @@ public: return nullptr; } - _NODISCARD bool MatchSequence(AbstractLexer* lexer, ParserState* state, unsigned& consumedTokenCount) const + _NODISCARD bool MatchSequence(ILexer* lexer, ParserState* state, unsigned& consumedTokenCount) const { if (!m_entry) return false; diff --git a/src/ZoneCodeGeneratorLib/Parsing/Sequence/SequenceResult.h b/src/ZoneCodeGeneratorLib/Parsing/Sequence/SequenceResult.h index e396d564..63b1b8a0 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Sequence/SequenceResult.h +++ b/src/ZoneCodeGeneratorLib/Parsing/Sequence/SequenceResult.h @@ -5,6 +5,7 @@ #include "Utils/ClassUtils.h" #include "Parsing/Matcher/AbstractMatcher.h" #include "Parsing/Matcher/MatcherResult.h" +#include "Parsing/ParsingException.h" template class SequenceResult @@ -30,7 +31,7 @@ class SequenceResult unsigned m_tag_offset; public: - SequenceResult(AbstractLexer* lexer, const MatcherResult& result) + SequenceResult(ILexer* lexer, const MatcherResult& result) : m_tags(result.m_tags), m_tag_offset(0) { diff --git a/src/ZoneCodeGeneratorNew/main.cpp b/src/ZoneCodeGeneratorNew/main.cpp index f21346df..9025da72 100644 --- a/src/ZoneCodeGeneratorNew/main.cpp +++ b/src/ZoneCodeGeneratorNew/main.cpp @@ -1,14 +1,7 @@ -#include -#include - #include "ZoneCodeGenerator.h" int main(const int argc, const char** argv) { - int i = 5; - std::vector> asdf; - asdf.emplace_back(1, i); - const ZoneCodeGenerator zoneCodeGenerator; return zoneCodeGenerator.Run(argc, argv); } diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Commands/Impl/CommandsLexerTests.cpp b/test/ZoneCodeGeneratorLibTests/Parsing/Commands/Impl/CommandsLexerTests.cpp index 1ce0d4aa..9bee8b00 100644 --- a/test/ZoneCodeGeneratorLibTests/Parsing/Commands/Impl/CommandsLexerTests.cpp +++ b/test/ZoneCodeGeneratorLibTests/Parsing/Commands/Impl/CommandsLexerTests.cpp @@ -3,7 +3,7 @@ #include "Parsing/Commands/Impl/CommandsLexer.h" #include "Parsing/Mock/MockParserLineStream.h" -namespace test::parsing::commands +namespace test::parsing::commands::impl::commands_lexer { TEST_CASE("CommandsLexer: ", "[parsing][commands]") { diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Header/Impl/HeaderLexerTests.cpp b/test/ZoneCodeGeneratorLibTests/Parsing/Header/Impl/HeaderLexerTests.cpp index bb580032..68f09fb3 100644 --- a/test/ZoneCodeGeneratorLibTests/Parsing/Header/Impl/HeaderLexerTests.cpp +++ b/test/ZoneCodeGeneratorLibTests/Parsing/Header/Impl/HeaderLexerTests.cpp @@ -3,7 +3,7 @@ #include "Parsing/Header/Impl/HeaderLexer.h" #include "Parsing/Mock/MockParserLineStream.h" -namespace test::parsing::header +namespace test::parsing::header::impl::header_lexer { void ExpectCharacterToken(HeaderLexer& lexer, char c) { diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Header/Matcher/HeaderMatcherCharacterTests.cpp b/test/ZoneCodeGeneratorLibTests/Parsing/Header/Matcher/HeaderMatcherCharacterTests.cpp new file mode 100644 index 00000000..e69de29b diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Header/Matcher/HeaderMatcherValueTypeTests.cpp b/test/ZoneCodeGeneratorLibTests/Parsing/Header/Matcher/HeaderMatcherValueTypeTests.cpp new file mode 100644 index 00000000..e69de29b diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Header/Sequence/SequenceNamespaceTests.cpp b/test/ZoneCodeGeneratorLibTests/Parsing/Header/Sequence/SequenceNamespaceTests.cpp new file mode 100644 index 00000000..fc671188 --- /dev/null +++ b/test/ZoneCodeGeneratorLibTests/Parsing/Header/Sequence/SequenceNamespaceTests.cpp @@ -0,0 +1,26 @@ +#include + +#include "Parsing/Header/Sequence/SequenceNamespace.h" +#include "Parsing/Mock/MockLexer.h" + +namespace test::parsing::header::sequence::sequence_namespace +{ + TEST_CASE("SequenceNamespace: Ensure can parse simple namespace directive", "[parsing][parsingstream]") + { + const TokenPos pos; + const auto lexer = std::make_unique>(MockLexer( + { + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("test_namespace")), + HeaderParserValue::Character(pos, '{') + }, HeaderParserValue::EndOfFile(pos))); + + const auto sequence = std::make_unique(); + const auto state = std::make_unique(); + + unsigned consumedTokenCount; + auto result = sequence->MatchSequence(lexer.get(), state.get(), consumedTokenCount); + + REQUIRE(result); + } +} diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Impl/CommentRemovingStreamProxyTest.cpp b/test/ZoneCodeGeneratorLibTests/Parsing/Impl/CommentRemovingStreamProxyTest.cpp index 71854447..44063a1f 100644 --- a/test/ZoneCodeGeneratorLibTests/Parsing/Impl/CommentRemovingStreamProxyTest.cpp +++ b/test/ZoneCodeGeneratorLibTests/Parsing/Impl/CommentRemovingStreamProxyTest.cpp @@ -1,10 +1,9 @@ #include - #include "Parsing/Impl/CommentRemovingStreamProxy.h" #include "Parsing/Mock/MockParserLineStream.h" -namespace test::parsing +namespace test::parsing::impl::comment_removing_stream_proxy { TEST_CASE("CommentRemovingStreamProxy: Ensure simple single line comment is working", "[parsing][parsingstream]") { diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Impl/DefinesStreamProxyTest.cpp b/test/ZoneCodeGeneratorLibTests/Parsing/Impl/DefinesStreamProxyTest.cpp index 7943e786..000a29b6 100644 --- a/test/ZoneCodeGeneratorLibTests/Parsing/Impl/DefinesStreamProxyTest.cpp +++ b/test/ZoneCodeGeneratorLibTests/Parsing/Impl/DefinesStreamProxyTest.cpp @@ -3,7 +3,7 @@ #include "Parsing/Impl/DefinesStreamProxy.h" #include "Parsing/Mock/MockParserLineStream.h" -namespace test::parsing +namespace test::parsing::impl::defines_stream_proxy { void ExpectLine(IParserLineStream* stream, const int lineNumber, const std::string& value) { diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Impl/IncludingStreamProxyTest.cpp b/test/ZoneCodeGeneratorLibTests/Parsing/Impl/IncludingStreamProxyTest.cpp index 556070d2..aa86b1ae 100644 --- a/test/ZoneCodeGeneratorLibTests/Parsing/Impl/IncludingStreamProxyTest.cpp +++ b/test/ZoneCodeGeneratorLibTests/Parsing/Impl/IncludingStreamProxyTest.cpp @@ -3,7 +3,7 @@ #include "Parsing/Impl/IncludingStreamProxy.h" #include "Parsing/Mock/MockParserLineStream.h" -namespace test::parsing +namespace test::parsing::impl::including_stream_proxy { TEST_CASE("IncludingStreamProxy: Ensure simple include is working", "[parsing][parsingstream]") { diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Matcher/MatcherTests.cpp b/test/ZoneCodeGeneratorLibTests/Parsing/Matcher/MatcherTests.cpp new file mode 100644 index 00000000..05ed573a --- /dev/null +++ b/test/ZoneCodeGeneratorLibTests/Parsing/Matcher/MatcherTests.cpp @@ -0,0 +1,848 @@ +#include + +#include "Utils/ClassUtils.h" +#include "Parsing/Header/Impl/HeaderParserValue.h" +#include "Parsing/Header/Matcher/HeaderMatcherFactory.h" +#include "Parsing/Impl/DefinesStreamProxy.h" +#include "Parsing/Mock/MockLexer.h" +#include "Parsing/Mock/MockSequence.h" + +namespace test::parsing::matcher +{ + // This class uses the header implementation of IParserValues + // This should not make a difference in behaviour and only affects test data + + typedef MockLexer lexer_t; + typedef MockSequence sequence_t; + typedef sequence_t::parent_t::matcher_t matcher_t; + typedef SequenceResult sequence_result_t; + typedef HeaderMatcherFactory factory_t; + + class MatchersTestsHelper + { + std::unique_ptr m_mock_state; + std::unique_ptr m_lexer; + std::unique_ptr m_sequence; + + unsigned m_consumed_token_count; + + public: + MatchersTestsHelper() + : m_mock_state(std::make_unique()), + m_sequence(std::make_unique()), + m_consumed_token_count(0) + { + } + + void Tokens(std::initializer_list> tokens) + { + m_lexer = std::make_unique(tokens, HeaderParserValue::EndOfFile(TokenPos())); + } + + void Matchers(const std::initializer_list>> matchers) const + { + m_sequence->AddMockMatchers(matchers); + } + + void LabeledMatchers(const std::initializer_list>> matchers, const int label) const + { + m_sequence->AddMockLabeledMatchers(matchers, label); + } + + _NODISCARD factory_t Factory() const + { + return HeaderMatcherFactory(m_sequence->GetLabelSupplier()); + } + + void MatchCallback(std::function cb) const + { + m_sequence->Handle(std::move(cb)); + } + + bool PerformTest() + { + // Tokens must be set first + REQUIRE(m_lexer.get() != nullptr); + return m_sequence->MatchSequence(m_lexer.get(), m_mock_state.get(), m_consumed_token_count); + } + + _NODISCARD unsigned GetConsumedTokenCount() const + { + return m_consumed_token_count; + } + }; + + TEST_CASE("Matcher: Ensure can match simple AND token sequence", "[parsing][matcher]") + { + MatchersTestsHelper test; + const TokenPos pos; + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("test_namespace")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE), + create.Identifier(), + create.Char('{') + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 3); + } + + TEST_CASE("Matcher: Ensure AND matcher can fail", "[parsing][matcher]") + { + MatchersTestsHelper test; + const TokenPos pos; + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("test_namespace")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE), + create.Identifier(), + create.Char('+') + }); + + REQUIRE(!test.PerformTest()); + } + + TEST_CASE("Matcher: Ensure match callback is called", "[parsing][matcher]") + { + MatchersTestsHelper test; + const TokenPos pos; + auto callbackCalled = false; + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("test_namespace")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE) + }); + test.MatchCallback([&callbackCalled](sequence_result_t& result) + { + callbackCalled = true; + }); + + REQUIRE(test.PerformTest()); + REQUIRE(callbackCalled); + } + + TEST_CASE("Matcher: Ensure match callback is not called on fail", "[parsing][matcher]") + { + MatchersTestsHelper test; + const TokenPos pos; + auto callbackCalled = false; + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Invalid(pos) + }); + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE) + }); + test.MatchCallback([&callbackCalled](sequence_result_t& result) + { + callbackCalled = true; + }); + + REQUIRE(!test.PerformTest()); + REQUIRE(!callbackCalled); + } + + TEST_CASE("Matcher: Ensure can match simple OR token sequence", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Or({ + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT), + }) + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("test_namespace")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_NAMESPACE); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 1); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Identifier(pos, new std::string("test_struct")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_STRUCT); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 1); + } + + TEST_CASE("Matcher: Ensure OR matcher can fail", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Or({ + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT), + }) + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::ENUM), + HeaderParserValue::Identifier(pos, new std::string("test_namespace")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + test.MatchCallback([](sequence_result_t& result) + { + FAIL(); + }); + + REQUIRE(!test.PerformTest()); + } + + TEST_CASE("Matcher: Ensure can match simple LOOP token sequence", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Loop(create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE)), + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT) + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Invalid(pos) + }); + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_NAMESPACE); + REQUIRE(result.NextTag() == TAG_NAMESPACE); + REQUIRE(result.NextTag() == TAG_NAMESPACE); + REQUIRE(result.NextTag() == TAG_STRUCT); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 4); + } + + TEST_CASE("Matcher: Ensure LOOP token sequence must be called at least once to succeed", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Loop(create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE)), + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT) + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Invalid(pos) + }); + + REQUIRE(!test.PerformTest()); + } + + TEST_CASE("Matcher: Ensure OPTIONAL_LOOP token sequence can be called zero times", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.OptionalLoop(create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE)), + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT) + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Invalid(pos) + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 1); + } + + TEST_CASE("Matcher: Ensure OPTIONAL token sequence can be called zero times", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + static constexpr auto TAG_ENUM = 3; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Optional(create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT)), + create.Type(HeaderParserValueType::ENUM).Tag(TAG_ENUM) + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Keyword(pos, HeaderParserValueType::ENUM), + HeaderParserValue::Invalid(pos) + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 2); + } + + TEST_CASE("Matcher: Ensure OPTIONAL token sequence can be called once", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + static constexpr auto TAG_ENUM = 3; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Optional(create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT)), + create.Type(HeaderParserValueType::ENUM).Tag(TAG_ENUM) + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Keyword(pos, HeaderParserValueType::ENUM), + HeaderParserValue::Invalid(pos) + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 3); + } + + TEST_CASE("Matcher: Ensure OPTIONAL token sequence can not be called more than once", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + static constexpr auto TAG_ENUM = 3; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Optional(create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT)), + create.Type(HeaderParserValueType::ENUM).Tag(TAG_ENUM) + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Keyword(pos, HeaderParserValueType::ENUM), + HeaderParserValue::Invalid(pos) + }); + + REQUIRE(!test.PerformTest()); + } + + TEST_CASE("Matcher: Ensure LOOP matchers are greedy", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + static constexpr auto TAG_ENUM = 3; + static constexpr auto TAG_TYPEDEF = 4; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Loop(create.Or({ + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT), + create.Type(HeaderParserValueType::TYPEDEF).Tag(TAG_TYPEDEF) + })), + create.Or({ + create.Type(HeaderParserValueType::TYPEDEF).Tag(TAG_TYPEDEF), + create.Type(HeaderParserValueType::ENUM).Tag(TAG_ENUM) + }) + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Keyword(pos, HeaderParserValueType::TYPEDEF), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Keyword(pos, HeaderParserValueType::TYPEDEF), + HeaderParserValue::Keyword(pos, HeaderParserValueType::ENUM), + HeaderParserValue::Invalid(pos) + }); + + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_NAMESPACE); + REQUIRE(result.NextTag() == TAG_STRUCT); + REQUIRE(result.NextTag() == TAG_STRUCT); + REQUIRE(result.NextTag() == TAG_TYPEDEF); + REQUIRE(result.NextTag() == TAG_STRUCT); + REQUIRE(result.NextTag() == TAG_TYPEDEF); + REQUIRE(result.NextTag() == TAG_ENUM); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 7); + } + + TEST_CASE("Matcher: Ensure LABEL matcher are working", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + + static constexpr auto LABEL_TEST = 1; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers( + { + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Label(LABEL_TEST) + }); + test.LabeledMatchers( + { + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT), + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT) + }, LABEL_TEST); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Invalid(pos) + }); + + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_NAMESPACE); + REQUIRE(result.NextTag() == TAG_STRUCT); + REQUIRE(result.NextTag() == TAG_STRUCT); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 3); + } + + TEST_CASE("Matcher: Ensure LABEL matchers can refer to themselves are working", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + + static constexpr auto LABEL_TEST = 1; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers( + { + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Label(LABEL_TEST) + }); + test.LabeledMatchers( + { + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT), + create.Optional(create.Label(LABEL_TEST)) + }, LABEL_TEST); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Invalid(pos) + }); + + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_NAMESPACE); + REQUIRE(result.NextTag() == TAG_STRUCT); + REQUIRE(result.NextTag() == TAG_STRUCT); + REQUIRE(result.NextTag() == TAG_STRUCT); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 4); + } + + TEST_CASE("Matcher: Can capture tokens", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + + static constexpr auto CAPTURE_NAMESPACE_NAME = 1; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Identifier().Capture(CAPTURE_NAMESPACE_NAME), + create.Char('{') + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("hello_world")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_NAMESPACE); + REQUIRE(result.NextTag() == matcher_t::NO_ID); + + REQUIRE(result.HasNextCapture(CAPTURE_NAMESPACE_NAME)); + { + const auto& capture = result.NextCapture(CAPTURE_NAMESPACE_NAME); + REQUIRE(capture.m_type == HeaderParserValueType::IDENTIFIER); + REQUIRE(capture.IdentifierValue() == "hello_world"); + } + + REQUIRE(!result.HasNextCapture(CAPTURE_NAMESPACE_NAME)); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 3); + } + + TEST_CASE("Matcher: Can capture in OR matchers", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + static constexpr auto TAG_STRUCT = 2; + + static constexpr auto CAPTURE_NAMESPACE_NAME = 1; + static constexpr auto CAPTURE_STRUCT_NAME = 2; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Or({ + create.And({ + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Identifier().Capture(CAPTURE_NAMESPACE_NAME), + }), + create.And({ + create.Type(HeaderParserValueType::STRUCT).Tag(TAG_STRUCT), + create.Identifier().Capture(CAPTURE_STRUCT_NAME), + }) + }), + create.Char('{') + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("hello_world")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_NAMESPACE); + REQUIRE(result.NextTag() == matcher_t::NO_ID); + + REQUIRE(result.HasNextCapture(CAPTURE_NAMESPACE_NAME)); + { + const auto& capture = result.NextCapture(CAPTURE_NAMESPACE_NAME); + REQUIRE(capture.m_type == HeaderParserValueType::IDENTIFIER); + REQUIRE(capture.IdentifierValue() == "hello_world"); + } + + REQUIRE(!result.HasNextCapture(CAPTURE_NAMESPACE_NAME)); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 3); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Identifier(pos, new std::string("bye_struct")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_STRUCT); + REQUIRE(result.NextTag() == matcher_t::NO_ID); + + REQUIRE(result.HasNextCapture(CAPTURE_STRUCT_NAME)); + { + const auto& capture = result.NextCapture(CAPTURE_STRUCT_NAME); + REQUIRE(capture.m_type == HeaderParserValueType::IDENTIFIER); + REQUIRE(capture.IdentifierValue() == "bye_struct"); + } + + REQUIRE(!result.HasNextCapture(CAPTURE_STRUCT_NAME)); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 3); + } + + TEST_CASE("Matcher: Can capture in LOOP matchers", "[parsing][matcher]") + { + static constexpr auto TAG_NAMESPACE = 1; + + static constexpr auto CAPTURE_NAMESPACE_NAME = 1; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Type(HeaderParserValueType::NAMESPACE).Tag(TAG_NAMESPACE), + create.Loop(create.Identifier().Capture(CAPTURE_NAMESPACE_NAME)), + create.Char('{') + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("hello_world")), + HeaderParserValue::Identifier(pos, new std::string("hello_universe")), + HeaderParserValue::Identifier(pos, new std::string("hello_everyone")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_NAMESPACE); + REQUIRE(result.NextTag() == matcher_t::NO_ID); + + REQUIRE(result.HasNextCapture(CAPTURE_NAMESPACE_NAME)); + { + const auto& capture = result.NextCapture(CAPTURE_NAMESPACE_NAME); + REQUIRE(capture.m_type == HeaderParserValueType::IDENTIFIER); + REQUIRE(capture.IdentifierValue() == "hello_world"); + } + + REQUIRE(result.HasNextCapture(CAPTURE_NAMESPACE_NAME)); + { + const auto& capture = result.NextCapture(CAPTURE_NAMESPACE_NAME); + REQUIRE(capture.m_type == HeaderParserValueType::IDENTIFIER); + REQUIRE(capture.IdentifierValue() == "hello_universe"); + } + + REQUIRE(result.HasNextCapture(CAPTURE_NAMESPACE_NAME)); + { + const auto& capture = result.NextCapture(CAPTURE_NAMESPACE_NAME); + REQUIRE(capture.m_type == HeaderParserValueType::IDENTIFIER); + REQUIRE(capture.IdentifierValue() == "hello_everyone"); + } + + REQUIRE(!result.HasNextCapture(CAPTURE_NAMESPACE_NAME)); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 5); + } + + TEST_CASE("Matcher: Capturing an AND group captures all matched tokens", "[parsing][matcher]") + { + static constexpr auto TAG_AND_GROUP = 1; + + static constexpr auto CAPTURE_AND_GROUP = 1; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.And({ + create.Type(HeaderParserValueType::NAMESPACE), + create.Identifier() + }).Tag(TAG_AND_GROUP).Capture(CAPTURE_AND_GROUP), + create.Char('{') + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("hello_world")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_AND_GROUP); + REQUIRE(result.NextTag() == matcher_t::NO_ID); + + REQUIRE(result.HasNextCapture(CAPTURE_AND_GROUP)); + REQUIRE(result.NextCapture(CAPTURE_AND_GROUP).m_type == HeaderParserValueType::NAMESPACE); + + REQUIRE(result.HasNextCapture(CAPTURE_AND_GROUP)); + { + const auto& capture = result.NextCapture(CAPTURE_AND_GROUP); + REQUIRE(capture.m_type == HeaderParserValueType::IDENTIFIER); + REQUIRE(capture.IdentifierValue() == "hello_world"); + } + + REQUIRE(!result.HasNextCapture(CAPTURE_AND_GROUP)); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 3); + } + + TEST_CASE("Matcher: Capturing an LOOP group captures all matched tokens", "[parsing][matcher]") + { + static constexpr auto TAG_LOOP_GROUP = 1; + + static constexpr auto CAPTURE_LOOP_GROUP = 1; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers({ + create.Loop(create.And({ + create.Type(HeaderParserValueType::NAMESPACE), + create.Identifier() + })).Tag(TAG_LOOP_GROUP).Capture(CAPTURE_LOOP_GROUP), + create.Char('{') + }); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("hello_world")), + HeaderParserValue::Keyword(pos, HeaderParserValueType::NAMESPACE), + HeaderParserValue::Identifier(pos, new std::string("hello_universe")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == TAG_LOOP_GROUP); + REQUIRE(result.NextTag() == matcher_t::NO_ID); + + REQUIRE(result.HasNextCapture(CAPTURE_LOOP_GROUP)); + REQUIRE(result.NextCapture(CAPTURE_LOOP_GROUP).m_type == HeaderParserValueType::NAMESPACE); + + REQUIRE(result.HasNextCapture(CAPTURE_LOOP_GROUP)); + { + const auto& capture = result.NextCapture(CAPTURE_LOOP_GROUP); + REQUIRE(capture.m_type == HeaderParserValueType::IDENTIFIER); + REQUIRE(capture.IdentifierValue() == "hello_world"); + } + + REQUIRE(result.HasNextCapture(CAPTURE_LOOP_GROUP)); + REQUIRE(result.NextCapture(CAPTURE_LOOP_GROUP).m_type == HeaderParserValueType::NAMESPACE); + + REQUIRE(result.HasNextCapture(CAPTURE_LOOP_GROUP)); + { + const auto& capture = result.NextCapture(CAPTURE_LOOP_GROUP); + REQUIRE(capture.m_type == HeaderParserValueType::IDENTIFIER); + REQUIRE(capture.IdentifierValue() == "hello_universe"); + } + + REQUIRE(!result.HasNextCapture(CAPTURE_LOOP_GROUP)); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 5); + } + + TEST_CASE("Matcher: Capture transform works", "[parsing][matcher]") + { + static constexpr auto LABEL_TYPENAME = 1; + + static constexpr auto CAPTURE_TYPENAME = 1; + + MatchersTestsHelper test; + const TokenPos pos; + const auto create = test.Factory(); + test.Matchers( + { + create.Type(HeaderParserValueType::STRUCT), + create.Label(LABEL_TYPENAME).Capture(CAPTURE_TYPENAME), + create.Char('{') + }); + test.LabeledMatchers( + { + create.And({ + create.Identifier(), + create.OptionalLoop(create.And({ + create.Char(':'), + create.Char(':'), + create.Identifier() + })) + }).Transform([](std::vector>& values) + { + return HeaderParserValue::TypeName(values[0].get().GetPos(), new std::string()); + }) + }, LABEL_TYPENAME); + + test.Tokens({ + HeaderParserValue::Keyword(pos, HeaderParserValueType::STRUCT), + HeaderParserValue::Identifier(pos, new std::string("hello")), + HeaderParserValue::Character(pos, ':'), + HeaderParserValue::Character(pos, ':'), + HeaderParserValue::Identifier(pos, new std::string("world")), + HeaderParserValue::Character(pos, '{'), + HeaderParserValue::Invalid(pos) + }); + + test.MatchCallback([](sequence_result_t& result) + { + REQUIRE(result.NextTag() == matcher_t::NO_ID); + + REQUIRE(result.HasNextCapture(CAPTURE_TYPENAME)); + { + const auto& capture = result.NextCapture(CAPTURE_TYPENAME); + REQUIRE(capture.m_type == HeaderParserValueType::TYPE_NAME); + REQUIRE(capture.TypeNameValue() == "hello::world"); + } + + REQUIRE(!result.HasNextCapture(CAPTURE_TYPENAME)); + }); + + REQUIRE(test.PerformTest()); + REQUIRE(test.GetConsumedTokenCount() == 6); + } +} diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Mock/MockLexer.h b/test/ZoneCodeGeneratorLibTests/Parsing/Mock/MockLexer.h new file mode 100644 index 00000000..bea4f5ac --- /dev/null +++ b/test/ZoneCodeGeneratorLibTests/Parsing/Mock/MockLexer.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +#include "Utils/ClassUtils.h" +#include "Parsing/ILexer.h" + +template +class MockLexer final : public ILexer +{ + // TokenType must inherit IParserValue + static_assert(std::is_base_of::value); + + std::vector m_tokens; + //std::vector m_tokens; + TokenType m_eof; + int m_pop_count; + +public: + MockLexer(std::initializer_list> tokens, TokenType eof) + : m_tokens(std::make_move_iterator(tokens.begin()), std::make_move_iterator(tokens.end())), + m_eof(std::move(eof)), + m_pop_count(0) + { + } + + ~MockLexer() override = default; + MockLexer(const MockLexer& other) = delete; + MockLexer(MockLexer&& other) noexcept = default; + MockLexer& operator=(const MockLexer& other) = delete; + MockLexer& operator=(MockLexer&& other) noexcept = default; + + const TokenType& GetToken(const unsigned index) override + { + if (index < m_tokens.size()) + return m_tokens[index]; + + return m_eof; + } + + void PopTokens(const int amount) override + { + m_pop_count += amount; + } + + bool IsEof() override + { + return !m_tokens.empty(); + } + + const TokenPos& GetPos() override + { + return !m_tokens.empty() ? m_tokens[0].GetPos() : m_eof.GetPos(); + } + + _NODISCARD int GetPopCount() const + { + return m_pop_count; + } +}; diff --git a/test/ZoneCodeGeneratorLibTests/Parsing/Mock/MockSequence.h b/test/ZoneCodeGeneratorLibTests/Parsing/Mock/MockSequence.h new file mode 100644 index 00000000..bd052e29 --- /dev/null +++ b/test/ZoneCodeGeneratorLibTests/Parsing/Mock/MockSequence.h @@ -0,0 +1,49 @@ +#pragma once + +#include "Parsing/Sequence/AbstractSequence.h" + +struct MockSequenceState +{ + char m_dummy; +}; + +template +class MockSequence final : public AbstractSequence +{ +public: + typedef AbstractSequence parent_t; + +private: + using parent_t::AddMatchers; + using parent_t::AddLabeledMatchers; + + std::function&)> m_handler; + +protected: + void ProcessMatch(MockSequenceState* state, SequenceResult& result) const override + { + if (m_handler) + m_handler(result); + } + +public: + void AddMockMatchers(std::initializer_list>> matchers) + { + AddMatchers(matchers); + } + + void AddMockLabeledMatchers(std::initializer_list>> matchers, const int label) + { + AddLabeledMatchers(matchers, label); + } + + void Handle(std::function&)> handler) + { + m_handler = std::move(handler); + } + + IMatcherForLabelSupplier* GetLabelSupplier() + { + return this; + } +}; \ No newline at end of file