Add support for defineproxy if directive parsing

This commit is contained in:
Jan 2021-11-25 18:01:18 +01:00
parent 887d14df54
commit c65c57ce72
12 changed files with 385 additions and 52 deletions

View File

@ -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--;

View File

@ -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<SimpleParserValue>& result) const
{
state->m_expression = DefinesIfExpressionMatchers(state).ProcessExpression(result);
}

View File

@ -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<SimpleParserValue>& result) const override;
};

View File

@ -0,0 +1,22 @@
#include "DefinesIfDirectiveParser.h"
#include "DefinesIfDirectiveExpressionSequence.h"
DefinesIfDirectiveParser::DefinesIfDirectiveParser(ILexer<SimpleParserValue>* lexer, const std::map<std::string, DefinesStreamProxy::Define>& defines)
: AbstractParser<SimpleParserValue, DefinesIfDirectiveParsingState>(lexer, std::make_unique<DefinesIfDirectiveParsingState>(defines))
{
}
const std::vector<AbstractParser<SimpleParserValue, DefinesIfDirectiveParsingState>::sequence_t*>& DefinesIfDirectiveParser::GetTestsForState()
{
static std::vector<sequence_t*> sequences
{
new DefinesIfDirectiveExpressionSequence()
};
return sequences;
}
std::unique_ptr<ISimpleExpression> DefinesIfDirectiveParser::GetParsedExpression() const
{
return std::move(m_state->m_expression);
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "DefinesIfDirectiveParsingState.h"
#include "Parsing/Impl/AbstractParser.h"
#include "Parsing/Simple/SimpleParserValue.h"
class DefinesIfDirectiveParser final : public AbstractParser<SimpleParserValue, DefinesIfDirectiveParsingState>
{
public:
DefinesIfDirectiveParser(ILexer<SimpleParserValue>* lexer, const std::map<std::string, DefinesStreamProxy::Define>& defines);
protected:
const std::vector<sequence_t*>& GetTestsForState() override;
public:
_NODISCARD std::unique_ptr<ISimpleExpression> GetParsedExpression() const;
};

View File

@ -0,0 +1,6 @@
#include "DefinesIfDirectiveParsingState.h"
DefinesIfDirectiveParsingState::DefinesIfDirectiveParsingState(const std::map<std::string, DefinesStreamProxy::Define>& defines)
: m_defines(defines)
{
}

View File

@ -0,0 +1,16 @@
#pragma once
#include <map>
#include <memory>
#include "Parsing/Impl/DefinesStreamProxy.h"
#include "Parsing/Simple/Expression/ISimpleExpression.h"
class DefinesIfDirectiveParsingState
{
public:
const std::map<std::string, DefinesStreamProxy::Define>& m_defines;
std::unique_ptr<ISimpleExpression> m_expression;
explicit DefinesIfDirectiveParsingState(const std::map<std::string, DefinesStreamProxy::Define>& defines);
};

View File

@ -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<SimpleExpressionMatchers::matcher_t> 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<ISimpleExpression> DefinesIfExpressionMatchers::ProcessOperandExtension(SequenceResult<SimpleParserValue>& 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<SimpleExpressionValue>(1);
return std::make_unique<SimpleExpressionValue>(0);
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <memory>
#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<matcher_t> ParseOperandExtension(const supplier_t* labelSupplier) const override;
std::unique_ptr<ISimpleExpression> ProcessOperandExtension(SequenceResult<SimpleParserValue>& result) const override;
public:
DefinesIfExpressionMatchers();
explicit DefinesIfExpressionMatchers(const DefinesIfDirectiveParsingState* state);
};

View File

@ -3,52 +3,15 @@
#include <sstream>
#include <utility>
#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<ISimpleExpression> m_expression;
};
class DefinesIfDirectiveParser final : public AbstractParser<SimpleParserValue, DefinesIfDirectiveParsingState>
{
protected:
explicit DefinesIfDirectiveParser(ILexer<SimpleParserValue>* lexer)
: AbstractParser<SimpleParserValue, DefinesIfDirectiveParsingState>(lexer, std::make_unique<DefinesIfDirectiveParsingState>())
{
}
const std::vector<sequence_t*>& GetTestsForState() override
{
static std::vector<sequence_t*> sequences
{
};
return sequences;
}
public:
static bool EvaluateIfDirective(std::map<std::string, DefinesStreamProxy::Define>& 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<ISimpleExpression> 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<char>::length(IF_DIRECTIVE)
|| !MatchString(line, currentPos, IF_DIRECTIVE, std::char_traits<char>::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<char>::length(ELIF_DIRECTIVE)
|| !MatchString(line, currentPos, ELIF_DIRECTIVE, std::char_traits<char>::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();
}

View File

@ -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<std::string, Define> m_defines;
std::stack<bool> m_modes;
std::stack<BlockMode> m_modes;
unsigned m_ignore_depth;
bool m_in_define;
@ -56,8 +66,11 @@ private:
static int GetLineEndEscapePos(const ParserLine& line);
static std::vector<std::string> MatchDefineParameters(const ParserLine& line, unsigned& parameterPosition);
void ContinueDefine(const ParserLine& line);
_NODISCARD std::unique_ptr<ISimpleExpression> 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);

View File

@ -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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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());
}
}