summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--LICENSE7
-rw-r--r--json.cpp456
-rw-r--r--json.hpp51
-rw-r--r--main.cpp19
5 files changed, 536 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6fb51a5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+json.exe
+json
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c9f0ea7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,7 @@
+Copyright (C) 2023 Slendi
+This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>
+
diff --git a/json.cpp b/json.cpp
new file mode 100644
index 0000000..c1f5d54
--- /dev/null
+++ b/json.cpp
@@ -0,0 +1,456 @@
+#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();
+}
diff --git a/json.hpp b/json.hpp
new file mode 100644
index 0000000..70c8fb4
--- /dev/null
+++ b/json.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <vector>
+#include <string>
+
+enum JSONNodeType {
+ JSON_UNKNOWN = -1,
+ JSON_OBJECT,
+ JSON_LIST,
+ JSON_KEY,
+ JSON_NUMBER,
+ JSON_STRING,
+};
+
+struct JSONKeyValue;
+struct JSONNode {
+ JSONNodeType type;
+ union {
+ long double ld;
+ char *s;
+ std::vector<JSONNode> *list;
+ std::vector<JSONKeyValue> *object;
+ } v;
+
+ JSONNode &operator[](std::string key);
+ JSONNode &operator[](size_t index);
+
+ void Free(void);
+
+ long double &Number(void);
+ long double Number(long double def);
+ char *&String(void);
+ char *String(char *def);
+ std::vector<JSONNode> *&List(void);
+ std::vector<JSONKeyValue> *&Object(void);
+
+ std::string const Name(void);
+
+ void PrettyPrint(std::string indent, bool last);
+};
+
+struct JSONKeyValue {
+ char *name;
+ JSONNode node;
+
+ void PrettyPrint(std::string indent, bool last);
+};
+
+JSONNode JSONParseFile(char *file);
+JSONNode JSONParseString(std::string string);
+
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..9ba2920
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,19 @@
+#include "json.hpp"
+
+#include <iostream>
+
+int main(int argc, char *argv[]) {
+ if (argc < 2) {
+ std::cout
+ << "You didnt specify a fucking JSON file you dipshit you absolute "
+ "bafoon you absolute niggercattle loving CIA piece of shit."
+ << std::endl;
+ }
+
+ JSONNode root = JSONParseFile(argv[1]);
+ root.PrettyPrint("", true);
+
+ root.Free();
+
+ return 0;
+}