mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 18:36:31 +00:00
252 lines
8.0 KiB
C++
252 lines
8.0 KiB
C++
#include "json.hpp"
|
|
#include <cstdlib>
|
|
#include <format>
|
|
#include <memory>
|
|
#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() -> Res<Tok>
|
|
{
|
|
auto pos = this->pos();
|
|
if (done()) {
|
|
return Tok(TokTyp::Eof, 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();
|
|
}
|
|
if (!test('"')) {
|
|
return Err {
|
|
.pos = pos,
|
|
.msg
|
|
= std::format("malformed string, expected '\"', got '{}' token",
|
|
this->cur()),
|
|
};
|
|
}
|
|
step();
|
|
return Tok(TokTyp::String, pos, intern_str(value));
|
|
}
|
|
auto step_n_ret = [&](auto tok) {
|
|
step();
|
|
return tok;
|
|
};
|
|
switch (cur()) {
|
|
case '0':
|
|
return step_n_ret(Tok(TokTyp::Float, pos, 0.0));
|
|
case '{':
|
|
return step_n_ret(Tok(TokTyp::LBrace, pos));
|
|
case '}':
|
|
return step_n_ret(Tok(TokTyp::RBrace, pos));
|
|
case '[':
|
|
return step_n_ret(Tok(TokTyp::LBracket, pos));
|
|
case ']':
|
|
return step_n_ret(Tok(TokTyp::RBracket, pos));
|
|
case ',':
|
|
return step_n_ret(Tok(TokTyp::Comma, pos));
|
|
case ':':
|
|
return step_n_ret(Tok(TokTyp::Colon, pos));
|
|
}
|
|
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()) {
|
|
return Err {
|
|
.pos = pos,
|
|
.msg = std::format("unknown identifier \"{}\"", value),
|
|
};
|
|
}
|
|
return Tok(ident_tok_typs.at(value), pos);
|
|
}
|
|
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, pos, value);
|
|
}
|
|
auto ch = cur();
|
|
step();
|
|
return Err {
|
|
.pos = pos,
|
|
.msg = std::format("unknown character '{}'", ch),
|
|
};
|
|
}
|
|
|
|
auto Parser::parse_val() -> Res<std::unique_ptr<Value>>
|
|
{
|
|
if (not this->cur.ok())
|
|
return this->cur.err();
|
|
auto cur = this->cur.val();
|
|
switch (cur.typ) {
|
|
case TokTyp::Eof:
|
|
return Err {
|
|
.pos = cur.pos,
|
|
.msg = "expected value, got eof",
|
|
};
|
|
case TokTyp::String: {
|
|
auto value = this->lexer.val(cur.val_id);
|
|
step();
|
|
return Res<std::unique_ptr<Value>>(std::make_unique<String>(value));
|
|
}
|
|
case TokTyp::Float: {
|
|
auto value = cur.float_val;
|
|
step();
|
|
return Res<std::unique_ptr<Value>>(std::make_unique<Number>(value));
|
|
}
|
|
case TokTyp::False: {
|
|
step();
|
|
return Res<std::unique_ptr<Value>>(std::make_unique<Bool>(false));
|
|
}
|
|
case TokTyp::True: {
|
|
step();
|
|
return Res<std::unique_ptr<Value>>(std::make_unique<Bool>(true));
|
|
}
|
|
case TokTyp::Null: {
|
|
step();
|
|
return Res<std::unique_ptr<Value>>(std::make_unique<Null>());
|
|
}
|
|
case TokTyp::LBrace: {
|
|
step();
|
|
ObjectFields fields;
|
|
if (curtyp() != TokTyp::RBrace) {
|
|
if (curtyp() != TokTyp::String) {
|
|
return unexpected_tok_err(
|
|
TokTyp::String, "malformed object");
|
|
}
|
|
auto key = this->lexer.val(this->cur.val().val_id);
|
|
step();
|
|
if (curtyp() != TokTyp::Colon) {
|
|
return unexpected_tok_err(
|
|
TokTyp::Colon, "malformed object");
|
|
}
|
|
step();
|
|
auto value = parse_val();
|
|
if (not value.ok()) {
|
|
return value.err();
|
|
}
|
|
fields.insert_or_assign(key, std::move(value.val()));
|
|
while (curtyp() == TokTyp::Comma) {
|
|
step();
|
|
if (curtyp() != TokTyp::String) {
|
|
return unexpected_tok_err(
|
|
TokTyp::String, "malformed object");
|
|
}
|
|
auto key = this->lexer.val(this->cur.val().val_id);
|
|
step();
|
|
if (curtyp() != TokTyp::Colon) {
|
|
return unexpected_tok_err(
|
|
TokTyp::Colon, "malformed object");
|
|
}
|
|
step();
|
|
auto value = parse_val();
|
|
if (not value.ok()) {
|
|
return value.err();
|
|
}
|
|
fields.insert_or_assign(key, std::move(value.val()));
|
|
}
|
|
}
|
|
if (curtyp() != TokTyp::RBrace) {
|
|
return unexpected_tok_err(TokTyp::RBrace, "malformed object");
|
|
}
|
|
step();
|
|
return Res<std::unique_ptr<Value>>(
|
|
std::make_unique<Object>(std::move(fields)));
|
|
}
|
|
case TokTyp::LBracket: {
|
|
step();
|
|
ArrayValues values;
|
|
if (curtyp() != TokTyp::RBracket) {
|
|
auto value = parse_val();
|
|
if (not value.ok()) {
|
|
return value.err();
|
|
}
|
|
values.push_back(std::move(value.val()));
|
|
while (curtyp() == TokTyp::Comma) {
|
|
step();
|
|
auto value = parse_val();
|
|
if (not value.ok()) {
|
|
return value.err();
|
|
}
|
|
values.push_back(std::move(value.val()));
|
|
}
|
|
}
|
|
if (curtyp() != TokTyp::RBracket) {
|
|
return unexpected_tok_err(TokTyp::RBracket, "malformed array");
|
|
}
|
|
step();
|
|
return Res<std::unique_ptr<Value>>(
|
|
std::make_unique<Array>(std::move(values)));
|
|
}
|
|
case TokTyp::RBrace:
|
|
case TokTyp::RBracket:
|
|
case TokTyp::Comma:
|
|
case TokTyp::Colon:
|
|
return Err {
|
|
.pos = cur.pos,
|
|
.msg = std::format("expected value, got '{}' token",
|
|
tok_typ_to_string(cur.typ)),
|
|
};
|
|
break;
|
|
}
|
|
return Err {
|
|
.pos = cur.pos,
|
|
.msg = std::format(
|
|
"internal error, could not parse '{}'", tok_typ_to_string(cur.typ)),
|
|
};
|
|
}
|
|
|
|
auto Parser::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)),
|
|
};
|
|
}
|