mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-06-11 07:18:11 -05:00
Extract commonly used Parser code to new Parser component
This commit is contained in:
26
src/Parser/Parsing/ILexer.h
Normal file
26
src/Parser/Parsing/ILexer.h
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "IParserLineStream.h"
|
||||
#include "Parsing/IParserValue.h"
|
||||
|
||||
template<typename TokenType>
|
||||
class ILexer
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
public:
|
||||
ILexer() = default;
|
||||
virtual ~ILexer() = default;
|
||||
ILexer(const ILexer& other) = default;
|
||||
ILexer(ILexer&& other) noexcept = default;
|
||||
ILexer& operator=(const ILexer& other) = default;
|
||||
ILexer& operator=(ILexer&& other) noexcept = default;
|
||||
|
||||
virtual const TokenType& GetToken(unsigned index) = 0;
|
||||
virtual void PopTokens(int amount) = 0;
|
||||
|
||||
_NODISCARD virtual bool IsEof() = 0;
|
||||
_NODISCARD virtual const TokenPos& GetPos() = 0;
|
||||
_NODISCARD virtual ParserLine GetLineForPos(const TokenPos& pos) const = 0;
|
||||
};
|
14
src/Parser/Parsing/IPackValueSupplier.h
Normal file
14
src/Parser/Parsing/IPackValueSupplier.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
class IPackValueSupplier
|
||||
{
|
||||
public:
|
||||
IPackValueSupplier() = default;
|
||||
virtual ~IPackValueSupplier() = default;
|
||||
IPackValueSupplier(const IPackValueSupplier& other) = default;
|
||||
IPackValueSupplier(IPackValueSupplier&& other) noexcept = default;
|
||||
IPackValueSupplier& operator=(const IPackValueSupplier& other) = default;
|
||||
IPackValueSupplier& operator=(IPackValueSupplier&& other) noexcept = default;
|
||||
|
||||
virtual int GetCurrentPack() const = 0;
|
||||
};
|
14
src/Parser/Parsing/IParser.h
Normal file
14
src/Parser/Parsing/IParser.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
class IParser
|
||||
{
|
||||
public:
|
||||
IParser() = default;
|
||||
virtual ~IParser() = default;
|
||||
IParser(const IParser& other) = default;
|
||||
IParser(IParser&& other) noexcept = default;
|
||||
IParser& operator=(const IParser& other) = default;
|
||||
IParser& operator=(IParser&& other) noexcept = default;
|
||||
|
||||
virtual bool Parse() = 0;
|
||||
};
|
21
src/Parser/Parsing/IParserLineStream.cpp
Normal file
21
src/Parser/Parsing/IParserLineStream.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "IParserLineStream.h"
|
||||
|
||||
const std::string ParserLine::EMPTY_STRING;
|
||||
|
||||
ParserLine::ParserLine()
|
||||
: m_filename(EMPTY_STRING),
|
||||
m_line_number(0)
|
||||
{
|
||||
}
|
||||
|
||||
ParserLine::ParserLine(const std::string& filename, const int lineNumber, std::string line)
|
||||
: m_filename(filename),
|
||||
m_line_number(lineNumber),
|
||||
m_line(std::move(line))
|
||||
{
|
||||
}
|
||||
|
||||
bool ParserLine::IsEof() const
|
||||
{
|
||||
return m_line_number <= 0;
|
||||
}
|
39
src/Parser/Parsing/IParserLineStream.h
Normal file
39
src/Parser/Parsing/IParserLineStream.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
|
||||
class ParserLine
|
||||
{
|
||||
static const std::string EMPTY_STRING;
|
||||
|
||||
public:
|
||||
std::reference_wrapper<const std::string> m_filename;
|
||||
int m_line_number;
|
||||
std::string m_line;
|
||||
|
||||
ParserLine();
|
||||
ParserLine(const std::string& filename, int lineNumber, std::string line);
|
||||
|
||||
_NODISCARD bool IsEof() const;
|
||||
};
|
||||
|
||||
class IParserLineStream
|
||||
{
|
||||
public:
|
||||
IParserLineStream() = default;
|
||||
virtual ~IParserLineStream() = default;
|
||||
|
||||
IParserLineStream(const IParserLineStream& other) = default;
|
||||
IParserLineStream(IParserLineStream&& other) noexcept = default;
|
||||
IParserLineStream& operator=(const IParserLineStream& other) = default;
|
||||
IParserLineStream& operator=(IParserLineStream&& other) noexcept = default;
|
||||
|
||||
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;
|
||||
};
|
20
src/Parser/Parsing/IParserValue.h
Normal file
20
src/Parser/Parsing/IParserValue.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "TokenPos.h"
|
||||
|
||||
class IParserValue
|
||||
{
|
||||
protected:
|
||||
IParserValue() = default;
|
||||
|
||||
public:
|
||||
virtual ~IParserValue() = default;
|
||||
IParserValue(const IParserValue& other) = default;
|
||||
IParserValue(IParserValue&& other) noexcept = default;
|
||||
IParserValue& operator=(const IParserValue& other) = default;
|
||||
IParserValue& operator=(IParserValue&& other) noexcept = default;
|
||||
|
||||
_NODISCARD virtual bool IsEof() const = 0;
|
||||
_NODISCARD virtual const TokenPos& GetPos() const = 0;
|
||||
};
|
111
src/Parser/Parsing/Impl/AbstractDirectiveStreamProxy.cpp
Normal file
111
src/Parser/Parsing/Impl/AbstractDirectiveStreamProxy.cpp
Normal 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;
|
||||
}
|
22
src/Parser/Parsing/Impl/AbstractDirectiveStreamProxy.h
Normal file
22
src/Parser/Parsing/Impl/AbstractDirectiveStreamProxy.h
Normal 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);
|
||||
};
|
348
src/Parser/Parsing/Impl/AbstractLexer.h
Normal file
348
src/Parser/Parsing/Impl/AbstractLexer.h
Normal file
@ -0,0 +1,348 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Parsing/ILexer.h"
|
||||
#include "Parsing/IParserLineStream.h"
|
||||
#include "Parsing/ParsingException.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class AbstractLexer : public ILexer<TokenType>
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
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;
|
||||
|
||||
explicit AbstractLexer(IParserLineStream* stream)
|
||||
: m_stream(stream),
|
||||
m_line_index(0u),
|
||||
m_current_line_offset(0u)
|
||||
{
|
||||
}
|
||||
|
||||
virtual TokenType GetNextToken() = 0;
|
||||
|
||||
int NextChar()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
while (m_line_index >= m_line_cache.size())
|
||||
{
|
||||
if (!m_line_cache.empty() && m_line_cache.back().IsEof())
|
||||
return EOF;
|
||||
|
||||
m_line_cache.push_back(m_stream->NextLine());
|
||||
}
|
||||
|
||||
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_line_cache[m_line_index].m_line[m_current_line_offset++];
|
||||
}
|
||||
|
||||
int PeekChar()
|
||||
{
|
||||
auto peekLineOffset = m_current_line_offset;
|
||||
auto peekLine = m_line_index;
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (peekLine >= m_line_cache.size())
|
||||
{
|
||||
if (m_line_cache.back().IsEof())
|
||||
return EOF;
|
||||
|
||||
m_line_cache.push_back(m_stream->NextLine());
|
||||
}
|
||||
|
||||
if (peekLineOffset >= m_line_cache[peekLine].m_line.size())
|
||||
{
|
||||
peekLine++;
|
||||
peekLineOffset = 0;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
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 >= CurrentLine().m_line.size();
|
||||
}
|
||||
|
||||
_NODISCARD bool NextCharInLineIs(const char c)
|
||||
{
|
||||
return !IsLineEnd() && PeekChar() == c;
|
||||
}
|
||||
|
||||
_NODISCARD TokenPos GetPreviousCharacterPos() const
|
||||
{
|
||||
const auto& currentLine = CurrentLine();
|
||||
return TokenPos(currentLine.m_filename, currentLine.m_line_number, m_current_line_offset);
|
||||
}
|
||||
|
||||
_NODISCARD TokenPos GetNextCharacterPos()
|
||||
{
|
||||
const auto& currentLine = CurrentLine();
|
||||
if (m_current_line_offset + 1 >= currentLine.m_line.size())
|
||||
{
|
||||
PeekChar();
|
||||
return TokenPos(m_line_cache.back().m_filename, m_line_cache.back().m_line_number, 1);
|
||||
}
|
||||
|
||||
return TokenPos(currentLine.m_filename, currentLine.m_line_number, m_current_line_offset + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reads an identifier from the current position
|
||||
* \return The value of the read identifier
|
||||
*/
|
||||
std::string ReadIdentifier()
|
||||
{
|
||||
const auto& currentLine = CurrentLine();
|
||||
assert(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 = currentLine.m_line.size();
|
||||
while (m_current_line_offset < lineSize)
|
||||
{
|
||||
const auto c = currentLine.m_line[m_current_line_offset];
|
||||
|
||||
if (!isalnum(c) && c != '_')
|
||||
break;
|
||||
|
||||
m_current_line_offset++;
|
||||
}
|
||||
|
||||
return std::string(currentLine.m_line, startPos, m_current_line_offset - startPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reads an identifier from the current position
|
||||
* \return The value of the read identifier
|
||||
*/
|
||||
std::string ReadString()
|
||||
{
|
||||
const auto& currentLine = CurrentLine();
|
||||
assert(m_current_line_offset >= 1);
|
||||
assert(currentLine.m_line[m_current_line_offset - 1] == '"');
|
||||
|
||||
const auto startPos = m_current_line_offset;
|
||||
const auto lineSize = currentLine.m_line.size();
|
||||
while (true)
|
||||
{
|
||||
if (m_current_line_offset >= lineSize)
|
||||
throw ParsingException(TokenPos(currentLine.m_filename, currentLine.m_line_number, m_current_line_offset), "Unclosed string");
|
||||
|
||||
if (currentLine.m_line[m_current_line_offset] == '\"')
|
||||
break;
|
||||
|
||||
m_current_line_offset++;
|
||||
}
|
||||
|
||||
return std::string(currentLine.m_line, startPos, m_current_line_offset++ - startPos);
|
||||
}
|
||||
|
||||
void ReadHexNumber(int& integerValue)
|
||||
{
|
||||
const auto& currentLine = CurrentLine();
|
||||
const auto* start = ¤tLine.m_line.c_str()[m_current_line_offset - 1];
|
||||
char* end;
|
||||
|
||||
integerValue = static_cast<int>(std::strtoul(start, &end, 16));
|
||||
const auto numberLength = static_cast<unsigned>(end - start);
|
||||
if (numberLength == 0 || isalnum(*end) || *end == '_')
|
||||
throw ParsingException(GetPreviousCharacterPos(), "Invalid hex number");
|
||||
|
||||
m_current_line_offset += numberLength - 1;
|
||||
}
|
||||
|
||||
_NODISCARD bool IsIntegerNumber() const
|
||||
{
|
||||
const auto& currentLine = CurrentLine();
|
||||
const auto* currentCharacter = ¤tLine.m_line.c_str()[m_current_line_offset - 1];
|
||||
auto isInteger = true;
|
||||
auto dot = false;
|
||||
auto exponent = false;
|
||||
|
||||
if (*currentCharacter == '-' || *currentCharacter == '+')
|
||||
currentCharacter++;
|
||||
|
||||
while (*currentCharacter)
|
||||
{
|
||||
if (isdigit(*currentCharacter))
|
||||
{
|
||||
}
|
||||
else if (*currentCharacter == '.')
|
||||
{
|
||||
if (dot)
|
||||
throw ParsingException(GetPreviousCharacterPos(), "Invalid number");
|
||||
dot = true;
|
||||
isInteger = false;
|
||||
}
|
||||
else if (*currentCharacter == 'e' || *currentCharacter == 'E')
|
||||
{
|
||||
if (exponent)
|
||||
throw ParsingException(GetPreviousCharacterPos(), "Invalid number");
|
||||
if (currentCharacter[1] == '-')
|
||||
currentCharacter++;
|
||||
exponent = true;
|
||||
isInteger = false;
|
||||
}
|
||||
else if (isalpha(*currentCharacter))
|
||||
{
|
||||
throw ParsingException(GetPreviousCharacterPos(), "Invalid number");
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
currentCharacter++;
|
||||
}
|
||||
|
||||
return isInteger;
|
||||
}
|
||||
|
||||
int ReadInteger()
|
||||
{
|
||||
const auto& currentLine = CurrentLine();
|
||||
const auto* start = ¤tLine.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)
|
||||
throw ParsingException(GetPreviousCharacterPos(), "Invalid number");
|
||||
|
||||
m_current_line_offset += numberLength - 1;
|
||||
|
||||
return integerValue;
|
||||
}
|
||||
|
||||
double ReadFloatingPoint()
|
||||
{
|
||||
const auto& currentLine = CurrentLine();
|
||||
const auto* start = ¤tLine.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);
|
||||
|
||||
if (numberLength == 0)
|
||||
throw ParsingException(GetPreviousCharacterPos(), "Invalid number");
|
||||
|
||||
m_current_line_offset += numberLength - 1;
|
||||
|
||||
return floatingPointValue;
|
||||
}
|
||||
|
||||
void ReadNumber(bool& isFloatingPoint, double& floatingPointValue, int& integerValue)
|
||||
{
|
||||
const auto& currentLine = CurrentLine();
|
||||
assert(m_current_line_offset >= 1);
|
||||
assert(isdigit(currentLine.m_line[m_current_line_offset - 1]));
|
||||
|
||||
const auto lineLength = currentLine.m_line.size();
|
||||
if (lineLength - m_current_line_offset >= 1
|
||||
&& currentLine.m_line[m_current_line_offset - 1] == '0'
|
||||
&& currentLine.m_line[m_current_line_offset] == 'x')
|
||||
{
|
||||
isFloatingPoint = false;
|
||||
ReadHexNumber(integerValue);
|
||||
}
|
||||
else if (IsIntegerNumber())
|
||||
{
|
||||
isFloatingPoint = false;
|
||||
integerValue = ReadInteger();
|
||||
}
|
||||
else
|
||||
{
|
||||
isFloatingPoint = true;
|
||||
floatingPointValue = ReadFloatingPoint();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
const TokenType& GetToken(unsigned index) override
|
||||
{
|
||||
assert(index >= 0);
|
||||
while (index >= m_token_cache.size())
|
||||
m_token_cache.emplace_back(GetNextToken());
|
||||
|
||||
return m_token_cache[index];
|
||||
}
|
||||
|
||||
void PopTokens(int amount) override
|
||||
{
|
||||
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
|
||||
{
|
||||
return GetToken(0).IsEof();
|
||||
}
|
||||
|
||||
_NODISCARD const TokenPos& GetPos() override
|
||||
{
|
||||
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();
|
||||
}
|
||||
};
|
97
src/Parser/Parsing/Impl/AbstractParser.h
Normal file
97
src/Parser/Parsing/Impl/AbstractParser.h
Normal file
@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "Parsing/IParser.h"
|
||||
#include "Parsing/ILexer.h"
|
||||
#include "Parsing/Sequence/AbstractSequence.h"
|
||||
#include "Parsing/ParsingException.h"
|
||||
|
||||
template <typename TokenType, typename ParserState>
|
||||
class AbstractParser : public IParser
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
public:
|
||||
typedef AbstractSequence<TokenType, ParserState> sequence_t;
|
||||
|
||||
protected:
|
||||
ILexer<TokenType>* m_lexer;
|
||||
std::unique_ptr<ParserState> m_state;
|
||||
|
||||
explicit AbstractParser(ILexer<TokenType>* lexer, std::unique_ptr<ParserState> state)
|
||||
: m_lexer(lexer),
|
||||
m_state(std::move(state))
|
||||
{
|
||||
}
|
||||
|
||||
virtual const std::vector<sequence_t*>& GetTestsForState() = 0;
|
||||
|
||||
public:
|
||||
virtual ~AbstractParser() override = default;
|
||||
AbstractParser(const AbstractParser& other) = default;
|
||||
AbstractParser(AbstractParser&& other) noexcept = default;
|
||||
AbstractParser& operator=(const AbstractParser& other) = default;
|
||||
AbstractParser& operator=(AbstractParser&& other) noexcept = default;
|
||||
|
||||
bool Parse() override
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!m_lexer->IsEof())
|
||||
{
|
||||
auto testSuccessful = false;
|
||||
const auto& availableTests = GetTestsForState();
|
||||
|
||||
for (const sequence_t* test : availableTests)
|
||||
{
|
||||
unsigned consumedTokenCount;
|
||||
|
||||
if (test->MatchSequence(m_lexer, m_state.get(), consumedTokenCount))
|
||||
{
|
||||
m_lexer->PopTokens(consumedTokenCount);
|
||||
testSuccessful = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!testSuccessful)
|
||||
{
|
||||
const TokenPos& pos = m_lexer->GetPos();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const ParsingException& e)
|
||||
{
|
||||
const auto pos = e.Position();
|
||||
const auto line = m_lexer->GetLineForPos(pos);
|
||||
|
||||
if (!line.IsEof())
|
||||
{
|
||||
std::cout << "Error: " << e.FullMessage() << "\n" << line.m_line.substr(pos.m_column - 1) << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Error: " << e.FullMessage() << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
79
src/Parser/Parsing/Impl/CommentRemovingStreamProxy.cpp
Normal file
79
src/Parser/Parsing/Impl/CommentRemovingStreamProxy.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "CommentRemovingStreamProxy.h"
|
||||
|
||||
CommentRemovingStreamProxy::CommentRemovingStreamProxy(IParserLineStream* stream)
|
||||
: m_stream(stream),
|
||||
m_inside_multi_line_comment(false),
|
||||
m_next_line_is_comment(false)
|
||||
{
|
||||
}
|
||||
|
||||
ParserLine CommentRemovingStreamProxy::NextLine()
|
||||
{
|
||||
auto line = m_stream->NextLine();
|
||||
|
||||
if (m_next_line_is_comment)
|
||||
{
|
||||
m_next_line_is_comment = !line.m_line.empty() && line.m_line[line.m_line.size() - 1] == '\\';
|
||||
return ParserLine(line.m_filename, line.m_line_number, std::string());
|
||||
}
|
||||
|
||||
unsigned multiLineCommentStart = 0;
|
||||
for (auto i = 0u; i < line.m_line.size(); i++)
|
||||
{
|
||||
const auto c = line.m_line[i];
|
||||
|
||||
if (m_inside_multi_line_comment)
|
||||
{
|
||||
if (c == '*' && i + 1 < line.m_line.size() && line.m_line[i + 1] == '/')
|
||||
{
|
||||
line.m_line.erase(multiLineCommentStart, i + 2 - multiLineCommentStart);
|
||||
multiLineCommentStart = 0;
|
||||
m_inside_multi_line_comment = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(c == '/' && i + 1 < line.m_line.size())
|
||||
{
|
||||
const auto c1 = line.m_line[i + 1];
|
||||
|
||||
if (c1 == '*')
|
||||
{
|
||||
multiLineCommentStart = i;
|
||||
m_inside_multi_line_comment = true;
|
||||
}
|
||||
else if(c1 == '/')
|
||||
{
|
||||
m_next_line_is_comment = line.m_line[line.m_line.size() - 1] == '\\';
|
||||
line.m_line.erase(i);
|
||||
return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_inside_multi_line_comment)
|
||||
line.m_line.erase(multiLineCommentStart);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
bool CommentRemovingStreamProxy::Eof() const
|
||||
{
|
||||
return m_stream->Eof();
|
||||
}
|
19
src/Parser/Parsing/Impl/CommentRemovingStreamProxy.h
Normal file
19
src/Parser/Parsing/Impl/CommentRemovingStreamProxy.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Parsing/IParserLineStream.h"
|
||||
|
||||
class CommentRemovingStreamProxy final : public IParserLineStream
|
||||
{
|
||||
IParserLineStream* const m_stream;
|
||||
bool m_inside_multi_line_comment;
|
||||
bool m_next_line_is_comment;
|
||||
|
||||
public:
|
||||
explicit CommentRemovingStreamProxy(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;
|
||||
};
|
470
src/Parser/Parsing/Impl/DefinesStreamProxy.cpp
Normal file
470
src/Parser/Parsing/Impl/DefinesStreamProxy.cpp
Normal file
@ -0,0 +1,470 @@
|
||||
#include "DefinesStreamProxy.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "Parsing/ParsingException.h"
|
||||
|
||||
DefinesStreamProxy::DefineParameterPosition::DefineParameterPosition()
|
||||
: m_parameter_index(0u),
|
||||
m_parameter_position(0u)
|
||||
{
|
||||
}
|
||||
|
||||
DefinesStreamProxy::DefineParameterPosition::DefineParameterPosition(const unsigned index, const unsigned position)
|
||||
: m_parameter_index(index),
|
||||
m_parameter_position(position)
|
||||
{
|
||||
}
|
||||
|
||||
DefinesStreamProxy::Define::Define()
|
||||
= default;
|
||||
|
||||
DefinesStreamProxy::Define::Define(std::string name, std::string value)
|
||||
: m_name(std::move(name)),
|
||||
m_value(std::move(value))
|
||||
{
|
||||
}
|
||||
|
||||
std::string DefinesStreamProxy::Define::Render(std::vector<std::string>& parameterValues)
|
||||
{
|
||||
if (parameterValues.empty() || m_parameter_positions.empty())
|
||||
return m_value;
|
||||
|
||||
std::ostringstream str;
|
||||
auto lastPos = 0u;
|
||||
for (const auto& parameterPosition : m_parameter_positions)
|
||||
{
|
||||
if (lastPos < parameterPosition.m_parameter_position)
|
||||
str << std::string(m_value, lastPos, parameterPosition.m_parameter_position - lastPos);
|
||||
|
||||
if (parameterPosition.m_parameter_index < parameterValues.size())
|
||||
{
|
||||
str << parameterValues[parameterPosition.m_parameter_index];
|
||||
}
|
||||
|
||||
lastPos = parameterPosition.m_parameter_position;
|
||||
}
|
||||
|
||||
if (lastPos < m_value.size())
|
||||
str << std::string(m_value, lastPos, m_value.size() - lastPos);
|
||||
|
||||
return str.str();
|
||||
}
|
||||
|
||||
void DefinesStreamProxy::Define::IdentifyParameters(std::vector<std::string>& parameterNames)
|
||||
{
|
||||
if (parameterNames.empty())
|
||||
return;
|
||||
|
||||
auto inWord = false;
|
||||
auto wordStart = 0u;
|
||||
for (auto i = 0u; i < m_value.size(); i++)
|
||||
{
|
||||
const auto c = m_value[i];
|
||||
if (!isalnum(c))
|
||||
{
|
||||
if (inWord)
|
||||
{
|
||||
const std::string word(m_value, wordStart, i - wordStart);
|
||||
|
||||
auto parameterIndex = 0u;
|
||||
for (; parameterIndex < parameterNames.size(); parameterIndex++)
|
||||
{
|
||||
if (word == parameterNames[parameterIndex])
|
||||
break;
|
||||
}
|
||||
|
||||
if (parameterIndex < parameterNames.size())
|
||||
{
|
||||
m_value.erase(wordStart, i - wordStart);
|
||||
m_parameter_positions.emplace_back(parameterIndex, wordStart);
|
||||
i = wordStart;
|
||||
}
|
||||
|
||||
inWord = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!inWord && (isalpha(c) || c == '_'))
|
||||
{
|
||||
inWord = true;
|
||||
wordStart = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inWord)
|
||||
{
|
||||
const std::string word(m_value, wordStart, m_value.size() - wordStart);
|
||||
|
||||
auto parameterIndex = 0u;
|
||||
for (; parameterIndex < parameterNames.size(); parameterIndex++)
|
||||
{
|
||||
if (word == parameterNames[parameterIndex])
|
||||
break;
|
||||
}
|
||||
|
||||
if (parameterIndex < parameterNames.size())
|
||||
{
|
||||
m_value.erase(wordStart, m_value.size() - wordStart);
|
||||
m_parameter_positions.emplace_back(parameterIndex, wordStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DefinesStreamProxy::DefinesStreamProxy(IParserLineStream* stream)
|
||||
: m_stream(stream),
|
||||
m_ignore_depth(0)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::string> DefinesStreamProxy::MatchDefineParameters(const ParserLine& line, unsigned& parameterPosition)
|
||||
{
|
||||
if (line.m_line[parameterPosition] != '(')
|
||||
return std::vector<std::string>();
|
||||
|
||||
parameterPosition++;
|
||||
std::vector<std::string> parameters;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!SkipWhitespace(line, parameterPosition) || parameterPosition >= line.m_line.size())
|
||||
throw ParsingException(CreatePos(line, parameterPosition), "Invalid define parameter list");
|
||||
|
||||
const auto nameStartPos = parameterPosition;
|
||||
if (!ExtractIdentifier(line, parameterPosition))
|
||||
throw ParsingException(CreatePos(line, parameterPosition), "Cannot extract name of parameter of define");
|
||||
|
||||
parameters.emplace_back(std::string(line.m_line, nameStartPos, parameterPosition - nameStartPos));
|
||||
|
||||
if (parameterPosition >= line.m_line.size())
|
||||
throw ParsingException(CreatePos(line, parameterPosition), "Unclosed define parameters");
|
||||
|
||||
if (line.m_line[parameterPosition] == ')')
|
||||
{
|
||||
parameterPosition++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.m_line[parameterPosition] != ',')
|
||||
throw ParsingException(CreatePos(line, parameterPosition), "Unknown symbol in define parameter list");
|
||||
|
||||
parameterPosition++;
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
bool DefinesStreamProxy::MatchDefineDirective(const ParserLine& line, unsigned directivePosition)
|
||||
{
|
||||
if (!MatchString(line, directivePosition, DEFINE_DIRECTIVE, std::char_traits<char>::length(DEFINE_DIRECTIVE)))
|
||||
return false;
|
||||
|
||||
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, directivePosition - nameStartPos);
|
||||
|
||||
auto parameters = MatchDefineParameters(line, directivePosition);
|
||||
|
||||
std::string value;
|
||||
if (directivePosition < line.m_line.size())
|
||||
value = line.m_line.substr(directivePosition + 1);
|
||||
|
||||
Define define(name, value);
|
||||
define.IdentifyParameters(parameters);
|
||||
AddDefine(std::move(define));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DefinesStreamProxy::MatchUndefDirective(const ParserLine& line, unsigned directivePosition)
|
||||
{
|
||||
if (!MatchString(line, directivePosition, UNDEF_DIRECTIVE, std::char_traits<char>::length(UNDEF_DIRECTIVE)))
|
||||
return false;
|
||||
|
||||
if (!SkipWhitespace(line, directivePosition))
|
||||
throw ParsingException(CreatePos(line, directivePosition), "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, directivePosition - nameStartPos);
|
||||
const auto entry = m_defines.find(name);
|
||||
|
||||
if (entry != m_defines.end())
|
||||
m_defines.erase(entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DefinesStreamProxy::MatchIfdefDirective(const ParserLine& line, unsigned directivePosition)
|
||||
{
|
||||
auto reverse = false;
|
||||
if (!MatchString(line, directivePosition, IFDEF_DIRECTIVE, std::char_traits<char>::length(IFDEF_DIRECTIVE)))
|
||||
{
|
||||
if (!MatchString(line, directivePosition, IFNDEF_DIRECTIVE, std::char_traits<char>::length(IFNDEF_DIRECTIVE)))
|
||||
return false;
|
||||
|
||||
reverse = true;
|
||||
}
|
||||
|
||||
if (!m_modes.empty() && !m_modes.top())
|
||||
{
|
||||
m_ignore_depth++;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!SkipWhitespace(line, directivePosition))
|
||||
throw ParsingException(CreatePos(line, directivePosition), "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, directivePosition - nameStartPos);
|
||||
const auto entry = m_defines.find(name);
|
||||
|
||||
if (entry != m_defines.end())
|
||||
m_modes.push(!reverse);
|
||||
else
|
||||
m_modes.push(reverse);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DefinesStreamProxy::MatchElseDirective(const ParserLine& line, unsigned directivePosition)
|
||||
{
|
||||
if (!MatchString(line, directivePosition, ELSE_DIRECTIVE, std::char_traits<char>::length(ELSE_DIRECTIVE)))
|
||||
return false;
|
||||
|
||||
if (m_ignore_depth > 0)
|
||||
return true;
|
||||
|
||||
if (!m_modes.empty())
|
||||
m_modes.top() = !m_modes.top();
|
||||
else
|
||||
throw ParsingException(CreatePos(line, directivePosition), "Cannot use else without ifdef");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DefinesStreamProxy::MatchEndifDirective(const ParserLine& line, unsigned directivePosition)
|
||||
{
|
||||
if (!MatchString(line, directivePosition, ENDIF_DIRECTIVE, std::char_traits<char>::length(ENDIF_DIRECTIVE)))
|
||||
return false;
|
||||
|
||||
if (m_ignore_depth > 0)
|
||||
{
|
||||
m_ignore_depth--;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!m_modes.empty())
|
||||
m_modes.pop();
|
||||
else
|
||||
throw ParsingException(CreatePos(line, directivePosition), "Cannot use endif without ifdef");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DefinesStreamProxy::MatchDirectives(const ParserLine& line)
|
||||
{
|
||||
unsigned directivePos;
|
||||
|
||||
if (!FindDirective(line, directivePos))
|
||||
return false;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool DefinesStreamProxy::FindDefineForWord(const ParserLine& line, const unsigned wordStart, const unsigned wordEnd, Define*& value)
|
||||
{
|
||||
const std::string word(line.m_line, wordStart, wordEnd - wordStart);
|
||||
const auto foundEntry = m_defines.find(word);
|
||||
if (foundEntry != m_defines.end())
|
||||
{
|
||||
value = &foundEntry->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DefinesStreamProxy::ExtractParametersFromDefineUsage(const ParserLine& line, const unsigned parameterStart, unsigned& parameterEnd, std::vector<std::string>& parameterValues)
|
||||
{
|
||||
if (line.m_line[parameterStart] != '(')
|
||||
return;
|
||||
|
||||
std::ostringstream currentValue;
|
||||
auto pos = parameterStart + 1;
|
||||
auto valueHasStarted = false;
|
||||
while (true)
|
||||
{
|
||||
if (pos >= line.m_line.size())
|
||||
throw ParsingException(CreatePos(line, pos), "Invalid use of define");
|
||||
|
||||
const auto c = line.m_line[pos];
|
||||
|
||||
if (c == ',')
|
||||
{
|
||||
parameterValues.emplace_back(currentValue.str());
|
||||
currentValue.clear();
|
||||
currentValue.str(std::string());
|
||||
valueHasStarted = false;
|
||||
}
|
||||
else if (c == ')')
|
||||
{
|
||||
parameterValues.emplace_back(currentValue.str());
|
||||
parameterEnd = pos + 1;
|
||||
break;
|
||||
}
|
||||
else if (valueHasStarted || !isspace(c))
|
||||
{
|
||||
valueHasStarted = true;
|
||||
currentValue << c;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
void DefinesStreamProxy::ExpandDefines(ParserLine& line)
|
||||
{
|
||||
auto wordStart = 0u;
|
||||
auto lastWordEnd = 0u;
|
||||
auto inWord = false;
|
||||
Define* value;
|
||||
|
||||
std::ostringstream str;
|
||||
auto usesDefines = false;
|
||||
|
||||
for (auto i = 0u; i < line.m_line.size(); i++)
|
||||
{
|
||||
const auto c = line.m_line[i];
|
||||
if (!inWord)
|
||||
{
|
||||
if (isalpha(c) || c == '_')
|
||||
{
|
||||
wordStart = i;
|
||||
inWord = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isalnum(c) && c != '_')
|
||||
{
|
||||
if (FindDefineForWord(line, wordStart, i, value))
|
||||
{
|
||||
std::vector<std::string> parameterValues;
|
||||
ExtractParametersFromDefineUsage(line, i, i, parameterValues);
|
||||
const auto defineValue = value->Render(parameterValues);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inWord)
|
||||
{
|
||||
if (FindDefineForWord(line, wordStart, line.m_line.size(), value))
|
||||
{
|
||||
std::vector<std::string> parameterValues;
|
||||
const auto defineValue = value->Render(parameterValues);
|
||||
|
||||
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 = line.m_line.size();
|
||||
}
|
||||
}
|
||||
|
||||
if (usesDefines)
|
||||
{
|
||||
if (lastWordEnd < line.m_line.size())
|
||||
str << std::string(line.m_line, lastWordEnd, line.m_line.size() - lastWordEnd);
|
||||
line.m_line = str.str();
|
||||
}
|
||||
}
|
||||
|
||||
void DefinesStreamProxy::AddDefine(Define define)
|
||||
{
|
||||
m_defines[define.m_name] = std::move(define);
|
||||
}
|
||||
|
||||
void DefinesStreamProxy::Undefine(const std::string& name)
|
||||
{
|
||||
const auto entry = m_defines.find(name);
|
||||
|
||||
if (entry != m_defines.end())
|
||||
m_defines.erase(entry);
|
||||
}
|
||||
|
||||
ParserLine DefinesStreamProxy::NextLine()
|
||||
{
|
||||
auto line = m_stream->NextLine();
|
||||
|
||||
if (MatchDirectives(line)
|
||||
|| !m_modes.empty() && !m_modes.top())
|
||||
{
|
||||
line.m_line.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
ExpandDefines(line);
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
bool DefinesStreamProxy::Eof() const
|
||||
{
|
||||
return m_stream->Eof();
|
||||
}
|
71
src/Parser/Parsing/Impl/DefinesStreamProxy.h
Normal file
71
src/Parser/Parsing/Impl/DefinesStreamProxy.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <stack>
|
||||
|
||||
#include "AbstractDirectiveStreamProxy.h"
|
||||
#include "Parsing/IParserLineStream.h"
|
||||
|
||||
class DefinesStreamProxy final : public AbstractDirectiveStreamProxy
|
||||
{
|
||||
static constexpr const char* DEFINE_DIRECTIVE = "define ";
|
||||
static constexpr const char* UNDEF_DIRECTIVE = "undef ";
|
||||
static constexpr const char* IFDEF_DIRECTIVE = "ifdef ";
|
||||
static constexpr const char* IFNDEF_DIRECTIVE = "ifndef ";
|
||||
static constexpr const char* ELSE_DIRECTIVE = "else";
|
||||
static constexpr const char* ENDIF_DIRECTIVE = "endif";
|
||||
|
||||
public:
|
||||
class DefineParameterPosition
|
||||
{
|
||||
public:
|
||||
unsigned m_parameter_index;
|
||||
unsigned m_parameter_position;
|
||||
|
||||
DefineParameterPosition();
|
||||
DefineParameterPosition(unsigned index, unsigned position);
|
||||
};
|
||||
|
||||
class Define
|
||||
{
|
||||
public:
|
||||
std::string m_name;
|
||||
std::string m_value;
|
||||
std::vector<DefineParameterPosition> m_parameter_positions;
|
||||
|
||||
Define();
|
||||
Define(std::string name, std::string value);
|
||||
void IdentifyParameters(std::vector<std::string>& parameterNames);
|
||||
std::string Render(std::vector<std::string>& parameterValues);
|
||||
};
|
||||
|
||||
private:
|
||||
IParserLineStream* const m_stream;
|
||||
std::unordered_map<std::string, Define> m_defines;
|
||||
std::stack<bool> m_modes;
|
||||
unsigned m_ignore_depth;
|
||||
|
||||
std::vector<std::string> MatchDefineParameters(const ParserLine& line, unsigned& parameterPosition);
|
||||
_NODISCARD bool MatchDefineDirective(const ParserLine& line, unsigned directivePosition);
|
||||
_NODISCARD bool MatchUndefDirective(const ParserLine& line, unsigned directivePosition);
|
||||
_NODISCARD bool MatchIfdefDirective(const ParserLine& line, unsigned directivePosition);
|
||||
_NODISCARD bool MatchElseDirective(const ParserLine& line, unsigned directivePosition);
|
||||
_NODISCARD bool MatchEndifDirective(const ParserLine& line, unsigned directivePosition);
|
||||
_NODISCARD bool MatchDirectives(const ParserLine& line);
|
||||
|
||||
void ExtractParametersFromDefineUsage(const ParserLine& line, unsigned parameterStart, unsigned& parameterEnd, std::vector<std::string>& parameterValues);
|
||||
bool FindDefineForWord(const ParserLine& line, unsigned wordStart, unsigned wordEnd, Define*& value);
|
||||
void ExpandDefines(ParserLine& line);
|
||||
|
||||
public:
|
||||
explicit DefinesStreamProxy(IParserLineStream* stream);
|
||||
|
||||
void AddDefine(Define define);
|
||||
void Undefine(const std::string& name);
|
||||
|
||||
ParserLine NextLine() override;
|
||||
bool IncludeFile(const std::string& filename) override;
|
||||
void PopCurrentFile() override;
|
||||
_NODISCARD bool IsOpen() const override;
|
||||
_NODISCARD bool Eof() const override;
|
||||
};
|
142
src/Parser/Parsing/Impl/IncludingStreamProxy.cpp
Normal file
142
src/Parser/Parsing/Impl/IncludingStreamProxy.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
#include "IncludingStreamProxy.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "Parsing/ParsingException.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
IncludingStreamProxy::IncludingStreamProxy(IParserLineStream* stream)
|
||||
: m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
bool IncludingStreamProxy::ExtractIncludeFilename(const ParserLine& line, const unsigned includeDirectivePosition, unsigned& filenameStartPosition, unsigned& filenameEndPosition)
|
||||
{
|
||||
auto currentPos = includeDirectivePosition;
|
||||
bool isDoubleQuotes;
|
||||
|
||||
while (isspace(line.m_line[currentPos]))
|
||||
{
|
||||
if (currentPos++ >= line.m_line.size())
|
||||
throw ParsingException(CreatePos(line, currentPos - 1), INCLUDE_QUOTES_ERROR);
|
||||
}
|
||||
|
||||
if (line.m_line[currentPos] == '"')
|
||||
isDoubleQuotes = true;
|
||||
else if (line.m_line[currentPos] == '<')
|
||||
isDoubleQuotes = false;
|
||||
else
|
||||
throw ParsingException(CreatePos(line, currentPos - 1), INCLUDE_QUOTES_ERROR);
|
||||
|
||||
filenameStartPosition = ++currentPos;
|
||||
filenameEndPosition = 0;
|
||||
|
||||
for (; currentPos < line.m_line.size(); currentPos++)
|
||||
{
|
||||
const auto c = line.m_line[currentPos];
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
if (!isDoubleQuotes)
|
||||
throw ParsingException(CreatePos(line, currentPos - 1), "");
|
||||
filenameEndPosition = currentPos;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c == '>')
|
||||
{
|
||||
if (isDoubleQuotes)
|
||||
throw ParsingException(CreatePos(line, currentPos - 1), INCLUDE_QUOTES_ERROR);
|
||||
filenameEndPosition = currentPos;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IncludingStreamProxy::MatchIncludeDirective(const ParserLine& line, unsigned directivePosition) const
|
||||
{
|
||||
if (!MatchString(line, directivePosition, INCLUDE_DIRECTIVE, std::char_traits<char>::length(INCLUDE_DIRECTIVE)))
|
||||
return false;
|
||||
|
||||
unsigned filenameStart, filenameEnd;
|
||||
|
||||
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(CreatePos(line, directivePosition), "No filename specified");
|
||||
|
||||
const auto filename = line.m_line.substr(filenameStart, filenameEnd - filenameStart);
|
||||
|
||||
if (!m_stream->IncludeFile(filename))
|
||||
{
|
||||
std::ostringstream errorStr;
|
||||
errorStr << "Could not include file \"" << filename << "\"";
|
||||
throw ParsingException(CreatePos(line, directivePosition), errorStr.str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IncludingStreamProxy::MatchPragmaOnceDirective(const ParserLine& line, unsigned directivePosition)
|
||||
{
|
||||
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()));
|
||||
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::MatchDirectives(const ParserLine& line)
|
||||
{
|
||||
unsigned directivePos;
|
||||
|
||||
if (!FindDirective(line, directivePos))
|
||||
return false;
|
||||
|
||||
directivePos++;
|
||||
return MatchIncludeDirective(line, directivePos)
|
||||
|| MatchPragmaOnceDirective(line, directivePos);
|
||||
}
|
||||
|
||||
ParserLine IncludingStreamProxy::NextLine()
|
||||
{
|
||||
auto line = m_stream->NextLine();
|
||||
|
||||
while(MatchDirectives(line))
|
||||
line = m_stream->NextLine();
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
bool IncludingStreamProxy::Eof() const
|
||||
{
|
||||
return m_stream->Eof();
|
||||
}
|
30
src/Parser/Parsing/Impl/IncludingStreamProxy.h
Normal file
30
src/Parser/Parsing/Impl/IncludingStreamProxy.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "AbstractDirectiveStreamProxy.h"
|
||||
#include "Parsing/IParserLineStream.h"
|
||||
|
||||
class IncludingStreamProxy final : public AbstractDirectiveStreamProxy
|
||||
{
|
||||
static constexpr const char* INCLUDE_QUOTES_ERROR = "Invalid include directive. Expected \"\" or <>";
|
||||
static constexpr const char* INCLUDE_DIRECTIVE = "include ";
|
||||
static constexpr const char* PRAGMA_ONCE_DIRECTIVE = "pragma once";
|
||||
|
||||
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 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;
|
||||
};
|
92
src/Parser/Parsing/Impl/PackDefinitionStreamProxy.cpp
Normal file
92
src/Parser/Parsing/Impl/PackDefinitionStreamProxy.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include "PackDefinitionStreamProxy.h"
|
||||
|
||||
#include "Parsing/ParsingException.h"
|
||||
|
||||
PackDefinitionStreamProxy::PackDefinitionStreamProxy(IParserLineStream* stream)
|
||||
: m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
bool PackDefinitionStreamProxy::MatchPackDirective(const ParserLine& line, unsigned directivePosition)
|
||||
{
|
||||
auto packValue = 0;
|
||||
|
||||
if (!MatchString(line, directivePosition, PRAGMA_PACK_DIRECTIVE, std::char_traits<char>::length(PRAGMA_PACK_DIRECTIVE)))
|
||||
return false;
|
||||
|
||||
if (!MatchNextCharacter(line, directivePosition, '('))
|
||||
throw ParsingException(CreatePos(line, directivePosition), "Invalid pack directive.");
|
||||
|
||||
bool isPush;
|
||||
if (MatchNextString(line, directivePosition, PUSH_KEYWORD, std::char_traits<char>::length(PUSH_KEYWORD)))
|
||||
isPush = true;
|
||||
else if (MatchNextString(line, directivePosition, POP_KEYWORD, std::char_traits<char>::length(POP_KEYWORD)))
|
||||
isPush = false;
|
||||
else
|
||||
throw ParsingException(CreatePos(line, directivePosition), "Unknown pack directive command.");
|
||||
|
||||
if(isPush)
|
||||
{
|
||||
if (!MatchNextCharacter(line, directivePosition, ','))
|
||||
throw ParsingException(CreatePos(line, directivePosition), "Invalid pack directive.");
|
||||
|
||||
if (!ExtractInteger(line, directivePosition, packValue))
|
||||
throw ParsingException(CreatePos(line, directivePosition), "Invalid pack value.");
|
||||
}
|
||||
|
||||
if (!MatchNextCharacter(line, directivePosition, ')'))
|
||||
throw ParsingException(CreatePos(line, directivePosition), "Invalid pack directive.");
|
||||
|
||||
if(isPush)
|
||||
m_current_pack.push(packValue);
|
||||
else if (!m_current_pack.empty())
|
||||
m_current_pack.pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PackDefinitionStreamProxy::MatchDirectives(const ParserLine& line)
|
||||
{
|
||||
unsigned directivePos;
|
||||
|
||||
if (!FindDirective(line, directivePos))
|
||||
return false;
|
||||
|
||||
directivePos++;
|
||||
return MatchPackDirective(line, directivePos);
|
||||
}
|
||||
|
||||
ParserLine PackDefinitionStreamProxy::NextLine()
|
||||
{
|
||||
auto line = m_stream->NextLine();
|
||||
|
||||
while (MatchDirectives(line))
|
||||
line = m_stream->NextLine();
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
bool PackDefinitionStreamProxy::IncludeFile(const std::string& filename)
|
||||
{
|
||||
return m_stream->IncludeFile(filename);
|
||||
}
|
||||
|
||||
void PackDefinitionStreamProxy::PopCurrentFile()
|
||||
{
|
||||
m_stream->PopCurrentFile();
|
||||
}
|
||||
|
||||
bool PackDefinitionStreamProxy::IsOpen() const
|
||||
{
|
||||
return m_stream->IsOpen();
|
||||
}
|
||||
|
||||
bool PackDefinitionStreamProxy::Eof() const
|
||||
{
|
||||
return m_stream->Eof();
|
||||
}
|
||||
|
||||
int PackDefinitionStreamProxy::GetCurrentPack() const
|
||||
{
|
||||
return m_current_pack.empty() ? DEFAULT_PACK : m_current_pack.top();
|
||||
}
|
35
src/Parser/Parsing/Impl/PackDefinitionStreamProxy.h
Normal file
35
src/Parser/Parsing/Impl/PackDefinitionStreamProxy.h
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <stack>
|
||||
|
||||
#include "AbstractDirectiveStreamProxy.h"
|
||||
#include "Parsing/IPackValueSupplier.h"
|
||||
#include "Parsing/IParserLineStream.h"
|
||||
|
||||
class PackDefinitionStreamProxy final : public AbstractDirectiveStreamProxy, public IPackValueSupplier
|
||||
{
|
||||
public:
|
||||
static constexpr int DEFAULT_PACK = 8;
|
||||
|
||||
private:
|
||||
static constexpr const char* PRAGMA_PACK_DIRECTIVE = "pragma pack";
|
||||
static constexpr const char* PUSH_KEYWORD = "push";
|
||||
static constexpr const char* POP_KEYWORD = "pop";
|
||||
|
||||
IParserLineStream* const m_stream;
|
||||
std::stack<int> m_current_pack;
|
||||
|
||||
_NODISCARD bool MatchPackDirective(const ParserLine& line, unsigned directivePosition);
|
||||
_NODISCARD bool MatchDirectives(const ParserLine& line);
|
||||
|
||||
public:
|
||||
explicit PackDefinitionStreamProxy(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;
|
||||
|
||||
int GetCurrentPack() const override;
|
||||
};
|
100
src/Parser/Parsing/Impl/ParserFilesystemStream.cpp
Normal file
100
src/Parser/Parsing/Impl/ParserFilesystemStream.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
#include "ParserFilesystemStream.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
ParserFilesystemStream::FileInfo::FileInfo(std::string filePath)
|
||||
: m_file_path(std::move(filePath)),
|
||||
m_stream(m_file_path),
|
||||
m_line_number(1)
|
||||
{
|
||||
}
|
||||
|
||||
ParserFilesystemStream::ParserFilesystemStream(std::string path)
|
||||
{
|
||||
const auto absolutePath = absolute(fs::path(path));
|
||||
m_files.emplace(FileInfo(absolutePath.string()));
|
||||
}
|
||||
|
||||
bool ParserFilesystemStream::IsOpen() const
|
||||
{
|
||||
return !m_files.empty()
|
||||
&& m_files.top().m_stream.is_open();
|
||||
}
|
||||
|
||||
ParserLine ParserFilesystemStream::NextLine()
|
||||
{
|
||||
std::ostringstream str;
|
||||
auto hasLength = false;
|
||||
|
||||
if (m_files.empty())
|
||||
return ParserLine();
|
||||
|
||||
while(!m_files.empty())
|
||||
{
|
||||
auto& fileInfo = m_files.top();
|
||||
|
||||
auto c = fileInfo.m_stream.get();
|
||||
while (c != EOF)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\r':
|
||||
c = fileInfo.m_stream.get();
|
||||
if (c == '\n')
|
||||
return ParserLine(fileInfo.m_file_path, fileInfo.m_line_number++, str.str());
|
||||
str << '\r';
|
||||
hasLength = true;
|
||||
continue;
|
||||
|
||||
case '\n':
|
||||
return ParserLine(fileInfo.m_file_path, fileInfo.m_line_number++, str.str());
|
||||
|
||||
default:
|
||||
str << static_cast<char>(c);
|
||||
hasLength = true;
|
||||
break;
|
||||
}
|
||||
|
||||
c = fileInfo.m_stream.get();
|
||||
}
|
||||
|
||||
if(hasLength)
|
||||
return ParserLine(fileInfo.m_file_path, fileInfo.m_line_number, str.str());
|
||||
m_files.pop();
|
||||
}
|
||||
|
||||
return ParserLine();
|
||||
}
|
||||
|
||||
bool ParserFilesystemStream::IncludeFile(const std::string& filename)
|
||||
{
|
||||
if (m_files.empty())
|
||||
return false;
|
||||
|
||||
auto newFilePath = fs::path(m_files.top().m_file_path);
|
||||
newFilePath.remove_filename().concat(filename);
|
||||
newFilePath = absolute(newFilePath);
|
||||
|
||||
FileInfo fileInfo(newFilePath.string());
|
||||
|
||||
if (!fileInfo.m_stream.is_open())
|
||||
return false;
|
||||
|
||||
m_files.emplace(std::move(fileInfo));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ParserFilesystemStream::PopCurrentFile()
|
||||
{
|
||||
if(!m_files.empty())
|
||||
m_files.pop();
|
||||
}
|
||||
|
||||
bool ParserFilesystemStream::Eof() const
|
||||
{
|
||||
return m_files.empty()
|
||||
|| m_files.top().m_stream.eof();
|
||||
}
|
29
src/Parser/Parsing/Impl/ParserFilesystemStream.h
Normal file
29
src/Parser/Parsing/Impl/ParserFilesystemStream.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stack>
|
||||
#include <fstream>
|
||||
|
||||
#include "Parsing/IParserLineStream.h"
|
||||
|
||||
class ParserFilesystemStream final : public IParserLineStream
|
||||
{
|
||||
class FileInfo
|
||||
{
|
||||
public:
|
||||
std::string m_file_path;
|
||||
std::ifstream m_stream;
|
||||
int m_line_number;
|
||||
|
||||
explicit FileInfo(std::string filePath);
|
||||
};
|
||||
std::stack<FileInfo> m_files;
|
||||
|
||||
public:
|
||||
explicit ParserFilesystemStream(std::string path);
|
||||
|
||||
ParserLine NextLine() override;
|
||||
bool IncludeFile(const std::string& filename) override;
|
||||
void PopCurrentFile() override;
|
||||
_NODISCARD bool IsOpen() const override;
|
||||
_NODISCARD bool Eof() const override;
|
||||
};
|
103
src/Parser/Parsing/Matcher/AbstractMatcher.h
Normal file
103
src/Parser/Parsing/Matcher/AbstractMatcher.h
Normal file
@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "Parsing/IParserValue.h"
|
||||
#include "Parsing/ILexer.h"
|
||||
#include "Parsing/Matcher/MatcherResult.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class AbstractMatcher
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
public:
|
||||
static constexpr int NO_ID = -1;
|
||||
|
||||
typedef std::vector<std::reference_wrapper<const TokenType>> token_list_t;
|
||||
|
||||
private:
|
||||
int m_tag_id;
|
||||
int m_capture_id;
|
||||
bool m_no_consume;
|
||||
std::function<TokenType(token_list_t&)> m_transform_func;
|
||||
|
||||
protected:
|
||||
AbstractMatcher()
|
||||
: m_tag_id(NO_ID),
|
||||
m_capture_id(NO_ID),
|
||||
m_no_consume(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual MatcherResult<TokenType> CanMatch(ILexer<TokenType>* lexer, unsigned tokenOffset) = 0;
|
||||
|
||||
public:
|
||||
virtual ~AbstractMatcher() = default;
|
||||
AbstractMatcher(const AbstractMatcher& other) = default;
|
||||
AbstractMatcher(AbstractMatcher&& other) noexcept = default;
|
||||
AbstractMatcher& operator=(const AbstractMatcher& other) = default;
|
||||
AbstractMatcher& operator=(AbstractMatcher&& other) noexcept = default;
|
||||
|
||||
void SetTag(const int tagId)
|
||||
{
|
||||
m_tag_id = tagId;
|
||||
}
|
||||
|
||||
void SetCapture(const int captureId)
|
||||
{
|
||||
m_capture_id = captureId;
|
||||
}
|
||||
|
||||
void SetConsume(const bool value)
|
||||
{
|
||||
m_no_consume = !value;
|
||||
}
|
||||
|
||||
void SetTransform(std::function<TokenType(std::vector<std::reference_wrapper<const TokenType>>&)> transform)
|
||||
{
|
||||
m_transform_func = std::move(transform);
|
||||
}
|
||||
|
||||
MatcherResult<TokenType> Match(ILexer<TokenType>* lexer, const unsigned tokenOffset)
|
||||
{
|
||||
MatcherResult<TokenType> result = CanMatch(lexer, tokenOffset);
|
||||
|
||||
if (!result.m_matches)
|
||||
return result;
|
||||
|
||||
if (m_tag_id != NO_ID)
|
||||
result.m_tags.insert(result.m_tags.begin(), m_tag_id);
|
||||
|
||||
if (m_transform_func)
|
||||
{
|
||||
std::vector<std::reference_wrapper<const TokenType>> tokens;
|
||||
tokens.reserve(result.m_consumed_token_count);
|
||||
|
||||
for (auto i = 0u; i < result.m_consumed_token_count; i++)
|
||||
tokens.emplace_back(lexer->GetToken(tokenOffset + i));
|
||||
|
||||
result.m_fabricated_tokens.push_back(m_transform_func(tokens));
|
||||
|
||||
result.m_matched_tokens.clear();
|
||||
result.m_matched_tokens.emplace_back(result.m_fabricated_tokens.size() - 1, true);
|
||||
}
|
||||
else if(result.m_matched_tokens.empty())
|
||||
{
|
||||
for (auto i = 0u; i < result.m_consumed_token_count; i++)
|
||||
result.m_matched_tokens.emplace_back(tokenOffset + i, false);
|
||||
}
|
||||
|
||||
if (m_capture_id != NO_ID)
|
||||
{
|
||||
for (const auto& match : result.m_matched_tokens)
|
||||
result.m_captures.emplace_back(m_capture_id, match);
|
||||
}
|
||||
|
||||
if (m_no_consume)
|
||||
result.m_consumed_token_count = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
117
src/Parser/Parsing/Matcher/AbstractMatcherFactory.h
Normal file
117
src/Parser/Parsing/Matcher/AbstractMatcherFactory.h
Normal file
@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "AbstractMatcher.h"
|
||||
#include "MatcherAnd.h"
|
||||
#include "MatcherLabel.h"
|
||||
#include "MatcherLoop.h"
|
||||
#include "MatcherOptional.h"
|
||||
#include "MatcherOr.h"
|
||||
#include "Parsing/IParserValue.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class MatcherFactoryWrapper
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
std::unique_ptr<AbstractMatcher<TokenType>> m_matcher;
|
||||
|
||||
public:
|
||||
typedef typename AbstractMatcher<TokenType>::token_list_t token_list_t;
|
||||
|
||||
explicit MatcherFactoryWrapper(std::unique_ptr<AbstractMatcher<TokenType>> matcher)
|
||||
: m_matcher(std::move(matcher))
|
||||
{
|
||||
}
|
||||
|
||||
MatcherFactoryWrapper<TokenType>& Tag(const int tagId)
|
||||
{
|
||||
m_matcher->SetTag(tagId);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MatcherFactoryWrapper<TokenType>& Capture(const int captureId)
|
||||
{
|
||||
m_matcher->SetCapture(captureId);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MatcherFactoryWrapper<TokenType>& NoConsume()
|
||||
{
|
||||
m_matcher->SetConsume(false);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MatcherFactoryWrapper<TokenType>& Transform(std::function<TokenType(token_list_t&)> transform)
|
||||
{
|
||||
m_matcher->SetTransform(std::move(transform));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractMatcher<TokenType>> Build()
|
||||
{
|
||||
return std::move(m_matcher);
|
||||
}
|
||||
|
||||
// ReSharper disable once CppNonExplicitConversionOperator
|
||||
operator std::unique_ptr<AbstractMatcher<TokenType>>()
|
||||
{
|
||||
return Build();
|
||||
}
|
||||
|
||||
// ReSharper disable once CppNonExplicitConversionOperator
|
||||
operator Movable<std::unique_ptr<AbstractMatcher<TokenType>>>()
|
||||
{
|
||||
return Build();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TokenType>
|
||||
class AbstractMatcherFactory
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
const IMatcherForLabelSupplier<TokenType>* m_label_supplier;
|
||||
|
||||
public:
|
||||
typedef typename AbstractMatcher<TokenType>::token_list_t token_list_t;
|
||||
|
||||
explicit AbstractMatcherFactory(const IMatcherForLabelSupplier<TokenType>* labelSupplier)
|
||||
: m_label_supplier(labelSupplier)
|
||||
{
|
||||
}
|
||||
|
||||
_NODISCARD MatcherFactoryWrapper<TokenType> And(std::initializer_list<Movable<std::unique_ptr<AbstractMatcher<TokenType>>>> matchers) const
|
||||
{
|
||||
return MatcherFactoryWrapper<TokenType>(std::make_unique<MatcherAnd<TokenType>>(matchers));
|
||||
}
|
||||
|
||||
_NODISCARD MatcherFactoryWrapper<TokenType> Or(std::initializer_list<Movable<std::unique_ptr<AbstractMatcher<TokenType>>>> matchers) const
|
||||
{
|
||||
return MatcherFactoryWrapper<TokenType>(std::make_unique<MatcherOr<TokenType>>(matchers));
|
||||
}
|
||||
|
||||
_NODISCARD MatcherFactoryWrapper<TokenType> Loop(std::unique_ptr<AbstractMatcher<TokenType>> matcher) const
|
||||
{
|
||||
return MatcherFactoryWrapper<TokenType>(std::make_unique<MatcherLoop<TokenType>>(std::move(matcher)));
|
||||
}
|
||||
|
||||
_NODISCARD MatcherFactoryWrapper<TokenType> OptionalLoop(std::unique_ptr<AbstractMatcher<TokenType>> matcher) const
|
||||
{
|
||||
return MatcherFactoryWrapper<TokenType>(std::make_unique<MatcherOptional<TokenType>>(std::make_unique<MatcherLoop<TokenType>>(std::move(matcher))));
|
||||
}
|
||||
|
||||
_NODISCARD MatcherFactoryWrapper<TokenType> Optional(std::unique_ptr<AbstractMatcher<TokenType>> matcher) const
|
||||
{
|
||||
return MatcherFactoryWrapper<TokenType>(std::make_unique<MatcherOptional<TokenType>>(std::move(matcher)));
|
||||
}
|
||||
|
||||
_NODISCARD MatcherFactoryWrapper<TokenType> Label(const int label) const
|
||||
{
|
||||
return MatcherFactoryWrapper<TokenType>(std::make_unique<MatcherLabel<TokenType>>(m_label_supplier, label));
|
||||
}
|
||||
};
|
40
src/Parser/Parsing/Matcher/MatcherAnd.h
Normal file
40
src/Parser/Parsing/Matcher/MatcherAnd.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
#include "Parsing/IParserValue.h"
|
||||
#include "AbstractMatcher.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class MatcherAnd final : public AbstractMatcher<TokenType>
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
std::vector<std::unique_ptr<AbstractMatcher<TokenType>>> m_matchers;
|
||||
|
||||
protected:
|
||||
MatcherResult<TokenType> CanMatch(ILexer<TokenType>* lexer, const unsigned tokenOffset) override
|
||||
{
|
||||
auto matchResult = MatcherResult<TokenType>::Match(0);
|
||||
|
||||
for (const std::unique_ptr<AbstractMatcher<TokenType>>& matcher : m_matchers)
|
||||
{
|
||||
MatcherResult<TokenType> result = matcher->Match(lexer, tokenOffset + matchResult.m_consumed_token_count);
|
||||
|
||||
if (!result.m_matches)
|
||||
return MatcherResult<TokenType>::NoMatch();
|
||||
|
||||
matchResult.Absorb(std::move(result));
|
||||
}
|
||||
|
||||
return matchResult;
|
||||
}
|
||||
|
||||
public:
|
||||
MatcherAnd(std::initializer_list<Movable<std::unique_ptr<AbstractMatcher<TokenType>>>> matchers)
|
||||
: m_matchers(std::make_move_iterator(matchers.begin()), std::make_move_iterator(matchers.end()))
|
||||
{
|
||||
}
|
||||
};
|
50
src/Parser/Parsing/Matcher/MatcherLabel.h
Normal file
50
src/Parser/Parsing/Matcher/MatcherLabel.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Parsing/IParserValue.h"
|
||||
#include "AbstractMatcher.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class IMatcherForLabelSupplier
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
public:
|
||||
IMatcherForLabelSupplier() = default;
|
||||
virtual ~IMatcherForLabelSupplier() = default;
|
||||
IMatcherForLabelSupplier(const IMatcherForLabelSupplier& other) = default;
|
||||
IMatcherForLabelSupplier(IMatcherForLabelSupplier&& other) noexcept = default;
|
||||
IMatcherForLabelSupplier& operator=(const IMatcherForLabelSupplier& other) = default;
|
||||
IMatcherForLabelSupplier& operator=(IMatcherForLabelSupplier&& other) noexcept = default;
|
||||
|
||||
_NODISCARD virtual AbstractMatcher<TokenType>* GetMatcherForLabel(int label) const = 0;
|
||||
};
|
||||
|
||||
template <typename TokenType>
|
||||
class MatcherLabel final : public AbstractMatcher<TokenType>
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
const IMatcherForLabelSupplier<TokenType>* m_supplier;
|
||||
int m_label;
|
||||
|
||||
protected:
|
||||
MatcherResult<TokenType> CanMatch(ILexer<TokenType>* lexer, unsigned tokenOffset) override
|
||||
{
|
||||
AbstractMatcher<TokenType>* matcher = m_supplier->GetMatcherForLabel(m_label);
|
||||
|
||||
if (matcher)
|
||||
return matcher->Match(lexer, tokenOffset);
|
||||
|
||||
return MatcherResult<TokenType>::NoMatch();
|
||||
}
|
||||
|
||||
public:
|
||||
MatcherLabel(const IMatcherForLabelSupplier<TokenType>* supplier, const int label)
|
||||
: m_supplier(supplier),
|
||||
m_label(label)
|
||||
{
|
||||
}
|
||||
};
|
44
src/Parser/Parsing/Matcher/MatcherLoop.h
Normal file
44
src/Parser/Parsing/Matcher/MatcherLoop.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Parsing/IParserValue.h"
|
||||
#include "AbstractMatcher.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class MatcherLoop final : public AbstractMatcher<TokenType>
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
std::unique_ptr<AbstractMatcher<TokenType>> m_matcher;
|
||||
|
||||
protected:
|
||||
MatcherResult<TokenType> CanMatch(ILexer<TokenType>* lexer, const unsigned tokenOffset) override
|
||||
{
|
||||
auto matchResult = MatcherResult<TokenType>::Match(0);
|
||||
auto loopedAtLeastOnce = false;
|
||||
|
||||
while(true)
|
||||
{
|
||||
auto result = m_matcher->Match(lexer, tokenOffset + matchResult.m_consumed_token_count);
|
||||
|
||||
if(!result.m_matches)
|
||||
{
|
||||
if (loopedAtLeastOnce)
|
||||
return matchResult;
|
||||
|
||||
return MatcherResult<TokenType>::NoMatch();
|
||||
}
|
||||
|
||||
loopedAtLeastOnce = true;
|
||||
matchResult.Absorb(std::move(result));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
explicit MatcherLoop(std::unique_ptr<AbstractMatcher<TokenType>> matcher)
|
||||
: m_matcher(std::move(matcher))
|
||||
{
|
||||
}
|
||||
};
|
32
src/Parser/Parsing/Matcher/MatcherOptional.h
Normal file
32
src/Parser/Parsing/Matcher/MatcherOptional.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "Parsing/IParserValue.h"
|
||||
#include "AbstractMatcher.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class MatcherOptional final : public AbstractMatcher<TokenType>
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
std::unique_ptr<AbstractMatcher<TokenType>> m_matcher;
|
||||
|
||||
protected:
|
||||
MatcherResult<TokenType> CanMatch(ILexer<TokenType>* lexer, unsigned tokenOffset) override
|
||||
{
|
||||
auto result = m_matcher->Match(lexer, tokenOffset);
|
||||
|
||||
if (result.m_matches)
|
||||
return result;
|
||||
|
||||
return MatcherResult<TokenType>::Match(0);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit MatcherOptional(std::unique_ptr<AbstractMatcher<TokenType>> matcher)
|
||||
: m_matcher(std::move(matcher))
|
||||
{
|
||||
}
|
||||
};
|
38
src/Parser/Parsing/Matcher/MatcherOr.h
Normal file
38
src/Parser/Parsing/Matcher/MatcherOr.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
#include "Parsing/IParserValue.h"
|
||||
#include "AbstractMatcher.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class MatcherOr final : public AbstractMatcher<TokenType>
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
std::vector<std::unique_ptr<AbstractMatcher<TokenType>>> m_matchers;
|
||||
|
||||
protected:
|
||||
MatcherResult<TokenType> CanMatch(ILexer<TokenType>* lexer, unsigned tokenOffset) override
|
||||
{
|
||||
for (const std::unique_ptr<AbstractMatcher<TokenType>>& matcher : m_matchers)
|
||||
{
|
||||
MatcherResult<TokenType> result = matcher->Match(lexer, tokenOffset);
|
||||
|
||||
if (!result.m_matches)
|
||||
continue;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return MatcherResult<TokenType>::NoMatch();
|
||||
}
|
||||
|
||||
public:
|
||||
MatcherOr(std::initializer_list<Movable<std::unique_ptr<AbstractMatcher<TokenType>>>> matchers)
|
||||
: m_matchers(std::make_move_iterator(matchers.begin()), std::make_move_iterator(matchers.end()))
|
||||
{
|
||||
}
|
||||
};
|
124
src/Parser/Parsing/Matcher/MatcherResult.h
Normal file
124
src/Parser/Parsing/Matcher/MatcherResult.h
Normal file
@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Parsing/IParserValue.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class MatcherResult
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
public:
|
||||
class TokenIndex
|
||||
{
|
||||
static constexpr unsigned FABRICATED_FLAG_MASK = std::numeric_limits<unsigned>::max() ^ std::numeric_limits<int>::max();
|
||||
static constexpr unsigned TOKEN_INDEX_MASK = ~FABRICATED_FLAG_MASK;
|
||||
|
||||
unsigned m_token_index;
|
||||
|
||||
public:
|
||||
TokenIndex(const unsigned index, const bool isFabricated)
|
||||
{
|
||||
m_token_index = index & TOKEN_INDEX_MASK;
|
||||
if (isFabricated)
|
||||
m_token_index |= FABRICATED_FLAG_MASK;
|
||||
}
|
||||
|
||||
_NODISCARD bool IsFabricated() const
|
||||
{
|
||||
return m_token_index & FABRICATED_FLAG_MASK;
|
||||
}
|
||||
|
||||
_NODISCARD unsigned GetTokenIndex() const
|
||||
{
|
||||
return m_token_index & TOKEN_INDEX_MASK;
|
||||
}
|
||||
};
|
||||
|
||||
class Capture
|
||||
{
|
||||
public:
|
||||
int m_capture_id;
|
||||
TokenIndex m_token_index;
|
||||
|
||||
Capture(const int captureId, const unsigned tokenIndex)
|
||||
: Capture(captureId, tokenIndex, false)
|
||||
{
|
||||
}
|
||||
|
||||
Capture(const int captureId, const unsigned tokenIndex, const bool isFabricated)
|
||||
: m_capture_id(captureId),
|
||||
m_token_index(tokenIndex, isFabricated)
|
||||
{
|
||||
}
|
||||
|
||||
Capture(const int captureId, const TokenIndex index)
|
||||
: m_capture_id(captureId),
|
||||
m_token_index(index)
|
||||
{
|
||||
}
|
||||
|
||||
_NODISCARD int GetCaptureId() const
|
||||
{
|
||||
return m_capture_id;
|
||||
}
|
||||
};
|
||||
|
||||
bool m_matches;
|
||||
unsigned m_consumed_token_count;
|
||||
std::vector<int> m_tags;
|
||||
std::vector<Capture> m_captures;
|
||||
std::vector<TokenIndex> m_matched_tokens;
|
||||
std::vector<TokenType> m_fabricated_tokens;
|
||||
|
||||
private:
|
||||
MatcherResult(const bool matches, const unsigned consumedTokenCount)
|
||||
: m_matches(matches),
|
||||
m_consumed_token_count(consumedTokenCount)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
static MatcherResult Match(unsigned consumedTokenCount)
|
||||
{
|
||||
return MatcherResult(true, consumedTokenCount);
|
||||
}
|
||||
|
||||
static MatcherResult NoMatch()
|
||||
{
|
||||
return MatcherResult(false, 0);
|
||||
}
|
||||
|
||||
void Absorb(MatcherResult<TokenType>&& other)
|
||||
{
|
||||
m_consumed_token_count += other.m_consumed_token_count;
|
||||
|
||||
if (!other.m_tags.empty())
|
||||
std::copy(other.m_tags.begin(), other.m_tags.end(), std::back_inserter(m_tags));
|
||||
|
||||
for (const auto& capture : other.m_captures)
|
||||
{
|
||||
if (capture.m_token_index.IsFabricated())
|
||||
m_captures.emplace_back(capture.GetCaptureId(), TokenIndex(m_fabricated_tokens.size() + capture.m_token_index.GetTokenIndex(), true));
|
||||
else
|
||||
m_captures.emplace_back(capture.GetCaptureId(), capture.m_token_index);
|
||||
}
|
||||
|
||||
for (const auto& token : other.m_matched_tokens)
|
||||
{
|
||||
if (token.IsFabricated())
|
||||
m_matched_tokens.emplace_back(m_fabricated_tokens.size() + token.GetTokenIndex(), true);
|
||||
else
|
||||
m_matched_tokens.emplace_back(token.GetTokenIndex(), false);
|
||||
}
|
||||
|
||||
for (auto& fabricated : other.m_fabricated_tokens)
|
||||
{
|
||||
m_fabricated_tokens.emplace_back(std::move(fabricated));
|
||||
}
|
||||
}
|
||||
};
|
32
src/Parser/Parsing/ParsingException.cpp
Normal file
32
src/Parser/Parsing/ParsingException.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
#include "ParsingException.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
ParsingException::ParsingException(const TokenPos position, std::string message)
|
||||
: m_pos(position),
|
||||
m_message(std::move(message))
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << position.m_filename.get() << " L" << m_pos.m_line << ':' << m_pos.m_column << ' ' << m_message;
|
||||
m_full_message = str.str();
|
||||
}
|
||||
|
||||
TokenPos ParsingException::Position() const
|
||||
{
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
const std::string& ParsingException::Message() const
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
|
||||
std::string ParsingException::FullMessage() const
|
||||
{
|
||||
return m_full_message;
|
||||
}
|
||||
|
||||
char const* ParsingException::what() const noexcept
|
||||
{
|
||||
return m_full_message.c_str();
|
||||
}
|
22
src/Parser/Parsing/ParsingException.h
Normal file
22
src/Parser/Parsing/ParsingException.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
#include "TokenPos.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
|
||||
class ParsingException final : std::exception
|
||||
{
|
||||
TokenPos m_pos;
|
||||
std::string m_message;
|
||||
std::string m_full_message;
|
||||
|
||||
public:
|
||||
ParsingException(TokenPos position, std::string message);
|
||||
|
||||
_NODISCARD TokenPos Position() const;
|
||||
_NODISCARD const std::string& Message() const;
|
||||
_NODISCARD std::string FullMessage() const;
|
||||
_NODISCARD char const* what() const noexcept override;
|
||||
};
|
93
src/Parser/Parsing/Sequence/AbstractSequence.h
Normal file
93
src/Parser/Parsing/Sequence/AbstractSequence.h
Normal file
@ -0,0 +1,93 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <cassert>
|
||||
|
||||
#include "SequenceResult.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Parsing/Matcher/AbstractMatcher.h"
|
||||
#include "Parsing/Matcher/MatcherAnd.h"
|
||||
#include "Parsing/Matcher/MatcherLabel.h"
|
||||
|
||||
template<typename TokenType, typename ParserState>
|
||||
class AbstractSequence : protected IMatcherForLabelSupplier<TokenType>
|
||||
{
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
public:
|
||||
typedef AbstractMatcher<TokenType> matcher_t;
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<matcher_t> m_entry;
|
||||
std::unordered_map<int, std::unique_ptr<matcher_t>> m_matchers;
|
||||
|
||||
protected:
|
||||
static constexpr int ENTRY_LABEL = 0;
|
||||
|
||||
AbstractSequence() = default;
|
||||
|
||||
virtual void ProcessMatch(ParserState* state, SequenceResult<TokenType>& result) const = 0;
|
||||
|
||||
void AddMatchers(std::unique_ptr<matcher_t> matcher)
|
||||
{
|
||||
assert(!m_entry);
|
||||
m_entry = std::move(matcher);
|
||||
}
|
||||
|
||||
void AddMatchers(std::initializer_list<Movable<std::unique_ptr<matcher_t>>> matchers)
|
||||
{
|
||||
assert(!m_entry);
|
||||
m_entry = std::make_unique<MatcherAnd<TokenType>>(matchers);
|
||||
}
|
||||
|
||||
void AddLabeledMatchers(std::unique_ptr<matcher_t> matcher, const int label)
|
||||
{
|
||||
assert(m_matchers.find(label) == m_matchers.end());
|
||||
m_matchers.emplace(label, std::move(matcher));
|
||||
}
|
||||
|
||||
void AddLabeledMatchers(std::initializer_list<Movable<std::unique_ptr<matcher_t>>> matchers, const int label)
|
||||
{
|
||||
assert(m_matchers.find(label) == m_matchers.end());
|
||||
m_matchers.emplace(label, std::make_unique<MatcherAnd<TokenType>>(matchers));
|
||||
}
|
||||
|
||||
public:
|
||||
~AbstractSequence() override = default;
|
||||
AbstractSequence(const AbstractSequence& other) = default;
|
||||
AbstractSequence(AbstractSequence&& other) noexcept = default;
|
||||
AbstractSequence& operator=(const AbstractSequence& other) = default;
|
||||
AbstractSequence& operator=(AbstractSequence&& other) noexcept = default;
|
||||
|
||||
_NODISCARD matcher_t* GetMatcherForLabel(const int label) const override
|
||||
{
|
||||
if (label == 0)
|
||||
return m_entry.get();
|
||||
|
||||
const auto foundEntry = m_matchers.find(label);
|
||||
|
||||
if (foundEntry != m_matchers.end())
|
||||
return foundEntry->second.get();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_NODISCARD bool MatchSequence(ILexer<TokenType>* lexer, ParserState* state, unsigned& consumedTokenCount) const
|
||||
{
|
||||
if (!m_entry)
|
||||
return false;
|
||||
|
||||
auto result = m_entry->Match(lexer, 0);
|
||||
|
||||
if (result.m_matches)
|
||||
{
|
||||
SequenceResult<TokenType> sequenceResult(lexer, result);
|
||||
ProcessMatch(state, sequenceResult);
|
||||
consumedTokenCount = result.m_consumed_token_count;
|
||||
}
|
||||
|
||||
return result.m_matches;
|
||||
}
|
||||
};
|
100
src/Parser/Parsing/Sequence/SequenceResult.h
Normal file
100
src/Parser/Parsing/Sequence/SequenceResult.h
Normal file
@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Parsing/Matcher/AbstractMatcher.h"
|
||||
#include "Parsing/Matcher/MatcherResult.h"
|
||||
#include "Parsing/ParsingException.h"
|
||||
|
||||
template <typename TokenType>
|
||||
class SequenceResult
|
||||
{
|
||||
class Capture
|
||||
{
|
||||
public:
|
||||
unsigned m_offset;
|
||||
std::vector<std::reference_wrapper<const TokenType>> m_tokens;
|
||||
|
||||
Capture()
|
||||
: m_offset(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// TokenType must inherit IParserValue
|
||||
static_assert(std::is_base_of<IParserValue, TokenType>::value);
|
||||
|
||||
std::vector<int> m_tags;
|
||||
std::unordered_map<int, Capture> m_captures;
|
||||
|
||||
unsigned m_tag_offset;
|
||||
|
||||
public:
|
||||
SequenceResult(ILexer<TokenType>* lexer, const MatcherResult<TokenType>& result)
|
||||
: m_tags(result.m_tags),
|
||||
m_tag_offset(0)
|
||||
{
|
||||
for (const typename MatcherResult<TokenType>::Capture& capture : result.m_captures)
|
||||
{
|
||||
if (capture.m_token_index.IsFabricated())
|
||||
m_captures[capture.GetCaptureId()].m_tokens.push_back(result.m_fabricated_tokens[capture.m_token_index.GetTokenIndex()]);
|
||||
else
|
||||
m_captures[capture.GetCaptureId()].m_tokens.push_back(lexer->GetToken(capture.m_token_index.GetTokenIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
_NODISCARD int PeekTag() const
|
||||
{
|
||||
if (m_tag_offset < m_tags.size())
|
||||
return m_tags[m_tag_offset];
|
||||
|
||||
return AbstractMatcher<TokenType>::NO_ID;
|
||||
}
|
||||
|
||||
int PeekAndRemoveIfTag(const int tag)
|
||||
{
|
||||
if (m_tag_offset < m_tags.size())
|
||||
{
|
||||
const auto result = m_tags[m_tag_offset];
|
||||
|
||||
if (result == tag)
|
||||
m_tag_offset++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return AbstractMatcher<TokenType>::NO_ID;
|
||||
}
|
||||
|
||||
int NextTag()
|
||||
{
|
||||
if (m_tag_offset < m_tags.size())
|
||||
return m_tags[m_tag_offset++];
|
||||
|
||||
return AbstractMatcher<TokenType>::NO_ID;
|
||||
}
|
||||
|
||||
_NODISCARD bool HasNextCapture(int captureId)
|
||||
{
|
||||
auto foundEntry = m_captures.find(captureId);
|
||||
|
||||
if (foundEntry == m_captures.end())
|
||||
return false;
|
||||
|
||||
return foundEntry->second.m_offset < foundEntry->second.m_tokens.size();
|
||||
}
|
||||
|
||||
const TokenType& NextCapture(int captureId)
|
||||
{
|
||||
auto foundEntry = m_captures.find(captureId);
|
||||
|
||||
if (foundEntry == m_captures.end())
|
||||
throw ParsingException(TokenPos(), "Tried to access next capture even though no captures exists!");
|
||||
|
||||
if(foundEntry->second.m_offset >= foundEntry->second.m_tokens.size())
|
||||
throw ParsingException(TokenPos(), "Tried to access next capture even though none exists!");
|
||||
|
||||
return foundEntry->second.m_tokens[foundEntry->second.m_offset++];
|
||||
}
|
||||
};
|
17
src/Parser/Parsing/TokenPos.cpp
Normal file
17
src/Parser/Parsing/TokenPos.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "TokenPos.h"
|
||||
|
||||
const std::string TokenPos::EMPTY_FILENAME;
|
||||
|
||||
TokenPos::TokenPos()
|
||||
: m_filename(EMPTY_FILENAME),
|
||||
m_line(1),
|
||||
m_column(1)
|
||||
{
|
||||
}
|
||||
|
||||
TokenPos::TokenPos(const std::string& filename, const int line, const int column)
|
||||
: m_filename(filename),
|
||||
m_line(line),
|
||||
m_column(column)
|
||||
{
|
||||
}
|
17
src/Parser/Parsing/TokenPos.h
Normal file
17
src/Parser/Parsing/TokenPos.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
class TokenPos
|
||||
{
|
||||
static const std::string EMPTY_FILENAME;
|
||||
|
||||
public:
|
||||
std::reference_wrapper<const std::string> m_filename;
|
||||
int m_line;
|
||||
int m_column;
|
||||
|
||||
TokenPos();
|
||||
TokenPos(const std::string& filename, int line, int column);
|
||||
};
|
Reference in New Issue
Block a user