From c65c57ce728c885036c1e60442edb32f24088b16 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 25 Nov 2021 18:01:18 +0100 Subject: [PATCH] Add support for defineproxy if directive parsing --- src/Parser/Parsing/Impl/AbstractLexer.h | 5 +- .../DefinesIfDirectiveExpressionSequence.cpp | 16 ++ .../DefinesIfDirectiveExpressionSequence.h | 13 ++ .../Impl/Defines/DefinesIfDirectiveParser.cpp | 22 +++ .../Impl/Defines/DefinesIfDirectiveParser.h | 17 ++ .../DefinesIfDirectiveParsingState.cpp | 6 + .../Defines/DefinesIfDirectiveParsingState.h | 16 ++ .../Defines/DefinesIfExpressionMatchers.cpp | 37 ++++ .../Defines/DefinesIfExpressionMatchers.h | 21 +++ .../Parsing/Impl/DefinesStreamProxy.cpp | 158 ++++++++++++------ src/Parser/Parsing/Impl/DefinesStreamProxy.h | 15 +- .../Parsing/Impl/DefinesStreamProxyTests.cpp | 111 ++++++++++++ 12 files changed, 385 insertions(+), 52 deletions(-) create mode 100644 src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveExpressionSequence.cpp create mode 100644 src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveExpressionSequence.h create mode 100644 src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParser.cpp create mode 100644 src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParser.h create mode 100644 src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParsingState.cpp create mode 100644 src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParsingState.h create mode 100644 src/Parser/Parsing/Impl/Defines/DefinesIfExpressionMatchers.cpp create mode 100644 src/Parser/Parsing/Impl/Defines/DefinesIfExpressionMatchers.h diff --git a/src/Parser/Parsing/Impl/AbstractLexer.h b/src/Parser/Parsing/Impl/AbstractLexer.h index 90ada5aa..4fc22002 100644 --- a/src/Parser/Parsing/Impl/AbstractLexer.h +++ b/src/Parser/Parsing/Impl/AbstractLexer.h @@ -317,8 +317,9 @@ public: { m_token_cache.erase(m_token_cache.begin(), m_token_cache.begin() + amount); const auto& firstToken = m_token_cache.front(); - while (m_line_cache.front().m_line_number != firstToken.GetPos().m_line - || *m_line_cache.front().m_filename != firstToken.GetPos().m_filename.get()) + while (!m_line_cache.empty() + && (m_line_cache.front().m_line_number != firstToken.GetPos().m_line + || *m_line_cache.front().m_filename != firstToken.GetPos().m_filename.get())) { m_line_cache.pop_front(); m_line_index--; diff --git a/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveExpressionSequence.cpp b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveExpressionSequence.cpp new file mode 100644 index 00000000..d3e68051 --- /dev/null +++ b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveExpressionSequence.cpp @@ -0,0 +1,16 @@ +#include "DefinesIfDirectiveExpressionSequence.h" + +#include "Parsing/Simple/Matcher/SimpleMatcherFactory.h" +#include "DefinesIfExpressionMatchers.h" + +DefinesIfDirectiveExpressionSequence::DefinesIfDirectiveExpressionSequence() +{ + const SimpleMatcherFactory create(this); + AddLabeledMatchers(DefinesIfExpressionMatchers().Expression(this), SimpleExpressionMatchers::LABEL_EXPRESSION); + AddMatchers(create.Label(SimpleExpressionMatchers::LABEL_EXPRESSION)); +} + +void DefinesIfDirectiveExpressionSequence::ProcessMatch(DefinesIfDirectiveParsingState* state, SequenceResult& result) const +{ + state->m_expression = DefinesIfExpressionMatchers(state).ProcessExpression(result); +} diff --git a/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveExpressionSequence.h b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveExpressionSequence.h new file mode 100644 index 00000000..b06b301a --- /dev/null +++ b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveExpressionSequence.h @@ -0,0 +1,13 @@ +#pragma once + +#include "DefinesIfDirectiveParser.h" +#include "DefinesIfDirectiveParsingState.h" + +class DefinesIfDirectiveExpressionSequence final : public DefinesIfDirectiveParser::sequence_t +{ +public: + DefinesIfDirectiveExpressionSequence(); + +protected: + void ProcessMatch(DefinesIfDirectiveParsingState* state, SequenceResult& result) const override; +}; diff --git a/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParser.cpp b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParser.cpp new file mode 100644 index 00000000..4b93ad3d --- /dev/null +++ b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParser.cpp @@ -0,0 +1,22 @@ +#include "DefinesIfDirectiveParser.h" + +#include "DefinesIfDirectiveExpressionSequence.h" + +DefinesIfDirectiveParser::DefinesIfDirectiveParser(ILexer* lexer, const std::map& defines) + : AbstractParser(lexer, std::make_unique(defines)) +{ +} + +const std::vector::sequence_t*>& DefinesIfDirectiveParser::GetTestsForState() +{ + static std::vector sequences + { + new DefinesIfDirectiveExpressionSequence() + }; + return sequences; +} + +std::unique_ptr DefinesIfDirectiveParser::GetParsedExpression() const +{ + return std::move(m_state->m_expression); +} diff --git a/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParser.h b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParser.h new file mode 100644 index 00000000..e962dde6 --- /dev/null +++ b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParser.h @@ -0,0 +1,17 @@ +#pragma once + +#include "DefinesIfDirectiveParsingState.h" +#include "Parsing/Impl/AbstractParser.h" +#include "Parsing/Simple/SimpleParserValue.h" + +class DefinesIfDirectiveParser final : public AbstractParser +{ +public: + DefinesIfDirectiveParser(ILexer* lexer, const std::map& defines); + +protected: + const std::vector& GetTestsForState() override; + +public: + _NODISCARD std::unique_ptr GetParsedExpression() const; +}; diff --git a/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParsingState.cpp b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParsingState.cpp new file mode 100644 index 00000000..8eaaf05a --- /dev/null +++ b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParsingState.cpp @@ -0,0 +1,6 @@ +#include "DefinesIfDirectiveParsingState.h" + +DefinesIfDirectiveParsingState::DefinesIfDirectiveParsingState(const std::map& defines) + : m_defines(defines) +{ +} diff --git a/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParsingState.h b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParsingState.h new file mode 100644 index 00000000..bbbd611f --- /dev/null +++ b/src/Parser/Parsing/Impl/Defines/DefinesIfDirectiveParsingState.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#include "Parsing/Impl/DefinesStreamProxy.h" +#include "Parsing/Simple/Expression/ISimpleExpression.h" + +class DefinesIfDirectiveParsingState +{ +public: + const std::map& m_defines; + std::unique_ptr m_expression; + + explicit DefinesIfDirectiveParsingState(const std::map& defines); +}; diff --git a/src/Parser/Parsing/Impl/Defines/DefinesIfExpressionMatchers.cpp b/src/Parser/Parsing/Impl/Defines/DefinesIfExpressionMatchers.cpp new file mode 100644 index 00000000..7d6c9cb1 --- /dev/null +++ b/src/Parser/Parsing/Impl/Defines/DefinesIfExpressionMatchers.cpp @@ -0,0 +1,37 @@ +#include "DefinesIfExpressionMatchers.h" + +#include "Parsing/Simple/Matcher/SimpleMatcherFactory.h" + +DefinesIfExpressionMatchers::DefinesIfExpressionMatchers() + : DefinesIfExpressionMatchers(nullptr) +{ +} + +DefinesIfExpressionMatchers::DefinesIfExpressionMatchers(const DefinesIfDirectiveParsingState* state) + : SimpleExpressionMatchers(false, false, true, true), + m_state(state) +{ +} + +std::unique_ptr DefinesIfExpressionMatchers::ParseOperandExtension(const supplier_t* labelSupplier) const +{ + const SimpleMatcherFactory create(labelSupplier); + + return create.And({ + create.Keyword("defined"), + create.Char('('), + create.Identifier().Capture(CAPTURE_DEFINE_NAME), + create.Char(')'), + }); +} + +std::unique_ptr DefinesIfExpressionMatchers::ProcessOperandExtension(SequenceResult& result) const +{ + const auto& defineCapture = result.NextCapture(CAPTURE_DEFINE_NAME); + + assert(m_state); + if(m_state && m_state->m_defines.find(defineCapture.IdentifierValue()) != m_state->m_defines.end()) + return std::make_unique(1); + + return std::make_unique(0); +} diff --git a/src/Parser/Parsing/Impl/Defines/DefinesIfExpressionMatchers.h b/src/Parser/Parsing/Impl/Defines/DefinesIfExpressionMatchers.h new file mode 100644 index 00000000..95995c8a --- /dev/null +++ b/src/Parser/Parsing/Impl/Defines/DefinesIfExpressionMatchers.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "DefinesIfDirectiveParsingState.h" +#include "Parsing/Simple/Expression/SimpleExpressionMatchers.h" + +class DefinesIfExpressionMatchers final : public SimpleExpressionMatchers +{ + static constexpr auto CAPTURE_DEFINE_NAME = CAPTURE_OFFSET_EXPRESSION_EXT + 1; + + const DefinesIfDirectiveParsingState* m_state; + +protected: + std::unique_ptr ParseOperandExtension(const supplier_t* labelSupplier) const override; + std::unique_ptr ProcessOperandExtension(SequenceResult& result) const override; + +public: + DefinesIfExpressionMatchers(); + explicit DefinesIfExpressionMatchers(const DefinesIfDirectiveParsingState* state); +}; diff --git a/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp b/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp index 45d63e9a..9523db6f 100644 --- a/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp +++ b/src/Parser/Parsing/Impl/DefinesStreamProxy.cpp @@ -3,52 +3,15 @@ #include #include +#include "Utils/ClassUtils.h" #include "AbstractParser.h" #include "ParserSingleInputStream.h" +#include "Defines/DefinesIfDirectiveParser.h" #include "Parsing/ParsingException.h" #include "Parsing/Simple/SimpleLexer.h" #include "Parsing/Simple/Expression/ISimpleExpression.h" - -class DefinesIfDirectiveParsingState -{ -public: - std::unique_ptr m_expression; -}; - -class DefinesIfDirectiveParser final : public AbstractParser -{ -protected: - explicit DefinesIfDirectiveParser(ILexer* lexer) - : AbstractParser(lexer, std::make_unique()) - { - } - - const std::vector& GetTestsForState() override - { - static std::vector sequences - { - }; - return sequences; - } - -public: - static bool EvaluateIfDirective(std::map& defines, const std::string& value) - { - std::istringstream ss(value); - ParserSingleInputStream inputStream(ss, ""); - SimpleLexer::Config config{}; - config.m_emit_new_line_tokens = false; - config.m_read_numbers = true; - config.m_read_strings = false; - SimpleLexer lexer(&inputStream, std::move(config)); - DefinesIfDirectiveParser parser(&lexer); - if (!parser.Parse()) - return false; - - const auto& expression = parser.m_state->m_expression; - return expression->IsStatic() && expression->Evaluate().IsTruthy(); - } -}; +#include "Parsing/Simple/Expression/SimpleExpressionMatchers.h" +#include "Parsing/Simple/Matcher/SimpleMatcherFactory.h" DefinesStreamProxy::DefineParameterPosition::DefineParameterPosition() : m_parameter_index(0u), @@ -315,6 +278,101 @@ bool DefinesStreamProxy::MatchUndefDirective(const ParserLine& line, const unsig return true; } +std::unique_ptr DefinesStreamProxy::ParseIfExpression(const std::string& expressionString) const +{ + std::istringstream ss(expressionString); + ParserSingleInputStream inputStream(ss, "#if expression"); + + SimpleLexer::Config lexerConfig; + lexerConfig.m_emit_new_line_tokens = false; + lexerConfig.m_read_numbers = true; + lexerConfig.m_read_strings = false; + SimpleExpressionMatchers().ApplyTokensToLexerConfig(lexerConfig); + + SimpleLexer lexer(&inputStream, std::move(lexerConfig)); + DefinesIfDirectiveParser parser(&lexer, m_defines); + + if (!parser.Parse()) + return nullptr; + + return parser.GetParsedExpression(); +} + +bool DefinesStreamProxy::MatchIfDirective(const ParserLine& line, const unsigned directiveStartPosition, const unsigned directiveEndPosition) +{ + auto currentPos = directiveStartPosition; + + if (directiveEndPosition - directiveStartPosition != std::char_traits::length(IF_DIRECTIVE) + || !MatchString(line, currentPos, IF_DIRECTIVE, std::char_traits::length(IF_DIRECTIVE))) + { + return false; + } + + if (!m_modes.empty() && m_modes.top() != BlockMode::IN_BLOCK) + { + m_ignore_depth++; + return true; + } + + if (!SkipWhitespace(line, currentPos)) + throw ParsingException(CreatePos(line, currentPos), "Cannot if without an expression."); + + const auto expressionString = line.m_line.substr(currentPos, line.m_line.size() - currentPos); + + if (expressionString.empty()) + throw ParsingException(CreatePos(line, currentPos), "Cannot if without an expression."); + + const auto expression = ParseIfExpression(expressionString); + if (!expression) + throw ParsingException(CreatePos(line, currentPos), "Failed to parse if expression"); + + m_modes.push(expression->Evaluate().IsTruthy() ? BlockMode::IN_BLOCK : BlockMode::NOT_IN_BLOCK); + + return true; +} + +bool DefinesStreamProxy::MatchElIfDirective(const ParserLine& line, const unsigned directiveStartPosition, const unsigned directiveEndPosition) +{ + auto currentPos = directiveStartPosition; + + if (directiveEndPosition - directiveStartPosition != std::char_traits::length(ELIF_DIRECTIVE) + || !MatchString(line, currentPos, ELIF_DIRECTIVE, std::char_traits::length(ELIF_DIRECTIVE))) + { + return false; + } + + if (m_ignore_depth > 0) + return true; + + if (m_modes.empty()) + throw ParsingException(CreatePos(line, currentPos), "Cannot use elif without if"); + + if (m_modes.top() == BlockMode::BLOCK_BLOCKED) + return true; + + if(m_modes.top() == BlockMode::IN_BLOCK) + { + m_modes.top() = BlockMode::BLOCK_BLOCKED; + return true; + } + + if (!SkipWhitespace(line, currentPos)) + throw ParsingException(CreatePos(line, currentPos), "Cannot elif without an expression."); + + const auto expressionString = line.m_line.substr(currentPos, line.m_line.size() - currentPos); + + if (expressionString.empty()) + throw ParsingException(CreatePos(line, currentPos), "Cannot elif without an expression."); + + const auto expression = ParseIfExpression(expressionString); + if (!expression) + throw ParsingException(CreatePos(line, currentPos), "Failed to parse elif expression"); + + m_modes.top() = expression->Evaluate().IsTruthy() ? BlockMode::IN_BLOCK : BlockMode::NOT_IN_BLOCK; + + return true; +} + bool DefinesStreamProxy::MatchIfdefDirective(const ParserLine& line, const unsigned directiveStartPosition, const unsigned directiveEndPosition) { auto currentPos = directiveStartPosition; @@ -332,7 +390,7 @@ bool DefinesStreamProxy::MatchIfdefDirective(const ParserLine& line, const unsig reverse = true; } - if (!m_modes.empty() && !m_modes.top()) + if (!m_modes.empty() && m_modes.top() != BlockMode::IN_BLOCK) { m_ignore_depth++; return true; @@ -349,9 +407,9 @@ bool DefinesStreamProxy::MatchIfdefDirective(const ParserLine& line, const unsig const auto entry = m_defines.find(name); if (entry != m_defines.end()) - m_modes.push(!reverse); + m_modes.push(!reverse ? BlockMode::IN_BLOCK : BlockMode::NOT_IN_BLOCK); else - m_modes.push(reverse); + m_modes.push(reverse ? BlockMode::IN_BLOCK : BlockMode::NOT_IN_BLOCK); return true; } @@ -369,11 +427,11 @@ bool DefinesStreamProxy::MatchElseDirective(const ParserLine& line, const unsign if (m_ignore_depth > 0) return true; - if (!m_modes.empty()) - m_modes.top() = !m_modes.top(); - else + if (m_modes.empty()) throw ParsingException(CreatePos(line, currentPos), "Cannot use else without ifdef"); + m_modes.top() = m_modes.top() == BlockMode::NOT_IN_BLOCK ? BlockMode::IN_BLOCK : BlockMode::BLOCK_BLOCKED; + return true; } @@ -411,7 +469,7 @@ bool DefinesStreamProxy::MatchDirectives(const ParserLine& line) directiveStartPos++; - if (m_modes.empty() || m_modes.top() == true) + if (m_modes.empty() || m_modes.top() == BlockMode::IN_BLOCK) { if (MatchDefineDirective(line, directiveStartPos, directiveEndPos) || MatchUndefDirective(line, directiveStartPos, directiveEndPos)) @@ -419,6 +477,8 @@ bool DefinesStreamProxy::MatchDirectives(const ParserLine& line) } return MatchIfdefDirective(line, directiveStartPos, directiveEndPos) + || MatchIfDirective(line, directiveStartPos, directiveEndPos) + || MatchElIfDirective(line, directiveStartPos, directiveEndPos) || MatchElseDirective(line, directiveStartPos, directiveEndPos) || MatchEndifDirective(line, directiveStartPos, directiveEndPos); } @@ -583,7 +643,7 @@ ParserLine DefinesStreamProxy::NextLine() ContinueDefine(line); line.m_line.clear(); } - else if (MatchDirectives(line) || !m_modes.empty() && !m_modes.top()) + else if (MatchDirectives(line) || !m_modes.empty() && m_modes.top() != BlockMode::IN_BLOCK) { line.m_line.clear(); } diff --git a/src/Parser/Parsing/Impl/DefinesStreamProxy.h b/src/Parser/Parsing/Impl/DefinesStreamProxy.h index 221b5faf..82edc827 100644 --- a/src/Parser/Parsing/Impl/DefinesStreamProxy.h +++ b/src/Parser/Parsing/Impl/DefinesStreamProxy.h @@ -6,11 +6,14 @@ #include "AbstractDirectiveStreamProxy.h" #include "Parsing/IParserLineStream.h" +#include "Parsing/Simple/Expression/ISimpleExpression.h" class DefinesStreamProxy final : public AbstractDirectiveStreamProxy { static constexpr const char* DEFINE_DIRECTIVE = "define"; static constexpr const char* UNDEF_DIRECTIVE = "undef"; + static constexpr const char* IF_DIRECTIVE = "if"; + static constexpr const char* ELIF_DIRECTIVE = "elif"; static constexpr const char* IFDEF_DIRECTIVE = "ifdef"; static constexpr const char* IFNDEF_DIRECTIVE = "ifndef"; static constexpr const char* ELSE_DIRECTIVE = "else"; @@ -43,9 +46,16 @@ public: }; private: + enum class BlockMode + { + NOT_IN_BLOCK, + IN_BLOCK, + BLOCK_BLOCKED + }; + IParserLineStream* const m_stream; std::map m_defines; - std::stack m_modes; + std::stack m_modes; unsigned m_ignore_depth; bool m_in_define; @@ -56,8 +66,11 @@ private: static int GetLineEndEscapePos(const ParserLine& line); static std::vector MatchDefineParameters(const ParserLine& line, unsigned& parameterPosition); void ContinueDefine(const ParserLine& line); + _NODISCARD std::unique_ptr ParseIfExpression(const std::string& expressionString) const; _NODISCARD bool MatchDefineDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition); _NODISCARD bool MatchUndefDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition); + _NODISCARD bool MatchIfDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition); + _NODISCARD bool MatchElIfDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition); _NODISCARD bool MatchIfdefDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition); _NODISCARD bool MatchElseDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition); _NODISCARD bool MatchEndifDirective(const ParserLine& line, unsigned directiveStartPosition, unsigned directiveEndPosition); diff --git a/test/ParserTests/Parsing/Impl/DefinesStreamProxyTests.cpp b/test/ParserTests/Parsing/Impl/DefinesStreamProxyTests.cpp index 941c8cc4..4a1d5f71 100644 --- a/test/ParserTests/Parsing/Impl/DefinesStreamProxyTests.cpp +++ b/test/ParserTests/Parsing/Impl/DefinesStreamProxyTests.cpp @@ -470,4 +470,115 @@ namespace test::parsing::impl::defines_stream_proxy REQUIRE(proxy.Eof()); } + + TEST_CASE("DefinesStreamProxy: Ensure simple if is working with truthy value", "[parsing][parsingstream]") + { + const std::vector lines + { + "#if 1", + "Hello World", + "#endif", + }; + + MockParserLineStream mockStream(lines); + DefinesStreamProxy proxy(&mockStream); + + ExpectLine(&proxy, 1, ""); + ExpectLine(&proxy, 2, "Hello World"); + ExpectLine(&proxy, 3, ""); + + REQUIRE(proxy.Eof()); + } + + TEST_CASE("DefinesStreamProxy: Ensure simple if is working with non-truthy value", "[parsing][parsingstream]") + { + const std::vector lines + { + "#if 0", + "Hello World", + "#endif", + }; + + MockParserLineStream mockStream(lines); + DefinesStreamProxy proxy(&mockStream); + + ExpectLine(&proxy, 1, ""); + ExpectLine(&proxy, 2, ""); + ExpectLine(&proxy, 3, ""); + + REQUIRE(proxy.Eof()); + } + + TEST_CASE("DefinesStreamProxy: Ensure simple if is working with calculated values", "[parsing][parsingstream]") + { + const std::vector lines + { + "#if 0 || 1", + "Hello World", + "#endif", + "#if 0 && 1", + "Hello World", + "#endif", + }; + + MockParserLineStream mockStream(lines); + DefinesStreamProxy proxy(&mockStream); + + ExpectLine(&proxy, 1, ""); + ExpectLine(&proxy, 2, "Hello World"); + ExpectLine(&proxy, 3, ""); + ExpectLine(&proxy, 4, ""); + ExpectLine(&proxy, 5, ""); + ExpectLine(&proxy, 6, ""); + + REQUIRE(proxy.Eof()); + } + + TEST_CASE("DefinesStreamProxy: Ensure can handle defined operator with defined definition", "[parsing][parsingstream]") + { + const std::vector lines + { + "#define someStuff 1", + "#if defined(someStuff)", + "Hello World", + "#endif", + }; + + MockParserLineStream mockStream(lines); + DefinesStreamProxy proxy(&mockStream); + + ExpectLine(&proxy, 1, ""); + ExpectLine(&proxy, 2, ""); + ExpectLine(&proxy, 3, "Hello World"); + ExpectLine(&proxy, 4, ""); + + REQUIRE(proxy.Eof()); + } + + TEST_CASE("DefinesStreamProxy: Ensure can handle defined operator with undefined definition", "[parsing][parsingstream]") + { + const std::vector lines + { + "#define someStuff 1", + "#if defined(someStuff) && defined(thisIsNotDefined)", + "Hello World", + "#endif", + "#if defined(thisIsNotDefined)", + "Hello World", + "#endif", + }; + + MockParserLineStream mockStream(lines); + DefinesStreamProxy proxy(&mockStream); + + ExpectLine(&proxy, 1, ""); + ExpectLine(&proxy, 2, ""); + ExpectLine(&proxy, 3, ""); + ExpectLine(&proxy, 4, ""); + ExpectLine(&proxy, 5, ""); + ExpectLine(&proxy, 6, ""); + ExpectLine(&proxy, 7, ""); + + REQUIRE(proxy.Eof()); + } }