Compare commits

...
11 Commits
Author SHA1 Message Date
Riyyi 75034a2c5a Util: Implement Json::Value type customization for print formatting 2022-07-22 02:13:12 +02:00
Riyyi 835481f4a5 Util: Add formatting and type printing capability
Extending the type compatibility is done with an easy to use API.
2022-07-22 01:59:19 +02:00
Riyyi 9b3489676e Util: Add two functions to GenericLexer
- tellRemaining
- consumeSpecific
2022-07-22 01:48:14 +02:00
Riyyi dd209a2609 Util: Move json/conversion.h -> meta/odr.h 2022-07-21 01:50:15 +02:00
Riyyi 2408be335d Util: Rename two functions to be more in line with GenericLexer 2022-07-20 23:34:31 +02:00
Riyyi 42143fed71 Util: Rename Lexer/Parser get functions to consume 2022-07-20 22:57:35 +02:00
Riyyi 8dd3f11d7b Util: Run clang-format on Json::Lexer 2022-07-20 22:49:41 +02:00
Riyyi e3041bfdc7 Util: Implement GenericLexer in Json::Lexer 2022-07-20 22:49:41 +02:00
Riyyi 4ce026ec78 Util: Add GenericLexer class 2022-07-20 22:49:41 +02:00
Riyyi 4e6c5ca341 Util: Change Job m_input string -> string_view 2022-07-20 21:32:17 +02:00
Riyyi b4100113fe Util: Add missing include 2022-07-20 21:30:15 +02:00
22 changed files with 1136 additions and 158 deletions
+6
View File
@@ -0,0 +1,6 @@
# -*- yaml -*-
---
# FIXME: Figure out why NOLINTBEGIN/NOLINTEND doesnt work
Checks: -misc-unused-using-decls
...
+62
View File
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <cstddef> // size_t
#include <iomanip> // setprecision
#include <ios> // fixed
#include <limits> // numeric_limits
#include <sstream> // stringstream
#include <string>
#include <string_view>
#include "util/format/builder.h"
namespace Util::Format {
void Builder::putLiteral(std::string_view literal)
{
for (size_t i = 0; i < literal.length(); ++i) {
putCharacter(literal[i]);
if (literal[i] == '{' || literal[i] == '}') {
++i;
}
}
}
void Builder::putF32(float number, size_t precision) const
{
precision = (precision > std::numeric_limits<float>::digits10) ? m_precision : precision;
std::stringstream stream;
stream
<< std::fixed
<< std::setprecision(precision)
<< number;
std::string string = stream.str();
string = string.substr(0, string.find_first_of('0', string.find('.')));
m_builder << string;
}
void Builder::putF64(double number, size_t precision) const
{
precision = (precision > std::numeric_limits<double>::digits10) ? m_precision : precision;
std::stringstream stream;
stream
<< std::fixed
<< std::setprecision(precision)
<< number;
std::string string = stream.str();
string = string.substr(0, string.find_first_of('0', string.find('.')));
m_builder << string;
}
void Builder::resetSpecifiers()
{
setPrecision();
}
} // namespace Util::Format
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#ifndef UTIL_FORMAT_BUILDER_H
#define UTIL_FORMAT_BUILDER_H
#include <cstddef> // size_t
#include <cstdint> // int32_t, uint32_t, int64_t
#include <sstream> // stringstream
#include <string_view>
namespace Util::Format {
class Builder {
public:
explicit Builder(std::stringstream& builder)
: m_precision(6)
, m_builder(builder)
{
}
void putLiteral(std::string_view literal);
void putI32(int32_t number) const { m_builder << number; } // int
void putU32(uint32_t number) const { m_builder << number; } // unsigned int
void putI64(int64_t number) const { m_builder << number; } // long int
void putU64(size_t number) const { m_builder << number; } // long unsigned int
void putF32(float number, size_t precision = -1) const;
void putF64(double number, size_t precision = -1) const;
void putCharacter(char character) const { m_builder.write(&character, 1); }
void putString(const std::string_view string) const { m_builder.write(string.data(), string.length()); }
void putPointer(const void* pointer) const { m_builder << pointer; }
void resetSpecifiers();
void setPrecision(size_t precision = 6) { m_precision = precision; };
const std::stringstream& builder() const { return m_builder; }
std::stringstream& builder() { return m_builder; }
private:
size_t m_precision { 6 };
std::stringstream& m_builder;
};
} // namespace Util::Format
#endif // UTIL_FORMAT_BUILDER_H
+216
View File
@@ -0,0 +1,216 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <sstream> // stringstream
#include <string>
#include <string_view>
#include "util/format/builder.h"
#include "util/format/format.h"
#include "util/format/parser.h"
namespace Util::Format {
void variadicFormatImpl(Builder& builder, Parser& parser, TypeErasedParameters& parameters)
{
const auto literal = parser.consumeLiteral();
builder.putLiteral(literal);
if (!parameters.isEOF()) {
std::string_view specifier;
if (parser.consumeSpecifier(specifier)) {
parser.applySpecifier(builder, specifier);
}
auto& parameter = parameters.parameter(parameters.tell());
parameter.format(builder, parameter.value);
parameters.ignore();
builder.resetSpecifiers();
}
if (!parser.isEOF()) {
variadicFormatImpl(builder, parser, parameters);
}
}
void variadicFormat(std::stringstream& stream, std::string_view format, TypeErasedParameters& parameters)
{
Builder builder { stream };
Parser parser { format, parameters.size() };
variadicFormatImpl(builder, parser, parameters);
}
// -----------------------------------------
void prettyVariadicFormat(Type type, bool bold, std::string_view format, TypeErasedParameters& parameters)
{
std::stringstream stream;
if (type != Type::None || bold) {
stream << "\033[";
switch (type) {
case Type::Info:
stream << "34";
break;
case Type::Warn:
stream << "33";
break;
case Type::Danger:
stream << "31";
break;
case Type::Success:
stream << "32";
break;
case Type::Comment:
stream << "37";
break;
default:
break;
};
if (bold) {
stream << ";1";
}
stream << "m";
}
variadicFormat(stream, format, parameters);
if (type != Type::None || bold) {
stream << "\033[0m";
}
printf("%s\n", stream.str().c_str());
}
// -----------------------------------------
Dbg::Dbg(Type type, bool bold)
: m_type(type)
, m_bold(bold)
, m_stream()
, m_builder(m_stream)
{
if (type != Type::None || m_bold) {
m_stream << "\033[";
switch (type) {
case Type::Info:
m_stream << "34";
break;
case Type::Warn:
m_stream << "33";
break;
case Type::Danger:
m_stream << "31";
break;
case Type::Success:
m_stream << "32";
break;
case Type::Comment:
m_stream << "37";
break;
default:
break;
};
if (m_bold) {
m_stream << ";1";
}
m_stream << "m";
}
}
Dbg::~Dbg()
{
if (m_type != Type::None || m_bold) {
m_stream << "\033[0m";
}
printf("%s", m_stream.str().c_str());
}
Dbg dbg()
{
return Dbg(Type::None, false);
}
Dbg dbgb()
{
return Dbg(Type::None, true);
}
Dbg info()
{
return Dbg(Type::Info, false);
}
Dbg infob()
{
return Dbg(Type::Info, true);
}
Dbg warn()
{
return Dbg(Type::Warn, false);
}
Dbg warnb()
{
return Dbg(Type::Warn, true);
}
Dbg danger()
{
return Dbg(Type::Danger, false);
}
Dbg dangerb()
{
return Dbg(Type::Danger, true);
}
Dbg success()
{
return Dbg(Type::Success, false);
}
Dbg successb()
{
return Dbg(Type::Success, true);
}
Dbg comment()
{
return Dbg(Type::Comment, false);
}
Dbg commentb()
{
return Dbg(Type::Comment, true);
}
// -----------------------------------------
Str::Str(std::string& fill)
: m_fill(fill)
, m_stream()
, m_builder(m_stream)
{
}
Str::~Str()
{
m_fill = m_stream.str();
}
Str str(std::string& fill)
{
return Str(fill);
}
} // namespace Util::Format
+198
View File
@@ -0,0 +1,198 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#ifndef UTIL_FORMAT_FORMAT_H
#define UTIL_FORMAT_FORMAT_H
#include <cstddef> // size_t
#include <sstream> // stringstream
#include <string>
#include <string_view>
#include <vector>
#include "util/format/builder.h"
#include "util/format/parser.h"
#include "util/format/toformat.h"
namespace Util::Format {
struct Parameter {
const void* value;
void (*format)(Builder& builder, const void* value);
};
template<typename T>
void formatParameterValue(Builder& builder, const void* value)
{
format(builder, *static_cast<const T*>(value));
}
class TypeErasedParameters {
public:
const Parameter parameter(size_t index) { return m_parameters[index]; }
size_t tell() const { return m_index; }
size_t size() const { return m_parameters.size(); }
bool isEOF() const { return m_index >= m_parameters.size(); }
void ignore() { m_index++; }
protected:
size_t m_index { 0 };
std::vector<Parameter> m_parameters;
};
template<typename... Parameters>
class VariadicParameters final : public TypeErasedParameters {
public:
VariadicParameters(const Parameters&... parameters)
{
m_parameters = { { &parameters, formatParameterValue<Parameters> }... };
}
};
// -----------------------------------------
void variadicFormatImpl(Builder& builder, Parser& parser, TypeErasedParameters& parameters);
void variadicFormat(std::stringstream& stream, std::string_view format, TypeErasedParameters& parameters);
// -----------------------------------------
enum class Type {
None, // Foreground
Info, // Blue
Warn, // Yellow
Danger, // Red
Success, // Green
Comment, // White
};
void prettyVariadicFormat(Type type, bool bold, std::string_view format, TypeErasedParameters& parameters);
#define FORMAT_FUNCTION(name, type, bold) \
template<size_t N, typename... Parameters> \
void name(const char(&format)[N] = "", const Parameters&... parameters) \
{ \
VariadicParameters variadicParameters { parameters... }; \
prettyVariadicFormat(Type::type, bold, { format, N - 1 }, variadicParameters); \
}
FORMAT_FUNCTION(dbgln, None, false);
FORMAT_FUNCTION(dbgbln, None, true);
FORMAT_FUNCTION(infoln, Info, false);
FORMAT_FUNCTION(infobln, Info, true);
FORMAT_FUNCTION(warnln, Warn, false);
FORMAT_FUNCTION(warnbln, Warn, true);
FORMAT_FUNCTION(dangerln, Danger, false);
FORMAT_FUNCTION(dangerbln, Danger, true);
FORMAT_FUNCTION(successln, Success, false);
FORMAT_FUNCTION(successbln, Success, true);
FORMAT_FUNCTION(commentln, Comment, false);
FORMAT_FUNCTION(commentbln, Comment, true);
// -----------------------------------------
class Dbg {
public:
Dbg(Type type, bool bold);
virtual ~Dbg();
Builder& builder() { return m_builder; }
private:
Type m_type;
bool m_bold;
std::stringstream m_stream;
Builder m_builder;
};
template<typename T>
const Dbg& operator<<(const Dbg& debug, const T& value)
{
format(const_cast<Dbg&>(debug).builder(), value);
return debug;
}
Dbg dbg();
Dbg dbgb();
Dbg info();
Dbg infob();
Dbg warn();
Dbg warnb();
Dbg danger();
Dbg dangerb();
Dbg success();
Dbg successb();
Dbg comment();
Dbg commentb();
// -----------------------------------------
template<typename... Parameters>
void strln(std::string& fill, std::string_view format, const Parameters&... parameters)
{
std::stringstream stream;
VariadicParameters variadicParameters { parameters... };
variadicFormat(stream, format, variadicParameters);
fill = stream.str();
}
class Str {
public:
Str(std::string& fill);
virtual ~Str();
Builder& builder() { return m_builder; }
private:
std::string& m_fill;
std::stringstream m_stream;
Builder m_builder;
};
template<typename T>
const Str& operator<<(const Str& string, const T& value)
{
format(const_cast<Str&>(string).builder(), value);
return string;
}
Str str(std::string& fill);
} // namespace Util::Format
using Util::Format::commentbln;
using Util::Format::commentln;
using Util::Format::dangerbln;
using Util::Format::dangerln;
using Util::Format::dbgbln;
using Util::Format::dbgln;
using Util::Format::infobln;
using Util::Format::infoln;
using Util::Format::successbln;
using Util::Format::successln;
using Util::Format::warnbln;
using Util::Format::warnln;
using Util::Format::comment;
using Util::Format::commentb;
using Util::Format::danger;
using Util::Format::dangerb;
using Util::Format::dbg;
using Util::Format::dbgb;
using Util::Format::info;
using Util::Format::infob;
using Util::Format::success;
using Util::Format::successb;
using Util::Format::warn;
using Util::Format::warnb;
using Util::Format::str;
using Util::Format::strln;
using FormatBuilder = Util::Format::Builder;
#endif // UTIL_FORMAT_FORMAT_H
+151
View File
@@ -0,0 +1,151 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <algorithm> // replace
#include <cassert> // assert
#include <cstddef> // size_t
#include <string>
#include <string_view>
#include "util/format/builder.h"
#include "util/format/parser.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();
// Format string does not reference all passed parameters
assert(length >= m_parameterCount * 2);
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++;
// Only empty references {} or specified references {:} are valid
assert(peek1 == '}' || peek1 == ':');
}
if (peek0 == '}') {
braceClose++;
}
ignore();
}
m_index = 0;
// Extra open braces in format string
assert(!(braceOpen < braceClose));
// Extra closing braces in format string
assert(!(braceOpen > braceClose));
// Format string does not reference all passed parameters
assert(!(braceOpen < m_parameterCount));
// Format string references nonexistent parameter
assert(!(braceOpen > m_parameterCount));
}
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(':')) {
assert(consumeSpecific('}'));
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
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#ifndef UTIL_FORMAT_PARSER_H
#define UTIL_FORMAT_PARSER_H
#include <cstddef> // size_t
#include <string_view>
#include "util/genericlexer.h"
namespace Util::Format {
class Builder;
class Parser final : public GenericLexer {
public:
Parser(std::string_view format, size_t parameterCount);
virtual ~Parser();
void checkFormatParameterConsistency();
std::string_view consumeLiteral();
bool consumeSpecifier(std::string_view& specifier);
void applySpecifier(Builder& builder, std::string_view specifier);
private:
size_t m_parameterCount { 0 };
};
} // namespace Util::Format
#endif // UTIL_FORMAT_PARSER_H
+168
View File
@@ -0,0 +1,168 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#ifndef UTIL_TO_FORMAT_H
#define UTIL_TO_FORMAT_H
#include <cstddef> // nullptr_t
#include <cstdint> // int64_t
#include <cstring> // strlen
#include <iterator> // next
#include <map>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility> // forward
#include <vector>
#include "util/meta/odr.h"
namespace Util::Format {
namespace Detail {
template<typename FormatBuilder>
void format(FormatBuilder& builder, std::nullptr_t)
{
builder.putString("(nil)");
}
template<typename FormatBuilder, typename T>
void format(FormatBuilder& builder, T* pointer)
{
builder.putPointer(static_cast<const void*>(pointer));
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, bool boolean)
{
builder.putString(boolean ? "true" : "false");
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, int32_t number) // int
{
builder.putI32(number);
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, uint32_t number) // unsigned int
{
builder.putU32(number);
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, int64_t number) // long int
{
builder.putI64(number);
}
template<typename FormatBuilder> // uint64_t
void format(FormatBuilder& builder, size_t number) // long unsigned int
{
builder.putU64(number);
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, float number)
{
builder.putF32(number);
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, double number)
{
builder.putF64(number);
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, char character)
{
builder.putCharacter(character);
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, const char* string)
{
builder.putString({ string, strlen(string) });
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, const std::string& string)
{
builder.putString(string);
}
template<typename FormatBuilder>
void format(FormatBuilder& builder, std::string_view string)
{
builder.putString(string);
}
template<typename FormatBuilder, typename T>
void format(FormatBuilder& builder, const std::vector<T>& array)
{
builder.putString("{\n");
for (auto it = array.cbegin(); it != array.cend(); ++it) {
builder.putString(" ");
format(builder, *it);
// Add comma, except after the last element
if (it != std::prev(array.end(), 1)) {
builder.putCharacter(',');
}
builder.putCharacter('\n');
}
builder.putCharacter('}');
}
#define FORMAT_MAP \
builder.putString("{\n"); \
auto last = map.end(); \
for (auto it = map.begin(); it != last; ++it) { \
builder.putString(R"( ")"); \
format(builder, it->first); \
builder.putString(R"(": )"); \
format(builder, it->second); \
\
/* Add comma, except after the last element */ \
if (std::next(it) != last) { \
builder.putCharacter(','); \
} \
\
builder.putCharacter('\n'); \
} \
builder.putCharacter('}');
template<typename FormatBuilder, typename K, typename V>
void format(FormatBuilder& builder, const std::map<K, V>& map)
{
FORMAT_MAP;
}
template<typename FormatBuilder, typename K, typename V>
void format(FormatBuilder& builder, const std::unordered_map<K, V>& map)
{
FORMAT_MAP;
}
struct formatFunction {
template<typename FormatBuilder, typename T>
auto operator()(FormatBuilder& builder, T&& value) const
{
return format(builder, std::forward<T>(value));
}
};
} // namespace Detail
namespace {
constexpr const auto& format = Util::Detail::staticConst<Detail::formatFunction>; // NOLINT(misc-definitions-in-headers,clang-diagnostic-unused-variable)
} // namespace
} // namespace Util::Format
#endif // UTIL_TO_FORMAT_H
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#include <algorithm> // max, min
#include "util/genericlexer.h"
namespace Util {
GenericLexer::GenericLexer(std::string_view input)
: m_input(input)
{
}
GenericLexer::~GenericLexer()
{
}
// -----------------------------------------
size_t GenericLexer::tell() const
{
return m_index;
}
size_t GenericLexer::tellRemaining() const
{
return m_input.length() - m_index;
}
bool GenericLexer::isEOF() const
{
return m_index >= m_input.length();
}
char GenericLexer::peek(size_t offset) const
{
return (m_index + offset >= 0 && m_index + offset < m_input.length())
? m_input[m_index + offset]
: '\0';
}
void GenericLexer::ignore(size_t count)
{
m_index += std::min(count, m_input.length() - m_index);
}
void GenericLexer::retreat(size_t count)
{
m_index -= std::min(count, m_index);
}
char GenericLexer::consume()
{
assert(!isEOF());
return m_input[m_index++];
}
bool GenericLexer::consumeSpecific(const char& character)
{
if (peek() != character) {
return false;
}
ignore();
return true;
}
} // namespace Util
+45
View File
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#ifndef UTIL_GENERIC_LEXER_H
#define UTIL_GENERIC_LEXER_H
#include <cassert> // assert
#include <cstddef> // size_t
#include <string_view>
namespace Util {
class GenericLexer {
public:
GenericLexer(std::string_view input);
virtual ~GenericLexer();
// Position
size_t tell() const;
size_t tellRemaining() const;
bool isEOF() const;
// Access
char peek(size_t offset = 0) const;
// Modifiers
void ignore(size_t count = 1);
void retreat(size_t count = 1);
char consume();
bool consumeSpecific(const char& character);
protected:
size_t m_index { 0 };
std::string_view m_input;
};
} // namespace Util
#endif // UTIL_GENERIC_LEXER_H
-22
View File
@@ -1,22 +0,0 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#ifndef JSON_CONVERSION_H
#define JSON_CONVERSION_H
namespace Json {
namespace Detail {
// Avoid ODR (One Definition Rule) violations
template<typename T>
constexpr T staticConst {};
} // namespace Detail
} // namespace Json
#endif // JSON_CONVERSION_H
+2 -2
View File
@@ -17,8 +17,8 @@
#include <vector>
#include "util/json/array.h"
#include "util/json/conversion.h"
#include "util/json/object.h"
#include "util/meta/odr.h"
namespace Json {
@@ -113,7 +113,7 @@ struct fromJsonFunction {
// Anonymous namespace prevents multiple definition of the reference
namespace {
// Function object
constexpr const auto& fromJson = Detail::staticConst<Detail::fromJsonFunction>; // NOLINT (misc-definitions-in-headers)
constexpr const auto& fromJson = Util::Detail::staticConst<Detail::fromJsonFunction>; // NOLINT(misc-definitions-in-headers,clang-diagnostic-unused-variable)
} // namespace
} // namespace Json
+2 -2
View File
@@ -15,7 +15,7 @@
namespace Json {
Job::Job(const std::string& input)
Job::Job(std::string_view input)
: m_input(input)
{
// FIXME: Make this work for all newline types: \n, \r, \r\n
@@ -68,7 +68,7 @@ void Job::printErrorLine(Token token, const char* message)
message);
// Get the JSON line that caused the error
std::istringstream input(m_input);
std::istringstream input(m_input.data());
std::string line;
for (size_t i = 0; std::getline(input, line); ++i) {
if (i == token.line) {
+4 -4
View File
@@ -8,7 +8,7 @@
#define JSON_JOB_H
#include <cstddef> // size_t
#include <string>
#include <string_view>
#include <vector>
#include "util/json/lexer.h"
@@ -19,7 +19,7 @@ class Value;
class Job {
public:
Job(const std::string& input);
Job(std::string_view input);
virtual ~Job();
enum class Color {
@@ -36,13 +36,13 @@ public:
void printErrorLine(Token token, const char* message);
bool success() const { return m_success; }
const std::string& input() const { return m_input; }
std::string_view input() const { return m_input; }
std::vector<Token>* tokens() { return &m_tokens; }
private:
bool m_success { true };
std::string m_input;
std::string_view m_input;
size_t m_lineNumbersWidth { 0 };
std::vector<Token> m_tokens;
+26 -54
View File
@@ -13,7 +13,8 @@
namespace Json {
Lexer::Lexer(Job* job)
: m_job(job)
: GenericLexer(job->input())
, m_job(job)
, m_tokens(job->tokens())
{
}
@@ -26,7 +27,7 @@ Lexer::~Lexer()
void Lexer::analyze()
{
while (m_index < m_job->input().length()) {
while (m_index < m_input.length()) {
switch (peek()) {
case '{':
m_tokens->push_back({ Token::Type::BraceOpen, m_line, m_column, "{" });
@@ -47,7 +48,7 @@ void Lexer::analyze()
m_tokens->push_back({ Token::Type::Comma, m_line, m_column, "," });
break;
case '"':
if (!getString()) {
if (!consumeString()) {
return;
}
break;
@@ -62,7 +63,7 @@ void Lexer::analyze()
case '7':
case '8':
case '9':
if (!getNumber()) {
if (!consumeNumber()) {
return;
}
break;
@@ -92,7 +93,7 @@ void Lexer::analyze()
case 'x':
case 'y':
case 'z':
if (!getLiteral()) {
if (!consumeLiteral()) {
return;
}
break;
@@ -100,7 +101,7 @@ void Lexer::analyze()
case '\t':
break;
case '\r':
if (peekNext() == '\n') { // CRLF \r\n
if (peek(1) == '\n') { // CRLF \r\n
break;
}
m_column = -1;
@@ -119,42 +120,14 @@ void Lexer::analyze()
break;
}
increment();
ignore();
m_column++;
}
}
// -----------------------------------------
char Lexer::peek()
{
return m_job->input()[m_index];
}
char Lexer::peekNext()
{
return m_job->input()[m_index + 1];
}
void Lexer::increment()
{
m_index++;
m_column++;
}
void Lexer::decrement()
{
m_index--;
m_column--;
}
char Lexer::consume()
{
char character = peek();
increment();
return character;
}
bool Lexer::getString()
bool Lexer::consumeString()
{
size_t column = m_column;
std::string symbol = "";
@@ -166,21 +139,21 @@ bool Lexer::getString()
if (!escape && character == '\\') {
symbol += '\\';
increment();
ignore();
escape = true;
continue;
}
if (!escape &&
(character == '"'
|| character == '\r'
|| character == '\n'
|| character == '\0')) {
if (!escape
&& (character == '"'
|| character == '\r'
|| character == '\n'
|| character == '\0')) {
break;
}
symbol += character;
increment();
ignore();
if (escape) {
escape = false;
@@ -197,7 +170,7 @@ bool Lexer::getString()
return true;
}
bool Lexer::getNumberOrLiteral(Token::Type type)
bool Lexer::consumeNumberOrLiteral(Token::Type type)
{
size_t index = m_index;
size_t column = m_column;
@@ -216,30 +189,29 @@ bool Lexer::getNumberOrLiteral(Token::Type type)
|| character == '\t'
|| character == '\r'
|| character == '\n'
|| character == '\0'
) {
|| character == '\0') {
break;
}
increment();
ignore();
}
m_tokens->push_back({ type, m_line, column,
m_job->input().substr(index, m_index - index) });
std::string(m_input.substr(index, m_index - index)) });
decrement();
retreat();
return true;
}
bool Lexer::getNumber()
bool Lexer::consumeNumber()
{
return getNumberOrLiteral(Token::Type::Number);
return consumeNumberOrLiteral(Token::Type::Number);
}
bool Lexer::getLiteral()
bool Lexer::consumeLiteral()
{
return getNumberOrLiteral(Token::Type::Literal);
return consumeNumberOrLiteral(Token::Type::Literal);
}
} // namespace Json
+7 -14
View File
@@ -11,10 +11,11 @@
// https://www.rfc-editor.org/rfc/pdfrfc/rfc8259.txt.pdf
#include <cstddef> // size_t
#include <memory> // shared_ptr
#include <string>
#include <vector>
#include "util/genericlexer.h"
namespace Json {
class Job;
@@ -40,7 +41,7 @@ struct Token {
};
// Lexical analyzer
class Lexer {
class Lexer final : public Util::GenericLexer {
public:
Lexer(Job* job);
virtual ~Lexer();
@@ -48,21 +49,13 @@ public:
void analyze();
private:
char peek();
char peekNext();
void increment();
void decrement();
char consume();
bool getString();
bool getNumberOrLiteral(Token::Type type);
bool getNumber();
bool getLiteral();
bool consumeString();
bool consumeNumberOrLiteral(Token::Type type);
bool consumeNumber();
bool consumeLiteral();
Job* m_job { nullptr };
size_t m_index { 0 };
size_t m_column { 0 };
size_t m_line { 0 };
+46 -48
View File
@@ -44,19 +44,19 @@ Value Parser::parse()
Token token = peek();
switch (token.type) {
case Token::Type::Literal:
result = getLiteral();
result = consumeLiteral();
break;
case Token::Type::Number:
result = getNumber();
result = consumeNumber();
break;
case Token::Type::String:
result = getString();
result = consumeString();
break;
case Token::Type::BracketOpen:
result = getArray();
result = consumeArray();
break;
case Token::Type::BraceOpen:
result = getObject();
result = consumeObject();
break;
case Token::Type::BracketClose:
m_job->printErrorLine(token, "expecting value, not ']'");
@@ -72,7 +72,7 @@ Value Parser::parse()
break;
}
if (!reachedEnd()) {
if (!isEOF()) {
m_job->printErrorLine(peek(), "multiple root elements");
}
@@ -81,36 +81,31 @@ Value Parser::parse()
// -----------------------------------------
bool Parser::reachedEnd()
bool Parser::isEOF()
{
return m_index >= m_tokens->size();
}
bool Parser::seekForward(Token::Type type)
{
for (; !reachedEnd(); ++m_index) {
if (peek().type == type) {
m_index++;
return true;
}
}
return false;
}
Token Parser::peek()
{
return m_tokens->at(m_index);
assert(!isEOF());
return (*m_tokens)[m_index];
}
Token Parser::consume()
{
Token token = peek();
m_index++;
return token;
assert(!isEOF());
return (*m_tokens)[m_index++];
}
Value Parser::getLiteral()
void Parser::ignoreUntil(Token::Type type)
{
while (!isEOF() && peek().type != type) {
++m_index;
}
}
Value Parser::consumeLiteral()
{
Token token = consume();
@@ -128,7 +123,7 @@ Value Parser::getLiteral()
return nullptr;
}
Value Parser::getNumber()
Value Parser::consumeNumber()
{
Token token = consume();
@@ -231,7 +226,7 @@ Value Parser::getNumber()
return std::stod(token.symbol);
}
Value Parser::getString()
Value Parser::consumeString()
{
Token token = consume();
@@ -305,7 +300,7 @@ Value Parser::getString()
return string;
}
Value Parser::getArray()
Value Parser::consumeArray()
{
m_index++;
@@ -313,33 +308,34 @@ Value Parser::getArray()
m_job->printErrorLine(token, message.c_str());
// After an error, try to find the closing bracket
seekForward(Token::Type::BracketClose);
ignoreUntil(Token::Type::BracketClose);
m_index++;
};
Value array = Value::Type::Array;
Token token;
for (;;) {
// EOF
if (reachedEnd()) {
if (isEOF()) {
reportError(m_tokens->at(m_index - 1), "expecting closing ']' at end");
break;
}
token = peek();
if (token.type == Token::Type::Literal) {
array.emplace_back(getLiteral());
array.emplace_back(consumeLiteral());
}
else if (token.type == Token::Type::Number) {
array.emplace_back(getNumber());
array.emplace_back(consumeNumber());
}
else if (token.type == Token::Type::String) {
array.emplace_back(getString());
array.emplace_back(consumeString());
}
else if (token.type == Token::Type::BracketOpen) {
array.emplace_back(getArray());
array.emplace_back(consumeArray());
}
else if (token.type == Token::Type::BraceOpen) {
array.emplace_back(getObject());
array.emplace_back(consumeObject());
}
else if (token.type == Token::Type::BracketClose) {
// Trailing comma
@@ -354,7 +350,7 @@ Value Parser::getArray()
}
// EOF
if (reachedEnd()) {
if (isEOF()) {
reportError(token, "expecting closing ']' at end");
break;
}
@@ -376,7 +372,7 @@ Value Parser::getArray()
return array;
}
Value Parser::getObject()
Value Parser::consumeObject()
{
m_index++;
@@ -384,7 +380,8 @@ Value Parser::getObject()
m_job->printErrorLine(token, message.c_str());
// After an error, try to find the closing brace
seekForward(Token::Type::BraceClose);
ignoreUntil(Token::Type::BraceClose);
m_index++;
};
Value object = Value::Type::Object;
@@ -393,7 +390,7 @@ Value Parser::getObject()
std::map<std::string, uint8_t> unique;
for (;;) {
// EOF
if (reachedEnd()) {
if (isEOF()) {
reportError(m_tokens->at(m_index - 1), "expecting closing '}' at end");
break;
}
@@ -414,9 +411,10 @@ Value Parser::getObject()
// Find member name
m_index--;
Value tmpName = getString();
Value tmpName = consumeString();
if (tmpName.m_type != Value::Type::String) {
seekForward(Token::Type::BraceClose);
ignoreUntil(Token::Type::BraceClose);
m_index++;
break;
}
@@ -430,7 +428,7 @@ Value Parser::getObject()
unique.insert({ name, 0 });
// EOF
if (reachedEnd()) {
if (isEOF()) {
reportError(token, "expecting colon, not 'EOF'");
reportError(token, "expecting closing '}' at end");
break;
@@ -444,7 +442,7 @@ Value Parser::getObject()
}
// EOF
if (reachedEnd()) {
if (isEOF()) {
reportError(token, "expecting value, not 'EOF'");
reportError(token, "expecting closing '}' at end");
break;
@@ -453,19 +451,19 @@ Value Parser::getObject()
// Add member (name:value pair) to object
token = peek();
if (token.type == Token::Type::Literal) {
object.emplace(name, getLiteral());
object.emplace(name, consumeLiteral());
}
else if (token.type == Token::Type::Number) {
object.emplace(name, getNumber());
object.emplace(name, consumeNumber());
}
else if (token.type == Token::Type::String) {
object.emplace(name, getString());
object.emplace(name, consumeString());
}
else if (token.type == Token::Type::BracketOpen) {
object.emplace(name, getArray());
object.emplace(name, consumeArray());
}
else if (token.type == Token::Type::BraceOpen) {
object.emplace(name, getObject());
object.emplace(name, consumeObject());
}
else {
reportError(token, "expecting value, not '" + token.symbol + "'");
@@ -473,7 +471,7 @@ Value Parser::getObject()
}
// EOF
if (reachedEnd()) {
if (isEOF()) {
reportError(token, "expecting closing '}' at end");
break;
}
+7 -8
View File
@@ -25,17 +25,16 @@ public:
Value parse();
private:
bool reachedEnd();
bool seekForward(Token::Type type);
bool isEOF();
Token peek();
Token consume();
void ignoreUntil(Token::Type type);
Value getLiteral();
Value getNumber();
Value getString();
Value getArray();
Value getObject();
Value consumeLiteral();
Value consumeNumber();
Value consumeString();
Value consumeArray();
Value consumeObject();
Job* m_job { nullptr };
+2 -2
View File
@@ -15,8 +15,8 @@
#include <utility> // forward
#include "util/json/array.h"
#include "util/json/conversion.h"
#include "util/json/object.h"
#include "util/meta/odr.h"
namespace Json {
@@ -133,7 +133,7 @@ struct toJsonFunction {
// Anonymous namespace prevents multiple definition of the reference
namespace {
// Function object
constexpr const auto& toJson = Detail::staticConst<Detail::toJsonFunction>; // NOLINT (misc-definitions-in-headers)
constexpr const auto& toJson = Util::Detail::staticConst<Detail::toJsonFunction>; // NOLINT(misc-definitions-in-headers,clang-diagnostic-unused-variable)
} // namespace
} // namespace Json
+8 -1
View File
@@ -7,10 +7,12 @@
#include <algorithm> // all_of
#include <cassert> // assert
#include <cstdint> // uint32_t
#include <fstream> // >>
#include <iostream> // istream, ostream
#include <string>
#include <utility> // move, swap
#include "util/format/format.h"
#include "util/json/array.h"
#include "util/json/job.h"
#include "util/json/object.h"
@@ -152,7 +154,7 @@ void Value::clear()
}
}
Value Value::parse(const std::string& input)
Value Value::parse(std::string_view input)
{
return Job(input).fire();
}
@@ -330,4 +332,9 @@ std::ostream& operator<<(std::ostream& output, const Value& value)
return output << value.dump(4);
}
void format(FormatBuilder& builder, const Value& value)
{
builder.putString(value.dump(4));
}
} // namespace Json
+4 -1
View File
@@ -14,6 +14,7 @@
#include <string>
#include <utility> // forward
#include "util/format/format.h"
#include "util/json/fromjson.h"
#include "util/json/tojson.h"
@@ -65,7 +66,7 @@ public:
// --------------------------------------
static Value parse(const std::string& input);
static Value parse(std::string_view input);
static Value parse(std::ifstream& file);
std::string dump(const uint32_t indent = 0, const char indentCharacter = ' ') const;
@@ -134,6 +135,8 @@ private:
std::istream& operator>>(std::istream& input, Value& value);
std::ostream& operator<<(std::ostream& output, const Value& value);
void format(FormatBuilder& builder, const Value& value);
} // namespace Json
/**
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2022 Riyyi
*
* SPDX-License-Identifier: MIT
*/
#ifndef META_ODR_H
#define META_ODR_H
namespace Util {
namespace Detail {
// Avoid ODR (One Definition Rule) violations,
// variable templates are required to have external linkage
template<typename T>
constexpr T staticConst {};
} // namespace Detail
} // namespace Util
#endif // META_ODR_H