Restructure StreamProxies to use common basis for matching directives

This commit is contained in:
Jan 2021-02-14 10:26:18 +01:00
parent e277de4517
commit efa39a8ac3
10 changed files with 244 additions and 115 deletions

View File

@ -0,0 +1,111 @@
#include "AbstractDirectiveStreamProxy.h"
TokenPos AbstractDirectiveStreamProxy::CreatePos(const ParserLine& line, const unsigned position)
{
return TokenPos(line.m_filename.get(), line.m_line_number, static_cast<int>(position + 1));
}
bool AbstractDirectiveStreamProxy::SkipWhitespace(const ParserLine& line, unsigned& position)
{
while(true)
{
if (position >= line.m_line.size())
return false;
if (isspace(line.m_line[position]))
position++;
else
break;
}
return true;
}
bool AbstractDirectiveStreamProxy::ExtractInteger(const ParserLine& line, unsigned& position, int& value)
{
if (position >= line.m_line.size())
return false;
const auto* startPosition = &line.m_line[position];
char* endPosition;
value = strtol(startPosition, &endPosition, 0);
const auto len = endPosition - startPosition;
if(len > 0)
{
position += len;
return true;
}
return false;
}
bool AbstractDirectiveStreamProxy::ExtractIdentifier(const ParserLine& line, unsigned& position)
{
auto firstChar = true;
while (true)
{
if (position >= line.m_line.size())
return !firstChar;
const auto c = line.m_line[position];
if (isalpha(c)
|| c == '_'
|| firstChar && isdigit(c))
{
position++;
}
else
return !firstChar;
firstChar = false;
}
}
bool AbstractDirectiveStreamProxy::MatchCharacter(const ParserLine& line, unsigned& position, char c)
{
if (position < line.m_line.size() && line.m_line[position] == c)
{
position++;
return true;
}
return false;
}
bool AbstractDirectiveStreamProxy::MatchNextCharacter(const ParserLine& line, unsigned& position, char c)
{
return SkipWhitespace(line, position) && MatchCharacter(line, position, c);
}
bool AbstractDirectiveStreamProxy::MatchString(const ParserLine& line, unsigned& position, const char* str, unsigned len)
{
if (line.m_line.compare(position, len, str) == 0)
{
position += len;
return true;
}
return false;
}
bool AbstractDirectiveStreamProxy::MatchNextString(const ParserLine& line, unsigned& position, const char* str, unsigned len)
{
return SkipWhitespace(line, position) && MatchString(line, position, str, len);
}
bool AbstractDirectiveStreamProxy::FindDirective(const ParserLine& line, unsigned& directivePosition)
{
directivePosition = 0;
for (; directivePosition < line.m_line.size(); directivePosition++)
{
const auto c = line.m_line[directivePosition];
if (isspace(c))
continue;
return c == '#';
}
return false;
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "Parsing/IParserLineStream.h"
#include "Parsing/TokenPos.h"
class AbstractDirectiveStreamProxy : public IParserLineStream
{
protected:
AbstractDirectiveStreamProxy() = default;
static TokenPos CreatePos(const ParserLine& line, unsigned position);
static bool SkipWhitespace(const ParserLine& line, unsigned& position);
static bool ExtractInteger(const ParserLine& line, unsigned& position, int& value);
static bool ExtractIdentifier(const ParserLine& line, unsigned& position);
static bool MatchCharacter(const ParserLine& line, unsigned& position, char c);
static bool MatchNextCharacter(const ParserLine& line, unsigned& position, char c);
static bool MatchString(const ParserLine& line, unsigned& position, const char* str, unsigned len);
static bool MatchNextString(const ParserLine& line, unsigned& position, const char* str, unsigned len);
static bool FindDirective(const ParserLine& line, unsigned& directivePosition);
};

View File

@ -11,67 +11,37 @@ DefinesStreamProxy::DefinesStreamProxy(IParserLineStream* stream)
{
}
bool DefinesStreamProxy::FindDirective(const ParserLine& line, unsigned& directivePosition)
bool DefinesStreamProxy::MatchDefineDirective(const ParserLine& line, unsigned directivePosition)
{
directivePosition = 0;
for (; directivePosition < line.m_line.size(); directivePosition++)
{
const auto c = line.m_line[directivePosition];
if (isspace(c))
continue;
return c == '#';
}
return false;
}
bool DefinesStreamProxy::MatchDefineDirective(const ParserLine& line, const unsigned directivePosition)
{
constexpr auto directiveLength = std::char_traits<char>::length(DEFINE_DIRECTIVE);
if (line.m_line.compare(directivePosition + 1, directiveLength, DEFINE_DIRECTIVE) != 0)
if (!MatchString(line, directivePosition, DEFINE_DIRECTIVE, std::char_traits<char>::length(DEFINE_DIRECTIVE)))
return false;
const auto nameStartPos = directivePosition + directiveLength + 1;
auto nameEndPos = nameStartPos;
for (; nameEndPos < line.m_line.size(); nameEndPos++)
{
if (isspace(line.m_line[nameEndPos]))
break;
}
const auto nameStartPos = directivePosition;
if (!ExtractIdentifier(line, directivePosition))
throw ParsingException(CreatePos(line, directivePosition), "Cannot ifdef without a name.");
if (nameStartPos == nameEndPos)
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, static_cast<int>(nameStartPos + 1)), "Defines need a name.");
const auto name = line.m_line.substr(nameStartPos, nameEndPos - nameStartPos);
const auto name = line.m_line.substr(nameStartPos, directivePosition - nameStartPos);
std::string value;
if (nameEndPos < line.m_line.size())
value = line.m_line.substr(nameEndPos + 1);
if (directivePosition < line.m_line.size())
value = line.m_line.substr(directivePosition + 1);
m_defines[name] = value;
return true;
}
bool DefinesStreamProxy::MatchUndefDirective(const ParserLine& line, const unsigned directivePosition)
bool DefinesStreamProxy::MatchUndefDirective(const ParserLine& line, unsigned directivePosition)
{
constexpr auto directiveLength = std::char_traits<char>::length(UNDEF_DIRECTIVE);
if (line.m_line.compare(directivePosition + 1, directiveLength, UNDEF_DIRECTIVE) != 0)
if (!MatchString(line, directivePosition, UNDEF_DIRECTIVE, std::char_traits<char>::length(UNDEF_DIRECTIVE)))
return false;
const auto nameStartPos = directivePosition + directiveLength + 1;
auto nameEndPos = nameStartPos;
for (; nameEndPos < line.m_line.size(); nameEndPos++)
{
if (isspace(line.m_line[nameEndPos]))
break;
}
if (!SkipWhitespace(line, directivePosition))
throw ParsingException(CreatePos(line, directivePosition), "Cannot undef without a name.");
if (nameStartPos == nameEndPos)
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, static_cast<int>(nameStartPos + 1)), "Cannot undef without a name.");
const auto nameStartPos = directivePosition;
if (!ExtractIdentifier(line, directivePosition))
throw ParsingException(CreatePos(line, directivePosition), "Cannot ifdef without a name.");
const auto name = line.m_line.substr(nameStartPos, nameEndPos - nameStartPos);
const auto name = line.m_line.substr(nameStartPos, directivePosition - nameStartPos);
const auto entry = m_defines.find(name);
if (entry != m_defines.end())
@ -80,18 +50,14 @@ bool DefinesStreamProxy::MatchUndefDirective(const ParserLine& line, const unsig
return true;
}
bool DefinesStreamProxy::MatchIfdefDirective(const ParserLine& line, const unsigned directivePosition)
bool DefinesStreamProxy::MatchIfdefDirective(const ParserLine& line, unsigned directivePosition)
{
constexpr auto directiveLengthIfdef = std::char_traits<char>::length(IFDEF_DIRECTIVE);
constexpr auto directiveLengthIfndef = std::char_traits<char>::length(IFNDEF_DIRECTIVE);
auto len = directiveLengthIfdef;
auto reverse = false;
if (line.m_line.compare(directivePosition + 1, directiveLengthIfdef, IFDEF_DIRECTIVE) != 0)
if(!MatchString(line, directivePosition, IFDEF_DIRECTIVE, std::char_traits<char>::length(IFDEF_DIRECTIVE)))
{
if (line.m_line.compare(directivePosition + 1, directiveLengthIfndef, IFNDEF_DIRECTIVE) != 0)
if (!MatchString(line, directivePosition, IFNDEF_DIRECTIVE, std::char_traits<char>::length(IFNDEF_DIRECTIVE)))
return false;
len = directiveLengthIfndef;
reverse = true;
}
@ -101,18 +67,14 @@ bool DefinesStreamProxy::MatchIfdefDirective(const ParserLine& line, const unsig
return true;
}
const auto nameStartPos = directivePosition + len + 1;
auto nameEndPos = nameStartPos;
for (; nameEndPos < line.m_line.size(); nameEndPos++)
{
if (isspace(line.m_line[nameEndPos]))
break;
}
if (!SkipWhitespace(line, directivePosition))
throw ParsingException(CreatePos(line, directivePosition), "Cannot ifdef without a name.");
if (nameStartPos == nameEndPos)
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, static_cast<int>(nameStartPos + 1)), "Cannot ifdef without a name.");
const auto nameStartPos = directivePosition;
if(!ExtractIdentifier(line, directivePosition))
throw ParsingException(CreatePos(line, directivePosition), "Cannot ifdef without a name.");
const auto name = line.m_line.substr(nameStartPos, nameEndPos - nameStartPos);
const auto name = line.m_line.substr(nameStartPos, directivePosition - nameStartPos);
const auto entry = m_defines.find(name);
if (entry != m_defines.end())
@ -123,10 +85,9 @@ bool DefinesStreamProxy::MatchIfdefDirective(const ParserLine& line, const unsig
return true;
}
bool DefinesStreamProxy::MatchElseDirective(const ParserLine& line, const unsigned directivePosition)
bool DefinesStreamProxy::MatchElseDirective(const ParserLine& line, unsigned directivePosition)
{
constexpr auto directiveLength = std::char_traits<char>::length(ELSE_DIRECTIVE);
if (line.m_line.compare(directivePosition + 1, directiveLength, ELSE_DIRECTIVE) != 0)
if (!MatchString(line, directivePosition, ELSE_DIRECTIVE, std::char_traits<char>::length(ELSE_DIRECTIVE)))
return false;
if (m_ignore_depth > 0)
@ -135,15 +96,14 @@ bool DefinesStreamProxy::MatchElseDirective(const ParserLine& line, const unsign
if (!m_modes.empty())
m_modes.top() = !m_modes.top();
else
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, static_cast<int>(directivePosition + 1)), "Cannot use else without ifdef");
throw ParsingException(CreatePos(line, directivePosition), "Cannot use else without ifdef");
return true;
}
bool DefinesStreamProxy::MatchEndifDirective(const ParserLine& line, const unsigned directivePosition)
bool DefinesStreamProxy::MatchEndifDirective(const ParserLine& line, unsigned directivePosition)
{
constexpr auto directiveLength = std::char_traits<char>::length(ENDIF_DIRECTIVE);
if (line.m_line.compare(directivePosition + 1, directiveLength, ENDIF_DIRECTIVE) != 0)
if (!MatchString(line, directivePosition, ENDIF_DIRECTIVE, std::char_traits<char>::length(ENDIF_DIRECTIVE)))
return false;
if (m_ignore_depth > 0)
@ -155,7 +115,7 @@ bool DefinesStreamProxy::MatchEndifDirective(const ParserLine& line, const unsig
if (!m_modes.empty())
m_modes.pop();
else
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, static_cast<int>(directivePosition + 1)), "Cannot use endif without ifdef");
throw ParsingException(CreatePos(line, directivePosition), "Cannot use endif without ifdef");
return true;
}
@ -167,9 +127,16 @@ bool DefinesStreamProxy::MatchDirectives(const ParserLine& line)
if (!FindDirective(line, directivePos))
return false;
return MatchDefineDirective(line, directivePos)
|| MatchUndefDirective(line, directivePos)
|| MatchIfdefDirective(line, directivePos)
directivePos++;
if(m_modes.empty() || m_modes.top() == true)
{
if (MatchDefineDirective(line, directivePos)
|| MatchUndefDirective(line, directivePos))
return true;
}
return MatchIfdefDirective(line, directivePos)
|| MatchElseDirective(line, directivePos)
|| MatchEndifDirective(line, directivePos);
}

View File

@ -3,9 +3,10 @@
#include <unordered_map>
#include <stack>
#include "AbstractDirectiveStreamProxy.h"
#include "Parsing/IParserLineStream.h"
class DefinesStreamProxy final : public IParserLineStream
class DefinesStreamProxy final : public AbstractDirectiveStreamProxy
{
static constexpr const char* DEFINE_DIRECTIVE = "define ";
static constexpr const char* UNDEF_DIRECTIVE = "undef ";
@ -14,12 +15,11 @@ class DefinesStreamProxy final : public IParserLineStream
static constexpr const char* ELSE_DIRECTIVE = "else";
static constexpr const char* ENDIF_DIRECTIVE = "endif";
std::unordered_map<std::string, std::string> m_defines;
IParserLineStream* const m_stream;
std::unordered_map<std::string, std::string> m_defines;
std::stack<bool> m_modes;
unsigned m_ignore_depth;
_NODISCARD static bool FindDirective(const ParserLine& line, unsigned& directivePosition);
_NODISCARD bool MatchDefineDirective(const ParserLine& line, unsigned directivePosition);
_NODISCARD bool MatchUndefDirective(const ParserLine& line, unsigned directivePosition);
_NODISCARD bool MatchIfdefDirective(const ParserLine& line, unsigned directivePosition);

View File

@ -20,7 +20,7 @@ bool IncludingStreamProxy::ExtractIncludeFilename(const ParserLine& line, const
while (isspace(line.m_line[currentPos]))
{
if (currentPos++ >= line.m_line.size())
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), INCLUDE_QUOTES_ERROR);
throw ParsingException(CreatePos(line, currentPos - 1), INCLUDE_QUOTES_ERROR);
}
if (line.m_line[currentPos] == '"')
@ -28,7 +28,7 @@ bool IncludingStreamProxy::ExtractIncludeFilename(const ParserLine& line, const
else if (line.m_line[currentPos] == '<')
isDoubleQuotes = false;
else
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), INCLUDE_QUOTES_ERROR);
throw ParsingException(CreatePos(line, currentPos - 1), INCLUDE_QUOTES_ERROR);
filenameStartPosition = ++currentPos;
filenameEndPosition = 0;
@ -40,7 +40,7 @@ bool IncludingStreamProxy::ExtractIncludeFilename(const ParserLine& line, const
if (c == '"')
{
if (!isDoubleQuotes)
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), "");
throw ParsingException(CreatePos(line, currentPos - 1), "");
filenameEndPosition = currentPos;
return true;
}
@ -48,7 +48,7 @@ bool IncludingStreamProxy::ExtractIncludeFilename(const ParserLine& line, const
if (c == '>')
{
if (isDoubleQuotes)
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), INCLUDE_QUOTES_ERROR);
throw ParsingException(CreatePos(line, currentPos - 1), INCLUDE_QUOTES_ERROR);
filenameEndPosition = currentPos;
return true;
}
@ -57,20 +57,18 @@ bool IncludingStreamProxy::ExtractIncludeFilename(const ParserLine& line, const
return false;
}
bool IncludingStreamProxy::MatchIncludeDirective(const ParserLine& line, const unsigned directivePosition) const
bool IncludingStreamProxy::MatchIncludeDirective(const ParserLine& line, unsigned directivePosition) const
{
constexpr auto directiveLength = std::char_traits<char>::length(INCLUDE_DIRECTIVE);
if (line.m_line.compare(directivePosition + 1, directiveLength, INCLUDE_DIRECTIVE) != 0)
if (!MatchString(line, directivePosition, INCLUDE_DIRECTIVE, std::char_traits<char>::length(INCLUDE_DIRECTIVE)))
return false;
const auto currentPos = directivePosition + directiveLength + 1;
unsigned filenameStart, filenameEnd;
if (!ExtractIncludeFilename(line, currentPos, filenameStart, filenameEnd))
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), INCLUDE_QUOTES_ERROR);
if (!ExtractIncludeFilename(line, directivePosition, filenameStart, filenameEnd))
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, directivePosition), INCLUDE_QUOTES_ERROR);
if (filenameEnd <= filenameStart)
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), "No filename specified");
throw ParsingException(CreatePos(line, directivePosition), "No filename specified");
const auto filename = line.m_line.substr(filenameStart, filenameEnd - filenameStart);
@ -78,16 +76,15 @@ bool IncludingStreamProxy::MatchIncludeDirective(const ParserLine& line, const u
{
std::ostringstream errorStr;
errorStr << "Could not include file \"" << filename << "\"";
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), errorStr.str());
throw ParsingException(CreatePos(line, directivePosition), errorStr.str());
}
return true;
}
bool IncludingStreamProxy::MatchPragmaOnceDirective(const ParserLine& line, const unsigned directivePosition)
bool IncludingStreamProxy::MatchPragmaOnceDirective(const ParserLine& line, unsigned directivePosition)
{
constexpr auto directiveLength = std::char_traits<char>::length(PRAGMA_ONCE_DIRECTIVE);
if (line.m_line.compare(directivePosition + 1, directiveLength, PRAGMA_ONCE_DIRECTIVE) != 0)
if(!MatchString(line, directivePosition, PRAGMA_ONCE_DIRECTIVE, std::char_traits<char>::length(PRAGMA_ONCE_DIRECTIVE)))
return false;
const auto absolutePath = absolute(fs::path(line.m_filename.get()));
@ -102,22 +99,6 @@ bool IncludingStreamProxy::MatchPragmaOnceDirective(const ParserLine& line, cons
return true;
}
bool IncludingStreamProxy::FindDirective(const ParserLine& line, unsigned& directivePosition)
{
directivePosition = 0;
for (; directivePosition < line.m_line.size(); directivePosition++)
{
const auto c = line.m_line[directivePosition];
if (isspace(c))
continue;
return c == '#';
}
return false;
}
bool IncludingStreamProxy::MatchDirectives(const ParserLine& line)
{
unsigned directivePos;
@ -125,6 +106,7 @@ bool IncludingStreamProxy::MatchDirectives(const ParserLine& line)
if (!FindDirective(line, directivePos))
return false;
directivePos++;
return MatchIncludeDirective(line, directivePos)
|| MatchPragmaOnceDirective(line, directivePos);
}

View File

@ -2,9 +2,10 @@
#include <set>
#include "AbstractDirectiveStreamProxy.h"
#include "Parsing/IParserLineStream.h"
class IncludingStreamProxy final : public IParserLineStream
class IncludingStreamProxy final : public AbstractDirectiveStreamProxy
{
static constexpr const char* INCLUDE_QUOTES_ERROR = "Invalid include directive. Expected \"\" or <>";
static constexpr const char* INCLUDE_DIRECTIVE = "include ";
@ -12,12 +13,10 @@ class IncludingStreamProxy final : public IParserLineStream
IParserLineStream* const m_stream;
std::set<std::string> m_included_files;
_NODISCARD static bool ExtractIncludeFilename(const ParserLine& line, unsigned includeDirectivePosition, unsigned& filenameStartPosition, unsigned& filenameEndPosition);
_NODISCARD bool MatchIncludeDirective(const ParserLine& line, unsigned directivePosition) const;
_NODISCARD bool MatchPragmaOnceDirective(const ParserLine& line, unsigned directivePosition);
_NODISCARD static bool FindDirective(const ParserLine& line, unsigned& directivePosition);
_NODISCARD bool MatchDirectives(const ParserLine& line);
public:

View File

@ -294,4 +294,48 @@ namespace test::parsing::impl::defines_stream_proxy
REQUIRE(proxy.Eof());
}
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"
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectLine(&proxy, 2, "");
ExpectLine(&proxy, 3, "");
ExpectLine(&proxy, 4, "hello");
REQUIRE(proxy.Eof());
}
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"
};
MockParserLineStream mockStream(lines);
DefinesStreamProxy proxy(&mockStream);
ExpectLine(&proxy, 1, "");
ExpectLine(&proxy, 2, "");
ExpectLine(&proxy, 3, "");
ExpectLine(&proxy, 4, "");
ExpectLine(&proxy, 5, "world");
REQUIRE(proxy.Eof());
}
}

View File

@ -13,7 +13,6 @@ class MockLexer final : public ILexer<TokenType>
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;
@ -58,4 +57,9 @@ public:
{
return m_pop_count;
}
_NODISCARD ParserLine GetLineForPos(const TokenPos& pos) const override
{
return ParserLine();
}
};