mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 18:16:31 +00:00
add json
This commit is contained in:
parent
fc65c193c3
commit
a644c0a630
106
runtime/json.cpp
Normal file
106
runtime/json.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "json.hpp"
|
||||
#include <cstdlib>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace sliger::json;
|
||||
|
||||
auto ident_tok_typs = std::unordered_map<std::string, TokTyp> {
|
||||
{ "null", TokTyp::Null },
|
||||
{ "false", TokTyp::False },
|
||||
{ "true", TokTyp::True },
|
||||
};
|
||||
|
||||
auto id_start_chars = "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ_";
|
||||
|
||||
auto id_tail_chars = "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"1234567890";
|
||||
|
||||
auto Lexer::next() -> Tok
|
||||
{
|
||||
if (done()) {
|
||||
return TokTyp::Eof;
|
||||
}
|
||||
auto pos = this->pos();
|
||||
if (test('"')) {
|
||||
step();
|
||||
auto value = std::string();
|
||||
while (!done() and !test('"')) {
|
||||
if (cur() == '\\') {
|
||||
step();
|
||||
if (done())
|
||||
break;
|
||||
value.push_back([&] {
|
||||
char ch = cur();
|
||||
switch (ch) {
|
||||
case 'n':
|
||||
return '\n';
|
||||
case 'r':
|
||||
return '\r';
|
||||
case 't':
|
||||
return '\t';
|
||||
case '0':
|
||||
return '\0';
|
||||
default:
|
||||
return ch;
|
||||
}
|
||||
}());
|
||||
} else {
|
||||
value.push_back(cur());
|
||||
}
|
||||
step();
|
||||
}
|
||||
}
|
||||
auto step_n_ret = [&](auto tok) {
|
||||
step();
|
||||
return tok;
|
||||
};
|
||||
switch (cur()) {
|
||||
case '0':
|
||||
return step_n_ret(Tok(TokTyp::Float, 0.0));
|
||||
case '{':
|
||||
return step_n_ret(TokTyp::LBrace);
|
||||
case '}':
|
||||
return step_n_ret(TokTyp::RBrace);
|
||||
case '[':
|
||||
return step_n_ret(TokTyp::LBracket);
|
||||
case ']':
|
||||
return step_n_ret(TokTyp::RBracket);
|
||||
case ',':
|
||||
return step_n_ret(TokTyp::Comma);
|
||||
case ':':
|
||||
return step_n_ret(TokTyp::Colon);
|
||||
}
|
||||
if (test_in(id_start_chars)) {
|
||||
auto value = std::string();
|
||||
while (test_in(id_tail_chars)) {
|
||||
value.push_back(cur());
|
||||
step();
|
||||
}
|
||||
if (ident_tok_typs.find(value) == ident_tok_typs.end()) {
|
||||
std::cerr << std::format("sliger::json::Lexer error: unknown "
|
||||
"identifier \"{}\" at {}:{}",
|
||||
value, pos.col, pos.line);
|
||||
return TokTyp::Error;
|
||||
}
|
||||
return ident_tok_typs.at(value);
|
||||
}
|
||||
if (test_in("123456789")) {
|
||||
auto value_str = std::string();
|
||||
while (test_in("1234567890")) {
|
||||
value_str.push_back(cur());
|
||||
step();
|
||||
}
|
||||
auto value = std::atof(value_str.c_str());
|
||||
return Tok(TokTyp::Float, value);
|
||||
}
|
||||
std::cerr << std::format(
|
||||
"sliger::json::Lexer error: unknown character '{}' at {}:{}", cur(),
|
||||
this->line, this->col);
|
||||
step();
|
||||
return TokTyp::Error;
|
||||
}
|
169
runtime/json.hpp
Normal file
169
runtime/json.hpp
Normal file
@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sliger::json {
|
||||
|
||||
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 {
|
||||
auto type() const -> Type override { return Type::String; }
|
||||
|
||||
std::string value;
|
||||
};
|
||||
|
||||
struct Number final : public Value {
|
||||
auto type() const -> Type override { return Type::Number; }
|
||||
|
||||
double value;
|
||||
};
|
||||
|
||||
struct Bool final : public Value {
|
||||
auto type() const -> Type override { return Type::Bool; }
|
||||
|
||||
bool value;
|
||||
};
|
||||
|
||||
struct Array final : public Value {
|
||||
auto type() const -> Type override { return Type::Array; }
|
||||
|
||||
std::vector<std::unique_ptr<Value>> values;
|
||||
};
|
||||
|
||||
struct Object final : public Value {
|
||||
auto type() const -> Type override { return Type::Object; }
|
||||
};
|
||||
|
||||
struct Pos {
|
||||
int line;
|
||||
int col;
|
||||
};
|
||||
|
||||
enum class TokTyp {
|
||||
Error,
|
||||
Eof,
|
||||
String,
|
||||
Float,
|
||||
False,
|
||||
True,
|
||||
Null,
|
||||
LBrace,
|
||||
RBrace,
|
||||
LBracket,
|
||||
RBracket,
|
||||
Comma,
|
||||
Colon,
|
||||
};
|
||||
|
||||
struct Tok {
|
||||
Tok(TokTyp typ)
|
||||
: typ(typ)
|
||||
, val_id(0)
|
||||
{
|
||||
}
|
||||
Tok(TokTyp typ, size_t val_id)
|
||||
: typ(typ)
|
||||
, val_id(val_id)
|
||||
{
|
||||
}
|
||||
Tok(TokTyp typ, double float_val)
|
||||
: typ(typ)
|
||||
, float_val(float_val)
|
||||
{
|
||||
}
|
||||
Tok(TokTyp typ, bool bool_val)
|
||||
: typ(typ)
|
||||
, bool_val(bool_val)
|
||||
{
|
||||
}
|
||||
|
||||
TokTyp typ;
|
||||
union {
|
||||
size_t val_id;
|
||||
double float_val;
|
||||
bool bool_val;
|
||||
};
|
||||
};
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
Lexer(std::string_view text)
|
||||
: text(text)
|
||||
{
|
||||
}
|
||||
|
||||
auto next() -> Tok;
|
||||
|
||||
inline auto pos() const -> Pos { return { this->line, this->col }; }
|
||||
|
||||
private:
|
||||
inline void step() { this->i += 1; }
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser(std::string_view text)
|
||||
: lexer(text)
|
||||
, cur(lexer.next())
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
Lexer lexer;
|
||||
Tok cur;
|
||||
};
|
||||
|
||||
struct Jsonable {
|
||||
virtual ~Jsonable() = default;
|
||||
|
||||
virtual auto to_json() const -> std::string = 0;
|
||||
};
|
||||
|
||||
template <typename T> auto from_json() -> T { T::from_json(); }
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user