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