From 98b27bdace7d9307b0a2c5c5c5332082fbcad7d6 Mon Sep 17 00:00:00 2001 From: nicolas-arnaud Date: Sun, 6 Nov 2022 20:54:34 +0100 Subject: [PATCH] missing json files --- srcs/json/Nodes.hpp | 131 ++++++++++++++++++++++++++ srcs/json/Parser.hpp | 213 +++++++++++++++++++++++++++++++++++++++++++ srcs/json/Token.hpp | 159 ++++++++++++++++++++++++++++++++ 3 files changed, 503 insertions(+) create mode 100644 srcs/json/Nodes.hpp create mode 100644 srcs/json/Parser.hpp create mode 100644 srcs/json/Token.hpp diff --git a/srcs/json/Nodes.hpp b/srcs/json/Nodes.hpp new file mode 100644 index 0000000..0a756a4 --- /dev/null +++ b/srcs/json/Nodes.hpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include + +#define DEBUG 0 + +using std::cout; +using std::string; + +class JSONNode; +typedef std::map JSONObject; +typedef std::vector JSONList; + +class JSONNode { + public: + JSONObject obj() { + if (type == OBJECT) + return *values.object; + throw std::logic_error("Improper return"); + } + JSONList lst() { + if (type == LIST) + return *values.list; + throw std::logic_error("Improper return"); + } + string str() { + if (type == STRING) + return *values.str; + throw std::logic_error("Improper return"); + } + int nbr() { + if (type == NUMBER) + return values.nbr; + throw std::logic_error("Improper return"); + } + + private: + enum Type { OBJECT, LIST, STRING, NUMBER, BOOLEAN, NULL_TYPE }; + union Values { + JSONObject *object; + JSONList *list; + string *str; + int nbr; + bool bValue; + } values; + Type type; + + void setObject(JSONObject *object) { + this->values.object = object; + type = OBJECT; + } + void setList(JSONList *list) { + this->values.list = list; + type = LIST; + } + void setString(string *str) { + this->values.str = str; + type = STRING; + } + void setNumber(int nbr) { + this->values.nbr = nbr; + type = NUMBER; + } + void setBoolean(bool v) { + this->values.bValue = v; + type = BOOLEAN; + } + void setNull() { type = NULL_TYPE; } + + string stringify(int indentationLevel) { + string spaceString = string(indentationLevel, ' '); + // sstreams + std::stringstream output; + // cout < type << "\n"; + switch (type) { + case STRING: { + output << spaceString << *values.str; + break; + } + case NUMBER: { + output << spaceString << values.nbr; + break; + } + case BOOLEAN: { + output << spaceString << (values.bValue ? "true" : "false"); + break; + } + case NULL_TYPE: { + output << spaceString << "null"; + break; + } + case LIST: { + // cout << "["; + output << spaceString << "[\n"; + unsigned int index = 0; + for (JSONList::iterator i = (*values.list).begin(); + i != (*values.list).end(); i++) { + output << (*i)->stringify(indentationLevel + 1); + if (index < (*values.list).size() - 1) { + output << ",\n"; + } + index++; + }; + output << "\n" << spaceString << "]\n"; + break; + } + case OBJECT: { + output << spaceString << "{\n"; + for (JSONObject::iterator i = (*values.object).begin(); + i != (*values.object).end(); i++) { + output << spaceString << " " + << "\"" << i->first << "\"" + << ": "; + output << i->second->stringify(indentationLevel + 1); + JSONObject::iterator next = i; + next++; + if ((next) != (*values.object).end()) { + output << ",\n"; + } + output << spaceString << "\n"; + } + output << spaceString << "}"; + return output.str(); + } + } + return output.str(); + } + friend class JSONParser; +}; diff --git a/srcs/json/Parser.hpp b/srcs/json/Parser.hpp new file mode 100644 index 0000000..a024071 --- /dev/null +++ b/srcs/json/Parser.hpp @@ -0,0 +1,213 @@ +#include "Token.hpp" +#include + +class JSONParser { + std::fstream file; + JSONNode *root; + JSONNode *current; + Tokenizer tokenizer; + + public: + JSONParser(const string filename) : tokenizer(filename) {} + JSONNode *parse() { + string key = ""; + JSONNode *parsed; + while (tokenizer.hasMoreTokens()) { + Token token; + try { + token = tokenizer.getToken(); + switch (token.type) { + case CURLY_OPEN: { + parsed = parseObject(); + break; + } + case ARRAY_OPEN: { + parsed = parseList(); + break; + } + case STRING: { + tokenizer.rollBackToken(); + parsed = parseString(); + break; + } + case NUMBER: { + tokenizer.rollBackToken(); + parsed = parseNumber(); + break; + } + case BOOLEAN: { + tokenizer.rollBackToken(); + parsed = parseBoolean(); + break; + } + default: + break; + } + } catch (std::logic_error &e) { + break; + } + } + return parsed; + } + + JSONNode *parseObject() { + JSONNode *node = new JSONNode; + JSONObject *keyObjectMap = new JSONObject; + while (1) { + if (tokenizer.hasMoreTokens()) { + Token token = tokenizer.getToken(); + string key = token.value; + tokenizer.getToken(); + token = tokenizer.getToken(); + switch (token.type) { + case CURLY_OPEN: { + if (DEBUG) + cout << "=object=|" << key << "|===>>\n"; + (*keyObjectMap)[key] = parseObject(); + if (DEBUG) + cout << "<<===end object\n"; + break; + } + case ARRAY_OPEN: { + if (DEBUG) + cout << "-list-|" << key << "|--->>\n"; + (*keyObjectMap)[key] = parseList(); + if (DEBUG) + cout << "<<---end list\n"; + break; + } + case STRING: { + tokenizer.rollBackToken(); + (*keyObjectMap)[key] = parseString(); + if (DEBUG) + cout << key << "='" << (*keyObjectMap)[key]->str() + << "'\n"; + break; + } + case NUMBER: { + tokenizer.rollBackToken(); + (*keyObjectMap)[key] = parseNumber(); + if (DEBUG) + cout << key << "=" << (*keyObjectMap)[key]->nbr() + << "\n"; + break; + } + case BOOLEAN: { + tokenizer.rollBackToken(); + (*keyObjectMap)[key] = parseBoolean(); + if (DEBUG) + cout << key << "(BOOL)\n"; + break; + } + case NULL_TYPE: { + (*keyObjectMap)[key] = parseNull(); + break; + } + default: + break; + } + token = tokenizer.getToken(); + if (token.type == CURLY_CLOSE) + break; + + } else { + throw std::logic_error("No more tokens"); + } + } + node->setObject(keyObjectMap); + return node; + } + + JSONNode *parseList() { + JSONNode *node = new JSONNode(); + JSONList *list = new JSONList(); + bool hasCompleted = false; + while (!hasCompleted) { + if (!tokenizer.hasMoreTokens()) { + throw std::logic_error("No more tokens"); + } else { + Token token = tokenizer.getToken(); + JSONNode *subNode; + switch (token.type) { + case CURLY_OPEN: { + if (DEBUG) + cout << "=object===>>\n"; + subNode = parseObject(); + if (DEBUG) + cout << "<<===end object\n"; + break; + } + case ARRAY_OPEN: { + if (DEBUG) + cout << "-list--->>\n"; + subNode = parseList(); + if (DEBUG) + cout << "<<---end list\n"; + break; + } + case STRING: { + tokenizer.rollBackToken(); + subNode = parseString(); + if (DEBUG) + cout << "|'" << subNode->str() << "'"; + break; + } + case NUMBER: { + tokenizer.rollBackToken(); + subNode = parseNumber(); + if (DEBUG) + cout << "|" << subNode->nbr(); + break; + } + case BOOLEAN: { + tokenizer.rollBackToken(); + subNode = parseBoolean(); + break; + } + case NULL_TYPE: { + subNode = parseNull(); + break; + } + default: + break; + } + list->push_back(subNode); + token = tokenizer.getToken(); + if (token.type == ARRAY_CLOSE) { + hasCompleted = true; + } + } + } + node->setList(list); + return node; + } + + JSONNode *parseString() { + JSONNode *node = new JSONNode(); + Token token = tokenizer.getToken(); + string *sValue = new string(token.value); + node->setString(sValue); + return node; + } + + JSONNode *parseNumber() { + JSONNode *node = new JSONNode(); + Token token = tokenizer.getToken(); + string value = token.value; + int nbr = std::atoi(value.c_str()); + node->setNumber(nbr); + return node; + } + JSONNode *parseBoolean() { + JSONNode *node = new JSONNode(); + Token token = tokenizer.getToken(); + node->setBoolean(token.value == "True" ? true : false); + return node; + } + + JSONNode *parseNull() { + JSONNode *node = new JSONNode(); + node->setNull(); + return node; + } +}; diff --git a/srcs/json/Token.hpp b/srcs/json/Token.hpp new file mode 100644 index 0000000..43a3ccd --- /dev/null +++ b/srcs/json/Token.hpp @@ -0,0 +1,159 @@ +#include "Nodes.hpp" +#include + +enum TOKEN { + CURLY_OPEN, + CURLY_CLOSE, + COLON, + STRING, + NUMBER, + ARRAY_OPEN, + ARRAY_CLOSE, + COMMA, + BOOLEAN, + NULL_TYPE +}; + +typedef struct Token_s { + string value; + TOKEN type; + string toString() { + switch (type) { + case CURLY_OPEN: { + return "Curly open"; + } + case CURLY_CLOSE: { + return "Curly close"; + } + case COLON: { + return "COLON"; + } + case NUMBER: { + return "Number: " + value; + } + case STRING: { + return "String: " + value; + } + + case ARRAY_OPEN: { + return "Array open"; + } + case ARRAY_CLOSE: { + return "Array close"; + } + case COMMA: { + return "Comma"; + } + case BOOLEAN: { + return "Boolean: " + value; + } + + case NULL_TYPE: { + return "Null"; + } + default: + return "Other..."; + } + } +} Token; + +class Tokenizer { + std::fstream file; + size_t prevPos; + + public: + Tokenizer(string fileName) { + file.open(fileName.c_str(), std::ios::in); + if (!file.good()) + cout << "File open error" + << "\n"; + }; + bool hasMoreTokens() { return !file.eof(); } + char getWithoutWhiteSpace() { + char c = ' '; + while ((c == ' ' || c == '\n') || c == '\t') { + file.get(c); // check + + if ((c == ' ' || c == '\n') && !file.good()) { + // cout << file.eof() << " " << file.fail() << "\n"; + throw std::logic_error("Ran out of tokens"); + } else if (!file.good()) { + return c; + } + } + + return c; + }; + void rollBackToken() { + if (file.eof()) + file.clear(); + file.seekg(prevPos); + }; + Token getToken() { + char c; + if (file.eof()) { + cout << "Exhaused tokens" + << "\n"; + // throw std::exception("Exhausted tokens"); + } + prevPos = file.tellg(); + c = getWithoutWhiteSpace(); + + Token token; + token.type = NULL_TYPE; + if (c == '"') { + token.type = STRING; + token.value = ""; + file.get(c); + while (c != '"') { + token.value += c; + file.get(c); + } + } else if (c == '{') { + token.type = CURLY_OPEN; + } else if (c == '}') { + token.type = CURLY_CLOSE; + } else if (c == '-' || (c >= '0' && c <= '9')) { + // Check if string is numeric + token.type = NUMBER; + token.value = ""; + token.value += c; + std::streampos prevCharPos = file.tellg(); + while ((c == '-') || (c >= '0' && c <= '9') || c == '.') { + prevCharPos = file.tellg(); + file.get(c); + + if (file.eof()) { + break; + } else { + if ((c == '-') || (c >= '0' && c <= '9') || (c == '.')) { + token.value += c; + } else { + file.seekg(prevCharPos); + } + } + } + } else if (c == 'f') { + token.type = BOOLEAN; + token.value = "False"; + file.seekg(4, std::ios_base::cur); + } else if (c == 't') { + token.type = BOOLEAN; + token.value = "True"; + file.seekg(3, std::ios_base::cur); + } else if (c == 'n') { + token.type = NULL_TYPE; + file.seekg(3, std::ios_base::cur); + } else if (c == '[') { + token.type = ARRAY_OPEN; + } else if (c == ']') { + token.type = ARRAY_CLOSE; + } else if (c == ':') { + token.type = COLON; + } else if (c == ',') { + token.type = COMMA; + } + // cout << token.type << " : " << token.value << "\n"; + return token; + }; +};