Cache lines in Lexer and show original input when running into an error

This commit is contained in:
Jan 2021-02-13 23:16:19 +01:00
parent 40fedc905d
commit 216125739c
19 changed files with 283 additions and 116 deletions

View File

@ -129,9 +129,9 @@ HeaderParserValue HeaderLexer::GetNextToken()
if (isspace(c))
break;
const auto pos = GetPreviousCharacterPos();
if(isdigit(c))
{
const auto pos = GetPreviousCharacterPos();
bool isFloatingPointValue;
double doubleValue;
int integerValue;
@ -150,12 +150,12 @@ HeaderParserValue HeaderLexer::GetNextToken()
const auto foundKeyword = m_keywords.find(identifier);
if (foundKeyword != m_keywords.end())
return HeaderParserValue::Keyword(GetPreviousCharacterPos(), foundKeyword->second);
return HeaderParserValue::Keyword(pos, foundKeyword->second);
return HeaderParserValue::Identifier(GetPreviousCharacterPos(), new std::string(std::move(identifier)));
return HeaderParserValue::Identifier(pos, new std::string(std::move(identifier)));
}
return HeaderParserValue::Character(GetPreviousCharacterPos(), static_cast<char>(c));
return HeaderParserValue::Character(pos, static_cast<char>(c));
}
}

View File

@ -111,8 +111,8 @@ HeaderParserValue HeaderParserValue::TypeName(const TokenPos pos, std::string* t
HeaderParserValue::HeaderParserValue(const TokenPos pos, const HeaderParserValueType type)
: m_pos(pos),
m_type(type),
m_value()
m_type(type),
m_value()
{
}
@ -134,14 +134,16 @@ HeaderParserValue::~HeaderParserValue()
}
HeaderParserValue::HeaderParserValue(HeaderParserValue&& other) noexcept
: m_type(other.m_type),
m_value(other.m_value)
: m_pos(other.m_pos),
m_type(other.m_type),
m_value(other.m_value)
{
other.m_value = ValueType();
}
HeaderParserValue& HeaderParserValue::operator=(HeaderParserValue&& other) noexcept
{
m_pos = other.m_pos;
m_type = other.m_type;
m_value = other.m_value;
other.m_value = ValueType();

View File

@ -1,5 +1,6 @@
#pragma once
#include "IParserLineStream.h"
#include "Parsing/IParserValue.h"
template<typename TokenType>
@ -21,4 +22,5 @@ public:
_NODISCARD virtual bool IsEof() = 0;
_NODISCARD virtual const TokenPos& GetPos() = 0;
_NODISCARD virtual ParserLine GetLineForPos(const TokenPos& pos) const = 0;
};

View File

@ -32,6 +32,7 @@ public:
virtual ParserLine NextLine() = 0;
virtual bool IncludeFile(const std::string& filename) = 0;
virtual void PopCurrentFile() = 0;
_NODISCARD virtual bool IsOpen() const = 0;
_NODISCARD virtual bool Eof() const = 0;
};

View File

@ -16,19 +16,16 @@ class AbstractLexer : public ILexer<TokenType>
protected:
std::deque<TokenType> m_token_cache;
std::deque<ParserLine> m_line_cache;
IParserLineStream* const m_stream;
unsigned m_line_index;
unsigned m_current_line_offset;
bool m_peeked_next_line;
bool m_start;
ParserLine m_current_line;
ParserLine m_next_line;
explicit AbstractLexer(IParserLineStream* stream)
: m_stream(stream),
m_current_line_offset(0u),
m_peeked_next_line(false),
m_start(true)
m_line_index(0u),
m_current_line_offset(0u)
{
}
@ -36,60 +33,63 @@ protected:
int NextChar()
{
if (m_current_line.IsEof())
while (true)
{
if (m_start)
m_start = false;
else
return EOF;
}
while (m_line_index >= m_line_cache.size())
{
if (!m_line_cache.empty() && m_line_cache.back().IsEof())
return EOF;
while (m_current_line_offset >= m_current_line.m_line.size())
{
m_current_line_offset = 0;
if (m_peeked_next_line)
{
m_current_line = m_next_line;
m_peeked_next_line = false;
}
else
{
m_current_line = m_stream->NextLine();
m_line_cache.push_back(m_stream->NextLine());
}
if (m_current_line.IsEof())
return EOF;
if (m_current_line_offset >= m_line_cache[m_line_index].m_line.size())
{
m_line_index++;
m_current_line_offset = 0;
}
else
break;
}
return m_current_line.m_line[m_current_line_offset++];
return m_line_cache[m_line_index].m_line[m_current_line_offset++];
}
int PeekChar()
{
if (m_current_line.IsEof())
return EOF;
auto peekLineOffset = m_current_line_offset;
auto peekLine = m_line_index;
if (m_current_line_offset >= m_current_line.m_line.size())
while (true)
{
m_peeked_next_line = true;
do
while (peekLine >= m_line_cache.size())
{
m_next_line = m_stream->NextLine();
if (m_next_line.IsEof())
if (m_line_cache.back().IsEof())
return EOF;
}
while (m_next_line.m_line.empty());
return m_next_line.m_line[0];
m_line_cache.push_back(m_stream->NextLine());
}
if (peekLineOffset >= m_line_cache[peekLine].m_line.size())
{
peekLine++;
peekLineOffset = 0;
}
else
break;
}
return m_current_line.m_line[m_current_line_offset];
return m_line_cache[peekLine].m_line[peekLineOffset];
}
_NODISCARD const ParserLine& CurrentLine() const
{
return m_line_cache[m_line_index];
}
_NODISCARD bool IsLineEnd() const
{
return m_current_line_offset >= m_current_line.m_line.size();
return m_current_line_offset >= CurrentLine().m_line.size();
}
_NODISCARD bool NextCharInLineIs(const char c)
@ -99,18 +99,20 @@ protected:
_NODISCARD TokenPos GetPreviousCharacterPos() const
{
return TokenPos(m_current_line.m_filename, m_current_line.m_line_number, m_current_line_offset);
const auto& currentLine = CurrentLine();
return TokenPos(currentLine.m_filename, currentLine.m_line_number, m_current_line_offset);
}
_NODISCARD TokenPos GetNextCharacterPos()
{
if (m_current_line_offset + 1 >= m_current_line.m_line.size())
const auto& currentLine = CurrentLine();
if (m_current_line_offset + 1 >= currentLine.m_line.size())
{
PeekChar();
return TokenPos(m_next_line.m_filename, m_next_line.m_line_number, 1);
return TokenPos(m_line_cache.back().m_filename, m_line_cache.back().m_line_number, 1);
}
return TokenPos(m_current_line.m_filename, m_current_line.m_line_number, m_current_line_offset + 1);
return TokenPos(currentLine.m_filename, currentLine.m_line_number, m_current_line_offset + 1);
}
/**
@ -119,14 +121,15 @@ protected:
*/
std::string ReadIdentifier()
{
const auto& currentLine = CurrentLine();
assert(m_current_line_offset >= 1);
assert(isalpha(m_current_line.m_line[m_current_line_offset - 1]) || m_current_line.m_line[m_current_line_offset - 1] == '_');
assert(isalpha(currentLine.m_line[m_current_line_offset - 1]) || currentLine.m_line[m_current_line_offset - 1] == '_');
const auto startPos = m_current_line_offset - 1;
const auto lineSize = m_current_line.m_line.size();
const auto lineSize = currentLine.m_line.size();
while (m_current_line_offset < lineSize)
{
const auto c = m_current_line.m_line[m_current_line_offset];
const auto c = currentLine.m_line[m_current_line_offset];
if (!isalnum(c) && c != '_')
break;
@ -134,7 +137,7 @@ protected:
m_current_line_offset++;
}
return std::string(m_current_line.m_line, startPos, m_current_line_offset - startPos);
return std::string(currentLine.m_line, startPos, m_current_line_offset - startPos);
}
/**
@ -143,28 +146,30 @@ protected:
*/
std::string ReadString()
{
const auto& currentLine = CurrentLine();
assert(m_current_line_offset >= 1);
assert(m_current_line.m_line[m_current_line_offset - 1] == '"');
assert(currentLine.m_line[m_current_line_offset - 1] == '"');
const auto startPos = m_current_line_offset;
const auto lineSize = m_current_line.m_line.size();
const auto lineSize = currentLine.m_line.size();
while (true)
{
if (m_current_line_offset >= lineSize)
throw ParsingException(TokenPos(m_current_line.m_filename, m_current_line.m_line_number, m_current_line_offset), "Unclosed string");
throw ParsingException(TokenPos(currentLine.m_filename, currentLine.m_line_number, m_current_line_offset), "Unclosed string");
if (m_current_line.m_line[m_current_line_offset] == '\"')
if (currentLine.m_line[m_current_line_offset] == '\"')
break;
m_current_line_offset++;
}
return std::string(m_current_line.m_line, startPos, m_current_line_offset++ - startPos);
return std::string(currentLine.m_line, startPos, m_current_line_offset++ - startPos);
}
void ReadHexNumber(int& integerValue)
{
const auto* start = &m_current_line.m_line.c_str()[m_current_line_offset - 1];
const auto& currentLine = CurrentLine();
const auto* start = &currentLine.m_line.c_str()[m_current_line_offset - 1];
char* end;
integerValue = static_cast<int>(std::strtoul(start, &end, 16));
@ -177,7 +182,8 @@ protected:
_NODISCARD bool IsIntegerNumber() const
{
const auto* currentCharacter = &m_current_line.m_line.c_str()[m_current_line_offset - 1];
const auto& currentLine = CurrentLine();
const auto* currentCharacter = &currentLine.m_line.c_str()[m_current_line_offset - 1];
auto isInteger = true;
auto dot = false;
auto exponent = false;
@ -223,12 +229,13 @@ protected:
int ReadInteger()
{
const auto* start = &m_current_line.m_line.c_str()[m_current_line_offset - 1];
const auto& currentLine = CurrentLine();
const auto* start = &currentLine.m_line.c_str()[m_current_line_offset - 1];
char* end;
const auto integerValue = std::strtol(start, &end, 10);
const auto numberLength = static_cast<unsigned>(end - start);
if(numberLength == 0)
if (numberLength == 0)
throw ParsingException(GetPreviousCharacterPos(), "Invalid number");
m_current_line_offset += numberLength - 1;
@ -238,7 +245,8 @@ protected:
double ReadFloatingPoint()
{
const auto* start = &m_current_line.m_line.c_str()[m_current_line_offset - 1];
const auto& currentLine = CurrentLine();
const auto* start = &currentLine.m_line.c_str()[m_current_line_offset - 1];
char* end;
const auto floatingPointValue = std::strtod(start, &end);
const auto numberLength = static_cast<unsigned>(end - start);
@ -253,13 +261,14 @@ protected:
void ReadNumber(bool& isFloatingPoint, double& floatingPointValue, int& integerValue)
{
const auto& currentLine = CurrentLine();
assert(m_current_line_offset >= 1);
assert(isdigit(m_current_line.m_line[m_current_line_offset - 1]));
assert(isdigit(currentLine.m_line[m_current_line_offset - 1]));
const auto lineLength = m_current_line.m_line.size();
const auto lineLength = currentLine.m_line.size();
if (lineLength - m_current_line_offset >= 1
&& m_current_line.m_line[m_current_line_offset - 1] == '0'
&& m_current_line.m_line[m_current_line_offset] == 'x')
&& currentLine.m_line[m_current_line_offset - 1] == '0'
&& currentLine.m_line[m_current_line_offset] == 'x')
{
isFloatingPoint = false;
ReadHexNumber(integerValue);
@ -288,7 +297,31 @@ public:
void PopTokens(int amount) override
{
m_token_cache.erase(m_token_cache.begin(), m_token_cache.begin() + amount);
if (amount <= 0 || m_token_cache.empty())
return;
if (static_cast<int>(m_token_cache.size()) <= amount)
{
const auto& lastToken = m_token_cache.back();
while (m_line_cache.front().m_line_number != lastToken.GetPos().m_line
|| m_line_cache.front().m_filename.get() != lastToken.GetPos().m_filename.get())
{
m_line_cache.pop_front();
m_line_index--;
}
m_token_cache.clear();
}
else
{
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.get() != firstToken.GetPos().m_filename.get())
{
m_line_cache.pop_front();
m_line_index--;
}
}
}
_NODISCARD bool IsEof() override
@ -300,4 +333,16 @@ public:
{
return GetToken(0).GetPos();
}
_NODISCARD ParserLine GetLineForPos(const TokenPos& pos) const override
{
for(const auto& line : m_line_cache)
{
if (line.m_filename.get() == pos.m_filename.get()
&& line.m_line_number == pos.m_line)
return line;
}
return ParserLine();
}
};

View File

@ -60,7 +60,17 @@ public:
if (!testSuccessful)
{
const TokenPos& pos = m_lexer->GetPos();
std::cout << "Error: " << pos.m_filename << " L" << pos.m_line << ':' << pos.m_column << " Could not parse expression." << std::endl;
const ParserLine line = m_lexer->GetLineForPos(pos);
if (!line.IsEof())
{
std::cout << "Error: " << pos.m_filename.get() << " L" << pos.m_line << ':' << pos.m_column << " Could not parse expression:\n"
<< line.m_line.substr(pos.m_column - 1) << std::endl;
}
else
{
std::cout << "Error: " << pos.m_filename.get() << " L" << pos.m_line << ':' << pos.m_column << " Could not parse expression." << std::endl;
}
return false;
}
}

View File

@ -63,6 +63,11 @@ bool CommentRemovingStreamProxy::IncludeFile(const std::string& filename)
return m_stream->IncludeFile(filename);
}
void CommentRemovingStreamProxy::PopCurrentFile()
{
m_stream->PopCurrentFile();
}
bool CommentRemovingStreamProxy::IsOpen() const
{
return m_stream->IsOpen();

View File

@ -13,6 +13,7 @@ public:
ParserLine NextLine() override;
bool IncludeFile(const std::string& filename) override;
void PopCurrentFile() override;
_NODISCARD bool IsOpen() const override;
_NODISCARD bool Eof() const override;
};

View File

@ -290,6 +290,11 @@ bool DefinesStreamProxy::IncludeFile(const std::string& filename)
return m_stream->IncludeFile(filename);
}
void DefinesStreamProxy::PopCurrentFile()
{
m_stream->PopCurrentFile();
}
bool DefinesStreamProxy::IsOpen() const
{
return m_stream->IsOpen();

View File

@ -38,6 +38,7 @@ public:
ParserLine NextLine() override;
bool IncludeFile(const std::string& filename) override;
void PopCurrentFile() override;
_NODISCARD bool IsOpen() const override;
_NODISCARD bool Eof() const override;
};

View File

@ -1,46 +1,25 @@
#include "IncludingStreamProxy.h"
#include <sstream>
#include <filesystem>
#include "Parsing/ParsingException.h"
namespace fs = std::filesystem;
IncludingStreamProxy::IncludingStreamProxy(IParserLineStream* stream)
: m_stream(stream)
{
}
bool IncludingStreamProxy::FindIncludeDirective(const ParserLine& line, unsigned& includeDirectivePosition)
{
includeDirectivePosition = 0;
for (; includeDirectivePosition < line.m_line.size() - INCLUDE_DIRECTIVE_MINIMUM_TOTAL_LENGTH; includeDirectivePosition++)
{
const auto c = line.m_line[includeDirectivePosition];
if (isspace(c))
continue;
if (c != '#')
return false;
if (line.m_line.compare(includeDirectivePosition + 1, INCLUDE_DIRECTIVE_LENGTH, INCLUDE_DIRECTIVE) != 0)
{
return false;
}
return true;
}
return false;
}
bool IncludingStreamProxy::ExtractIncludeFilename(const ParserLine& line, const unsigned includeDirectivePosition, unsigned& filenameStartPosition, unsigned& filenameEndPosition)
{
auto currentPos = includeDirectivePosition;
bool isDoubleQuotes;
while(isspace(line.m_line[currentPos]))
while (isspace(line.m_line[currentPos]))
{
if(currentPos++ >= line.m_line.size())
if (currentPos++ >= line.m_line.size())
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), INCLUDE_QUOTES_ERROR);
}
@ -78,25 +57,24 @@ bool IncludingStreamProxy::ExtractIncludeFilename(const ParserLine& line, const
return false;
}
bool IncludingStreamProxy::MatchIncludeDirective(const ParserLine& line) const
bool IncludingStreamProxy::MatchIncludeDirective(const ParserLine& line, const unsigned directivePosition) const
{
unsigned includeDirectivePos;
if (!FindIncludeDirective(line, includeDirectivePos))
constexpr auto directiveLength = std::char_traits<char>::length(INCLUDE_DIRECTIVE);
if (line.m_line.compare(directivePosition + 1, directiveLength, INCLUDE_DIRECTIVE) != 0)
return false;
const auto currentPos = includeDirectivePos + INCLUDE_DIRECTIVE_LENGTH + 1;
const auto currentPos = directivePosition + directiveLength + 1;
unsigned filenameStart, filenameEnd;
if(!ExtractIncludeFilename(line, currentPos, filenameStart, filenameEnd))
if (!ExtractIncludeFilename(line, currentPos, filenameStart, filenameEnd))
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), INCLUDE_QUOTES_ERROR);
if(filenameEnd <= filenameStart)
if (filenameEnd <= filenameStart)
throw ParsingException(TokenPos(line.m_filename, line.m_line_number, currentPos), "No filename specified");
const auto filename = line.m_line.substr(filenameStart, filenameEnd - filenameStart);
if(!m_stream->IncludeFile(filename))
if (!m_stream->IncludeFile(filename))
{
std::ostringstream errorStr;
errorStr << "Could not include file \"" << filename << "\"";
@ -106,12 +84,57 @@ bool IncludingStreamProxy::MatchIncludeDirective(const ParserLine& line) const
return true;
}
bool IncludingStreamProxy::MatchPragmaOnceDirective(const ParserLine& line, const 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)
return false;
const auto absolutePath = absolute(fs::path(line.m_filename.get()));
const auto absolutePathStr = absolutePath.string();
const auto existingPath = m_included_files.find(absolutePathStr);
if (existingPath != m_included_files.end())
m_stream->PopCurrentFile();
else
m_included_files.emplace(absolutePathStr);
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;
if (!FindDirective(line, directivePos))
return false;
return MatchIncludeDirective(line, directivePos)
|| MatchPragmaOnceDirective(line, directivePos);
}
ParserLine IncludingStreamProxy::NextLine()
{
auto line = m_stream->NextLine();
if (MatchIncludeDirective(line))
return m_stream->NextLine();
while(MatchDirectives(line))
line = m_stream->NextLine();
return line;
}
@ -121,6 +144,11 @@ bool IncludingStreamProxy::IncludeFile(const std::string& filename)
return m_stream->IncludeFile(filename);
}
void IncludingStreamProxy::PopCurrentFile()
{
m_stream->PopCurrentFile();
}
bool IncludingStreamProxy::IsOpen() const
{
return m_stream->IsOpen();

View File

@ -1,25 +1,31 @@
#pragma once
#include <set>
#include "Parsing/IParserLineStream.h"
class IncludingStreamProxy final : public IParserLineStream
{
static constexpr const char* INCLUDE_QUOTES_ERROR = "Invalid include directive. Expected \"\" or <>";
static constexpr const char* INCLUDE_DIRECTIVE = "include ";
static constexpr int INCLUDE_DIRECTIVE_LENGTH = std::char_traits<char>::length(INCLUDE_DIRECTIVE);
static constexpr int INCLUDE_DIRECTIVE_MINIMUM_TOTAL_LENGTH = INCLUDE_DIRECTIVE_LENGTH + 1 + 2; // #=+1 ""=+2
static constexpr const char* PRAGMA_ONCE_DIRECTIVE = "pragma once";
IParserLineStream* const m_stream;
std::set<std::string> m_included_files;
_NODISCARD static bool FindIncludeDirective(const ParserLine& line, unsigned& includeDirectivePosition);
_NODISCARD static bool ExtractIncludeFilename(const ParserLine& line, unsigned includeDirectivePosition, unsigned& filenameStartPosition, unsigned& filenameEndPosition);
_NODISCARD bool MatchIncludeDirective(const ParserLine& line) const;
_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:
explicit IncludingStreamProxy(IParserLineStream* stream);
ParserLine NextLine() override;
bool IncludeFile(const std::string& filename) override;
void PopCurrentFile() override;
_NODISCARD bool IsOpen() const override;
_NODISCARD bool Eof() const override;
};

View File

@ -14,7 +14,8 @@ ParserFilesystemStream::FileInfo::FileInfo(std::string filePath)
ParserFilesystemStream::ParserFilesystemStream(std::string path)
{
m_files.emplace(FileInfo(std::move(path)));
const auto absolutePath = absolute(fs::path(path));
m_files.emplace(FileInfo(absolutePath.string()));
}
bool ParserFilesystemStream::IsOpen() const
@ -75,6 +76,7 @@ bool ParserFilesystemStream::IncludeFile(const std::string& filename)
auto newFilePath = fs::path(m_files.top().m_file_path);
newFilePath.remove_filename().concat(filename);
newFilePath = absolute(newFilePath);
FileInfo fileInfo(newFilePath.string());
@ -85,6 +87,12 @@ bool ParserFilesystemStream::IncludeFile(const std::string& filename)
return true;
}
void ParserFilesystemStream::PopCurrentFile()
{
if(!m_files.empty())
m_files.pop();
}
bool ParserFilesystemStream::Eof() const
{
return m_files.empty()

View File

@ -23,6 +23,7 @@ public:
ParserLine NextLine() override;
bool IncludeFile(const std::string& filename) override;
void PopCurrentFile() override;
_NODISCARD bool IsOpen() const override;
_NODISCARD bool Eof() const override;
};

View File

@ -7,7 +7,7 @@ ParsingException::ParsingException(const TokenPos position, std::string message)
m_message(std::move(message))
{
std::ostringstream str;
str << position.m_filename << " L" << m_pos.m_line << ':' << m_pos.m_column << ' ' << m_message;
str << position.m_filename.get() << " L" << m_pos.m_line << ':' << m_pos.m_column << ' ' << m_message;
m_full_message = str.str();
}

View File

@ -7,7 +7,7 @@ class TokenPos
static const std::string EMPTY_FILENAME;
public:
const std::string& m_filename;
std::reference_wrapper<const std::string> m_filename;
int m_line;
int m_column;

View File

@ -133,4 +133,49 @@ namespace test::parsing::impl::including_stream_proxy
REQUIRE(proxy.Eof());
}
TEST_CASE("IncludingStreamProxy: Ensure pragma once prevents including the same file more than once", "[parsing][parsingstream]")
{
const std::vector<std::string> lines
{
"Hello world",
"#include \"ASDF.txt\"",
"#include \"ASDF.txt\"",
"and bye"
};
const std::vector<std::string> asdf
{
"#pragma once",
"Hello galaxy"
};
MockParserLineStream mockStream(lines);
mockStream.AddIncludeLines("ASDF.txt", asdf);
IncludingStreamProxy proxy(&mockStream);
{
auto line = proxy.NextLine();
REQUIRE(line.m_line_number == 1);
REQUIRE(line.m_filename.get() == MockParserLineStream::MOCK_FILENAME);
REQUIRE(line.m_line == "Hello world");
}
{
auto line = proxy.NextLine();
REQUIRE(line.m_line_number == 2);
REQUIRE(line.m_filename.get() == "ASDF.txt");
REQUIRE(line.m_line == "Hello galaxy");
}
{
auto line = proxy.NextLine();
REQUIRE(line.m_line_number == 4);
REQUIRE(line.m_filename.get() == MockParserLineStream::MOCK_FILENAME);
REQUIRE(line.m_line == "and bye");
}
REQUIRE(proxy.Eof());
}
}

View File

@ -50,6 +50,12 @@ bool MockParserLineStream::IncludeFile(const std::string& filename)
return true;
}
void MockParserLineStream::PopCurrentFile()
{
if (!m_include_positions.empty())
m_include_positions.pop_back();
}
bool MockParserLineStream::IsOpen() const
{
return true;

View File

@ -30,6 +30,7 @@ public:
void AddIncludeLines(const std::string& filename, const std::vector<std::string>& lines);
ParserLine NextLine() override;
bool IncludeFile(const std::string& filename) override;
void PopCurrentFile() override;
_NODISCARD bool IsOpen() const override;
_NODISCARD bool Eof() const override;
};