Merge pull request #57 from Laupetin/fix/macro-expansion-multiple-lines

Fix macro expansion and definition over multiple lines
This commit is contained in:
Jan 2023-12-24 01:11:22 +01:00 committed by GitHub
commit 846a9d36e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 612 additions and 198 deletions

View File

@ -86,11 +86,18 @@ jobs:
- name: Test
working-directory: ${{ github.workspace }}/build/lib/Release_x86/tests
run: |
$combinedExitCode = 0
./ObjCommonTests
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
./ObjLoadingTests
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
./ParserTests
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
./ZoneCodeGeneratorLibTests
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
./ZoneCommonTests
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
exit $combinedExitCode
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:

View File

@ -123,7 +123,10 @@ DefinesStreamProxy::DefinesStreamProxy(IParserLineStream* stream, const bool ski
: m_stream(stream),
m_skip_directive_lines(skipDirectiveLines),
m_ignore_depth(0),
m_in_define(false)
m_in_define(false),
m_parameter_state(ParameterState::NOT_IN_PARAMETERS),
m_current_macro(nullptr),
m_macro_parameter_state(ParameterState::NOT_IN_PARAMETERS)
{
}
@ -142,12 +145,19 @@ int DefinesStreamProxy::GetLineEndEscapePos(const ParserLine& line)
return -1;
}
void DefinesStreamProxy::ContinueDefine(const ParserLine& line)
void DefinesStreamProxy::ContinueDefine(const ParserLine& line, const unsigned currentPos)
{
const auto lineEndEscapePos = GetLineEndEscapePos(line);
if (lineEndEscapePos < 0)
{
if (m_parameter_state != ParameterState::NOT_IN_PARAMETERS)
throw ParsingException(CreatePos(line, currentPos), "Unclosed macro parameters");
if (currentPos <= 0)
m_current_define_value << line.m_line;
else
m_current_define_value << line.m_line.substr(currentPos);
m_current_define.m_value = m_current_define_value.str();
m_current_define.IdentifyParameters(m_current_define_parameters);
AddDefine(std::move(m_current_define));
@ -159,49 +169,58 @@ void DefinesStreamProxy::ContinueDefine(const ParserLine& line)
}
else
{
if (line.m_line.size() > static_cast<unsigned>(lineEndEscapePos))
m_current_define_value << line.m_line.substr(0, static_cast<unsigned>(lineEndEscapePos));
if (line.m_line.size() > static_cast<unsigned>(lineEndEscapePos) && currentPos < static_cast<unsigned>(lineEndEscapePos))
m_current_define_value << line.m_line.substr(currentPos, static_cast<unsigned>(lineEndEscapePos) - currentPos);
}
}
std::vector<std::string> DefinesStreamProxy::MatchDefineParameters(const ParserLine& line, unsigned& parameterPosition)
void DefinesStreamProxy::ContinueParameters(const ParserLine& line, unsigned& currentPos)
{
if (line.m_line[parameterPosition] != '(')
return std::vector<std::string>();
parameterPosition++;
std::vector<std::string> parameters;
const auto lineEndEscapePos = GetLineEndEscapePos(line);
while (true)
{
if (!SkipWhitespace(line, parameterPosition) || parameterPosition >= line.m_line.size())
throw ParsingException(CreatePos(line, parameterPosition), "Invalid define parameter list");
if (!SkipWhitespace(line, currentPos))
throw ParsingException(CreatePos(line, currentPos), "Invalid define parameter list");
const auto nameStartPos = parameterPosition;
if (!ExtractIdentifier(line, parameterPosition))
throw ParsingException(CreatePos(line, parameterPosition), "Cannot extract name of parameter of define");
if (lineEndEscapePos >= 0 && currentPos >= static_cast<unsigned>(lineEndEscapePos))
return;
parameters.emplace_back(std::string(line.m_line, nameStartPos, parameterPosition - nameStartPos));
if (currentPos >= line.m_line.size())
throw ParsingException(CreatePos(line, currentPos), "Invalid define parameter list");
if (!SkipWhitespace(line, parameterPosition))
throw ParsingException(CreatePos(line, parameterPosition), "Unclosed define parameters");
if (parameterPosition >= line.m_line.size())
throw ParsingException(CreatePos(line, parameterPosition), "Unclosed define parameters");
if (line.m_line[parameterPosition] == ')')
if ((m_parameter_state == ParameterState::AFTER_OPEN || m_parameter_state == ParameterState::AFTER_PARAM) && line.m_line[currentPos] == ')')
{
parameterPosition++;
break;
currentPos++;
m_parameter_state = ParameterState::NOT_IN_PARAMETERS;
return;
}
if (line.m_line[parameterPosition] != ',')
throw ParsingException(CreatePos(line, parameterPosition), "Unknown symbol in define parameter list");
parameterPosition++;
if (m_parameter_state == ParameterState::AFTER_PARAM && line.m_line[currentPos] == ',')
{
currentPos++;
m_parameter_state = ParameterState::AFTER_COMMA;
continue;
}
return parameters;
const auto nameStartPos = currentPos;
if (!ExtractIdentifier(line, currentPos))
throw ParsingException(CreatePos(line, currentPos), "Cannot extract name of parameter of define");
m_current_define_parameters.emplace_back(line.m_line, nameStartPos, currentPos - nameStartPos);
m_parameter_state = ParameterState::AFTER_PARAM;
}
}
void DefinesStreamProxy::MatchDefineParameters(const ParserLine& line, unsigned& currentPos)
{
m_current_define_parameters = std::vector<std::string>();
if (line.m_line[currentPos] != '(')
return;
m_parameter_state = ParameterState::AFTER_OPEN;
currentPos++;
ContinueParameters(line, currentPos);
}
bool DefinesStreamProxy::MatchDefineDirective(const ParserLine& line, const unsigned directiveStartPosition, const unsigned directiveEndPosition)
@ -223,29 +242,14 @@ bool DefinesStreamProxy::MatchDefineDirective(const ParserLine& line, const unsi
const auto name = line.m_line.substr(nameStartPos, currentPos - nameStartPos);
auto parameters = MatchDefineParameters(line, currentPos);
MatchDefineParameters(line, currentPos);
SkipWhitespace(line, currentPos);
const auto lineEndEscapePos = GetLineEndEscapePos(line);
if (lineEndEscapePos < 0)
{
std::string value;
if (currentPos < line.m_line.size())
value = line.m_line.substr(currentPos + 1);
Define define(name, value);
define.IdentifyParameters(parameters);
AddDefine(std::move(define));
}
else
{
m_in_define = true;
m_current_define = Define(name, std::string());
m_current_define_value.str(std::string());
m_current_define_parameters = std::move(parameters);
if (currentPos < line.m_line.size() && (currentPos + 1) < static_cast<unsigned>(lineEndEscapePos))
m_current_define_value << line.m_line.substr(currentPos + 1, static_cast<unsigned>(lineEndEscapePos) - (currentPos + 1));
}
ContinueDefine(line, currentPos);
return true;
}
@ -276,8 +280,7 @@ bool DefinesStreamProxy::MatchUndefDirective(const ParserLine& line, const unsig
return true;
}
std::unique_ptr<ISimpleExpression>
DefinesStreamProxy::ParseExpression(std::shared_ptr<std::string> fileName, int lineNumber, std::string expressionString) const
std::unique_ptr<ISimpleExpression> DefinesStreamProxy::ParseExpression(std::shared_ptr<std::string> fileName, int lineNumber, std::string expressionString)
{
ParserLine pseudoLine(std::move(fileName), lineNumber, std::move(expressionString));
ExpandDefinedExpressions(pseudoLine);
@ -451,7 +454,7 @@ bool DefinesStreamProxy::MatchEndifDirective(const ParserLine& line, const unsig
return true;
}
bool DefinesStreamProxy::MatchDirectives(const ParserLine& line)
bool DefinesStreamProxy::MatchDirectives(ParserLine& line)
{
unsigned directiveStartPos;
unsigned directiveEndPos;
@ -472,9 +475,9 @@ bool DefinesStreamProxy::MatchDirectives(const ParserLine& line)
|| MatchEndifDirective(line, directiveStartPos, directiveEndPos);
}
bool DefinesStreamProxy::FindDefineForWord(const ParserLine& line, const unsigned wordStart, const unsigned wordEnd, const Define*& value) const
bool DefinesStreamProxy::FindDefineForWord(const std::string& line, const unsigned wordStart, const unsigned wordEnd, const Define*& value) const
{
const std::string word(line.m_line, wordStart, wordEnd - wordStart);
const std::string word(line, wordStart, wordEnd - wordStart);
const auto foundEntry = m_defines.find(word);
if (foundEntry != m_defines.end())
{
@ -485,71 +488,122 @@ bool DefinesStreamProxy::FindDefineForWord(const ParserLine& line, const unsigne
return false;
}
void DefinesStreamProxy::ExtractParametersFromDefineUsage(const ParserLine& line,
const unsigned parameterStart,
unsigned& parameterEnd,
std::vector<std::string>& parameterValues)
void DefinesStreamProxy::ContinueMacroParameters(const ParserLine& line, unsigned& pos)
{
if (line.m_line[parameterStart] != '(')
return;
std::ostringstream currentValue;
auto pos = parameterStart + 1;
auto valueHasStarted = false;
auto parenthesisDepth = 0;
while (true)
const auto lineLength = line.m_line.size();
while (m_macro_parameter_state != ParameterState::NOT_IN_PARAMETERS && pos < lineLength)
{
if (pos >= line.m_line.size())
throw ParsingException(CreatePos(line, pos), "Invalid use of define");
const auto c = line.m_line[pos];
if (c == ',')
{
if (parenthesisDepth > 0)
if (!m_macro_bracket_depth.empty())
{
valueHasStarted = true;
currentValue << c;
m_macro_parameter_state = ParameterState::AFTER_PARAM;
m_current_macro_parameter << c;
}
else
{
parameterValues.emplace_back(currentValue.str());
currentValue.clear();
currentValue.str(std::string());
valueHasStarted = false;
m_macro_parameters.emplace_back(m_current_macro_parameter.str());
m_current_macro_parameter.clear();
m_current_macro_parameter.str(std::string());
m_macro_parameter_state = ParameterState::AFTER_COMMA;
}
}
else if (c == '(')
else if (c == '(' || c == '[' || c == '{')
{
valueHasStarted = true;
parenthesisDepth++;
currentValue << c;
m_macro_parameter_state = ParameterState::AFTER_PARAM;
m_macro_bracket_depth.push(c);
m_current_macro_parameter << c;
}
else if (c == ')')
{
if (parenthesisDepth > 0)
if (!m_macro_bracket_depth.empty())
{
valueHasStarted = true;
parenthesisDepth--;
currentValue << c;
if (m_macro_bracket_depth.top() != '(')
throw ParsingException(CreatePos(line, pos), "Unbalanced brackets in macro parameters");
m_macro_bracket_depth.pop();
m_macro_parameter_state = ParameterState::AFTER_PARAM;
m_current_macro_parameter << c;
}
else if (m_macro_parameter_state == ParameterState::AFTER_COMMA)
{
throw ParsingException(CreatePos(line, pos), "Cannot close macro parameters after comma");
}
else
{
parameterValues.emplace_back(currentValue.str());
parameterEnd = pos + 1;
break;
m_macro_parameters.emplace_back(m_current_macro_parameter.str());
m_macro_parameter_state = ParameterState::NOT_IN_PARAMETERS;
}
}
else if (valueHasStarted || !isspace(c))
else if (c == ']' || c == '}')
{
valueHasStarted = true;
currentValue << c;
if (!m_macro_bracket_depth.empty())
{
const auto otherBracket = c == ']' ? '[' : '{';
if (m_macro_bracket_depth.top() != otherBracket)
throw ParsingException(CreatePos(line, pos), "Unbalanced brackets in macro parameters");
m_macro_bracket_depth.pop();
}
m_macro_parameter_state = ParameterState::AFTER_PARAM;
m_current_macro_parameter << c;
}
else if (m_macro_parameter_state == ParameterState::AFTER_PARAM || !isspace(c))
{
m_macro_parameter_state = ParameterState::AFTER_PARAM;
m_current_macro_parameter << c;
}
pos++;
}
}
void DefinesStreamProxy::ContinueMacro(ParserLine& line)
{
auto pos = 0u;
ContinueMacroParameters(line, pos);
if (m_macro_parameter_state == ParameterState::NOT_IN_PARAMETERS)
{
const auto defineValue = m_current_macro->Render(m_macro_parameters);
if (pos < line.m_line.size())
{
std::ostringstream ss;
ss << defineValue;
ss << std::string(line.m_line, pos, line.m_line.size() - pos);
line.m_line = ss.str();
}
else
{
line.m_line = defineValue;
}
ExpandDefines(line);
}
else
{
line.m_line = "";
}
}
void DefinesStreamProxy::ExtractParametersFromDefineUsage(const ParserLine& line, const unsigned parameterStart, unsigned& parameterEnd)
{
if (line.m_line[parameterStart] != '(')
return;
m_macro_parameter_state = ParameterState::AFTER_OPEN;
m_macro_parameters = std::vector<std::string>();
m_current_macro_parameter.clear();
m_current_macro_parameter.str(std::string());
m_macro_bracket_depth = std::stack<char>();
parameterEnd = parameterStart + 1;
ContinueMacroParameters(line, parameterEnd);
}
bool DefinesStreamProxy::MatchDefinedExpression(const ParserLine& line, unsigned& pos, std::string& definitionName)
{
unsigned currentPos = pos;
@ -601,33 +655,32 @@ void DefinesStreamProxy::ExpandDefinedExpressions(ParserLine& line) const
}
}
void DefinesStreamProxy::ExpandDefines(ParserLine& line) const
void DefinesStreamProxy::ProcessDefine(const ParserLine& line, unsigned& pos, std::ostringstream& out)
{
bool usesDefines;
auto defineIterations = 0u;
ExtractParametersFromDefineUsage(line, pos, pos);
do
if (m_macro_parameter_state == ParameterState::NOT_IN_PARAMETERS)
{
if (defineIterations > MAX_DEFINE_ITERATIONS)
throw ParsingException(CreatePos(line, 1),
"Potential define loop? Exceeded max define iterations of " + std::to_string(MAX_DEFINE_ITERATIONS) + " iterations.");
usesDefines = false;
const auto defineValue = m_current_macro->Render(m_macro_parameters);
out << defineValue;
}
}
bool DefinesStreamProxy::FindNextDefine(const std::string& line, unsigned& pos, unsigned& defineStart, const DefinesStreamProxy::Define*& define)
{
const auto lineSize = line.size();
auto wordStart = 0u;
auto lastWordEnd = 0u;
auto inWord = false;
const Define* value;
std::ostringstream str;
for (auto i = 0u; i < line.m_line.size(); i++)
for (; pos < lineSize; pos++)
{
const auto c = line.m_line[i];
const auto c = line[pos];
if (!inWord)
{
if (isalpha(c) || c == '_')
{
wordStart = i;
wordStart = pos;
inWord = true;
}
}
@ -635,23 +688,12 @@ void DefinesStreamProxy::ExpandDefines(ParserLine& line) const
{
if (!isalnum(c) && c != '_')
{
if (FindDefineForWord(line, wordStart, i, value))
if (FindDefineForWord(line, wordStart, pos, define))
{
std::vector<std::string> parameterValues;
ExtractParametersFromDefineUsage(line, i, i, parameterValues);
const auto defineValue = value->Render(parameterValues);
defineStart = wordStart;
return true;
}
if (!usesDefines)
{
str << std::string(line.m_line, 0, wordStart) << defineValue;
usesDefines = true;
}
else
{
str << std::string(line.m_line, lastWordEnd, wordStart - lastWordEnd) << defineValue;
}
lastWordEnd = i;
}
inWord = false;
}
}
@ -659,28 +701,56 @@ void DefinesStreamProxy::ExpandDefines(ParserLine& line) const
if (inWord)
{
if (FindDefineForWord(line, wordStart, line.m_line.size(), value))
if (FindDefineForWord(line, wordStart, pos, define))
{
const std::vector<std::string> parameterValues;
const auto defineValue = value->Render(parameterValues);
defineStart = wordStart;
return true;
}
}
return false;
}
void DefinesStreamProxy::ExpandDefines(ParserLine& line)
{
auto defineIterations = 0u;
bool usesDefines;
do
{
if (defineIterations > MAX_DEFINE_ITERATIONS)
{
throw ParsingException(CreatePos(line, 1),
"Potential define loop? Exceeded max define iterations of " + std::to_string(MAX_DEFINE_ITERATIONS) + " iterations.");
}
usesDefines = false;
auto pos = 0u;
auto defineStart = 0u;
auto lastDefineEnd = 0u;
std::ostringstream str;
while (FindNextDefine(line.m_line, pos, defineStart, m_current_macro))
{
if (!usesDefines)
{
str << std::string(line.m_line, 0, wordStart) << defineValue;
usesDefines = true;
str << std::string(line.m_line, 0, defineStart);
}
else
{
str << std::string(line.m_line, lastWordEnd, wordStart - lastWordEnd) << defineValue;
}
lastWordEnd = line.m_line.size();
str << std::string(line.m_line, lastDefineEnd, defineStart - (lastDefineEnd));
}
ProcessDefine(line, pos, str);
lastDefineEnd = pos;
}
if (usesDefines)
{
if (lastWordEnd < line.m_line.size())
str << std::string(line.m_line, lastWordEnd, line.m_line.size() - lastWordEnd);
if (lastDefineEnd < line.m_line.size())
str << std::string(line.m_line, lastDefineEnd, line.m_line.size() - lastDefineEnd);
line.m_line = str.str();
}
@ -709,7 +779,15 @@ ParserLine DefinesStreamProxy::NextLine()
{
if (m_in_define)
{
ContinueDefine(line);
unsigned currentPos = 0u;
if (m_parameter_state != ParameterState::NOT_IN_PARAMETERS)
{
ContinueParameters(line, currentPos);
SkipWhitespace(line, currentPos);
}
ContinueDefine(line, currentPos);
if (!m_skip_directive_lines)
{
line.m_line = std::string();
@ -718,6 +796,11 @@ ParserLine DefinesStreamProxy::NextLine()
line = m_stream->NextLine();
}
else if (m_macro_parameter_state != ParameterState::NOT_IN_PARAMETERS)
{
ContinueMacro(line);
return line;
}
else if (MatchDirectives(line) || !m_modes.empty() && m_modes.top() != BlockMode::IN_BLOCK)
{
if (!m_skip_directive_lines)

View File

@ -47,13 +47,21 @@ public:
};
private:
enum class BlockMode
enum class BlockMode : uint8_t
{
NOT_IN_BLOCK,
IN_BLOCK,
BLOCK_BLOCKED
};
enum class ParameterState : uint8_t
{
NOT_IN_PARAMETERS,
AFTER_OPEN,
AFTER_PARAM,
AFTER_COMMA
};
IParserLineStream* const m_stream;
const bool m_skip_directive_lines;
std::map<std::string, Define> m_defines;
@ -61,13 +69,21 @@ private:
unsigned m_ignore_depth;
bool m_in_define;
ParameterState m_parameter_state;
Define m_current_define;
std::ostringstream m_current_define_value;
std::vector<std::string> m_current_define_parameters;
const Define* m_current_macro;
ParameterState m_macro_parameter_state;
std::vector<std::string> m_macro_parameters;
std::ostringstream m_current_macro_parameter;
std::stack<char> m_macro_bracket_depth;
static int GetLineEndEscapePos(const ParserLine& line);
static std::vector<std::string> MatchDefineParameters(const ParserLine& line, unsigned& parameterPosition);
void ContinueDefine(const ParserLine& line);
void MatchDefineParameters(const ParserLine& line, unsigned& currentPos);
void ContinueDefine(const ParserLine& line, unsigned currentPos);
void ContinueParameters(const ParserLine& line, unsigned& currentPos);
_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);
@ -75,24 +91,28 @@ private:
_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);
_NODISCARD bool MatchDirectives(const ParserLine& line);
_NODISCARD bool MatchDirectives(ParserLine& line);
static void
ExtractParametersFromDefineUsage(const ParserLine& line, unsigned parameterStart, unsigned& parameterEnd, std::vector<std::string>& parameterValues);
bool FindDefineForWord(const ParserLine& line, unsigned wordStart, unsigned wordEnd, const Define*& value) const;
void ExtractParametersFromDefineUsage(const ParserLine& line, unsigned parameterStart, unsigned& parameterEnd);
bool FindDefineForWord(const std::string& line, unsigned wordStart, unsigned wordEnd, const Define*& value) const;
static bool MatchDefinedExpression(const ParserLine& line, unsigned& pos, std::string& definitionName);
void ExpandDefinedExpressions(ParserLine& line) const;
void ContinueMacroParameters(const ParserLine& line, unsigned& pos);
void ContinueMacro(ParserLine& line);
void ProcessDefine(const ParserLine& line, unsigned& pos, std::ostringstream& out);
bool FindNextDefine(const std::string& line, unsigned& pos, unsigned& defineStart, const DefinesStreamProxy::Define*& define);
public:
explicit DefinesStreamProxy(IParserLineStream* stream, bool skipDirectiveLines = false);
void AddDefine(Define define);
void Undefine(const std::string& name);
void ExpandDefines(ParserLine& line) const;
void ExpandDefines(ParserLine& line);
_NODISCARD std::unique_ptr<ISimpleExpression> ParseExpression(std::shared_ptr<std::string> fileName, int lineNumber, std::string expressionString) const;
_NODISCARD std::unique_ptr<ISimpleExpression> ParseExpression(std::shared_ptr<std::string> fileName, int lineNumber, std::string expressionString);
ParserLine NextLine() override;
bool IncludeFile(const std::string& filename) override;

View File

@ -6,7 +6,7 @@
#include <exception>
#include <string>
class ParsingException final : std::exception
class ParsingException final : public std::exception
{
TokenPos m_pos;
std::string m_message;

View File

@ -1,8 +1,14 @@
#include "Parsing/Impl/DefinesStreamProxy.h"
#include "Parsing/Mock/MockParserLineStream.h"
#include "Parsing/ParsingException.h"
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/matchers/catch_matchers_exception.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
using namespace Catch::Matchers;
namespace test::parsing::impl::defines_stream_proxy
{
@ -13,9 +19,21 @@ namespace test::parsing::impl::defines_stream_proxy
REQUIRE(line.m_line == value);
}
void ExpectErrorInLine(IParserLineStream* stream, const int lineNumber, const int columnNumber)
{
REQUIRE_THROWS_MATCHES(
stream->NextLine(), ParsingException, MessageMatches(ContainsSubstring("L" + std::to_string(lineNumber) + ":" + std::to_string(columnNumber))));
}
TEST_CASE("DefinesStreamProxy: Ensure simple define and positive ifdef is working", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define ASDF", "#ifdef ASDF", "Hello World", "#endif", "Hello Galaxy"};
const std::vector<std::string> lines{
"#define ASDF",
"#ifdef ASDF",
"Hello World",
"#endif",
"Hello Galaxy",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -31,7 +49,13 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure simple define and negative ifdef is working", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define ASDF", "#ifdef NONO", "Hello World", "#endif", "Hello Galaxy"};
const std::vector<std::string> lines{
"#define ASDF",
"#ifdef NONO",
"Hello World",
"#endif",
"Hello Galaxy",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -47,7 +71,13 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure simple define and positive ifndef is working", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define ASDF", "#ifndef NONO", "Hello World", "#endif", "Hello Galaxy"};
const std::vector<std::string> lines{
"#define ASDF",
"#ifndef NONO",
"Hello World",
"#endif",
"Hello Galaxy",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -63,7 +93,13 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure simple define and negative ifndef is working", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define ASDF", "#ifndef ASDF", "Hello World", "#endif", "Hello Galaxy"};
const std::vector<std::string> lines{
"#define ASDF",
"#ifndef ASDF",
"Hello World",
"#endif",
"Hello Galaxy",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -79,7 +115,15 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure else is working", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define ASDF", "#ifdef NONO", "Hello World1", "#else", "Hello World2", "#endif", "Hello Galaxy"};
const std::vector<std::string> lines{
"#define ASDF",
"#ifdef NONO",
"Hello World1",
"#else",
"Hello World2",
"#endif",
"Hello Galaxy",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -97,7 +141,8 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure nested ifdef is working", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define ASDF",
const std::vector<std::string> lines{
"#define ASDF",
"#ifdef ASDF",
"#ifdef NONO",
"Hello World1",
@ -111,7 +156,8 @@ namespace test::parsing::impl::defines_stream_proxy
"Hello World4",
"#endif",
"#endif",
"Hello Galaxy"};
"Hello Galaxy",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -138,7 +184,16 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure undef is working", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define ASDF", "#ifdef ASDF", "Hello World", "#endif", "#undef ASDF", "#ifdef ASDF", "Hello World", "#endif", "Hello Galaxy"};
"#define ASDF",
"#ifdef ASDF",
"Hello World",
"#endif",
"#undef ASDF",
"#ifdef ASDF",
"Hello World",
"#endif",
"Hello Galaxy",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -159,7 +214,16 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure undef does not undefine everything", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define ASDF", "#ifdef ASDF", "Hello World", "#endif", "#undef NONO", "#ifdef ASDF", "Hello World", "#endif", "Hello Galaxy"};
"#define ASDF",
"#ifdef ASDF",
"Hello World",
"#endif",
"#undef NONO",
"#ifdef ASDF",
"Hello World",
"#endif",
"Hello Galaxy",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -179,7 +243,13 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure simple defines are working", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define ASDF LOL", "ASDF", "A ASDF B", "ASDF B", "A ASDF"};
const std::vector<std::string> lines{
"#define ASDF LOL",
"ASDF",
"A ASDF B",
"ASDF B",
"A ASDF",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -195,7 +265,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure defines can be surrounded by symbols", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define ASDF LOL", "!ASDF%"};
const std::vector<std::string> lines{
"#define ASDF LOL",
"!ASDF%",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -208,7 +281,11 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can use multiple defines in one line", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define A Hello", "#define B world", "A my dear B!"};
const std::vector<std::string> lines{
"#define A Hello",
"#define B world",
"A my dear B!",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -222,7 +299,12 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure defines in disabled block are ignored", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#ifdef LOLO", "#define hello world", "#endif", "hello"};
const std::vector<std::string> lines{
"#ifdef LOLO",
"#define hello world",
"#endif",
"hello",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -237,7 +319,13 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure undefs in disabled block are ignored", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define hello world", "#ifdef LOLO", "#undef hello", "#endif", "hello"};
const std::vector<std::string> lines{
"#define hello world",
"#ifdef LOLO",
"#undef hello",
"#endif",
"hello",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -253,7 +341,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can define name with underscores and digits", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define __int16 short", "unsigned __int16 value;"};
const std::vector<std::string> lines{
"#define __int16 short",
"unsigned __int16 value;",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -288,7 +379,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can add define with parameters", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define test(x) alignas(x)", "struct test(1337) test_struct"};
const std::vector<std::string> lines{
"#define test(x) alignas(x)",
"struct test(1337) test_struct",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -301,7 +395,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can use parameter multiple times", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define test(x) x|x|x|x", "struct test(1337) test_struct"};
const std::vector<std::string> lines{
"#define test(x) x|x|x|x",
"struct test(1337) test_struct",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -314,7 +411,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can use parameterized define in between symbols", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define test(x) x|x|x|x", "%test(5)%"};
const std::vector<std::string> lines{
"#define test(x) x|x|x|x",
"%test(5)%",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -327,7 +427,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can define multiple parameters", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define test(a1, a2, a3) a1 + a2 = a3", "make calc test(1, 2, 3);"};
const std::vector<std::string> lines{
"#define test(a1, a2, a3) a1 + a2 = a3",
"make calc test(1, 2, 3);",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -340,7 +443,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can define multiple parameters without spacing", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define test(a1,a2,a3) a1+a2=a3", "make calc test(1,2,3);"};
const std::vector<std::string> lines{
"#define test(a1,a2,a3) a1+a2=a3",
"make calc test(1,2,3);",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -353,7 +459,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can define parameters with underscore", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define test(test_parameter) this is test_parameter", "Apparently test(a very cool text);"};
const std::vector<std::string> lines{
"#define test(test_parameter) this is test_parameter",
"Apparently test(a very cool text);",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -558,7 +667,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can use parenthesis in parameters values", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define someStuff(param1) Hello param1 World", "someStuff(A sentence with (parenthesis) and stuff)"};
const std::vector<std::string> lines{
"#define someStuff(param1) Hello param1 World",
"someStuff(A sentence with (parenthesis) and stuff)",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -571,8 +683,10 @@ namespace test::parsing::impl::defines_stream_proxy
TEST_CASE("DefinesStreamProxy: Ensure can use comma in parenthesis in parameters values", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define someStuff(param1) Hello param1 World",
"someStuff(A sentence with (parenthesis and a , character) and stuff)"};
const std::vector<std::string> lines{
"#define someStuff(param1) Hello param1 World",
"someStuff(A sentence with (parenthesis and a , character) and stuff)",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -583,9 +697,83 @@ namespace test::parsing::impl::defines_stream_proxy
REQUIRE(proxy.Eof());
}
TEST_CASE("DefinesStreamProxy: Ensure can use comma in square brackets in parameters values", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define someStuff(param1) Hello param1 World",
"someStuff(A sentence with [brackets and a , character] and stuff)",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectLine(&proxy, 2, "Hello A sentence with [brackets and a , character] and stuff World");
REQUIRE(proxy.Eof());
}
TEST_CASE("DefinesStreamProxy: Ensure can use comma in curly braces in parameters values", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define someStuff(param1) Hello param1 World",
"someStuff(A sentence with {braces and a , character} and stuff)",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectLine(&proxy, 2, "Hello A sentence with {braces and a , character} and stuff World");
REQUIRE(proxy.Eof());
}
TEST_CASE("DefinesStreamProxy: Ensure throws error on unclosed parenthesis in params", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define someStuff(param1) Hello param1 World",
"someStuff(A sentence with [brackets and a , character and stuff)",
"someStuff(A sentence with {braces and a , character and stuff)",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectErrorInLine(&proxy, 2, 64);
ExpectErrorInLine(&proxy, 3, 62);
REQUIRE(proxy.Eof());
}
TEST_CASE("DefinesStreamProxy: Ensure throws error on parenthesis in params closed with wrong equivalent", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define someStuff(param1) Hello param1 World",
"someStuff(A sentence with (parenthesis and a , character] and stuff)",
"someStuff(A sentence with [brackets and a , character} and stuff)",
"someStuff(A sentence with {braces and a , character) and stuff)",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectErrorInLine(&proxy, 2, 57);
ExpectErrorInLine(&proxy, 3, 54);
ExpectErrorInLine(&proxy, 4, 52);
REQUIRE(proxy.Eof());
}
TEST_CASE("DefinesStreamProxy: Ensure defines can go over multiple lines", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{"#define someStuff(param1) Hello param1 World \\", "and hello universe", "someStuff(lovely)"};
const std::vector<std::string> lines{
"#define someStuff(param1) Hello param1 World \\",
"and hello universe",
"someStuff(lovely)",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
@ -596,4 +784,120 @@ namespace test::parsing::impl::defines_stream_proxy
REQUIRE(proxy.Eof());
}
TEST_CASE("DefinesStreamProxy: Macro definition can span multiple lines when used with backslash", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define testMacro( \\",
" a, \\",
" b, \\",
" c) a + b - c",
"testMacro(1, 2, 3)",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectLine(&proxy, 2, "");
ExpectLine(&proxy, 3, "");
ExpectLine(&proxy, 4, "");
ExpectLine(&proxy, 5, "1 + 2 - 3");
REQUIRE(proxy.Eof());
}
TEST_CASE("DefinesStreamProxy: Macro definition that has unclosed parameters throws an error", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define testMacro(",
" a,",
" b,",
" c) a + b - c",
"testMacro(1, 2, 3)",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectErrorInLine(&proxy, 1, 19);
}
TEST_CASE("DefinesStreamProxy: Macro usages can span multiple lines if they have args", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define testMacro(a, b, c) Hello a, this is b. Lets meet at c!",
"testMacro(",
"Peter,",
"Anna,",
"the cinema",
")",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectLine(&proxy, 2, "");
ExpectLine(&proxy, 3, "");
ExpectLine(&proxy, 4, "");
ExpectLine(&proxy, 5, "");
ExpectLine(&proxy, 6, "Hello Peter, this is Anna. Lets meet at the cinema!");
REQUIRE(proxy.Eof());
}
TEST_CASE("DefinesStreamProxy: Can use second macro after multi-line macro", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define LOL HAHA",
"#define testMacro(a, b) a likes b",
"testMacro(",
"Peter,",
"Anna",
") LOL funny",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectLine(&proxy, 2, "");
ExpectLine(&proxy, 3, "");
ExpectLine(&proxy, 4, "");
ExpectLine(&proxy, 5, "");
ExpectLine(&proxy, 6, "Peter likes Anna HAHA funny");
REQUIRE(proxy.Eof());
}
TEST_CASE("DefinesStreamProxy: Can use second multi-line macro after multi-line macro", "[parsing][parsingstream]")
{
const std::vector<std::string> lines{
"#define LOL HAHA",
"#define testMacro(a, b) a likes b",
"testMacro(",
"Peter,",
"Anna",
") and testMacro(",
"Anna,",
"Peter",
")",
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectLine(&proxy, 2, "");
ExpectLine(&proxy, 3, "");
ExpectLine(&proxy, 4, "");
ExpectLine(&proxy, 5, "");
ExpectLine(&proxy, 6, "Peter likes Anna and ");
ExpectLine(&proxy, 7, "");
ExpectLine(&proxy, 8, "");
ExpectLine(&proxy, 9, "Anna likes Peter");
REQUIRE(proxy.Eof());
}
} // namespace test::parsing::impl::defines_stream_proxy