diff options
author | Mike Buland <mbuland@penny-arcade.com> | 2017-06-06 12:30:52 -0700 |
---|---|---|
committer | Mike Buland <mbuland@penny-arcade.com> | 2017-06-06 12:30:52 -0700 |
commit | b7751f0136502592e9b1897b51859b1133339a93 (patch) | |
tree | 835b4a78c6b2322039d7b4b70652976bd539a06b /src/unstable | |
parent | 8e0f468e79263dc7f9a6364ca1a26036906642bc (diff) | |
download | libbu++-b7751f0136502592e9b1897b51859b1133339a93.tar.gz libbu++-b7751f0136502592e9b1897b51859b1133339a93.tar.bz2 libbu++-b7751f0136502592e9b1897b51859b1133339a93.tar.xz libbu++-b7751f0136502592e9b1897b51859b1133339a93.zip |
Hey! This is a much better structure for the Json class.
This new setup is one class for everything, the values are kept in a union, and
the instance knows what type it is. The tree of objects is a tree of
instantiations of the same class. It's much simpler, it's much easier to write,
and maybe even easier to use.
Diffstat (limited to 'src/unstable')
-rw-r--r-- | src/unstable/json.cpp | 316 | ||||
-rw-r--r-- | src/unstable/json.h | 113 |
2 files changed, 323 insertions, 106 deletions
diff --git a/src/unstable/json.cpp b/src/unstable/json.cpp index 0769836..c31c7f9 100644 --- a/src/unstable/json.cpp +++ b/src/unstable/json.cpp | |||
@@ -1,87 +1,331 @@ | |||
1 | #include "bu/json.h" | 1 | #include "bu/json.h" |
2 | #include "bu/staticmembuf.h" | ||
2 | 3 | ||
3 | Bu::Json::Base::Base() | 4 | #include <stdlib.h> |
4 | { | ||
5 | } | ||
6 | 5 | ||
7 | Bu::Json::Base::~Base() | 6 | #define next( txt ) readChar( c, sInput, "Unexpected end of stream while reading " txt "." ) |
8 | { | ||
9 | } | ||
10 | 7 | ||
11 | Bu::Json::Object::Object() | 8 | Bu::Json::Json() : |
9 | eType( Invalid ) | ||
12 | { | 10 | { |
13 | } | 11 | } |
14 | 12 | ||
15 | Bu::Json::Object::~Object() | 13 | Bu::Json::Json( const Bu::String &sJson ) : |
14 | eType( Invalid ) | ||
16 | { | 15 | { |
16 | Bu::StaticMemBuf mIn( sJson.getStr(), sJson.getSize() ); | ||
17 | parse( mIn ); | ||
17 | } | 18 | } |
18 | 19 | ||
19 | Bu::Json::Type Bu::Json::Object::getType() | 20 | Bu::Json::Json( Bu::Stream &sInput ) : |
21 | eType( Invalid ) | ||
20 | { | 22 | { |
21 | return tObject; | 23 | parse( sInput ); |
22 | } | 24 | } |
23 | 25 | ||
24 | Bu::Json::Array::Array() | 26 | Bu::Json::~Json() |
25 | { | 27 | { |
28 | reset(); | ||
26 | } | 29 | } |
27 | 30 | ||
28 | Bu::Json::Array::~Array() | 31 | void Bu::Json::parse( Bu::Stream &sInput ) |
29 | { | 32 | { |
30 | } | 33 | reset(); |
31 | 34 | ||
32 | Bu::Json::Type Bu::Json::Array::getType() | 35 | char c; |
33 | { | 36 | for(;;) |
34 | return tArray; | 37 | { |
38 | next( "json" ); | ||
39 | if( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) | ||
40 | continue; | ||
41 | if( c == '"' ) | ||
42 | { | ||
43 | // String | ||
44 | parseString( c, sInput ); | ||
45 | } | ||
46 | else if( c == '{' ) | ||
47 | { | ||
48 | // Object | ||
49 | parseObject( c, sInput ); | ||
50 | } | ||
51 | else if( c == '[' ) | ||
52 | { | ||
53 | // Array | ||
54 | parseArray( c, sInput ); | ||
55 | } | ||
56 | else if( c == '-' || (c >= '0' && c <= '9') ) | ||
57 | { | ||
58 | // Number -- apparently they can't start with a period | ||
59 | parseNumber( c, sInput ); | ||
60 | } | ||
61 | else if( c == 't' || c == 'f' || c == 'n' ) | ||
62 | { | ||
63 | // True / false / null | ||
64 | parseLiteral( c, sInput ); | ||
65 | } | ||
66 | } | ||
35 | } | 67 | } |
36 | 68 | ||
37 | Bu::Json::String::String() | 69 | void Bu::Json::reset() |
38 | { | 70 | { |
39 | } | 71 | switch( eType ) |
72 | { | ||
73 | case Object: | ||
74 | for( JsonHash::iterator i = uDat.pObject->begin(); i; i++ ) | ||
75 | { | ||
76 | delete i.getValue(); | ||
77 | } | ||
78 | delete uDat.pObject; | ||
79 | break; | ||
40 | 80 | ||
41 | Bu::Json::String::~String() | 81 | case Array: |
42 | { | 82 | for( JsonList::iterator i = uDat.pArray->begin(); i; i++ ) |
43 | } | 83 | { |
84 | delete *i; | ||
85 | } | ||
86 | delete uDat.pArray; | ||
87 | break; | ||
44 | 88 | ||
45 | Bu::Json::Type Bu::Json::String::getType() | 89 | case String: |
46 | { | 90 | delete uDat.pString; |
47 | return tString; | 91 | break; |
92 | |||
93 | case Invalid: | ||
94 | case Number: | ||
95 | case Boolean: | ||
96 | case Null: | ||
97 | break; | ||
98 | } | ||
99 | uDat.pObject = NULL; | ||
48 | } | 100 | } |
49 | 101 | ||
50 | Bu::Json::Number::Number() | 102 | void Bu::Json::parseString( char &c, Bu::Stream &sInput, Bu::String &sOut ) |
51 | { | 103 | { |
104 | while( c != '"' ) | ||
105 | { | ||
106 | next( "string" ); | ||
107 | } | ||
108 | bool bEscape = false; | ||
109 | for(;;) | ||
110 | { | ||
111 | next( "string" ); | ||
112 | if( bEscape ) | ||
113 | { | ||
114 | switch( c ) | ||
115 | { | ||
116 | case '"': | ||
117 | case '\\': | ||
118 | case '/': | ||
119 | sOut += c; | ||
120 | break; | ||
121 | |||
122 | case 'b': | ||
123 | sOut += '\b'; | ||
124 | break; | ||
125 | |||
126 | case 'f': | ||
127 | sOut += '\f'; | ||
128 | break; | ||
129 | |||
130 | case 'n': | ||
131 | sOut += '\n'; | ||
132 | break; | ||
133 | |||
134 | case 'r': | ||
135 | sOut += '\r'; | ||
136 | break; | ||
137 | |||
138 | case 't': | ||
139 | sOut += '\t'; | ||
140 | break; | ||
141 | |||
142 | case 'u': | ||
143 | // Not implimented yet, followed by four hex diigts | ||
144 | break; | ||
145 | |||
146 | default: | ||
147 | throw Bu::ExceptionBase( | ||
148 | "Invalid escape sequence encountered in string." | ||
149 | ); | ||
150 | break; | ||
151 | } | ||
152 | bEscape = false; | ||
153 | } | ||
154 | else | ||
155 | { | ||
156 | if( c == '\\' ) | ||
157 | bEscape = true; | ||
158 | if( c == '"' ) | ||
159 | { | ||
160 | break; | ||
161 | } | ||
162 | sOut += c; | ||
163 | } | ||
164 | } | ||
52 | } | 165 | } |
53 | 166 | ||
54 | Bu::Json::Number::~Number() | 167 | void Bu::Json::parseString( char &c, Bu::Stream &sInput ) |
55 | { | 168 | { |
169 | eType = String; | ||
170 | uDat.pString = new Bu::String(); | ||
171 | parseString( c, sInput, *uDat.pString ); | ||
56 | } | 172 | } |
57 | 173 | ||
58 | Bu::Json::Type Bu::Json::Number::getType() | 174 | void Bu::Json::parseObject( char &c, Bu::Stream &sInput ) |
59 | { | 175 | { |
60 | return tNumber; | 176 | while( c != '{' ) |
177 | { | ||
178 | next( "object" ); | ||
179 | } | ||
180 | |||
181 | eType = Object; | ||
182 | uDat.pObject = new JsonHash(); | ||
183 | |||
184 | next( "object" ); | ||
185 | for(;;) | ||
186 | { | ||
187 | Bu::String sKey; | ||
188 | parseString( c, sInput, sKey ); | ||
189 | next( "object" ); | ||
190 | if( c != ':' ) | ||
191 | { | ||
192 | throw Bu::ExceptionBase( | ||
193 | "Invalid json, expected colon after key in object." | ||
194 | ); | ||
195 | } | ||
196 | uDat.pObject->insert( sKey, new Json( sInput ) ); | ||
197 | next( "object" ); | ||
198 | if( c == '}' ) | ||
199 | break; | ||
200 | else if( c == ',' ) | ||
201 | next( "object" ); | ||
202 | else | ||
203 | throw Bu::ExceptionBase( | ||
204 | "Invalid json, expected comma or } after value in object." | ||
205 | ); | ||
206 | } | ||
61 | } | 207 | } |
62 | 208 | ||
63 | Bu::Json::Boolean::Boolean() | 209 | void Bu::Json::parseArray( char &c, Bu::Stream &sInput ) |
64 | { | 210 | { |
211 | while( c != '[' ) | ||
212 | { | ||
213 | next( "array" ); | ||
214 | } | ||
215 | |||
216 | eType = Array; | ||
217 | uDat.pArray = new JsonList(); | ||
218 | |||
219 | next( "array" ); | ||
220 | for(;;) | ||
221 | { | ||
222 | uDat.pArray->append( new Json( sInput ) ); | ||
223 | next( "array" ); | ||
224 | if( c == ']' ) | ||
225 | break; | ||
226 | else if( c == ',' ) | ||
227 | continue; | ||
228 | else | ||
229 | { | ||
230 | throw Bu::ExceptionBase( | ||
231 | "Invalid json, expected comma or ] after value in array." | ||
232 | ); | ||
233 | } | ||
234 | } | ||
65 | } | 235 | } |
66 | 236 | ||
67 | Bu::Json::Boolean::~Boolean() | 237 | void Bu::Json::parseNumber( char &c, Bu::Stream &sInput ) |
68 | { | 238 | { |
239 | while( c != '-' && c < '0' && c > '9' ) | ||
240 | { | ||
241 | next( "number" ); | ||
242 | } | ||
243 | |||
244 | Bu::String sBuf; | ||
245 | if( c == '-' ) | ||
246 | { | ||
247 | sBuf += c; | ||
248 | next( "number" ); | ||
249 | } | ||
250 | bool bIntPart = true; | ||
251 | do | ||
252 | { | ||
253 | if( c >= '0' && c <= '9' ) | ||
254 | sBuf += c; | ||
255 | else if( c == '.' && bIntPart == true ) | ||
256 | { | ||
257 | bIntPart = false; | ||
258 | sBuf += c; | ||
259 | } | ||
260 | else if( c == ' ' || c == '\t' || c == '\n' || c == '\r' || | ||
261 | c == '}' || c == ']' || c == ',' ) | ||
262 | { | ||
263 | break; | ||
264 | } | ||
265 | else | ||
266 | { | ||
267 | throw Bu::ExceptionBase("Invalid character in number."); | ||
268 | } | ||
269 | } while( readChar( c, sInput ) ); | ||
270 | |||
271 | eType = Number; | ||
272 | uDat.dNumber = atof( sBuf.getStr() ); | ||
69 | } | 273 | } |
70 | 274 | ||
71 | Bu::Json::Type Bu::Json::Boolean::getType() | 275 | void Bu::Json::parseLiteral( char &c, Bu::Stream &sInput ) |
72 | { | 276 | { |
73 | return tBoolean; | 277 | while( c != 'f' && c != 't' && c != 'n' ) |
278 | { | ||
279 | next( "literal" ); | ||
280 | } | ||
281 | |||
282 | Bu::String s; | ||
283 | do | ||
284 | { | ||
285 | if( isWs( c ) || c == ',' || c == '}' || c == ']' ) | ||
286 | break; | ||
287 | else | ||
288 | s += c; | ||
289 | } while( readChar( c, sInput ) ); | ||
290 | |||
291 | if( s == "true" ) | ||
292 | { | ||
293 | eType = Boolean; | ||
294 | uDat.bBoolean = true; | ||
295 | } | ||
296 | else if( s == "false" ) | ||
297 | { | ||
298 | eType = Boolean; | ||
299 | uDat.bBoolean = false; | ||
300 | } | ||
301 | else if( s == "null" ) | ||
302 | { | ||
303 | eType = Null; | ||
304 | uDat.pObject = NULL; | ||
305 | } | ||
306 | else | ||
307 | { | ||
308 | throw Bu::ExceptionBase("Invalid literal token found."); | ||
309 | } | ||
74 | } | 310 | } |
75 | 311 | ||
76 | Bu::Json::Null::Null() | 312 | bool Bu::Json::readChar( char &c, Bu::Stream &sInput ) |
77 | { | 313 | { |
314 | if( sInput.read( &c, 1 ) == 0 ) | ||
315 | return false; | ||
316 | return true; | ||
78 | } | 317 | } |
79 | 318 | ||
80 | Bu::Json::Null::~Null() | 319 | void Bu::Json::readChar( char &c, Bu::Stream &sInput, const char *sSection ) |
81 | { | 320 | { |
321 | if( sInput.read( &c, 1 ) == 0 ) | ||
322 | { | ||
323 | throw Bu::ExceptionBase( sSection ); | ||
324 | } | ||
82 | } | 325 | } |
83 | 326 | ||
84 | Bu::Json::Type Bu::Json::Null::getType() | 327 | bool Bu::Json::isWs( char c ) |
85 | { | 328 | { |
86 | return tNull; | 329 | return c == ' ' || c == '\t' || c == '\r' || c == '\n'; |
87 | } | 330 | } |
331 | |||
diff --git a/src/unstable/json.h b/src/unstable/json.h index 660d1c6..47009cb 100644 --- a/src/unstable/json.h +++ b/src/unstable/json.h | |||
@@ -1,89 +1,62 @@ | |||
1 | #ifndef BU_JSON_H | 1 | #ifndef BU_JSON_H |
2 | #define BU_JSON_H | 2 | #define BU_JSON_H |
3 | 3 | ||
4 | #include "bu/hash.h" | ||
5 | #include "bu/list.h" | ||
6 | #include "bu/string.h" | ||
7 | |||
4 | namespace Bu | 8 | namespace Bu |
5 | { | 9 | { |
10 | class Stream; | ||
11 | |||
6 | class Json | 12 | class Json |
7 | { | 13 | { |
8 | public: | 14 | public: |
9 | Json(); | ||
10 | virtual ~Json(); | ||
11 | |||
12 | public: | ||
13 | enum Type | 15 | enum Type |
14 | { | 16 | { |
15 | tObject, | 17 | Invalid, |
16 | tArray, | 18 | Object, |
17 | tString, | 19 | Array, |
18 | tNumber, | 20 | String, |
19 | tBoolean, | 21 | Number, |
20 | tNull | 22 | Boolean, |
21 | }; | 23 | Null |
22 | |||
23 | class Base | ||
24 | { | ||
25 | public: | ||
26 | Base(); | ||
27 | virtual ~Base(); | ||
28 | |||
29 | virtual Type getType()=0; | ||
30 | }; | ||
31 | |||
32 | class Object : public Base | ||
33 | { | ||
34 | public: | ||
35 | Object(); | ||
36 | virtual ~Object(); | ||
37 | |||
38 | virtual Type getType(); | ||
39 | }; | ||
40 | |||
41 | class Array : public Base | ||
42 | { | ||
43 | public: | ||
44 | Array(); | ||
45 | virtual ~Array(); | ||
46 | |||
47 | virtual Type getType(); | ||
48 | }; | 24 | }; |
49 | 25 | ||
50 | class String : public Base | 26 | public: |
51 | { | 27 | Json(); |
52 | public: | 28 | Json( const Bu::String &sJson ); |
53 | String(); | 29 | Json( Bu::Stream &sInput ); |
54 | virtual ~String(); | 30 | virtual ~Json(); |
55 | |||
56 | virtual Type getType(); | ||
57 | }; | ||
58 | |||
59 | class Number : public Base | ||
60 | { | ||
61 | public: | ||
62 | Number(); | ||
63 | virtual ~Number(); | ||
64 | |||
65 | virtual Type getType(); | ||
66 | }; | ||
67 | |||
68 | class Boolean : public Base | ||
69 | { | ||
70 | public: | ||
71 | Boolean(); | ||
72 | virtual ~Boolean(); | ||
73 | |||
74 | virtual Type getType(); | ||
75 | }; | ||
76 | 31 | ||
77 | class Null : public Base | 32 | void parse( Bu::Stream &sInput ); |
78 | { | 33 | void reset(); |
79 | public: | ||
80 | Null(); | ||
81 | virtual ~Null(); | ||
82 | 34 | ||
83 | virtual Type getType(); | 35 | private: |
84 | }; | 36 | void parseString( char &c, Bu::Stream &sInput, Bu::String &sOut ); |
37 | void parseString( char &c, Bu::Stream &sInput ); | ||
38 | void parseObject( char &c, Bu::Stream &sInput ); | ||
39 | void parseArray( char &c, Bu::Stream &sInput ); | ||
40 | void parseNumber( char &c, Bu::Stream &sInput ); | ||
41 | void parseLiteral( char &c, Bu::Stream &sInput ); | ||
42 | bool readChar( char &c, Bu::Stream &sInput ); | ||
43 | void readChar( char &c, Bu::Stream &sInput, const char *sSection ); | ||
44 | bool isWs( char c ); | ||
85 | 45 | ||
86 | private: | 46 | private: |
47 | typedef Bu::Hash<Bu::String, Json *> JsonHash; | ||
48 | typedef Bu::List<Json *> JsonList; | ||
49 | |||
50 | Type eType; | ||
51 | union DatUnion | ||
52 | { | ||
53 | DatUnion() : pObject( NULL ) { } | ||
54 | JsonHash *pObject; | ||
55 | JsonList *pArray; | ||
56 | Bu::String *pString; | ||
57 | double dNumber; | ||
58 | bool bBoolean; | ||
59 | } uDat; | ||
87 | }; | 60 | }; |
88 | }; | 61 | }; |
89 | 62 | ||