/mordor/json.rl

http://github.com/mozy/mordor · Unknown · 465 lines · 419 code · 46 blank · 0 comment · 0 complexity · 91e6a643637aa52cdc8773ab4bff0c40 MD5 · raw file

  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "mordor/pch.h"
  3. #include "mordor/json.h"
  4. #include "mordor/assert.h"
  5. #include "mordor/string.h"
  6. namespace Mordor {
  7. namespace JSON {
  8. template<>
  9. void
  10. Value::set<bool>(const bool& value,
  11. boost::enable_if<boost::is_arithmetic<bool> >::type *)
  12. {
  13. (ValueBase &)*this = value;
  14. }
  15. namespace {
  16. class BoolVisitor : public boost::static_visitor<>
  17. {
  18. public:
  19. void operator()(const Array &array)
  20. {
  21. result = array.empty();
  22. }
  23. void operator()(const Object &object)
  24. {
  25. result = object.empty();
  26. }
  27. template <class T>
  28. void operator()(const T& value)
  29. {
  30. MORDOR_THROW_EXCEPTION(boost::bad_get());
  31. }
  32. bool result;
  33. };
  34. class SizeVisitor : public boost::static_visitor<>
  35. {
  36. public:
  37. void operator()(const Array &array)
  38. {
  39. result = array.size();
  40. }
  41. void operator()(const Object &object)
  42. {
  43. result = object.size();
  44. }
  45. template <class T>
  46. void operator()(const T& value)
  47. {
  48. MORDOR_THROW_EXCEPTION(boost::bad_get());
  49. }
  50. size_t result;
  51. };
  52. }
  53. #if defined(GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
  54. #pragma GCC diagnostic push
  55. #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
  56. #endif
  57. bool
  58. Value::empty() const
  59. {
  60. BoolVisitor visitor;
  61. boost::apply_visitor(visitor, *this);
  62. return visitor.result;
  63. }
  64. size_t
  65. Value::size() const
  66. {
  67. SizeVisitor visitor;
  68. boost::apply_visitor(visitor, *this);
  69. return visitor.result;
  70. }
  71. #if defined(GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
  72. #pragma GCC diagnostic pop
  73. #endif
  74. static Value g_blank;
  75. const Value &
  76. Value::operator[](const std::string &key) const
  77. {
  78. const Object &object = get<const Object>();
  79. const_iterator it = object.find(key);
  80. if (it == object.end())
  81. return g_blank;
  82. return it->second;
  83. }
  84. std::string unquote(const std::string &string)
  85. {
  86. MORDOR_ASSERT(string.size() >= 2);
  87. MORDOR_ASSERT(string[0] == '"');
  88. MORDOR_ASSERT(string[string.size() - 1] == '"');
  89. std::string result = string.substr(1, string.size() - 2);
  90. const char *c = string.c_str() + 1;
  91. const char *end = c + string.size() - 2;
  92. bool differed = false;
  93. utf16char utf16, priorUtf16 = 0;
  94. while (c < end)
  95. {
  96. if (*c == '\\') {
  97. MORDOR_ASSERT(c + 1 < end);
  98. if (!differed) {
  99. result.resize(c - (string.c_str() + 1));
  100. differed = true;
  101. }
  102. ++c;
  103. switch (*c) {
  104. case '"':
  105. case '\\':
  106. case '/':
  107. result.append(1, *c);
  108. priorUtf16 = 0;
  109. break;
  110. case 'b':
  111. result.append(1, '\b');
  112. priorUtf16 = 0;
  113. break;
  114. case 'f':
  115. result.append(1, '\f');
  116. priorUtf16 = 0;
  117. break;
  118. case 'n':
  119. result.append(1, '\n');
  120. priorUtf16 = 0;
  121. break;
  122. case 'r':
  123. result.append(1, '\r');
  124. priorUtf16 = 0;
  125. break;
  126. case 't':
  127. result.append(1, '\t');
  128. priorUtf16 = 0;
  129. break;
  130. case 'u':
  131. MORDOR_ASSERT(c + 4 < end);
  132. utf16 = 0;
  133. ++c;
  134. for (int i = 0; i < 4; ++i) {
  135. utf16 *= 16;
  136. if (*c >= '0' && *c <= '9')
  137. utf16 += *c - '0';
  138. else if (*c >= 'a' && *c <= 'f')
  139. utf16 += *c - 'a' + 10;
  140. else if (*c >= 'A' && *c <= 'F')
  141. utf16 += *c - 'A' + 10;
  142. else
  143. MORDOR_NOTREACHED();
  144. ++c;
  145. }
  146. if (isHighSurrogate(priorUtf16) && isLowSurrogate(utf16)) {
  147. // Back out the incorrect UTF8 we previously saw
  148. result.resize(result.size() - 3);
  149. result.append(toUtf8(priorUtf16, utf16));
  150. priorUtf16 = 0;
  151. } else {
  152. result.append(toUtf8(utf16));
  153. priorUtf16 = utf16;
  154. }
  155. continue;
  156. default:
  157. MORDOR_NOTREACHED();
  158. }
  159. } else if (differed) {
  160. result.append(1, *c);
  161. priorUtf16 = 0;
  162. }
  163. ++c;
  164. }
  165. return result;
  166. }
  167. %%{
  168. machine json_parser;
  169. action mark { mark = fpc;}
  170. prepush {
  171. prepush();
  172. }
  173. postpop {
  174. postpop();
  175. }
  176. ws = ' ' | '\t' | '\r' | '\n';
  177. unescaped = (any - ('"' | '\\') - cntrl);
  178. char = unescaped | ('\\' ('"' | '\\' | '/' | 'b' | 'f' | 'n' | 'r' | 't' | ('u' [0-9A-Za-z]{4})));
  179. string = '"' char* '"';
  180. action begin_number
  181. {
  182. m_nonIntegral = false;
  183. }
  184. action set_non_integral
  185. {
  186. m_nonIntegral = true;
  187. }
  188. action parse_number
  189. {
  190. if (m_nonIntegral) {
  191. *m_stack.top() = strtod(mark, NULL);
  192. } else {
  193. *m_stack.top() = strtoll(mark, NULL, 10);
  194. }
  195. }
  196. int = (digit | ([1-9] digit*));
  197. frac = '.' >set_non_integral digit+;
  198. exp = 'e'i >set_non_integral ('+' | '-')? digit+;
  199. number = ('-'? int frac? exp?) >mark >begin_number %parse_number;
  200. action parse_string
  201. {
  202. *m_stack.top() = unquote(std::string(mark, fpc - mark));
  203. }
  204. action call_parse_object
  205. {
  206. *m_stack.top() = Object();
  207. fcall *json_parser_en_parse_object;
  208. }
  209. action call_parse_array
  210. {
  211. *m_stack.top() = Array();
  212. fcall *json_parser_en_parse_array;
  213. }
  214. action parse_true
  215. {
  216. *m_stack.top() = true;
  217. }
  218. action parse_false
  219. {
  220. *m_stack.top() = false;
  221. }
  222. action parse_null
  223. {
  224. *m_stack.top() = boost::blank();
  225. }
  226. value = string >mark %parse_string | number | '{' @call_parse_object |
  227. '[' @call_parse_array | 'true' @parse_true | 'false' @parse_false |
  228. 'null' @parse_null;
  229. object = '{' ws* (string ws* ':' ws* value ws* (',' ws* string ws* ':' ws* value ws*)*)? '}';
  230. array = '[' ws* (value ws* (',' ws* value ws*)*)? ']';
  231. action new_key
  232. {
  233. m_stack.push(&boost::get<Object>(*m_stack.top())[unquote(std::string(mark, fpc - mark))]);
  234. }
  235. action new_element
  236. {
  237. boost::get<Array>(*m_stack.top()).push_back(Value());
  238. m_stack.push(&boost::get<Array>(*m_stack.top()).back());
  239. }
  240. action pop_stack
  241. {
  242. m_stack.pop();
  243. }
  244. action ret {
  245. fret;
  246. }
  247. parse_object := parse_object_lbl: ws* (string >mark %new_key ws* ':' ws* value %pop_stack ws* (',' ws* string >mark %new_key ws* ':' ws* value %pop_stack ws*)*)? '}' @ret;
  248. parse_array := parse_array_lbl: ws* (value >new_element %pop_stack ws* (',' ws* value >new_element %pop_stack ws*)*)? ']' @ret;
  249. main := ws* value ws*;
  250. write data;
  251. }%%
  252. void
  253. Parser::init()
  254. {
  255. while (m_stack.size() > 1)
  256. m_stack.pop();
  257. *m_stack.top() = boost::blank();
  258. RagelParserWithStack::init();
  259. %% write init;
  260. }
  261. void
  262. Parser::exec()
  263. {
  264. #ifdef MSVC
  265. #pragma warning(push)
  266. #pragma warning(disable : 4244)
  267. #endif
  268. %% write exec;
  269. #ifdef MSVC
  270. #pragma warning(pop)
  271. #endif
  272. }
  273. bool
  274. Parser::final() const
  275. {
  276. return cs >= json_parser_first_final;
  277. }
  278. bool
  279. Parser::error() const
  280. {
  281. return cs == json_parser_error;
  282. }
  283. std::string
  284. quote(const std::string &str)
  285. {
  286. std::string result;
  287. result.reserve(str.length() + 2);
  288. result.append(1, '"');
  289. static const char *escaped =
  290. "\0\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
  291. "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
  292. "\\\"";
  293. size_t lastEscape = 0;
  294. size_t nextEscape = str.find_first_of(escaped, 0, 34);
  295. std::ostringstream os;
  296. os.fill('0');
  297. os << std::hex << std::setw(4);
  298. while (nextEscape != std::string::npos) {
  299. result.append(str.substr(lastEscape, nextEscape - lastEscape));
  300. result.append(1, '\\');
  301. switch (str[nextEscape]) {
  302. case '"':
  303. case '\\':
  304. result.append(1, str[nextEscape]);
  305. break;
  306. case '\b':
  307. result.append(1, 'b');
  308. break;
  309. case '\f':
  310. result.append(1, 'f');
  311. break;
  312. case '\n':
  313. result.append(1, 'n');
  314. break;
  315. case '\r':
  316. result.append(1, 'r');
  317. break;
  318. case '\t':
  319. result.append(1, 't');
  320. break;
  321. default:
  322. result.append(1, 'u');
  323. os.str();
  324. os << (int)str[nextEscape];
  325. result.append(os.str());
  326. break;
  327. }
  328. lastEscape = nextEscape + 1;
  329. nextEscape = str.find_first_of(escaped, lastEscape, 34);
  330. }
  331. result.append(str.substr(lastEscape));
  332. result.append(1, '"');
  333. return result;
  334. }
  335. namespace {
  336. class JSONVisitor : public boost::static_visitor<>
  337. {
  338. public:
  339. JSONVisitor(std::ostream &os)
  340. : os(os),
  341. depth(0)
  342. {}
  343. void operator()(const boost::blank &b)
  344. {
  345. os << "null";
  346. }
  347. void operator()(const bool &b)
  348. {
  349. os << (b ? "true" : "false");
  350. }
  351. template <class T>
  352. void operator()(const T &t)
  353. {
  354. os << t;
  355. }
  356. void operator()(const std::string &str)
  357. {
  358. os << quote(str);
  359. }
  360. void operator()(const Object &object)
  361. {
  362. if (object.empty()) {
  363. os << "{ }";
  364. } else {
  365. ++depth;
  366. std::string prefix(depth * 4, ' ');
  367. os << "{\n";
  368. for (Object::const_iterator it(object.begin());
  369. it != object.end();
  370. ++it) {
  371. if (it != object.begin())
  372. os << ",\n";
  373. os << prefix << quote(it->first) << " : ";
  374. boost::apply_visitor(*this, it->second);
  375. }
  376. --depth;
  377. prefix.clear();
  378. prefix.append(depth * 4, ' ');
  379. os << '\n' << prefix << "}";
  380. }
  381. }
  382. void operator()(const Array &array)
  383. {
  384. if (array.empty()) {
  385. os << "[ ]";
  386. } else {
  387. ++depth;
  388. std::string prefix(depth * 4, ' ');
  389. os << "[\n";
  390. for (Array::const_iterator it(array.begin());
  391. it != array.end();
  392. ++it) {
  393. if (it != array.begin())
  394. os << ",\n";
  395. os << prefix;
  396. boost::apply_visitor(*this, *it);
  397. }
  398. --depth;
  399. prefix.clear();
  400. prefix.append(depth * 4, ' ');
  401. os << '\n' << prefix << "]";
  402. }
  403. }
  404. std::ostream &os;
  405. int depth;
  406. };
  407. }
  408. std::ostream &operator <<(std::ostream &os, const Value &json)
  409. {
  410. JSONVisitor visitor(os);
  411. boost::apply_visitor(visitor, json);
  412. return os;
  413. }
  414. bool isBlank(Value value)
  415. {
  416. boost::blank *isItBlank = boost::get<boost::blank>(&value);
  417. return !!isItBlank;
  418. }
  419. }}