Matcher and sequence testing stuffs

This commit is contained in:
Jan
2021-02-13 14:54:34 +01:00
parent 0f70f9586c
commit 37232e3176
36 changed files with 1070 additions and 62 deletions

View File

@ -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]")
{

View File

@ -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)
{

View File

@ -0,0 +1,26 @@
#include <catch2/catch.hpp>
#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>>(MockLexer<HeaderParserValue>(
{
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<SequenceNamespace>();
const auto state = std::make_unique<HeaderParserState>();
unsigned consumedTokenCount;
auto result = sequence->MatchSequence(lexer.get(), state.get(), consumedTokenCount);
REQUIRE(result);
}
}

View File

@ -1,10 +1,9 @@
#include <catch2/catch.hpp>
#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]")
{

View File

@ -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)
{

View File

@ -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]")
{

View File

@ -0,0 +1,848 @@
#include <catch2/catch.hpp>
#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<HeaderParserValue> lexer_t;
typedef MockSequence<HeaderParserValue> sequence_t;
typedef sequence_t::parent_t::matcher_t matcher_t;
typedef SequenceResult<HeaderParserValue> sequence_result_t;
typedef HeaderMatcherFactory factory_t;
class MatchersTestsHelper
{
std::unique_ptr<MockSequenceState> m_mock_state;
std::unique_ptr<lexer_t> m_lexer;
std::unique_ptr<sequence_t> m_sequence;
unsigned m_consumed_token_count;
public:
MatchersTestsHelper()
: m_mock_state(std::make_unique<MockSequenceState>()),
m_sequence(std::make_unique<sequence_t>()),
m_consumed_token_count(0)
{
}
void Tokens(std::initializer_list<Movable<HeaderParserValue>> tokens)
{
m_lexer = std::make_unique<lexer_t>(tokens, HeaderParserValue::EndOfFile(TokenPos()));
}
void Matchers(const std::initializer_list<Movable<std::unique_ptr<matcher_t>>> matchers) const
{
m_sequence->AddMockMatchers(matchers);
}
void LabeledMatchers(const std::initializer_list<Movable<std::unique_ptr<matcher_t>>> matchers, const int label) const
{
m_sequence->AddMockLabeledMatchers(matchers, label);
}
_NODISCARD factory_t Factory() const
{
return HeaderMatcherFactory(m_sequence->GetLabelSupplier());
}
void MatchCallback(std::function<void(sequence_result_t& result)> 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<std::reference_wrapper<const HeaderParserValue>>& 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);
}
}

View File

@ -0,0 +1,61 @@
#pragma once
#include <vector>
#include <iterator>
#include "Utils/ClassUtils.h"
#include "Parsing/ILexer.h"
template <typename TokenType>
class MockLexer final : public ILexer<TokenType>
{
// TokenType must inherit IParserValue
static_assert(std::is_base_of<IParserValue, TokenType>::value);
std::vector<TokenType> m_tokens;
//std::vector<HeaderParserValue> m_tokens;
TokenType m_eof;
int m_pop_count;
public:
MockLexer(std::initializer_list<Movable<TokenType>> 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;
}
};

View File

@ -0,0 +1,49 @@
#pragma once
#include "Parsing/Sequence/AbstractSequence.h"
struct MockSequenceState
{
char m_dummy;
};
template<typename TokenType>
class MockSequence final : public AbstractSequence<TokenType, MockSequenceState>
{
public:
typedef AbstractSequence<TokenType, MockSequenceState> parent_t;
private:
using parent_t::AddMatchers;
using parent_t::AddLabeledMatchers;
std::function<void(SequenceResult<TokenType>&)> m_handler;
protected:
void ProcessMatch(MockSequenceState* state, SequenceResult<TokenType>& result) const override
{
if (m_handler)
m_handler(result);
}
public:
void AddMockMatchers(std::initializer_list<Movable<std::unique_ptr<typename parent_t::matcher_t>>> matchers)
{
AddMatchers(matchers);
}
void AddMockLabeledMatchers(std::initializer_list<Movable<std::unique_ptr<typename parent_t::matcher_t>>> matchers, const int label)
{
AddLabeledMatchers(matchers, label);
}
void Handle(std::function<void(SequenceResult<TokenType>&)> handler)
{
m_handler = std::move(handler);
}
IMatcherForLabelSupplier<TokenType>* GetLabelSupplier()
{
return this;
}
};