MARLEY (Model of Argon Reaction Low Energy Yields)  v1.2.0
A Monte Carlo event generator for tens-of-MeV neutrino interactions
JSON.hh
1 //
5 // This file is part of MARLEY (Model of Argon Reaction Low Energy Yields)
6 //
7 // MARLEY is free software: you can redistribute it and/or modify it under the
8 // terms of version 3 of the GNU General Public License as published by the
9 // Free Software Foundation.
10 //
11 // For the full text of the license please see COPYING or
12 // visit http://opensource.org/licenses/GPL-3.0
13 //
14 // Please respect the MCnet academic usage guidelines. See GUIDELINES
15 // or visit https://www.montecarlonet.org/GUIDELINES for details.
16 
17 // Based on https://github.com/nbsdx/SimpleJSON
18 
19 #pragma once
20 
21 // standard library includes
22 #include <cctype>
23 #include <cmath>
24 #include <cstdint>
25 #include <deque>
26 #include <initializer_list>
27 #include <iostream>
28 #include <istream>
29 #include <ostream>
30 #include <limits>
31 #include <sstream>
32 #include <map>
33 #include <string>
34 #include <type_traits>
35 
36 // MARLEY includes
37 #include "marley/marley_utils.hh"
38 #include "marley/Error.hh"
39 #include "marley/Logger.hh"
40 
41 namespace marley {
42 
43  namespace {
44  std::string json_escape( const std::string& str ) {
45  std::string output;
46  for( unsigned i = 0; i < str.length(); ++i )
47  switch( str[i] ) {
48  case '\"': output += "\\\""; break;
49  case '\\': output += "\\\\"; break;
50  case '\b': output += "\\b"; break;
51  case '\f': output += "\\f"; break;
52  case '\n': output += "\\n"; break;
53  case '\r': output += "\\r"; break;
54  case '\t': output += "\\t"; break;
55  default : output += str[i]; break;
56  }
57  return output;
58  }
59  }
60 
61  class JSON
62  {
63  union Data {
64  Data(double d) : float_(d) {}
65  Data(long l) : integer_(l) {}
66  Data(bool b) : boolean_(b) {}
67  Data(const std::string& s) : string_(new std::string(s)) {}
68  Data() : integer_(0) {}
69 
70  std::deque<JSON>* list_;
71  std::map<std::string, JSON>* map_;
72  std::string* string_;
73  double float_;
74  long integer_;
75  bool boolean_;
76  } data_;
77 
78  public:
79  enum class DataType {
80  Null,
81  Object,
82  Array,
83  String,
84  Floating,
85  Integral,
86  Boolean
87  };
88 
89  template <typename Container> class JSONWrapper {
90 
91  private:
92  Container* object;
93 
94  public:
95  JSONWrapper(Container* val) : object(val) {}
96  JSONWrapper(std::nullptr_t) : object(nullptr) {}
97 
98  typename Container::iterator begin() {
99  return object ? object->begin() : typename Container::iterator();
100  }
101  typename Container::iterator end() {
102  return object ? object->end() : typename Container::iterator();
103  }
104  typename Container::const_iterator begin() const {
105  return object ? object->begin() : typename Container::iterator();
106  }
107  typename Container::const_iterator end() const {
108  return object ? object->end() : typename Container::iterator();
109  }
110  };
111 
112  template <typename Container>
114 
115  private:
116  const Container* object;
117 
118  public:
119  JSONConstWrapper(const Container* val) : object(val) {}
120  JSONConstWrapper(std::nullptr_t) : object(nullptr) {}
121 
122  typename Container::const_iterator begin() const
123  { return object ? object->begin()
124  : typename Container::const_iterator(); }
125  typename Container::const_iterator end() const
126  { return object ? object->end()
127  : typename Container::const_iterator(); }
128  };
129 
130  JSON() : data_(), type_(DataType::Null) {}
131 
132  JSON(std::initializer_list<JSON> list) : JSON()
133  {
134  set_type(DataType::Object);
135  for(auto i = list.begin(), e = list.end(); i != e; ++i, ++i)
136  operator[](i->to_string()) = *std::next(i);
137  }
138 
139  JSON(JSON&& other) : data_(other.data_), type_(other.type_)
140  { other.type_ = DataType::Null; other.data_.map_ = nullptr; }
141 
142  JSON& operator=(JSON&& other) {
143  data_ = other.data_;
144  type_ = other.type_;
145  other.data_.map_ = nullptr;
146  other.type_ = DataType::Null;
147  return *this;
148  }
149 
150  JSON(const JSON& other) {
151  switch(other.type_) {
152  case DataType::Object:
153  data_.map_ = new std::map<std::string,JSON>(
154  other.data_.map_->begin(), other.data_.map_->end());
155  break;
156  case DataType::Array:
157  data_.list_ = new std::deque<JSON>(other.data_.list_->begin(),
158  other.data_.list_->end());
159  break;
160  case DataType::String:
161  data_.string_ = new std::string(*other.data_.string_);
162  break;
163  default:
164  data_ = other.data_;
165  }
166  type_ = other.type_;
167  }
168 
169  JSON& operator=(const JSON& other) {
170  switch(other.type_) {
171  case DataType::Object:
172  data_.map_ = new std::map<std::string,JSON>(
173  other.data_.map_->begin(), other.data_.map_->end());
174  break;
175  case DataType::Array:
176  data_.list_ = new std::deque<JSON>( other.data_.list_->begin(),
177  other.data_.list_->end());
178  break;
179  case DataType::String:
180  data_.string_ = new std::string(*other.data_.string_);
181  break;
182  default:
183  data_ = other.data_;
184  }
185  type_ = other.type_;
186  return *this;
187  }
188 
189  ~JSON() {
190  switch(type_) {
191  case DataType::Array:
192  delete data_.list_;
193  break;
194  case DataType::Object:
195  delete data_.map_;
196  break;
197  case DataType::String:
198  delete data_.string_;
199  break;
200  default:;
201  }
202  }
203 
204  template <typename T> JSON(T b,
205  typename std::enable_if<std::is_same<T,bool>::value>::type* = 0)
206  : data_(b), type_(DataType::Boolean) {}
207 
208  template <typename T> JSON(T i,
209  typename std::enable_if<std::is_integral<T>::value
210  && !std::is_same<T,bool>::value>::type* = 0)
211  : data_(static_cast<long>(i)), type_(DataType::Integral) {}
212 
213  template <typename T> JSON(T f,
214  typename std::enable_if<std::is_floating_point<T>::value>::type* = 0)
215  : data_(static_cast<double>(f)), type_(DataType::Floating) {}
216 
217  explicit JSON(const std::string& s)
218  : data_(s), type_(DataType::String) {}
219 
220  //template <typename T> JSON(T s,
221  // typename std::enable_if<std::is_convertible<T,
222  // std::string>::value>::type* = 0) : data_(std::string(s)),
223  // type_(DataType::String) {}
224 
225  JSON(std::nullptr_t) : data_(), type_(DataType::Null) {}
226 
227  static inline JSON make(DataType type) {
228  JSON ret;
229  ret.set_type(type);
230  return ret;
231  }
232 
233  static inline JSON array() {
234  return JSON::make(JSON::DataType::Array);
235  }
236 
237  template <typename... T>
238  static JSON array( T... args )
239  {
240  JSON arr = JSON::make(JSON::DataType::Array);
241  arr.append(args...);
242  return arr;
243  }
244 
245  static inline JSON object() {
246  return JSON::make(JSON::DataType::Object);
247  }
248 
249  static inline JSON load(const std::string& s);
250  static inline JSON load(std::istream& is);
251  static inline JSON load_file(const std::string& s);
252 
253  template <typename T> void append(T arg) {
254  set_type(DataType::Array);
255  data_.list_->emplace_back(arg);
256  }
257 
258  template <typename T, typename... U> void append(T arg, U... args) {
259  append(arg); append(args...);
260  }
261 
262  template <typename T>
263  typename std::enable_if<std::is_same<T,bool>::value, JSON&>::type
264  operator=(T b)
265  {
266  set_type(DataType::Boolean);
267  data_.boolean_ = b;
268  return *this;
269  }
270 
271  template <typename T> typename std::enable_if<std::is_integral<T>::value
272  && !std::is_same<T,bool>::value, JSON&>::type operator=(T i)
273  {
274  set_type( DataType::Integral );
275  data_.integer_ = i;
276  return *this;
277  }
278 
279  template <typename T>
280  typename std::enable_if<std::is_floating_point<T>::value, JSON&>::type
281  operator=(T f)
282  {
283  set_type(DataType::Floating);
284  data_.float_ = f;
285  return *this;
286  }
287 
288  template <typename T> typename std::enable_if<std::is_convertible<T,
289  std::string>::value, JSON&>::type operator=(T s)
290  {
291  set_type(DataType::String);
292  *data_.string_ = std::string(s);
293  return *this;
294  }
295 
296  JSON& operator[](const std::string& key) {
297  set_type(DataType::Object);
298  return data_.map_->operator[](key);
299  }
300 
301  JSON& operator[](unsigned index) {
302  set_type(DataType::Array);
303  if (index >= data_.list_->size()) data_.list_->resize(index + 1);
304  return data_.list_->operator[](index);
305  }
306 
307  JSON& at(const std::string& key) {
308  return operator[](key);
309  }
310 
311  const JSON& at(const std::string &key) const {
312  return data_.map_->at(key);
313  }
314 
315  JSON& at(unsigned index) {
316  return operator[](index);
317  }
318 
319  const JSON& at(unsigned index) const {
320  return data_.list_->at(index);
321  }
322 
323  int length() const {
324  if (type_ == DataType::Array) return data_.list_->size();
325  else return -1;
326  }
327 
328  bool has_key(const std::string& key) const {
329  if (type_ == DataType::Object)
330  return data_.map_->find( key ) != data_.map_->end();
331  else return false;
332  }
333 
334  int size() const {
335  if (type_ == DataType::Object)
336  return data_.map_->size();
337  else if (type_ == DataType::Array)
338  return data_.list_->size();
339  else
340  return -1;
341  }
342 
343  inline DataType type() const { return type_; }
344 
346  inline bool is_null() const { return type_ == DataType::Null; }
347  inline bool is_object() const { return type_ == DataType::Object; }
348  inline bool is_array() const { return type_ == DataType::Array; }
349  inline bool is_string() const { return type_ == DataType::String; }
350  inline bool is_float() const { return type_ == DataType::Floating; }
351  inline bool is_integer() const { return type_ == DataType::Integral; }
352  inline bool is_bool() const { return type_ == DataType::Boolean; }
353 
354  std::string to_string() const {
355  bool b;
356  return to_string(b);
357  }
358 
359  std::string to_string(bool& ok) const {
360  ok = (type_ == DataType::String);
361  return ok ? json_escape(*data_.string_) : std::string("");
362  }
363 
364  std::string to_string_or_throw() const {
365  bool ok;
366  std::string result = to_string(ok);
367  if (!ok) throw marley::Error("Failed to convert JSON value to string");
368  return result;
369  }
370 
371  double to_double() const {
372  bool b;
373  return to_double(b);
374  }
375 
376  double to_double(bool& ok) const {
377  ok = (type_ == DataType::Floating);
378  if (ok) return data_.float_;
379  ok = (type_ == DataType::Integral);
380  if (ok) return data_.integer_;
381  return 0.;
382  }
383 
384  double to_double_or_throw() const {
385  bool ok;
386  double result = to_double(ok);
387  if (!ok) throw marley::Error("Failed to convert JSON value '"
388  + to_string() + "' to double");
389  return result;
390  }
391 
392  long to_long() const {
393  bool b;
394  return to_long( b );
395  }
396 
397  long to_long(bool& ok) const {
398  ok = (type_ == DataType::Integral);
399  return ok ? data_.integer_ : 0;
400  }
401 
402  long to_long_or_throw() const {
403  bool ok;
404  double result = to_long(ok);
405  if (!ok) throw marley::Error("Failed to convert JSON value '"
406  + to_string() + "' to long");
407  return result;
408  }
409 
410  bool to_bool() const {
411  bool b;
412  return to_bool( b );
413  }
414 
415  bool to_bool(bool& ok) const {
416  ok = (type_ == DataType::Boolean);
417  return ok ? data_.boolean_ : false;
418  }
419 
420  bool to_bool_or_throw() const {
421  bool ok;
422  double result = to_bool(ok);
423  if (!ok) throw marley::Error("Failed to convert JSON value '"
424  + to_string() + "' to bool");
425  return result;
426  }
427 
428  JSONWrapper<std::map<std::string,JSON> > object_range() {
429  if (type_ == DataType::Object)
430  return JSONWrapper<std::map<std::string,JSON>>(data_.map_);
431  else return JSONWrapper<std::map<std::string,JSON>>(nullptr);
432  }
433 
434  JSONWrapper<std::deque<JSON> > array_range() {
435  if (type_ == DataType::Array)
436  return JSONWrapper<std::deque<JSON>>(data_.list_);
437  else return JSONWrapper<std::deque<JSON>>(nullptr);
438  }
439 
440  JSONConstWrapper<std::map<std::string,JSON> > object_range() const {
441  if (type_ == DataType::Object)
442  return JSONConstWrapper<std::map<std::string,JSON>>(data_.map_);
443  else return JSONConstWrapper<std::map<std::string,JSON>>(nullptr);
444  }
445 
446 
447  JSONConstWrapper<std::deque<JSON>> array_range() const {
448  if ( type_ == DataType::Array )
449  return JSONConstWrapper<std::deque<JSON>>(data_.list_);
450  else return JSONConstWrapper<std::deque<JSON>>(nullptr);
451  }
452 
453  // Portions of the serialization functions (dump_string, print)
454  // are based on techniques used in the JSON for Modern C++
455  // library by Niels Lohmann (https://github.com/nlohmann/json).
456  std::string dump_string(const int indent_step = -1) const {
457  std::stringstream out;
458  // Enable pretty-printing if the user specified a nonnegative
459  // indent_step value
460  if (indent_step >= 0)
461  print(out, static_cast<unsigned int>(indent_step), true);
462  // Otherwise, print the JSON object in the most compact form possible
463  else print(out, 0, false);
464 
465  // Return the completed JSON string
466  return out.str();
467  }
468 
469  // Implementation of serialization to text. Used by the public
470  // dump_string() method.
471  void print(std::ostream& out, const unsigned int indent_step,
472  bool pretty, const unsigned int current_indent = 0) const
473  {
474  // Use max_digits10 for outputting double-precision floating-point
475  // numbers. This ensures that repeated input/output via JSON will
476  // not result in any loss of precision. For more information, please
477  // see http://tinyurl.com/p8wyhnn
478  static std::ostringstream out_float;
479  static bool set_precision = false;
480  if (!set_precision) {
481  out_float.precision(std::numeric_limits<double>::max_digits10);
482  set_precision = true;
483  }
484 
485  unsigned int indent = current_indent;
486 
487  switch( type_ ) {
488  case DataType::Null:
489  out << "null";
490  return;
491  case DataType::Object: {
492  out << '{';
493  if (pretty) {
494  indent += indent_step;
495  out << '\n';
496  }
497  bool skip = true;
498  for( auto &p : *data_.map_ ) {
499  if ( !skip ) {
500  out << ',';
501  if (pretty) out << '\n';
502  }
503 
504  out << std::string(indent, ' ') << '\"'
505  << json_escape( p.first ) << '\"';
506 
507  if (pretty) out << " : ";
508  else out << ':';
509 
510  p.second.print( out, indent_step, pretty, indent );
511  skip = false;
512  }
513  if (pretty) {
514  indent -= indent_step;
515  out << '\n';
516  }
517  out << std::string(indent, ' ') + '}';
518  return;
519  }
520  case DataType::Array: {
521  out << '[';
522  if (pretty) {
523  indent += indent_step;
524  out << '\n';
525  }
526  bool skip = true;
527  for( auto &p : *data_.list_ ) {
528  if ( !skip ) {
529  out << ',';
530  if (pretty) out << '\n';
531  }
532  out << std::string(indent, ' ');
533  p.print( out, indent_step, pretty, indent );
534  skip = false;
535  }
536  if (pretty) {
537  indent -= indent_step;
538  out << '\n';
539  }
540  out << std::string(indent, ' ') << ']';
541  return;
542  }
543  case DataType::String:
544  out << '\"' + json_escape( *data_.string_ ) + '\"';
545  return;
546  case DataType::Floating:
547  // Clear any previous contents of the stringstream
548  out_float.str("");
549  out_float.clear();
550  // Fill it with the new floating-point number
551  out_float << data_.float_;
552  // Output the resulting string to the stream
553  out << out_float.str();
554  return;
555  case DataType::Integral:
556  out << data_.integer_;
557  return;
558  case DataType::Boolean:
559  out << (data_.boolean_ ? "true" : "false");
560  return;
561  default:
562  break;
563  }
564 
565  return;
566  }
567 
568  private:
569 
570  void check_if_object(const std::string& key) {
571  if (type_ != DataType::Object) throw marley::Error("Attempted"
572  " to retrieve a value for the key '" + key + " from a JSON primitive"
573  " that is not an object");
574  }
575 
576  void set_type(DataType type) {
577  if (type == type_) return;
578 
579  switch(type_) {
580  case DataType::Object:
581  delete data_.map_;
582  break;
583  case DataType::Array:
584  delete data_.list_;
585  break;
586  case DataType::String:
587  delete data_.string_;
588  break;
589  default:;
590  }
591 
592  switch(type) {
593  case DataType::Null:
594  data_.map_ = nullptr;
595  break;
596  case DataType::Object:
597  data_.map_ = new std::map<std::string, JSON>();
598  break;
599  case DataType::Array:
600  data_.list_ = new std::deque<JSON>();
601  break;
602  case DataType::String:
603  data_.string_ = new std::string();
604  break;
605  case DataType::Floating:
606  data_.float_ = 0.;
607  break;
608  case DataType::Integral:
609  data_.integer_ = 0;
610  break;
611  case DataType::Boolean:
612  data_.boolean_ = false;
613  break;
614  }
615 
616  type_ = type;
617  }
618 
619  public:
620 
621  // Attempts to get a floating point number from a JSON object with
622  // a given key. If the attempt fails, throw a marley::Error.
623  double get_double(const std::string& key) {
624  check_if_object(key);
625  if (has_key(key)) return this->at(key).to_double_or_throw();
626  else throw marley::Error("Missing JSON key '" + key + '\'');
627  return 0.;
628  }
629 
630  // Attempts to get a floating point number from a JSON object with
631  // a given key. If the key doesn't exist, use a default value. If a
632  // conversion attempt fails, throw a marley::Error.
633  double get_double(const std::string& key, double default_value) {
634  check_if_object(key);
635  if (!has_key(key)) return default_value;
636  else return this->at(key).to_double_or_throw();
637  }
638 
639  // Attempts to get an integer from a JSON object with
640  // a given key. If the attempt fails, throw a marley::Error.
641  long get_long(const std::string& key) {
642  check_if_object(key);
643  if (has_key(key)) return this->at(key).to_long_or_throw();
644  else throw marley::Error("Missing JSON key '" + key + '\'');
645  return 0.;
646  }
647 
648  // Attempts to get an integer from a JSON object with
649  // a given key. If the key doesn't exist, use a default value. If a
650  // conversion attempt fails, throw a marley::Error.
651  long get_long(const std::string& key, long default_value) {
652  check_if_object(key);
653  if (!has_key(key)) return default_value;
654  else return this->at(key).to_long_or_throw();
655  }
656 
657  // Attempts to get a bool from a JSON object with
658  // a given key. If the attempt fails, throw a marley::Error.
659  bool get_bool(const std::string& key) {
660  check_if_object(key);
661  if (has_key(key)) return this->at(key).to_bool_or_throw();
662  else throw marley::Error("Missing JSON key '" + key + '\'');
663  return 0.;
664  }
665 
666  // Attempts to get a bool from a JSON object with
667  // a given key. If the key doesn't exist, use a default value. If a
668  // conversion attempt fails, throw a marley::Error.
669  bool get_bool(const std::string& key, bool default_value) {
670  check_if_object(key);
671  if (!has_key(key)) return default_value;
672  else return this->at(key).to_bool_or_throw();
673  }
674 
675  // Attempts to get a string from a JSON object with
676  // a given key. If the attempt fails, throw a marley::Error.
677  std::string get_string(const std::string& key) {
678  check_if_object(key);
679  if (has_key(key)) return this->at(key).to_string_or_throw();
680  else throw marley::Error("Missing JSON key '" + key + '\'');
681  return std::string("");
682  }
683 
684  // Attempts to get a string from a JSON object with
685  // a given key. If the key doesn't exist, use a default value. If a
686  // conversion attempt fails, throw a marley::Error.
687  std::string get_string(const std::string& key,
688  const std::string& default_value)
689  {
690  check_if_object(key);
691  if (!has_key(key)) return default_value;
692  else return this->at(key).to_string_or_throw();
693  }
694 
695  // Copies a subobject from a JSON object with a given key. If the attempt
696  // fails, throw a marley::Error, unless the user asks us not to do so.
697  marley::JSON get_object(const std::string& key, bool throw_error = true)
698  {
699  check_if_object(key);
700  if (has_key(key)) return this->at(key);
701  else if (throw_error) marley::Error("Missing JSON key '" + key + '\'');
702  return JSON::make(JSON::DataType::Object);
703  }
704 
705  private:
706 
707  DataType type_ = DataType::Null;
708  };
709 
710  namespace {
711 
712  JSON parse_next(std::istream&);
713 
714  void issue_parse_error(char found_char, const std::string& message)
715  {
716  std::string msg(message);
717  if (found_char == std::ifstream::traits_type::eof())
718  msg += "end-of-file";
719  else msg += std::string("\'") + found_char + '\'';
720  //MARLEY_LOG_WARNING() << msg;
721  throw marley::Error( msg );
722  }
723 
724  void issue_parse_error(const std::string& found_str,
725  const std::string& message, const std::istream& is)
726  {
727  std::string msg(message);
728  if (!is) msg += "end-of-file";
729  else msg += '\'' + found_str + '\'';
730  //MARLEY_LOG_WARNING() << msg;
731  throw marley::Error( msg );
732  }
733 
734  // Skips single-line comments // and multi-line comments /* */
735  // These are technically not valid in JSON (the standard doesn't allow
736  // comments), but they are valid in Javascript object literals.
737  void skip_comment(std::istream& in, bool is_multiline = false) {
738  if (is_multiline) {
739  char c;
740  while (in.get(c)) {
741  if (c == '*' && in.peek() == '/') {
742  in.ignore();
743  break;
744  }
745  }
746  }
747  // Ignore all further characters until either a newline or end-of-file
748  else in.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
749  }
750 
751  // Skips whitespace and comments, saving the last character read to
752  // read_char.
753  void skip_ws(std::istream& in, char& read_char) {
754  while (read_char = in.get(), std::isspace(read_char)) continue;
755  if (read_char == '/') {
756  char c = in.peek();
757  if (c == '/' || c == '*') {
758  read_char = in.get();
759  skip_comment(in, c == '*');
760  return skip_ws(in, read_char);
761  }
762  }
763  }
764 
765  // Removes whitespace and comments from the input stream, putting back
766  // the first non-whitespace and non-comment character it finds.
767  void consume_ws(std::istream& in) {
768  static char next;
769  skip_ws(in, next);
770  in.putback(next);
771  }
772 
773  // Removes whitespace and comments from the input stream, returning the
774  // first non-whitespace and non-comment character it finds.
775  char get_next_char(std::istream& in)
776  {
777  static char next;
778  skip_ws(in, next);
779  return next;
780  }
781 
782  JSON parse_object(std::istream& in) {
783 
784  JSON object = JSON::make(JSON::DataType::Object);
785 
786  for (;;) {
787 
788  consume_ws(in);
789  JSON key;
790 
791  if ( in.peek() == '}' ) {
792  in.ignore();
793  return object;
794  }
795  else if ( in.peek() == '\"' ) {
796  key = parse_next(in);
797  }
798  // The key isn't quoted, so assume it's a single word followed
799  // by a colon. Note that vanilla JSON requires all keys to be quoted,
800  // but Javascript object literals allow unquoted keys.
801  else {
802  std::string key_str;
803  char c;
804  while (in.get(c)) {
805  if (c == ':' || std::isspace(c)) {
806  in.putback(c);
807  break;
808  }
809  key_str += c;
810  }
811  key = key_str;
812  }
813 
814  char next = get_next_char(in);
815  if ( next != ':' ) {
816  issue_parse_error(next, "JSON object: Expected colon, found ");
817  break;
818  }
819 
820  consume_ws(in);
821  JSON value = parse_next(in);
822  object[key.to_string()] = value;
823 
824  next = get_next_char(in);
825  if ( next == ',' ) continue;
826  else if ( next == '}' ) break;
827  else {
828  issue_parse_error(next, "JSON object: Expected comma, found ");
829  break;
830  }
831  }
832 
833  return object;
834  }
835 
836  JSON parse_array(std::istream& in) {
837  JSON array = JSON::make(JSON::DataType::Array);
838  unsigned index = 0;
839 
840  for (;;) {
841 
842  consume_ws(in);
843  if (in.peek() == ']') {
844  in.ignore();
845  return array;
846  }
847 
848  array[index++] = parse_next(in);
849  consume_ws(in);
850 
851  char next = in.get();
852  if (next == ',') continue;
853  else if (next == ']') break;
854  else {
855  issue_parse_error(next, "JSON array: Expected ',' or ']'"
856  ", found ");
857  return JSON::make(JSON::DataType::Array);
858  }
859  }
860 
861  return array;
862  }
863 
864  JSON parse_string(std::istream& in) {
865  JSON str;
866  std::string val;
867  for(char c = in.get(); c != '\"' && in; c = in.get()) {
868  if (c == '\\') {
869  switch( in.get() ) {
870  case '\"': val += '\"'; break;
871  case '\\': val += '\\'; break;
872  case '/' : val += '/' ; break;
873  case 'b' : val += '\b'; break;
874  case 'f' : val += '\f'; break;
875  case 'n' : val += '\n'; break;
876  case 'r' : val += '\r'; break;
877  case 't' : val += '\t'; break;
878  case 'u' : {
879  val += "\\u" ;
880  for(unsigned i = 1; i <= 4; ++i) {
881  c = in.get();
882  if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
883  || (c >= 'A' && c <= 'F')) val += c;
884  else {
885  issue_parse_error(c, "JSON string: Expected hex character"
886  " in unicode escape, found ");
887  return JSON::make(JSON::DataType::String);
888  }
889  }
890  break;
891  }
892  default: val += '\\'; break;
893  }
894  }
895  else val += c;
896  }
897  str = val;
898  return str;
899  }
900 
901  //FIXME
902  JSON parse_number(std::istream& in, char old) {
903  JSON Number;
904  std::string val, exp_str;
905  char c = old;
906  bool isDouble = false;
907  long exp = 0;
908  for (;;) {
909  if ( (c == '-') || (c >= '0' && c <= '9') )
910  val += c;
911  else if ( c == '.' ) {
912  val += c;
913  isDouble = true;
914  }
915  else
916  break;
917  c = in.get();
918  }
919  if ( c == 'E' || c == 'e' ) {
920  if ( in.peek() == '-' ){ in.ignore(); exp_str += '-';}
921  for (;;) {
922  c = in.get();
923  if ( c >= '0' && c <= '9' )
924  exp_str += c;
925  else if ( !std::isspace( c ) && c != ',' && c != ']' && c != '}' ) {
926  issue_parse_error(c, "JSON number: Expected a number for"
927  " exponent, found ");
928  return JSON::make(JSON::DataType::Null);
929  }
930  else
931  break;
932  }
933  exp = std::stol( exp_str );
934  }
935  else if ( !std::isspace( c ) && c != ',' && c != ']' && c != '}' ) {
936  issue_parse_error(c, "JSON number: unexpected character ");
937  return JSON::make(JSON::DataType::Null);
938  }
939  in.putback(c);
940 
941  if ( isDouble )
942  Number = std::stod( val ) * std::pow( 10, exp );
943  else {
944  if ( !exp_str.empty() )
945  Number = std::stol( val ) * std::pow( 10, exp );
946  else
947  Number = std::stol( val );
948  }
949  return Number ;
950  }
951 
952  JSON parse_bool(std::istream& in, char old) {
953  JSON b;
954  std::string s(1, old);
955  if (old == 't') {
956  for (size_t i = 0; i < 3; ++i) s += in.get();
957  if (s == "true") b = true;
958  }
959  else if (old == 'f') {
960  for (size_t i = 0; i < 4; ++i) s += in.get();
961  if (s == "false") b = false;
962  }
963  if (b.type() == JSON::DataType::Null) {
964  // Get the entire string if the user supplied an invalid value
965  while (in.good() && !std::isspace(in.peek())) s += in.get();
966  marley_utils::trim_inplace(s);
967 
968  issue_parse_error(s, "JSON bool: Expected 'true' or 'false', found ",
969  in);
970  return JSON::make(JSON::DataType::Null);
971  }
972  return b;
973  }
974 
975  JSON parse_null(std::istream& in) {
976  JSON null;
977  std::string s(1, 'n');
978  for (size_t i = 0; i < 3; ++i) s += in.get();
979  if ( s != "null") {
980  issue_parse_error("JSON null: Expected 'null', found ", s, in);
981  return JSON::make(JSON::DataType::Null);
982  }
983  return null;
984  }
985 
986  JSON parse_next(std::istream& in) {
987  char value = get_next_char(in);
988  switch(value) {
989  case '[' : return parse_array(in);
990  case '{' : return parse_object(in);
991  case '\"': return parse_string(in);
992  case 't' :
993  case 'f' : return parse_bool(in, value);
994  case 'n' : return parse_null(in);
995  default :
996  if ((value <= '9' && value >= '0') || value == '-')
997  return parse_number(in, value);
998  }
999  // Complain and throw an error if there was a problem
1000  if (!in) throw marley::Error("Unexpected end of JSON configuration"
1001  " file found\n");
1002  else throw marley::Error(std::string("JSON parse:")
1003  + " Unknown starting character '" + value + "'\n");
1004  return JSON();
1005  }
1006  }
1007 
1008  inline JSON JSON::load_file(const std::string& filename) {
1009  std::ifstream in(filename);
1010  if (in.good()) return load(in);
1011  else {
1012  throw marley::Error("Could not open the file \"" + filename + "\"");
1013  return JSON::make(JSON::DataType::Null);
1014  }
1015  }
1016 
1017  inline JSON JSON::load(std::istream& in) {
1018  char first = get_next_char(in);
1019  if (first != '{') {
1020  throw marley::Error("Missing '{' at beginning of JSON object");
1021  in.putback(first);
1022  return parse_object(in);
1023  }
1024  else {
1025  in.putback(first);
1026  return parse_next(in);
1027  }
1028  }
1029 
1030  inline JSON JSON::load(const std::string& str) {
1031  std::stringstream iss(str);
1032  return load(iss);
1033  }
1034 
1035 }
1036 
1037 // Stream operators for JSON input and output using C++ streams
1038 inline std::ostream& operator<<(std::ostream &os, const marley::JSON& json) {
1039  os << json.dump_string();
1040  return os;
1041 }
1042 
1043 inline std::istream& operator>>(std::istream &is, marley::JSON& json) {
1044  json = marley::JSON::load(is);
1045  return is;
1046 }
Base class for all exceptions thrown by MARLEY functions.
Definition: Error.hh:32
Definition: JSON.hh:113
Definition: JSON.hh:89
Definition: JSON.hh:62
bool is_null() const
Functions for getting primitives from the JSON object.
Definition: JSON.hh:346