mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 10:36:31 +00:00
358 lines
7.5 KiB
C++
358 lines
7.5 KiB
C++
#pragma once
|
|
|
|
#include <concepts>
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace sliger::json {
|
|
|
|
struct Pos {
|
|
int line;
|
|
int col;
|
|
};
|
|
|
|
struct Err {
|
|
Pos pos;
|
|
std::string msg;
|
|
};
|
|
|
|
template <typename T> class Res {
|
|
public:
|
|
Res(T value)
|
|
: maybe_value(std::move(value))
|
|
, maybe_error()
|
|
{
|
|
}
|
|
Res(Err error)
|
|
: maybe_value()
|
|
, maybe_error(std::move(error))
|
|
{
|
|
}
|
|
|
|
inline auto ok() const -> bool { return this->maybe_value.has_value(); }
|
|
|
|
inline auto val() const& -> const T& { return this->maybe_value.value(); }
|
|
inline auto val() & -> T& { return this->maybe_value.value(); }
|
|
inline auto val() && -> T&& { return std::move(this->maybe_value.value()); }
|
|
|
|
inline auto err() const& -> const Err& { return this->maybe_error.value(); }
|
|
inline auto err() & -> Err& { return this->maybe_error.value(); }
|
|
inline auto err() && -> Err&&
|
|
{
|
|
return std::move(this->maybe_error.value());
|
|
}
|
|
|
|
private:
|
|
std::optional<T> maybe_value;
|
|
std::optional<Err> maybe_error;
|
|
};
|
|
|
|
template <> class Res<void> {
|
|
public:
|
|
Res()
|
|
: maybe_error()
|
|
{
|
|
}
|
|
Res(Err error)
|
|
: maybe_error(std::move(error))
|
|
{
|
|
}
|
|
|
|
inline auto ok() const -> bool { return not this->maybe_error.has_value(); }
|
|
|
|
inline auto err() const& -> const Err& { return this->maybe_error.value(); }
|
|
inline auto err() & -> Err& { return this->maybe_error.value(); }
|
|
inline auto err() && -> Err&&
|
|
{
|
|
return std::move(this->maybe_error.value());
|
|
}
|
|
|
|
private:
|
|
std::optional<Err> maybe_error;
|
|
};
|
|
|
|
enum class Type {
|
|
Null,
|
|
String,
|
|
Number,
|
|
Bool,
|
|
Array,
|
|
Object,
|
|
};
|
|
|
|
struct Value {
|
|
virtual ~Value() = default;
|
|
virtual auto type() const -> Type = 0;
|
|
|
|
template <typename T>
|
|
requires std::derived_from<T, Value>
|
|
inline auto as() & -> T&
|
|
{
|
|
return static_cast<T&>(*this);
|
|
}
|
|
|
|
template <typename T>
|
|
requires std::derived_from<T, Value>
|
|
inline auto as() const& -> const T&
|
|
{
|
|
return static_cast<const T&>(*this);
|
|
}
|
|
};
|
|
|
|
struct Null final : public Value {
|
|
auto type() const -> Type override { return Type::Null; }
|
|
};
|
|
|
|
struct String final : public Value {
|
|
String(std::string value)
|
|
: value(std::move(value))
|
|
{
|
|
}
|
|
auto type() const -> Type override { return Type::String; }
|
|
|
|
std::string value;
|
|
};
|
|
|
|
struct Number final : public Value {
|
|
Number(double value)
|
|
: value(value)
|
|
{
|
|
}
|
|
auto type() const -> Type override { return Type::Number; }
|
|
|
|
double value;
|
|
};
|
|
|
|
struct Bool final : public Value {
|
|
Bool(bool value)
|
|
: value(value)
|
|
{
|
|
}
|
|
auto type() const -> Type override { return Type::Bool; }
|
|
|
|
bool value;
|
|
};
|
|
|
|
using ArrayValues = std::vector<std::unique_ptr<Value>>;
|
|
struct Array final : public Value {
|
|
Array(ArrayValues values)
|
|
: values(std::move(values))
|
|
{
|
|
}
|
|
auto type() const -> Type override { return Type::Array; }
|
|
|
|
ArrayValues values;
|
|
};
|
|
|
|
using ObjectFields = std::unordered_map<std::string, std::unique_ptr<Value>>;
|
|
struct Object final : public Value {
|
|
Object(ObjectFields fields)
|
|
: fields(std::move(fields))
|
|
{
|
|
}
|
|
auto type() const -> Type override { return Type::Object; }
|
|
|
|
ObjectFields fields;
|
|
};
|
|
|
|
enum class TokTyp {
|
|
Eof,
|
|
String,
|
|
Float,
|
|
False,
|
|
True,
|
|
Null,
|
|
LBrace,
|
|
RBrace,
|
|
LBracket,
|
|
RBracket,
|
|
Comma,
|
|
Colon,
|
|
};
|
|
|
|
auto tok_typ_to_string(TokTyp typ) -> std::string;
|
|
|
|
struct Tok {
|
|
Tok(TokTyp typ, Pos pos)
|
|
: typ(typ)
|
|
, pos(pos)
|
|
, val_id(0)
|
|
{
|
|
}
|
|
Tok(TokTyp typ, Pos pos, size_t val_id)
|
|
: typ(typ)
|
|
, pos(pos)
|
|
, val_id(val_id)
|
|
{
|
|
}
|
|
Tok(TokTyp typ, Pos pos, double float_val)
|
|
: typ(typ)
|
|
, pos(pos)
|
|
, float_val(float_val)
|
|
{
|
|
}
|
|
Tok(TokTyp typ, Pos pos, bool bool_val)
|
|
: typ(typ)
|
|
, pos(pos)
|
|
, bool_val(bool_val)
|
|
{
|
|
}
|
|
|
|
TokTyp typ;
|
|
Pos pos;
|
|
union {
|
|
size_t val_id;
|
|
double float_val;
|
|
bool bool_val;
|
|
};
|
|
};
|
|
|
|
class Lexer {
|
|
public:
|
|
Lexer(std::string_view text)
|
|
: text(text)
|
|
{
|
|
}
|
|
|
|
auto next() -> Res<Tok>;
|
|
|
|
inline auto pos() const -> Pos { return { this->line, this->col }; }
|
|
|
|
inline auto val(size_t val_id) const -> const std::string&
|
|
{
|
|
return this->strs_vals.at(val_id);
|
|
}
|
|
|
|
private:
|
|
inline void step()
|
|
{
|
|
if (this->cur() == '\n') {
|
|
this->col = 1;
|
|
this->line += 1;
|
|
} else {
|
|
this->col += 1;
|
|
}
|
|
this->i += 1;
|
|
}
|
|
|
|
inline auto intern_str(std::string val) -> size_t
|
|
{
|
|
if (this->strs_val_id_map.contains(val))
|
|
return strs_val_id_map.at(val);
|
|
auto id = this->strs_vals.size();
|
|
this->strs_vals.push_back(val);
|
|
this->strs_val_id_map.insert_or_assign(val, id);
|
|
return id;
|
|
}
|
|
|
|
inline auto test_in(std::string_view chs) const -> bool
|
|
{
|
|
for (auto ch : chs)
|
|
if (test(ch))
|
|
return true;
|
|
return false;
|
|
}
|
|
inline auto test(char ch) const -> bool { return !done() && cur() == ch; }
|
|
inline auto done() const -> bool { return this->i >= this->text.size(); }
|
|
inline auto cur() const -> char { return this->text.at(this->i); }
|
|
|
|
std::string_view text;
|
|
size_t i = 0;
|
|
int line = 1;
|
|
int col = 1;
|
|
std::unordered_map<std::string, size_t> strs_val_id_map;
|
|
std::vector<std::string> strs_vals;
|
|
};
|
|
|
|
class Parser {
|
|
public:
|
|
Parser(std::string_view text)
|
|
: lexer(text)
|
|
, cur(lexer.next())
|
|
{
|
|
}
|
|
|
|
auto parse_val() -> Res<std::unique_ptr<Value>>;
|
|
|
|
private:
|
|
auto unexpected_tok_err(TokTyp expected, std::string_view msg)
|
|
-> Res<std::unique_ptr<Value>>;
|
|
|
|
inline auto step() -> Res<void>
|
|
{
|
|
auto tok = this->lexer.next();
|
|
if (not tok.ok())
|
|
return tok.err();
|
|
this->cur = tok;
|
|
return {};
|
|
}
|
|
inline auto curtyp() const -> TokTyp { return this->cur.val().typ; }
|
|
|
|
Lexer lexer;
|
|
Res<Tok> cur;
|
|
};
|
|
|
|
inline auto parse_json(std::string_view value) -> Res<std::unique_ptr<Value>>
|
|
{
|
|
return Parser(value).parse_val();
|
|
}
|
|
|
|
class Writer {
|
|
public:
|
|
inline auto operator<<(auto value) -> Writer&
|
|
{
|
|
this->add(value);
|
|
return *this;
|
|
}
|
|
inline void add(const char* value) { this->data.append(value); }
|
|
inline void add(std::string_view value) { this->data.append(value); }
|
|
inline void add(const std::string& value) { this->data.append(value); }
|
|
inline void add(auto value) { this->data.push_back(value); }
|
|
|
|
template <typename T, typename F>
|
|
auto add_comma_seperated(const T& values, F f)
|
|
{
|
|
auto first = true;
|
|
for (const auto& value : values) {
|
|
if (!first) {
|
|
add(",");
|
|
}
|
|
first = false;
|
|
f(*this, value);
|
|
}
|
|
}
|
|
|
|
inline auto done() -> std::string { return std::move(this->data); }
|
|
|
|
private:
|
|
std::string data;
|
|
};
|
|
|
|
struct ToAndFromJson {
|
|
virtual ~ToAndFromJson() = default;
|
|
|
|
virtual void to_json(Writer& writer) const = 0;
|
|
};
|
|
|
|
auto to_json(const std::derived_from<ToAndFromJson> auto& value) -> std::string
|
|
{
|
|
auto writer = Writer();
|
|
value.to_json(writer);
|
|
return writer.done();
|
|
}
|
|
|
|
template <typename T>
|
|
requires std::derived_from<T, ToAndFromJson>
|
|
auto from_json(std::string_view json_string) -> T
|
|
{
|
|
return T::from_json(json_string);
|
|
}
|
|
|
|
}
|