diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | LICENSE | 7 | ||||
-rw-r--r-- | json.cpp | 456 | ||||
-rw-r--r-- | json.hpp | 51 | ||||
-rw-r--r-- | main.cpp | 19 |
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 + @@ -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; +} |