You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
2.7 KiB
145 lines
2.7 KiB
/* |
|
* Copyright (C) 2022 Riyyi |
|
* |
|
* SPDX-License-Identifier: MIT |
|
*/ |
|
|
|
#include <algorithm> // replace |
|
#include <cstddef> // size_t |
|
#include <string> |
|
#include <string_view> |
|
|
|
#include "util/format/builder.h" |
|
#include "util/format/parser.h" |
|
#include "util/meta/assert.h" |
|
|
|
namespace Util::Format { |
|
|
|
Parser::Parser(std::string_view format, size_t parameterCount) |
|
: GenericLexer(format) |
|
, m_parameterCount(parameterCount) |
|
{ |
|
checkFormatParameterConsistency(); |
|
} |
|
|
|
Parser::~Parser() |
|
{ |
|
} |
|
|
|
// ----------------------------------------- |
|
|
|
void Parser::checkFormatParameterConsistency() |
|
{ |
|
size_t length = m_input.length(); |
|
|
|
VERIFY(length >= m_parameterCount * 2, "format string does not reference all parameters"); |
|
|
|
size_t braceOpen = 0; |
|
size_t braceClose = 0; |
|
while (!isEOF()) { |
|
char peek0 = peek(); |
|
char peek1 = peek(1); |
|
|
|
if (peek0 == '{' && peek1 == '{') { |
|
ignore(2); |
|
continue; |
|
} |
|
|
|
if (peek0 == '}' && peek1 == '}') { |
|
ignore(2); |
|
continue; |
|
} |
|
|
|
if (peek0 == '{') { |
|
braceOpen++; |
|
|
|
VERIFY(peek1 == '}' || peek1 == ':', "invalid parameter reference"); |
|
} |
|
if (peek0 == '}') { |
|
braceClose++; |
|
} |
|
|
|
ignore(); |
|
} |
|
m_index = 0; |
|
|
|
VERIFY(!(braceOpen < braceClose), "extra open braces in format string"); |
|
|
|
VERIFY(!(braceOpen > braceClose), "extra closing braces in format string"); |
|
|
|
VERIFY(!(braceOpen < m_parameterCount), "format string does not reference all passed parameters"); |
|
|
|
VERIFY(!(braceOpen > m_parameterCount), "format string references nonexistent parameter"); |
|
} |
|
|
|
std::string_view Parser::consumeLiteral() |
|
{ |
|
const auto begin = tell(); |
|
|
|
while (!isEOF()) { |
|
char peek0 = peek(); |
|
char peek1 = peek(1); |
|
|
|
if (peek0 == '{' && peek1 == '{') { |
|
ignore(2); |
|
continue; |
|
} |
|
|
|
if (peek0 == '}' && peek1 == '}') { |
|
ignore(2); |
|
continue; |
|
} |
|
|
|
// Get literal before the specifier {} |
|
if (peek0 == '{' || peek0 == '}') { |
|
return m_input.substr(begin, tell() - begin); |
|
} |
|
|
|
ignore(); |
|
} |
|
|
|
return m_input.substr(begin); |
|
} |
|
|
|
bool Parser::consumeSpecifier(std::string_view& specifier) |
|
{ |
|
if (!consumeSpecific('{')) { |
|
return false; |
|
} |
|
|
|
if (!consumeSpecific(':')) { |
|
VERIFY(consumeSpecific('}'), "invalid parameter reference"); |
|
specifier = ""; |
|
} |
|
else { |
|
const auto begin = tell(); |
|
|
|
// Go until AFTER the closing brace |
|
while (peek(-1) != '}') { |
|
consume(); |
|
} |
|
|
|
specifier = m_input.substr(begin, tell() - begin - 1); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void Parser::applySpecifier(Builder& builder, std::string_view specifier) |
|
{ |
|
if (specifier[0] == '.') { |
|
size_t value = 0; |
|
|
|
for (size_t i = 1; i < specifier.length(); ++i) { |
|
if (specifier[i] < '0' || specifier[i] > '9') { |
|
return; |
|
} |
|
value *= 10; |
|
value += specifier[i] - '0'; // Subtract ASCII 48 to get the number |
|
} |
|
|
|
builder.setPrecision(value); |
|
} |
|
} |
|
|
|
} // namespace Util::Format
|
|
|