457 lines
8.9 KiB
C++
457 lines
8.9 KiB
C++
#include <cstring>
|
|
#include <exception>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
|
|
#include "json.hpp"
|
|
|
|
#include <stdarg.h>
|
|
|
|
using namespace std;
|
|
|
|
char buffer[512];
|
|
char *format(char const *base, ...) {
|
|
va_list lst;
|
|
va_start(lst, base);
|
|
|
|
vsnprintf(buffer, sizeof buffer, base, lst);
|
|
|
|
va_end(lst);
|
|
return buffer;
|
|
}
|
|
|
|
enum TokenType {
|
|
TOK_OPEN_OBJ = 0,
|
|
TOK_CLOSE_OBJ,
|
|
TOK_OPEN_ARR,
|
|
TOK_CLOSE_ARR,
|
|
|
|
TOK_ASSIGNMENT,
|
|
TOK_COMMA,
|
|
|
|
TOK_STRING,
|
|
TOK_NUMBER,
|
|
|
|
TOK_EOF
|
|
};
|
|
|
|
char const *TName(TokenType tok) {
|
|
switch (tok) {
|
|
case TOK_OPEN_OBJ:
|
|
return "{";
|
|
case TOK_CLOSE_OBJ:
|
|
return "}";
|
|
case TOK_OPEN_ARR:
|
|
return "[";
|
|
case TOK_CLOSE_ARR:
|
|
return "]";
|
|
case TOK_ASSIGNMENT:
|
|
return ":";
|
|
case TOK_COMMA:
|
|
return ",";
|
|
case TOK_STRING:
|
|
return "STRING!!!";
|
|
case TOK_NUMBER:
|
|
return "NUMBER FUUAAAAA";
|
|
case TOK_EOF:
|
|
return "EOF";
|
|
}
|
|
|
|
return "How the fuck did we even get here";
|
|
}
|
|
|
|
union TokenValue {
|
|
long double d;
|
|
char *s;
|
|
};
|
|
|
|
struct Token {
|
|
TokenType type;
|
|
TokenValue v;
|
|
|
|
char const *Name(void) { return TName(type); }
|
|
|
|
void PrettyPrint(void) {
|
|
cout << Name() << ' ';
|
|
if (type == TOK_STRING)
|
|
printf("%s", v.s);
|
|
if (type == TOK_NUMBER)
|
|
cout << v.d;
|
|
cout << endl;
|
|
}
|
|
};
|
|
|
|
struct Tokeniser {
|
|
istream *f = nullptr;
|
|
|
|
void Init(istream *stream) { f = stream; }
|
|
Token Next(void);
|
|
|
|
Token ParseIdent(string &str, bool &in_string);
|
|
} TS;
|
|
|
|
const std::string WHITESPACE = " \n\r\t\f\v";
|
|
|
|
std::string ltrim(const std::string &s) {
|
|
size_t start = s.find_first_not_of(WHITESPACE);
|
|
return (start == std::string::npos) ? "" : s.substr(start);
|
|
}
|
|
|
|
std::string rtrim(const std::string &s) {
|
|
size_t end = s.find_last_not_of(WHITESPACE);
|
|
return (end == std::string::npos) ? "" : s.substr(0, end + 1);
|
|
}
|
|
|
|
std::string trim(const std::string &s) { return rtrim(ltrim(s)); }
|
|
|
|
Token Tokeniser::ParseIdent(string &str, bool &in_string) {
|
|
Token res{.type = TOK_NUMBER};
|
|
|
|
str = trim(str);
|
|
|
|
for (size_t i = 0; i < str.length(); i++) {
|
|
if (!isdigit(str[i]) && str[i] != '.' && str[i] != '\r' && str[i] != '\n') {
|
|
res.type = TOK_STRING;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (res.type == TOK_NUMBER) {
|
|
res.v.d = stold(str.c_str());
|
|
} else {
|
|
char *amogus = (char *)calloc(1, str.length() + 1);
|
|
if ((str.front() == '"' || str.front() == '\'') &&
|
|
(str.back() == '"' || str.back() == '\'')) {
|
|
memcpy(amogus, str.c_str() + 1, str.length() - 2);
|
|
amogus[str.length() - 1] = 0;
|
|
} else {
|
|
memcpy(amogus, str.c_str(), str.length());
|
|
}
|
|
res.v.s = amogus;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
Token Tokeniser::Next(void) {
|
|
char ch;
|
|
string ident = "";
|
|
bool string_mode = false;
|
|
bool in_string = false;
|
|
|
|
while (f->get(ch)) {
|
|
static char const *chars = "{}[]:,";
|
|
static unsigned char chars_len = strlen(chars);
|
|
|
|
if (ch == '\r' || ch == '\n') {
|
|
if (string_mode == false)
|
|
continue;
|
|
}
|
|
|
|
if (ch == '"' || ch == '\'') {
|
|
in_string = !in_string;
|
|
string_mode = !string_mode;
|
|
}
|
|
|
|
if (!in_string)
|
|
for (unsigned char i = 0; i < chars_len; i++) {
|
|
if (ch == chars[i] || ch == ' ') {
|
|
if (string_mode && ident.length() > 0) {
|
|
auto a = ParseIdent(ident, in_string);
|
|
string_mode = false;
|
|
ident = "";
|
|
f->unget();
|
|
return a;
|
|
}
|
|
|
|
if (ch == ' ')
|
|
continue;
|
|
return Token{.type = (TokenType)i};
|
|
} else {
|
|
string_mode = true;
|
|
}
|
|
}
|
|
|
|
if (string_mode)
|
|
ident.push_back(ch);
|
|
}
|
|
|
|
return Token{.type = TOK_EOF};
|
|
}
|
|
|
|
void JSONNode::Free(void) {
|
|
switch (type) {
|
|
case JSON_STRING:
|
|
if (v.s)
|
|
delete v.s;
|
|
break;
|
|
case JSON_LIST:
|
|
if (v.list) {
|
|
for (auto i : *v.list)
|
|
i.Free();
|
|
delete v.list;
|
|
}
|
|
break;
|
|
case JSON_OBJECT:
|
|
if (v.object) {
|
|
for (auto i : *v.object) {
|
|
if (i.name)
|
|
delete i.name;
|
|
i.node.Free();
|
|
}
|
|
delete v.object;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
long double &JSONNode::Number(void) {
|
|
if (type != JSON_NUMBER)
|
|
throw runtime_error("JSON node is not a number.");
|
|
return v.ld;
|
|
}
|
|
|
|
long double JSONNode::Number(long double def) {
|
|
if (type != JSON_NUMBER)
|
|
return def;
|
|
return v.ld;
|
|
}
|
|
|
|
char *&JSONNode::String(void) {
|
|
if (type != JSON_STRING)
|
|
throw runtime_error("JSON node is not a string.");
|
|
return v.s;
|
|
}
|
|
|
|
char *JSONNode::String(char *def) {
|
|
if (type != JSON_STRING)
|
|
return def;
|
|
|
|
return v.s;
|
|
}
|
|
|
|
vector<JSONNode> *&JSONNode::List(void) {
|
|
if (type != JSON_LIST)
|
|
throw runtime_error("JSON node is not a list.");
|
|
return v.list;
|
|
}
|
|
|
|
vector<JSONKeyValue> *&JSONNode::Object(void) {
|
|
if (type != JSON_OBJECT)
|
|
throw runtime_error("JSON node is not a object.");
|
|
return v.object;
|
|
}
|
|
|
|
string const JSONNode::Name(void) {
|
|
switch (type) {
|
|
case JSON_UNKNOWN:
|
|
return "Unknown";
|
|
case JSON_OBJECT:
|
|
return "Object";
|
|
case JSON_LIST:
|
|
return "List";
|
|
case JSON_KEY:
|
|
return "Key";
|
|
case JSON_NUMBER:
|
|
return "Number";
|
|
case JSON_STRING:
|
|
return "String";
|
|
}
|
|
return "Unknown node";
|
|
}
|
|
|
|
JSONNode UNKNOWN{.type = JSON_UNKNOWN};
|
|
|
|
void JSONKeyValue::PrettyPrint(string indent, bool last) {
|
|
printf("%s", indent.c_str());
|
|
if (last) {
|
|
printf("\\-");
|
|
indent += " ";
|
|
} else {
|
|
printf("|-");
|
|
indent += "| ";
|
|
}
|
|
|
|
printf(" %s\n", name);
|
|
node.PrettyPrint(indent, false);
|
|
}
|
|
|
|
JSONNode &JSONNode::operator[](string key) {
|
|
for (size_t i = 0; i < v.object->size(); i++) {
|
|
JSONKeyValue &kv = v.object->at(i);
|
|
|
|
if (strcmp(kv.name, key.c_str()) == 0) {
|
|
return kv.node;
|
|
}
|
|
}
|
|
|
|
return UNKNOWN;
|
|
}
|
|
|
|
JSONNode &JSONNode::operator[](size_t index) { return v.list->at(index); }
|
|
|
|
void JSONNode::PrettyPrint(string indent = "", bool last = true) {
|
|
printf("%s", indent.c_str());
|
|
if (last) {
|
|
printf("\\-");
|
|
indent += " ";
|
|
} else {
|
|
printf("|-");
|
|
indent += "| ";
|
|
}
|
|
printf(" {%s}", Name().c_str());
|
|
printf("\r\e[40C");
|
|
|
|
if (type == JSON_NUMBER)
|
|
cout << v.ld;
|
|
if (type == JSON_STRING)
|
|
cout << v.s;
|
|
|
|
printf("\n");
|
|
|
|
if (type == JSON_OBJECT) {
|
|
for (size_t i = 0; i < v.object->size(); i++)
|
|
v.object->at(i).PrettyPrint(indent, i == v.list->size() - 1);
|
|
}
|
|
if (type == JSON_LIST) {
|
|
for (size_t i = 0; i < v.list->size(); i++)
|
|
v.list->at(i).PrettyPrint(indent, i == v.list->size() - 1);
|
|
}
|
|
}
|
|
|
|
struct ParserState {
|
|
Token tok;
|
|
|
|
Token Next(void) {
|
|
tok = TS.Next();
|
|
return tok;
|
|
}
|
|
|
|
bool Accept(TokenType other) {
|
|
if (tok.type == other) {
|
|
Next();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Expect(TokenType other) {
|
|
bool flag = false;
|
|
if (Accept(other))
|
|
flag = true;
|
|
else {
|
|
throw runtime_error(
|
|
format("Expected token: {}, got {}.", tok.Name(), TName(other)));
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
JSONNode ParseObject(void);
|
|
JSONNode ParseArray(void);
|
|
JSONNode ParseValue(void);
|
|
|
|
JSONNode Parse(void);
|
|
} PS;
|
|
|
|
JSONNode ParserState::ParseObject(void) {
|
|
JSONNode node = {.type = JSON_OBJECT,
|
|
.v = {.object = new vector<JSONKeyValue>}};
|
|
|
|
if (Accept(TOK_CLOSE_OBJ))
|
|
return node;
|
|
|
|
while (1) {
|
|
JSONKeyValue pair;
|
|
if (tok.type != TOK_STRING) {
|
|
throw runtime_error("Expected key.");
|
|
}
|
|
pair.name = tok.v.s;
|
|
Next();
|
|
Expect(TOK_ASSIGNMENT);
|
|
pair.node = ParseValue();
|
|
node.v.object->push_back(pair);
|
|
Accept(TOK_COMMA);
|
|
if (tok.type == TOK_CLOSE_OBJ)
|
|
break;
|
|
}
|
|
|
|
Expect(TOK_CLOSE_OBJ);
|
|
|
|
return node;
|
|
}
|
|
|
|
JSONNode ParserState::ParseArray(void) {
|
|
JSONNode node = {.type = JSON_LIST, .v = {.list = new vector<JSONNode>}};
|
|
|
|
if (Accept(TOK_CLOSE_ARR))
|
|
return node;
|
|
|
|
while (1) {
|
|
node.v.list->push_back(ParseValue());
|
|
Accept(TOK_COMMA);
|
|
if (tok.type == TOK_CLOSE_ARR)
|
|
break;
|
|
}
|
|
|
|
Expect(TOK_CLOSE_ARR);
|
|
|
|
return node;
|
|
}
|
|
|
|
JSONNode ParserState::ParseValue(void) {
|
|
TokenValue value = tok.v;
|
|
if (Accept(TOK_OPEN_OBJ)) {
|
|
JSONNode node = ParseObject();
|
|
return node;
|
|
} else if (Accept(TOK_OPEN_ARR)) {
|
|
JSONNode node = ParseArray();
|
|
return node;
|
|
} else if (Accept(TOK_NUMBER)) {
|
|
JSONNode node = {
|
|
.type = JSON_NUMBER,
|
|
.v = {.ld = value.d},
|
|
};
|
|
return node;
|
|
} else if (Accept(TOK_STRING)) {
|
|
JSONNode node = {
|
|
.type = JSON_STRING,
|
|
.v = {.s = value.s},
|
|
};
|
|
return node;
|
|
} else if (Accept(TOK_EOF)) {
|
|
return JSONNode{
|
|
.type = JSON_UNKNOWN,
|
|
};
|
|
} else {
|
|
throw runtime_error(format("Invalid value: %s", tok.Name()));
|
|
}
|
|
}
|
|
|
|
JSONNode ParserState::Parse(void) {
|
|
JSONNode root{JSON_UNKNOWN, {0}};
|
|
|
|
Next();
|
|
if (tok.type == TOK_EOF) {
|
|
throw runtime_error("Empty file");
|
|
}
|
|
root = ParseValue();
|
|
|
|
return root;
|
|
}
|
|
|
|
JSONNode JSONParseFile(char *file) {
|
|
ifstream f(file);
|
|
TS.Init(&f);
|
|
return PS.Parse();
|
|
}
|
|
|
|
JSONNode JSONParseString(std::string string) {
|
|
istringstream f(string);
|
|
TS.Init(&f);
|
|
return PS.Parse();
|
|
}
|