Core: Rework ParameterFile

Improve parsing and usage
This commit is contained in:
SirLynix
2023-11-17 11:57:05 +01:00
parent 592845e353
commit ddc8cc6797
4 changed files with 409 additions and 238 deletions

View File

@@ -9,106 +9,196 @@
namespace Nz
{
bool ParameterFile::EnsureLine(bool peek)
auto ParameterFile::Advance() -> Token
{
while (m_currentLine.find_first_not_of(" \r\t\n") == m_currentLine.npos)
auto IsAlphaNum = [&](char c)
{
m_currentLine.clear();
m_stream.ReadLine(m_currentLine);
if (m_currentLine.empty())
{
if (!peek)
throw std::runtime_error("unexpected end of file");
return std::isalnum(c) || c == '_';
};
return false;
// Remove processed tokens from buffer
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + m_bufferOffset);
m_bufferOffset = 0;
Token currentToken = std::move(m_nextToken);
for (;;)
{
char c = PeekCharacter(0);
if (c == '\0')
{
m_nextToken = EndOfStream{};
return currentToken;
}
}
return true;
}
std::string ParameterFile::ReadKeyword(bool peek)
{
std::size_t beginOffset;
do
{
if (!EnsureLine(peek))
return {};
beginOffset = m_currentLine.find_first_not_of(" \r\t\n");
} while (beginOffset == m_currentLine.npos);
if (m_currentLine[beginOffset] == '"')
throw std::runtime_error("expected a keyword, got a string");
std::size_t endOffset = m_currentLine.find_first_of(" \r\t\n", beginOffset + 1);
if (endOffset == m_currentLine.npos)
endOffset = m_currentLine.size();
std::string currentToken = std::string(m_currentLine.substr(beginOffset, endOffset - beginOffset));
if (!peek)
m_currentLine.erase(m_currentLine.begin(), m_currentLine.begin() + endOffset);
return currentToken;
}
std::string ParameterFile::ReadString()
{
std::size_t beginOffset;
do
{
EnsureLine();
beginOffset = m_currentLine.find_first_not_of(" \r\t\n");
} while (beginOffset == m_currentLine.npos);
if (m_currentLine[beginOffset] != '"')
throw std::runtime_error("expected a string, got a keyword");
std::string str;
for (std::size_t i = beginOffset + 1; i < m_currentLine.size(); ++i)
{
switch (m_currentLine[i])
switch (c)
{
case '\0':
case '\n':
case ' ':
case '\t':
case '\r':
throw std::runtime_error("expected a string, got a keyword");
break; //< Ignore blank spaces
case '"':
case '\n':
m_currentLine++;
break;
case '{':
ConsumeChar();
m_nextToken = OpenCurlyBracket{};
return currentToken;
case '}':
ConsumeChar();
m_nextToken = ClosingCurlyBracket{};
return currentToken;
case '/':
{
m_currentLine.erase(m_currentLine.begin(), m_currentLine.begin() + beginOffset + i);
return str;
}
case '\\':
{
i++;
char character;
switch (m_currentLine[i])
char next = PeekCharacter();
if (next == '/')
{
case 'n': character = '\n'; break;
case 'r': character = '\r'; break;
case 't': character = '\t'; break;
case '"': character = '"'; break;
case '\\': character = '\\'; break;
default:
throw std::runtime_error(Format("unrecognized character {}", character));
// Line comment
do
{
ConsumeChar();
next = PeekCharacter();
}
while (next != '\0' && next != '\n');
}
else if (next == '*')
{
// Block comment
for (;;)
{
ConsumeChar();
next = PeekCharacter();
if (next == '*')
{
if (PeekCharacter(2) == '/')
{
ConsumeChar(2);
break;
}
}
else if (next == '\n')
m_currentLine++;
else if (next == '\0')
throw std::runtime_error(Format("unfinished block comment on line {}", m_currentLine));
}
}
str.push_back(character);
break;
}
case '"':
{
// string literal
ConsumeChar();
std::string literal;
char cur;
while ((cur = PeekCharacter(0)) != '"')
{
char character;
switch (cur)
{
case '\0':
case '\n':
case '\r':
throw std::runtime_error(Format("unfinished string on line {}", m_currentLine));
case '\\':
{
ConsumeChar();
char next = PeekCharacter(0);
switch (next)
{
case 'n': character = '\n'; break;
case 'r': character = '\r'; break;
case 't': character = '\t'; break;
case '"': character = '"'; break;
case '\\': character = '\\'; break;
default:
throw std::runtime_error(Format("unrecognized character on line {}", m_currentLine));
}
break;
}
default:
character = cur;
break;
}
literal.push_back(character);
ConsumeChar();
}
ConsumeChar();
m_nextToken = String{ std::move(literal) };
return currentToken;
}
default:
str.push_back(m_currentLine[i]);
{
if (IsAlphaNum(c))
{
// Identifier or keyword
std::size_t start = m_bufferOffset;
while (IsAlphaNum(PeekCharacter()))
ConsumeChar();
m_nextToken = Identifier{ m_buffer.substr(start, m_bufferOffset - start) };
return currentToken;
}
else
throw std::runtime_error(Format("unrecognized token on line {}", m_currentLine));
}
}
ConsumeChar();
}
}
void ParameterFile::ConsumeChar(std::size_t count)
{
assert(m_bufferOffset < m_buffer.size());
assert(m_bufferOffset + count <= m_buffer.size());
m_bufferOffset += count;
}
auto ParameterFile::PeekToken() -> Token&
{
if (std::holds_alternative<std::monostate>(m_nextToken))
Advance();
return m_nextToken;
}
char ParameterFile::PeekCharacter(std::size_t advance)
{
constexpr std::size_t Capacity = 512;
if NAZARA_UNLIKELY(m_bufferOffset + advance >= m_buffer.size())
{
if NAZARA_UNLIKELY(m_stream.EndOfStream())
return '\0';
std::size_t prevSize = m_buffer.size();
m_buffer.resize(prevSize + Capacity);
std::size_t readSize = m_stream.Read(&m_buffer[prevSize], Capacity);
m_buffer.resize(prevSize + readSize);
if (m_bufferOffset + advance >= m_buffer.size())
return '\0';
}
throw std::runtime_error("unfinished string");
return m_buffer[m_bufferOffset];
}
// Required for MinGW (it tris to import those symbols, probably a bug)
constexpr ParameterFile::Array_t ParameterFile::Array;
constexpr ParameterFile::List_t ParameterFile::List;
constexpr ParameterFile::OptionalBlock_t ParameterFile::OptionalBlock;
}

View File

@@ -26,24 +26,15 @@ namespace Nz::Loaders
{
std::string finalOutputAttachment;
m_paramFile.Handle(
"passlist", [&](std::string passListName)
m_paramFile.Parse(
"passlist", [&](ParameterFileSection section, std::string passListName)
{
m_current.emplace();
m_current->passList = std::make_shared<PipelinePassList>();
m_paramFile.Block(
"attachment", [this](std::string attachmentName)
{
HandleAttachment(std::move(attachmentName));
},
"attachmentproxy", [this](std::string proxyName, std::string targetName)
{
HandleAttachmentProxy(std::move(proxyName), std::move(targetName));
},
"pass", [this](std::string passName)
{
HandlePass(std::move(passName));
},
section.Block(
"attachment", &PassListLoader::HandleAttachment, this,
"attachmentproxy", &PassListLoader::HandleAttachmentProxy, this,
"pass", &PassListLoader::HandlePass, this,
"output", &finalOutputAttachment
);
}
@@ -73,11 +64,11 @@ namespace Nz::Loaders
}
private:
void HandleAttachment(std::string attachmentName)
void HandleAttachment(ParameterFileSection section, std::string attachmentName)
{
std::string format;
m_paramFile.Block(
section.Block(
"format", &format
);
@@ -134,7 +125,7 @@ namespace Nz::Loaders
m_current->attachmentsByName.emplace(std::move(proxyName), proxyId);
}
void HandlePass(std::string passName)
void HandlePass(ParameterFileSection section, std::string passName)
{
struct InputOutput
{
@@ -150,17 +141,18 @@ namespace Nz::Loaders
std::vector<InputOutput> outputs;
std::vector<std::string> flags;
m_paramFile.Block(
section.Block(
"depthstencilinput", &depthstencilInput,
"depthstenciloutput", &depthstencilOutput,
"impl", [&](std::string passImpl)
"impl", [&](ParameterFileSection implSection, std::string passImpl)
{
impl = std::move(passImpl);
m_paramFile.Block(ParameterFile::OptionalBlock,
ParameterFile::Array, [&](ParameterFile::Keyword key, std::string value)
{
implConfig.SetParameter(std::move(key.str), std::move(value));
});
implSection.Block(ParameterFile::OptionalBlock,
ParameterFile::List, [&](ParameterFile::Identifier key, std::string value)
{
implConfig.SetParameter(std::move(key.value), std::move(value));
}
);
},
"input", [&](std::string name, std::string attachment)
{