-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathJSON.h
177 lines (164 loc) · 6.84 KB
/
JSON.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#pragma once
#include <string>
#include <fstream>
#include <iostream>
#include <map>
#include <any>
#include <exception>
#include <type_traits>
#include "lexer.h"
#include "types.h"
#include "parser.h"
class JSON {
public:
std::map<std::string, std::any> variables;
std::vector<std::any> rootArray;
std::string text;
std::string fileName;
JSON(std::string fileName);
template <class T>
T get(std::string key) {
std::string raw_key = key;
bool isRootArray = false;
if(key.substr(0, 4) == "(ar)") {
isRootArray = true;
key.erase(0, 4);
}
std::vector<std::string> keys = this->genKeyList(key);
std::any value;
for(int i = 0; i < keys.size(); i++) {
if(i == 0) {
if(!isRootArray) {
if(this->variables[keys[0]].has_value()) value = this->variables[keys[0]];
else throw std::runtime_error("There is no root object. Add \"(ar)\" to your key to read from the root array array.");
}
else {
if(this->rootArray.size() != 0) value = this->rootArray[std::stoi(keys[0])];
else throw std::runtime_error("There is no root array. Remove \"(ar)\" from your key to read from the root object.");
}
}
else {
try {
// for object
value = std::any_cast<std::map<std::string, std::any>>(value)[keys[i]];
} catch(std::bad_any_cast const&) {
// for array
value = std::any_cast<std::vector<std::any>>(value)[std::stoi(keys[i])];
}
}
}
try {
return std::any_cast<T>(value);
} catch(std::bad_any_cast const&) {
std::string type = "unknown or invalid type";
if(std::is_same<T, std::string>()) type = "string";
if(std::is_same<T, float>()) type = "float";
if(std::is_same<T, bool>()) type = "bool";
throw std::runtime_error("Cannot convert data from location " + raw_key + " to type " + type);
}
}
// Modifiying an existing json file
/*
This function modifies the AST and then will modify
a json file out of the values stored in that AST.
A flag is available to determine whether or not the
user would like the json file to be modified (true by default)
*/
template <class T>
void write(std::string key, T value, bool editFile=true) {
/*
convert key into a vector of key segments.
Given the key "(ar)users.0.name" the array of segments is: ["users", "0", "name"]
*/
std::vector<std::string> segs;
segs = this->genKeyList(key);
/*
Modify the node at the key's location in the AST
then rewrite the file's json using the modified AST`
*/
// Gen the AST so that we can modify it
auto ast = Parser(Lexer(this->text)).ast();
this->findAndModAST(ast, segs, value);
// Rewrite the json file
this->text = "";
this->rewriteJSON(ast);
if(editFile) {
// write newly generated text to json file
std::ofstream file;
file.open(this->fileName, std::ofstream::out | std::ofstream::trunc);
file << this->text;
file.close();
}
// refresh the AST so that this->get<>() works
// empty variables and root array
std::map<std::string, std::any> map;
this->variables = map;
std::vector<std::any> rootArray;
this->rootArray = rootArray;
Lexer lexer(this->text);
Parser parser(lexer);
this->visitNode(parser.ast(), this->variables);
}
private:
void generate();
// reading json
void visitNode(std::shared_ptr<AST> node, std::map<std::string, std::any> &map);
std::string visitString(std::shared_ptr<Value> node);
float visitNumber(std::shared_ptr<Value> node);
bool visitBoolean(std::shared_ptr<Value> node);
std::vector<std::any> visitArray(std::shared_ptr<Value> node);
std::vector<std::string> genKeyList(std::string key) {
std::vector<std::string> keys;
std::string word;
for(int i = 0; i < key.size(); i++) {
if(key[i] == '.') {
keys.push_back(word);
word = "";
} else word += key[i];
}
// last word
keys.push_back(word);
return keys;
}
/*
Function takes in an AST, key segments, and a value.
It will then find the node the key (in segmented form)
points to and modify it's value
*/
template <class T>
void findAndModAST(std::shared_ptr<AST> &ast, std::vector<std::string> segs, T value) {
if(ast->ASTtype == "value" && std::dynamic_pointer_cast<Value>(ast)->type == "object") {
for(auto elem : std::dynamic_pointer_cast<Object>(ast)->values) {
if(elem->left->value == segs[0]) {
if(segs.size() > 1) segs.erase(segs.begin());
this->findAndModAST((std::shared_ptr<AST>&)elem, segs, value);
break;
}
}
} else if(ast->ASTtype == "value" && (std::dynamic_pointer_cast<Value>(ast)->type != "object" && std::dynamic_pointer_cast<Value>(ast)->type != "array")) {
ast = this->createNode(value);
} else if(ast->ASTtype == "value" && std::dynamic_pointer_cast<Value>(ast)->type == "array") {
if(std::stoi(segs[0]) < std::dynamic_pointer_cast<Array>(ast)->values.size()) {
if(segs.size() > 1) segs.erase(segs.begin());
this->findAndModAST((std::shared_ptr<AST>&)std::dynamic_pointer_cast<Array>(ast)->values[std::stoi(segs[0])], segs, value);
}
} else if(ast->ASTtype == "assign") {
auto node = std::dynamic_pointer_cast<Assign>(ast);
if(node->right->type == "object" || node->right->type == "array") this->findAndModAST((std::shared_ptr<AST>&)node->right, segs, value);
else if(node->left->value == segs[0]) {
/*
Will add JSON object classes that are user friendly in coming updates so that it is possible
to modify a whole json object/array instead of an individual value
*/
node->right = this->createNode(value);
}
}
}
std::shared_ptr<Value> createNode(std::string value);
std::shared_ptr<Value> createNode(const char* value);
std::shared_ptr<Value> createNode(bool value);
std::shared_ptr<Value> createNode(float value);
std::shared_ptr<Value> createNode(long long int value);
void rewriteJSON(std::shared_ptr<AST> ast);
void writeVal(std::string type, std::shared_ptr<Value> value);
};