#pragma once #include <concepts> #include <cstddef> #include <format> #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); } }; 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: inline auto unexpected_tok_err( TokTyp expected, std::string_view msg) -> Res<std::unique_ptr<Value>> { return Err { .pos = this->cur.val().pos, .msg = std::format("{}, expected '{}', got '{}'", msg, tok_typ_to_string(expected), tok_typ_to_string(this->cur.val().typ)), }; } 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); } }