nicolas-arnaud
2 years ago
3 changed files with 503 additions and 0 deletions
@ -0,0 +1,131 @@ |
|||||
|
#include <iostream> |
||||
|
#include <map> |
||||
|
#include <sstream> |
||||
|
#include <string> |
||||
|
#include <vector> |
||||
|
|
||||
|
#define DEBUG 0 |
||||
|
|
||||
|
using std::cout; |
||||
|
using std::string; |
||||
|
|
||||
|
class JSONNode; |
||||
|
typedef std::map<string, JSONNode *> JSONObject; |
||||
|
typedef std::vector<JSONNode *> 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; |
||||
|
}; |
@ -0,0 +1,213 @@ |
|||||
|
#include "Token.hpp" |
||||
|
#include <cstdlib> |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
}; |
@ -0,0 +1,159 @@ |
|||||
|
#include "Nodes.hpp" |
||||
|
#include <fstream> |
||||
|
|
||||
|
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; |
||||
|
}; |
||||
|
}; |
Loading…
Reference in new issue