1 // Author: Hong Jiang <hong@hjiang.net>
2 /*
3 source: http://github.com/hjiang/jsonxx/
4 
5 Copyright (c) 2010 Hong Jiang
6 
7 Permission is hereby granted, free of charge, to any person
8 obtaining a copy of this software and associated documentation
9 files (the "Software"), to deal in the Software without
10 restriction, including without limitation the rights to use,
11 copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the
13 Software is furnished to do so, subject to the following
14 conditions:
15 
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 OTHER DEALINGS IN THE SOFTWARE.
27 
28 */
29 
30 #include "jsonxx.h"
31 
32 #include <cctype>
33 #include <iostream>
34 #include <sstream>
35 
36 #include <math.h>
37 
38 #include "log.h"
39 
40 namespace jsonxx {
41 
eat_whitespaces(std::istream & input)42 void eat_whitespaces(std::istream& input) {
43     char ch;
44     do {
45         input.get(ch);
46     } while(isspace(ch));
47     input.putback(ch);
48 }
49 
50 // Try to consume characters from the input stream and match the
51 // pattern string. Leading whitespaces from the input are ignored if
52 // ignore_ws is true.
match(const std::string & pattern,std::istream & input,bool ignore_ws)53 bool match(const std::string& pattern, std::istream& input,
54            bool ignore_ws) {
55     if (ignore_ws) {
56         eat_whitespaces(input);
57     }
58     std::string::const_iterator cur(pattern.begin());
59     char ch(0);
60     while(input && !input.eof() && cur != pattern.end()) {
61         input.get(ch);
62         if (ch != *cur) {
63             input.putback(ch);
64             return false;
65         } else {
66             cur++;
67         }
68     }
69     return cur == pattern.end();
70 }
71 
parse_string(std::istream & input,std::string * value)72 bool parse_string(std::istream& input, std::string* value) {
73     if (!match("\"", input))  {
74         return false;
75     }
76     char ch;
77     while(!input.eof() && input.good()) {
78         input.get(ch);
79         if (ch == '"' ) {
80             break;
81         }
82 	if (ch == '\\') {
83 	  if (input.eof())
84 	    return false;
85 	  char ch1;
86 	  input.get(ch1);
87 	  switch (ch1) {
88 	  case '"':
89 	  case '\\':
90 	  case '/': value->push_back(ch1); break;
91 	  case 'b': value->push_back('\b'); break;
92 	  case 'f': value->push_back('\f'); break;
93 	  case 'n': value->push_back('\n'); break;
94 	  case 'r': value->push_back('\r'); break;
95 	  case 't': value->push_back('\t'); break;
96 	  case 'u':
97             value->push_back('\\');
98             value->push_back('u');
99             break;
100 	  default: return false;
101 	  }
102 	}
103         else value->push_back(ch);
104     }
105     if (input && ch == '"') {
106         return true;
107     } else {
108         return false;
109     }
110 }
111 
parse_bool(std::istream & input,bool * value)112 bool parse_bool(std::istream& input, bool* value) {
113     if (match("true", input))  {
114         *value = true;
115         return true;
116     }
117     if (match("false", input)) {
118         *value = false;
119         return true;
120     }
121     return false;
122 }
123 
parse_null(std::istream & input)124 bool parse_null(std::istream& input) {
125     if (match("null", input))  {
126         return true;
127     }
128     return false;
129 }
130 
131   /*
132 bool parse_float(std::istream& input, double* value) {
133     eat_whitespaces(input);
134     char ch;
135     bool has_dot = false;
136     std::string value_str;
137     int sign = 1;
138     if (match("-", input)) {
139         sign = -1;
140     } else {
141         match("+", input);
142     }
143     while(input && !input.eof()) {
144         input.get(ch);
145 	if (ch=='.')
146 	  has_dot = true;
147         if (!isdigit(ch) && (!(ch == '.'))) {
148             input.putback(ch);
149             break;
150         }
151         value_str.push_back(ch);
152     }
153     if (!has_dot) {
154       for (std::string::reverse_iterator r_it=
155 	     value_str.rbegin(); r_it != value_str.rend(); r_it++)
156 	input.putback(*r_it);
157       return false;
158     }
159     if (value_str.size() > 0) {
160         std::istringstream(value_str) >> *value;
161 	*value*=sign;
162         return true;
163     } else {
164         return false;
165     }
166 }
167   */
168 
169 
170 // bool parse_number(std::istream& input, long* value) {
171 //     eat_whitespaces(input);
172 //     char ch;
173 //     std::string value_str;
174 //     int sign = 1;
175 //     if (match("-", input)) {
176 //         sign = -1;
177 //     } else {
178 //         match("+", input);
179 //     }
180 //     while(input && !input.eof()) {
181 //         input.get(ch);
182 //         if (!isdigit(ch)) {
183 //             input.putback(ch);
184 //             break;
185 //         }
186 //         value_str.push_back(ch);
187 //     }
188 //     if (value_str.size() > 0) {
189 //         std::istringstream(value_str) >> *value;
190 // 	*value*=sign;
191 //         return true;
192 //     } else {
193 //         return false;
194 //     }
195 // }
196 
197 
parse_number(std::istream & input,long * value)198 bool parse_number(std::istream& input, long* value) {
199     eat_whitespaces(input);
200     char ch;
201     std::string value_str;
202     std::string exp_str; // whole exp part
203     std::string exp_value_str; // value of exp part
204     int sign = 1;
205     int e_sign = 1;
206     bool correct = true;
207     long e_value;
208 
209     enum {
210       p_number,
211       p_e,
212       p_enumber
213     } p_state = p_number;
214 
215     if (match("-", input)) {
216         sign = -1;
217     } else {
218         match("+", input);
219     }
220 
221     while(input && !input.eof()) {
222       input.get(ch);
223       switch (p_state) {
224       case p_number: {
225 	// DBG("st = p_number, ch=%c\n",ch);
226 	if (ch == 'E' || ch == 'e') {
227 	  exp_str.push_back(ch);
228 	  p_state = p_e;
229 	  continue;
230 	}
231 	if (!isdigit(ch)) {
232 	  input.putback(ch);
233 	  correct = false;
234 	  break;
235 	}
236 	value_str.push_back(ch);
237       } break;
238 
239       case p_e: {
240 	// DBG("st = p_e, ch=%c\n",ch);
241 
242 	if (ch == '+') {
243 	  exp_str.push_back(ch);
244 	  p_state = p_enumber;
245 	} else if (ch == '-') {
246 	  e_sign = -1;
247 	  exp_str.push_back(ch);
248 	  p_state = p_enumber;
249 	} else if (isdigit(ch)) {
250 	  exp_value_str.push_back(ch);
251 	  exp_str.push_back(ch);
252 	  p_state = p_enumber;
253 	} else {
254 	  input.putback(ch);
255 	  correct = false;
256 	}
257       } break;
258 
259       case p_enumber: {
260 	// DBG("st = p_enumber, ch=%c\n",ch);
261 
262 	if (isdigit(ch)) {
263 	  exp_value_str.push_back(ch);
264 	  exp_str.push_back(ch);
265 	} else {
266 	  input.putback(ch);
267 	  correct = false;
268 	}
269       } break;
270 
271       }
272 
273       if (!correct)
274 	break;
275     }
276 
277     if (p_state == p_e) {
278       // todo: check also some other error states
279       for (std::string::reverse_iterator r_it=
280     	     exp_str.rbegin(); r_it != exp_str.rend(); r_it++)
281     	input.putback(*r_it);
282       for (std::string::reverse_iterator r_it=
283     	     value_str.rbegin(); r_it != value_str.rend(); r_it++)
284     	input.putback(*r_it);
285       return false;
286     }
287 
288     if (value_str.size() > 0) {
289         std::istringstream(value_str) >> *value;
290 	*value*=sign;
291 
292 	if (exp_value_str.size()) {
293 	  std::istringstream(exp_value_str) >> e_value;
294 
295 	  if (e_value && e_sign==-1) {
296 	    // should have been catched by parse_float
297 	    for (std::string::reverse_iterator r_it=
298 		   exp_str.rbegin(); r_it != exp_str.rend(); r_it++)
299 	      input.putback(*r_it);
300 	    for (std::string::reverse_iterator r_it=
301 		   value_str.rbegin(); r_it != value_str.rend(); r_it++)
302 	      input.putback(*r_it);
303 
304 	    return false;
305 	  }
306 	  *value *= pow(10, e_value);
307 	}
308 
309         return true;
310     } else {
311         return false;
312     }
313 }
314 
315 
parse_float(std::istream & input,double * value)316 bool parse_float(std::istream& input, double* value) {
317     eat_whitespaces(input);
318     char ch;
319     std::string value_str;
320     std::string exp_str; // whole exp part
321     std::string exp_value_str; // value of exp part
322     int sign = 1;
323     int e_sign = 1;
324     bool correct = true;
325     int e_value;
326     bool has_dot = false;
327 
328     enum {
329       p_number,
330       p_e,
331       p_enumber
332     } p_state = p_number;
333 
334     if (match("-", input)) {
335         sign = -1;
336     } else {
337         match("+", input);
338     }
339 
340     while(input && !input.eof()) {
341       input.get(ch);
342       bool end = false;
343       switch (p_state) {
344       case p_number: {
345 	 // DBG("st = p_number, ch=%c\n",ch);
346 	if (ch == 'E' || ch == 'e') {
347 	  exp_str.push_back(ch);
348 	  p_state = p_e;
349 	  continue;
350 	}
351 	if (ch == '.') {
352 	  if (has_dot) {
353 	    correct = false;
354 	    break;
355 	  }
356 	  has_dot = true;
357 	  value_str.push_back(ch);
358 	  continue;
359 	}
360 
361 	if (!isdigit(ch)) {
362 	  input.putback(ch);
363 	  end = true;
364 	  break;
365 	}
366 	value_str.push_back(ch);
367       } break;
368 
369       case p_e: {
370 	 // DBG("st = p_e, ch=%c\n",ch);
371 	if (ch == '+') {
372 	  exp_str.push_back(ch);
373 	  p_state = p_enumber;
374 	} else if (ch == '-') {
375 	  e_sign = -1;
376 	  exp_str.push_back(ch);
377 	  p_state = p_enumber;
378 	} else if (isdigit(ch)) {
379 	  exp_value_str.push_back(ch);
380 	  exp_str.push_back(ch);
381 	  p_state = p_enumber;
382 	} else {
383 	  input.putback(ch);
384 	  correct = false;
385 	}
386       } break;
387 
388       case p_enumber: {
389 	// DBG("st = p_enumber, ch=%c\n",ch);
390 
391 	if (isdigit(ch)) {
392 	  exp_value_str.push_back(ch);
393 	  exp_str.push_back(ch);
394 	} else {
395 	  input.putback(ch);
396 	  end = true;
397 	}
398       } break;
399 
400       }
401 
402       if (end || !correct)
403 	break;
404     }
405 
406     // DBG("correct = %s, has_dot = %s, e_sign = %d, exp_value_str.size() = %zd\n",
407     // 	correct?"true":"false",has_dot?"true":"false",e_sign,exp_value_str.size());
408     if (!correct || (!has_dot && !(e_sign == -1 && exp_value_str.size())) || p_state == p_e) {
409       // todo: check also some other error states
410       for (std::string::reverse_iterator r_it=
411     	     exp_str.rbegin(); r_it != exp_str.rend(); r_it++)
412     	input.putback(*r_it);
413       for (std::string::reverse_iterator r_it=
414     	     value_str.rbegin(); r_it != value_str.rend(); r_it++)
415     	input.putback(*r_it);
416       return false;
417     }
418 
419     if (value_str.size() > 0) {
420         std::istringstream(value_str) >> *value;
421 	*value*=sign;
422 
423 	if (exp_value_str.size()) {
424 	  std::istringstream(exp_value_str) >> e_value;
425 
426 	  *value *= pow(10, e_sign*e_value);
427 	}
428 
429         return true;
430     } else {
431         return false;
432     }
433 }
434 
Object()435 Object::Object() : value_map_() {}
436 
~Object()437 Object::~Object() {
438     std::map<std::string, Value*>::iterator i;
439     for (i = value_map_.begin(); i != value_map_.end(); ++i) {
440         delete i->second;
441     }
442 }
443 
parse(std::istream & input)444 bool Object::parse(std::istream& input) {
445     if (!match("{", input)) {
446         return false;
447     }
448 
449     do {
450         std::string key;
451         if (!parse_string(input, &key)) {
452             return false;
453         }
454         if (!match(":", input)) {
455             return false;
456         }
457         Value* v = new Value();
458         if (!v->parse(input)) {
459             delete v;
460             break;
461         }
462         value_map_[key] = v;
463     } while (match(",", input));
464 
465     if (!match("}", input)) {
466         return false;
467     }
468     return true;
469 }
470 
Value()471 Value::Value() : type_(INVALID_) {}
472 
~Value()473 Value::~Value() {
474     if (type_ == STRING_) {
475         delete string_value_;
476     }
477     if (type_ == OBJECT_) {
478         delete object_value_;
479     }
480     if (type_ == ARRAY_) {
481         delete array_value_;
482     }
483 }
484 
parse(std::istream & input)485 bool Value::parse(std::istream& input) {
486     std::string string_value;
487     if (parse_string(input, &string_value)) {
488         string_value_ = new std::string();
489         string_value_->swap(string_value);
490         type_ = STRING_;
491         return true;
492     }
493     if (parse_number(input, &integer_value_)) {
494         type_ = INTEGER_;
495         return true;
496     }
497 
498     if (parse_bool(input, &bool_value_)) {
499         type_ = BOOL_;
500         return true;
501     }
502     if (parse_null(input)) {
503         type_ = NULL_;
504         return true;
505     }
506     array_value_ = new Array();
507     if (array_value_->parse(input)) {
508         type_ = ARRAY_;
509         return true;
510     }
511     delete array_value_;
512     object_value_ = new Object();
513     if (object_value_->parse(input)) {
514         type_ = OBJECT_;
515         return true;
516     }
517     delete object_value_;
518     return false;
519 }
520 
Array()521 Array::Array() : values_() {}
522 
~Array()523 Array::~Array() {
524     for (unsigned int i = 0; i < values_.size(); ++i) {
525         delete values_[i];
526     }
527 }
528 
parse(std::istream & input)529 bool Array::parse(std::istream& input) {
530     if (!match("[", input)) {
531         return false;
532     }
533 
534     do {
535         Value* v = new Value();
536         if (!v->parse(input)) {
537             delete v;
538             break;
539         }
540         values_.push_back(v);
541     } while (match(",", input));
542 
543     if (!match("]", input)) {
544         return false;
545     }
546     return true;
547 }
548 
549 }  // namespace jsonxx
550