as2js: /home/snapwebsites/snapcpp/contrib/as2js/lib/json.cpp Source File

as2js  0.1.14
AlexScript to JavaScript
json.cpp
Go to the documentation of this file.
1 /* lib/json.cpp
2 
3 Copyright (c) 2005-2019 Made to Order Software Corp. All Rights Reserved
4 
6 
7 Permission is hereby granted, free of charge, to any
8 person obtaining a copy of this software and
9 associated documentation files (the "Software"), to
10 deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify,
12 merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom
14 the Software is furnished to do so, subject to the
15 following conditions:
16 
17 The above copyright notice and this permission notice
18 shall be included in all copies or substantial
19 portions of the Software.
20 
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
22 ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
23 LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
24 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
28 ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 SOFTWARE.
31 
32 */
33 
34 #include "as2js/json.h"
35 
36 #include "as2js/exceptions.h"
37 #include "as2js/message.h"
38 
39 #include <iomanip>
40 
41 
87 namespace as2js
88 {
89 
95 namespace
96 {
97 
108 void append_string(String& result, String const& str)
109 {
110  result += '"';
111  size_t const max_chars(str.length());
112  for(size_t idx(0); idx < max_chars; ++idx)
113  {
114  switch(str[idx])
115  {
116  case '\b':
117  result += '\\';
118  result += 'b';
119  break;
120 
121  case '\f':
122  result += '\\';
123  result += 'f';
124  break;
125 
126  case '\n':
127  result += '\\';
128  result += 'n';
129  break;
130 
131  case '\r':
132  result += '\\';
133  result += 'r';
134  break;
135 
136  case '\t':
137  result += '\\';
138  result += 't';
139  break;
140 
141  case '"':
142  result += '\\';
143  result += '"';
144  break;
145 
146  // Escaping a single quote (') is not valid JSON
147  //case '\'':
148  // result += '\\';
149  // result += '\'';
150  // break;
151 
152  default:
153  if(str[idx] < 0x0020)
154  {
155  // other controls must be escaped using Unicode
156  std::stringstream ss;
157  ss << std::hex << "\\u" << std::setfill('0') << std::setw(4) << static_cast<int>(str[idx]);
158  result += ss.str().c_str();
159  }
160  else
161  {
162  result += str[idx];
163  }
164  break;
165 
166  }
167  }
168  result += '"';
169 }
170 
171 }
172 // no name namespace
173 
174 
196  : f_value(const_cast<JSONValue&>(value))
197 {
198  if(f_value.f_saving)
199  {
200  throw exception_cyclical_structure("JSON cannot stringify a set of objects and arrays which are cyclical");
201  }
202  f_value.f_saving = true;
203 }
204 
205 
219 {
220  f_value.f_saving = false;
221 }
222 
223 
224 
225 
236  : f_type(type_t::JSON_TYPE_NULL)
237  , f_position(position)
238 {
239 }
240 
241 
255 JSON::JSONValue::JSONValue(Position const &position, Int64 integer)
256  : f_type(type_t::JSON_TYPE_INT64)
257  , f_position(position)
258  , f_integer(integer)
259 {
260 }
261 
262 
276 JSON::JSONValue::JSONValue(Position const &position, Float64 floating_point)
277  : f_type(type_t::JSON_TYPE_FLOAT64)
278  , f_position(position)
279  , f_float(floating_point)
280 {
281 }
282 
283 
296 JSON::JSONValue::JSONValue(Position const &position, String const& string)
297  : f_type(type_t::JSON_TYPE_STRING)
298  , f_position(position)
299  , f_string(string)
300 {
301 }
302 
303 
319 JSON::JSONValue::JSONValue(Position const &position, bool boolean)
320  : f_type(boolean ? type_t::JSON_TYPE_TRUE : type_t::JSON_TYPE_FALSE)
321  , f_position(position)
322 {
323 }
324 
325 
338 JSON::JSONValue::JSONValue(Position const &position, array_t const& array)
339  : f_type(type_t::JSON_TYPE_ARRAY)
340  , f_position(position)
341  , f_array(array)
342 {
343 }
344 
345 
358 JSON::JSONValue::JSONValue(Position const &position, object_t const& object)
359  : f_type(type_t::JSON_TYPE_OBJECT)
360  , f_position(position)
361  , f_object(object)
362 {
363 }
364 
365 
395 {
396  return f_type;
397 }
398 
399 
416 {
418  {
419  throw exception_internal_error("get_int64() called with a non-int64 value type");
420  }
421  return f_integer;
422 }
423 
424 
441 {
443  {
444  throw exception_internal_error("get_float64() called with a non-float64 value type");
445  }
446  return f_float;
447 }
448 
449 
461 {
463  {
464  throw exception_internal_error("get_string() called with a non-string value type");
465  }
466  return f_string;
467 }
468 
469 
487 {
489  {
490  throw exception_internal_error("get_array() called with a non-array value type");
491  }
492  return f_array;
493 }
494 
495 
526 {
528  {
529  throw exception_internal_error("set_item() called with a non-array value type");
530  }
531  if(idx > f_array.size())
532  {
533  throw exception_index_out_of_range("JSON::JSONValue::set_item() called with an index out of bounds");
534  }
535  if(!value)
536  {
537  throw exception_invalid_data("JSON::JSONValue::set_item() called with a null pointer as the value");
538  }
539  if(idx == f_array.size())
540  {
541  // append value
542  f_array.push_back(value);
543  }
544  else
545  {
546  // replace previous value
547  f_array[idx] = value;
548  }
549 }
550 
551 
569 {
571  {
572  throw exception_internal_error("get_object() called with a non-object value type");
573  }
574  return f_object;
575 }
576 
577 
611 {
613  {
614  throw exception_internal_error("set_member() called with a non-object value type");
615  }
616  if(name.empty())
617  {
618  // TBD: is that really not allowed?
619  throw exception_invalid_index("JSON::JSONValue::set_member() called with an empty string as the member name");
620  }
621 
622  // this one is easy enough
623  if(value)
624  {
625  // add/replace
626  f_object[name] = value;
627  }
628  else
629  {
630  // remove
631  f_object.erase(name);
632  }
633 }
634 
635 
650 {
651  return f_position;
652 }
653 
654 
672 {
673  String result;
674 
675  switch(f_type)
676  {
678  result += "[";
679  if(f_array.size() > 0)
680  {
681  saving_t s(*this);
682  result += f_array[0]->to_string(); // recursive
683  size_t const max_elements(f_array.size());
684  for(size_t i(1); i < max_elements; ++i)
685  {
686  result += ",";
687  result += f_array[i]->to_string(); // recursive
688  }
689  }
690  result += "]";
691  break;
692 
694  return "false";
695 
697  {
698  Float64 f(f_float.get());
699  if(f.is_NaN())
700  {
701  return "NaN";
702  }
703  if(f.is_positive_infinity())
704  {
705  return "Infinity";
706  }
707  if(f.is_negative_infinity())
708  {
709  return "-Infinity";
710  }
711  return std::to_string(f_float.get());
712  }
713 
715  return std::to_string(f_integer.get());
716 
718  return "null";
719 
721  result += "{";
722  if(f_object.size() > 0)
723  {
724  saving_t s(*this);
725  object_t::const_iterator obj(f_object.begin());
726  append_string(result, obj->first);
727  result += ":";
728  result += obj->second->to_string(); // recursive
729  for(++obj; obj != f_object.end(); ++obj)
730  {
731  result += ",";
732  append_string(result, obj->first);
733  result += ":";
734  result += obj->second->to_string(); // recursive
735  }
736  }
737  result += "}";
738  break;
739 
741  append_string(result, f_string);
742  break;
743 
745  return "true";
746 
748  throw exception_internal_error("JSON type \"Unknown\" is not valid and should never be used (it should not be possible to use it to create a JSONValue in the first place!)"); // LCOV_EXCL_LINE
749 
750  }
751 
752  return result;
753 }
754 
755 
756 
757 
766 {
767  Position pos;
768  pos.set_filename(filename);
769 
770  // we could not find this module, try to load the it
772  if(!in->open(filename))
773  {
775  msg << "cannot open JSON file \"" << filename << "\".";
776  // should we throw here?
777  return JSONValue::pointer_t();
778  }
779 
780  return parse(in);
781 }
782 
783 
805 {
806  // Parse the JSON file
807  //
808  // Note:
809  // We do not allow external options because it does not make sense
810  // (i.e. JSON is very simple and no additional options should affect
811  // the lexer!)
812  Options::pointer_t options(new Options);
813  // Make sure it is marked as JSON (line terminators change in this case)
814  options->set_option(Options::option_t::OPTION_JSON, 1);
815  f_lexer.reset(new Lexer(in, options));
816  f_value = read_json_value(f_lexer->get_next_token());
817 
818  if(!f_value)
819  {
821  msg << "could not interpret this JSON input \"" << in->get_position().get_filename() << "\".";
822  // Alexis: should we throw here?
823  // Doug: YES!!!!
824  throw exception_invalid_data(msg.str());
825  }
826 
827  f_lexer.reset(); // release 'in' and 'options' pointers
828 
829  return f_value;
830 }
831 
832 
864 {
865  if(n->get_type() == Node::node_t::NODE_EOF)
866  {
868  msg << "the end of the file was reached while reading JSON data.";
869  return JSONValue::pointer_t();
870  }
871  switch(n->get_type())
872  {
874  // positive number...
875  n = f_lexer->get_next_token();
876  switch(n->get_type())
877  {
879  return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_float64()));
880 
882  return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_int64()));
883 
884  default:
886  msg << "unexpected token (" << n->get_type_name() << ") found after a '+' sign, a number was expected.";
887  return JSONValue::pointer_t();
888 
889  }
890  /*NOT_REACHED*/
891  break;
892 
894  return JSONValue::pointer_t(new JSONValue(n->get_position(), false));
895 
897  return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_float64()));
898 
900  return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_int64()));
901 
903  return JSONValue::pointer_t(new JSONValue(n->get_position()));
904 
905  case Node::node_t::NODE_OPEN_CURVLY_BRACKET: // read an object
906  {
908 
909  Position pos(n->get_position());
910  n = f_lexer->get_next_token();
911  if(n->get_type() != Node::node_t::NODE_CLOSE_CURVLY_BRACKET)
912  {
913  for(;;)
914  {
915  if(n->get_type() != Node::node_t::NODE_STRING)
916  {
918  msg << "expected a string as the JSON object member name.";
919  return JSONValue::pointer_t();
920  }
921  String name(n->get_string());
922  n = f_lexer->get_next_token();
923  if(n->get_type() != Node::node_t::NODE_COLON)
924  {
926  msg << "expected a colon (:) as the JSON object member name and member value separator.";
927  return JSONValue::pointer_t();
928  }
929  // skip the colon
930  n = f_lexer->get_next_token();
931  JSONValue::pointer_t value(read_json_value(n)); // recursive
932  if(!value)
933  {
934  // empty values mean we got an error, stop short!
935  return value;
936  }
937  if(obj.find(name) != obj.end())
938  {
939  // TBD: we should verify that JSON indeed forbids such
940  // nonsense; because we may have it wrong
942  msg << "the same object member \"" << name << "\" was defined twice, which is not allowed in JSON.";
943  // continue because (1) the existing element is valid
944  // and (2) the new element is valid
945  }
946  else
947  {
948  obj[name] = value;
949  }
950  n = f_lexer->get_next_token();
951  if(n->get_type() == Node::node_t::NODE_CLOSE_CURVLY_BRACKET)
952  {
953  break;
954  }
955  if(n->get_type() != Node::node_t::NODE_COMMA)
956  {
958  msg << "expected a comma (,) to separate two JSON object members.";
959  return JSONValue::pointer_t();
960  }
961  n = f_lexer->get_next_token();
962  }
963  }
964 
965  return JSONValue::pointer_t(new JSONValue(pos, obj));
966  }
967  break;
968 
969  case Node::node_t::NODE_OPEN_SQUARE_BRACKET: // read an array
970  {
971  JSONValue::array_t array;
972 
973  Position pos(n->get_position());
974  n = f_lexer->get_next_token();
975  if(n->get_type() != Node::node_t::NODE_CLOSE_SQUARE_BRACKET)
976  {
977  for(;;)
978  {
979  JSONValue::pointer_t value(read_json_value(n)); // recursive
980  if(!value)
981  {
982  // empty values mean we got an error, stop short!
983  return value;
984  }
985  array.push_back(value);
986  n = f_lexer->get_next_token();
987  if(n->get_type() == Node::node_t::NODE_CLOSE_SQUARE_BRACKET)
988  {
989  break;
990  }
991  if(n->get_type() != Node::node_t::NODE_COMMA)
992  {
994  msg << "expected a comma (,) to separate two JSON array items.";
995  return JSONValue::pointer_t();
996  }
997  n = f_lexer->get_next_token();
998  }
999  }
1000 
1001  return JSONValue::pointer_t(new JSONValue(pos, array));
1002  }
1003  break;
1004 
1006  return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_string()));
1007 
1009  // negative number...
1010  n = f_lexer->get_next_token();
1011  switch(n->get_type())
1012  {
1014  {
1015  Float64 f(n->get_float64());
1016  if(!f.is_NaN())
1017  {
1018  f.set(-f.get());
1019  n->set_float64(f);
1020  }
1021  // else ... should we err about this one?
1022  }
1023  return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_float64()));
1024 
1026  {
1027  Int64 i(n->get_int64());
1028  i.set(-i.get());
1029  n->set_int64(i);
1030  }
1031  return JSONValue::pointer_t(new JSONValue(n->get_position(), n->get_int64()));
1032 
1033  default:
1035  msg << "unexpected token (" << n->get_type_name() << ") found after a '-' sign, a number was expected.";
1036  return JSONValue::pointer_t();
1037 
1038  }
1039  /*NOT_REACHED*/
1040  break;
1041 
1043  return JSONValue::pointer_t(new JSONValue(n->get_position(), true));
1044 
1045  default:
1047  msg << "unexpected token (" << n->get_type_name() << ") found in a JSON input stream.";
1048  return JSONValue::pointer_t();
1049 
1050  }
1051 }
1052 
1053 
1069 bool JSON::save(String const& filename, String const& header) const
1070 {
1071  FileOutput::pointer_t out(new FileOutput());
1072  if(!out->open(filename))
1073  {
1075  msg << "could not open output file \"" << filename << "\".";
1076  return false;
1077  }
1078 
1079  return output(out, header);
1080 }
1081 
1082 
1113 bool JSON::output(Output::pointer_t out, String const& header) const
1114 {
1115  if(!f_value)
1116  {
1117  // should we instead output "null"?
1118  throw exception_invalid_data("this JSON has no value to output");
1119  }
1120 
1121  if( std::dynamic_pointer_cast<FileOutput>(out) )
1122  {
1123  // Only do this if we are outputting to a file!
1124  // start with a BOM so the file is clearly marked as being UTF-8
1125  //
1126  as2js::String bom;
1127  bom += String::STRING_BOM;
1128  out->write(bom);
1129  }
1130 
1131  if(!header.empty())
1132  {
1133  out->write(header);
1134  out->write("\n");
1135  }
1136 
1137  out->write(f_value->to_string());
1138 
1139  return true;
1140 }
1141 
1142 
1152 {
1153  f_value = value;
1154 }
1155 
1156 
1172 {
1173  return f_value;
1174 }
1175 
1176 
1177 }
1178 // namespace as2js
1179 
1180 // vim: ts=4 sw=4 et
void set_value(JSONValue::pointer_t value)
Set the value of this JSON object.
Definition: json.cpp:1151
float64_type get() const
Definition: float64.h:71
String const & get_string() const
Get the string.
Definition: json.cpp:460
void set_member(String const &name, JSONValue::pointer_t value)
Change the value of an object member.
Definition: json.cpp:610
JSONValue::pointer_t load(String const &filename)
Read a JSON value.
Definition: json.cpp:765
void set(float64_type const new_float)
Definition: float64.h:76
void append_string(String &result, String const &str)
Append a raw string to a stringified string.
Definition: json.cpp:108
std::map< String, JSONValue::pointer_t > object_t
Definition: json.h:73
void set_item(size_t idx, JSONValue::pointer_t value)
Change the value of an array item.
Definition: json.cpp:525
String to_string() const
Get the JSONValue as a string.
Definition: json.cpp:671
std::shared_ptr< Input > pointer_t
Definition: stream.h:152
JSONValue(Position const &position)
Initialize a JSONValue object.
Definition: json.cpp:235
saving_t(JSONValue const &value)
Initialize a JSONValue saving_t object.
Definition: json.cpp:195
std::shared_ptr< FileOutput > pointer_t
Definition: stream.h:289
void set(int64_type const new_int)
Definition: int64.h:74
bool output(Output::pointer_t out, String const &header) const
Output this JSON to the specified output.
Definition: json.cpp:1113
~saving_t()
Destroy a JSONValue saving_t object.
Definition: json.cpp:218
int64_type get() const
Definition: int64.h:69
std::shared_ptr< Node > pointer_t
Definition: node.h:67
std::shared_ptr< Options > pointer_t
The smart pointer used when creating an Options object.
Definition: options.h:53
static as_char_t const STRING_BOM
Definition: string.h:56
Int64 get_int64() const
Get the integer.
Definition: json.cpp:415
Position f_position
Definition: json.h:124
bool save(String const &filename, String const &header) const
Save the JSON in the specified file.
Definition: json.cpp:1069
std::shared_ptr< JSONValue > pointer_t
Definition: json.h:71
JSONValue::pointer_t read_json_value(Node::pointer_t n)
Read only JSON value.
Definition: json.cpp:863
Position const & get_position() const
Get a constant reference to the JSONValue position.
Definition: json.cpp:649
array_t const & get_array() const
Get a reference to this JSONValue array.
Definition: json.cpp:486
std::vector< JSONValue::pointer_t > array_t
Definition: json.h:72
The AlexScript to JavaScript namespace.
Definition: compiler.cpp:37
object_t f_object
Definition: json.h:131
std::shared_ptr< FileInput > pointer_t
Definition: stream.h:202
type_t get_type() const
Retrieve the type of this JSONValue object.
Definition: json.cpp:394
std::shared_ptr< Output > pointer_t
Definition: stream.h:256
object_t const & get_object() const
Get a reference to this JSONValue object.
Definition: json.cpp:568
JSONValue::pointer_t get_value() const
Retrieve the value of the JSON object.
Definition: json.cpp:1171
JSONValue::pointer_t f_value
Definition: json.h:148
void set_filename(String const &filename)
Set the filename being read.
Definition: position.cpp:52
JSONValue::pointer_t parse(Input::pointer_t in)
Parse a JSON object.
Definition: json.cpp:804
type_t const f_type
Definition: json.h:123
Change the lexer to read data for our JSON implementation.
Lexer::pointer_t f_lexer
Definition: json.h:147
Float64 get_float64() const
Get the floating point.
Definition: json.cpp:440

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.

Syndicate content

Snap! Websites
An Open Source CMS System in C++

Contact Us Directly