Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2007-2011 Baptiste Lepilleur | |
| 2 // Distributed under MIT license, or public domain if desired and | |
| 3 // recognized in your jurisdiction. | |
| 4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE | |
| 5 | |
| 6 #if !defined(JSON_IS_AMALGAMATION) | |
| 7 # include <json/assertions.h> | |
| 8 # include <json/reader.h> | |
| 9 # include <json/value.h> | |
| 10 # include "json_tool.h" | |
| 11 #endif // if !defined(JSON_IS_AMALGAMATION) | |
| 12 #include <utility> | |
| 13 #include <cstdio> | |
| 14 #include <cassert> | |
| 15 #include <cstring> | |
| 16 #include <stdexcept> | |
| 17 #include <string> | |
| 18 #include <iostream> | |
|
JF
2013/12/06 16:53:29
The above two lines are new.
| |
| 19 | |
| 20 #if _MSC_VER >= 1400 // VC++ 8.0 | |
| 21 #pragma warning( disable : 4996 ) // disable warning about strdup being deprec ated. | |
| 22 #endif | |
| 23 | |
| 24 namespace Json { | |
| 25 | |
| 26 // Implementation of class Features | |
| 27 // //////////////////////////////// | |
| 28 | |
| 29 Features::Features() | |
| 30 : allowComments_( true ) | |
| 31 , strictRoot_( false ) | |
| 32 { | |
| 33 } | |
| 34 | |
| 35 | |
| 36 Features | |
| 37 Features::all() | |
| 38 { | |
| 39 return Features(); | |
| 40 } | |
| 41 | |
| 42 | |
| 43 Features | |
| 44 Features::strictMode() | |
| 45 { | |
| 46 Features features; | |
| 47 features.allowComments_ = false; | |
| 48 features.strictRoot_ = true; | |
| 49 return features; | |
| 50 } | |
| 51 | |
| 52 // Implementation of class Reader | |
| 53 // //////////////////////////////// | |
| 54 | |
| 55 | |
| 56 static inline bool | |
| 57 in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::C har c4 ) | |
| 58 { | |
| 59 return c == c1 || c == c2 || c == c3 || c == c4; | |
| 60 } | |
| 61 | |
| 62 static inline bool | |
| 63 in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::C har c4, Reader::Char c5 ) | |
| 64 { | |
| 65 return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; | |
| 66 } | |
| 67 | |
| 68 | |
| 69 static bool | |
| 70 containsNewLine( Reader::Location begin, | |
| 71 Reader::Location end ) | |
| 72 { | |
| 73 for ( ;begin < end; ++begin ) | |
| 74 if ( *begin == '\n' || *begin == '\r' ) | |
| 75 return true; | |
| 76 return false; | |
| 77 } | |
| 78 | |
| 79 | |
| 80 // Class Reader | |
| 81 // ////////////////////////////////////////////////////////////////// | |
| 82 | |
| 83 Reader::Reader() | |
| 84 : errors_(), | |
| 85 document_(), | |
| 86 begin_(), | |
| 87 end_(), | |
| 88 current_(), | |
| 89 lastValueEnd_(), | |
| 90 lastValue_(), | |
| 91 commentsBefore_(), | |
| 92 features_( Features::all() ), | |
| 93 collectComments_() | |
| 94 { | |
| 95 } | |
| 96 | |
| 97 | |
| 98 Reader::Reader( const Features &features ) | |
| 99 : errors_(), | |
| 100 document_(), | |
| 101 begin_(), | |
| 102 end_(), | |
| 103 current_(), | |
| 104 lastValueEnd_(), | |
| 105 lastValue_(), | |
| 106 commentsBefore_(), | |
| 107 features_( features ), | |
| 108 collectComments_() | |
| 109 { | |
| 110 } | |
| 111 | |
| 112 | |
| 113 bool | |
| 114 Reader::parse( const std::string &document, | |
| 115 Value &root, | |
| 116 bool collectComments ) | |
| 117 { | |
| 118 document_ = document; | |
| 119 const char *begin = document_.c_str(); | |
| 120 const char *end = begin + document_.length(); | |
| 121 return parse( begin, end, root, collectComments ); | |
| 122 } | |
| 123 | |
| 124 | |
| 125 bool | |
| 126 Reader::parse( std::istream& sin, | |
| 127 Value &root, | |
| 128 bool collectComments ) | |
| 129 { | |
| 130 //std::istream_iterator<char> begin(sin); | |
| 131 //std::istream_iterator<char> end; | |
| 132 // Those would allow streamed input from a file, if parse() were a | |
| 133 // template function. | |
| 134 | |
| 135 // Since std::string is reference-counted, this at least does not | |
| 136 // create an extra copy. | |
| 137 std::string doc; | |
| 138 std::getline(sin, doc, (char)EOF); | |
| 139 return parse( doc, root, collectComments ); | |
| 140 } | |
| 141 | |
| 142 bool | |
| 143 Reader::parse( const char *beginDoc, const char *endDoc, | |
| 144 Value &root, | |
| 145 bool collectComments ) | |
| 146 { | |
| 147 if ( !features_.allowComments_ ) | |
| 148 { | |
| 149 collectComments = false; | |
| 150 } | |
| 151 | |
| 152 begin_ = beginDoc; | |
| 153 end_ = endDoc; | |
| 154 collectComments_ = collectComments; | |
| 155 current_ = begin_; | |
| 156 lastValueEnd_ = 0; | |
| 157 lastValue_ = 0; | |
| 158 commentsBefore_ = ""; | |
| 159 errors_.clear(); | |
| 160 while ( !nodes_.empty() ) | |
| 161 nodes_.pop(); | |
| 162 nodes_.push( &root ); | |
| 163 | |
| 164 bool successful = readValue(); | |
| 165 Token token; | |
| 166 skipCommentTokens( token ); | |
| 167 if ( collectComments_ && !commentsBefore_.empty() ) | |
| 168 root.setComment( commentsBefore_, commentAfter ); | |
| 169 if ( features_.strictRoot_ ) | |
| 170 { | |
| 171 if ( !root.isArray() && !root.isObject() ) | |
| 172 { | |
| 173 // Set error location to start of doc, ideally should be first token fo und in doc | |
| 174 token.type_ = tokenError; | |
| 175 token.start_ = beginDoc; | |
| 176 token.end_ = endDoc; | |
| 177 addError( "A valid JSON document must be either an array or an object v alue.", | |
| 178 token ); | |
| 179 return false; | |
| 180 } | |
| 181 } | |
| 182 return successful; | |
| 183 } | |
| 184 | |
| 185 | |
| 186 bool | |
| 187 Reader::readValue() | |
| 188 { | |
| 189 Token token; | |
| 190 skipCommentTokens( token ); | |
| 191 bool successful = true; | |
| 192 | |
| 193 if ( collectComments_ && !commentsBefore_.empty() ) | |
| 194 { | |
| 195 currentValue().setComment( commentsBefore_, commentBefore ); | |
| 196 commentsBefore_ = ""; | |
| 197 } | |
| 198 | |
| 199 | |
| 200 switch ( token.type_ ) | |
| 201 { | |
| 202 case tokenObjectBegin: | |
| 203 successful = readObject( token ); | |
| 204 break; | |
| 205 case tokenArrayBegin: | |
| 206 successful = readArray( token ); | |
| 207 break; | |
| 208 case tokenNumber: | |
| 209 successful = decodeNumber( token ); | |
| 210 break; | |
| 211 case tokenString: | |
| 212 successful = decodeString( token ); | |
| 213 break; | |
| 214 case tokenTrue: | |
| 215 currentValue() = true; | |
| 216 break; | |
| 217 case tokenFalse: | |
| 218 currentValue() = false; | |
| 219 break; | |
| 220 case tokenNull: | |
| 221 currentValue() = Value(); | |
| 222 break; | |
| 223 default: | |
| 224 return addError( "Syntax error: value, object or array expected.", token ) ; | |
| 225 } | |
| 226 | |
| 227 if ( collectComments_ ) | |
| 228 { | |
| 229 lastValueEnd_ = current_; | |
| 230 lastValue_ = ¤tValue(); | |
| 231 } | |
| 232 | |
| 233 return successful; | |
| 234 } | |
| 235 | |
| 236 | |
| 237 void | |
| 238 Reader::skipCommentTokens( Token &token ) | |
| 239 { | |
| 240 if ( features_.allowComments_ ) | |
| 241 { | |
| 242 do | |
| 243 { | |
| 244 readToken( token ); | |
| 245 } | |
| 246 while ( token.type_ == tokenComment ); | |
| 247 } | |
| 248 else | |
| 249 { | |
| 250 readToken( token ); | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 | |
| 255 bool | |
| 256 Reader::expectToken( TokenType type, Token &token, const char *message ) | |
| 257 { | |
| 258 readToken( token ); | |
| 259 if ( token.type_ != type ) | |
| 260 return addError( message, token ); | |
| 261 return true; | |
| 262 } | |
| 263 | |
| 264 | |
| 265 bool | |
| 266 Reader::readToken( Token &token ) | |
| 267 { | |
| 268 skipSpaces(); | |
| 269 token.start_ = current_; | |
| 270 Char c = getNextChar(); | |
| 271 bool ok = true; | |
| 272 switch ( c ) | |
| 273 { | |
| 274 case '{': | |
| 275 token.type_ = tokenObjectBegin; | |
| 276 break; | |
| 277 case '}': | |
| 278 token.type_ = tokenObjectEnd; | |
| 279 break; | |
| 280 case '[': | |
| 281 token.type_ = tokenArrayBegin; | |
| 282 break; | |
| 283 case ']': | |
| 284 token.type_ = tokenArrayEnd; | |
| 285 break; | |
| 286 case '"': | |
| 287 token.type_ = tokenString; | |
| 288 ok = readString(); | |
| 289 break; | |
| 290 case '/': | |
| 291 token.type_ = tokenComment; | |
| 292 ok = readComment(); | |
| 293 break; | |
| 294 case '0': | |
| 295 case '1': | |
| 296 case '2': | |
| 297 case '3': | |
| 298 case '4': | |
| 299 case '5': | |
| 300 case '6': | |
| 301 case '7': | |
| 302 case '8': | |
| 303 case '9': | |
| 304 case '-': | |
| 305 token.type_ = tokenNumber; | |
| 306 readNumber(); | |
| 307 break; | |
| 308 case 't': | |
| 309 token.type_ = tokenTrue; | |
| 310 ok = match( "rue", 3 ); | |
| 311 break; | |
| 312 case 'f': | |
| 313 token.type_ = tokenFalse; | |
| 314 ok = match( "alse", 4 ); | |
| 315 break; | |
| 316 case 'n': | |
| 317 token.type_ = tokenNull; | |
| 318 ok = match( "ull", 3 ); | |
| 319 break; | |
| 320 case ',': | |
| 321 token.type_ = tokenArraySeparator; | |
| 322 break; | |
| 323 case ':': | |
| 324 token.type_ = tokenMemberSeparator; | |
| 325 break; | |
| 326 case 0: | |
| 327 token.type_ = tokenEndOfStream; | |
| 328 break; | |
| 329 default: | |
| 330 ok = false; | |
| 331 break; | |
| 332 } | |
| 333 if ( !ok ) | |
| 334 token.type_ = tokenError; | |
| 335 token.end_ = current_; | |
| 336 return true; | |
| 337 } | |
| 338 | |
| 339 | |
| 340 void | |
| 341 Reader::skipSpaces() | |
| 342 { | |
| 343 while ( current_ != end_ ) | |
| 344 { | |
| 345 Char c = *current_; | |
| 346 if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) | |
| 347 ++current_; | |
| 348 else | |
| 349 break; | |
| 350 } | |
| 351 } | |
| 352 | |
| 353 | |
| 354 bool | |
| 355 Reader::match( Location pattern, | |
| 356 int patternLength ) | |
| 357 { | |
| 358 if ( end_ - current_ < patternLength ) | |
| 359 return false; | |
| 360 int index = patternLength; | |
| 361 while ( index-- ) | |
| 362 if ( current_[index] != pattern[index] ) | |
| 363 return false; | |
| 364 current_ += patternLength; | |
| 365 return true; | |
| 366 } | |
| 367 | |
| 368 | |
| 369 bool | |
| 370 Reader::readComment() | |
| 371 { | |
| 372 Location commentBegin = current_ - 1; | |
| 373 Char c = getNextChar(); | |
| 374 bool successful = false; | |
| 375 if ( c == '*' ) | |
| 376 successful = readCStyleComment(); | |
| 377 else if ( c == '/' ) | |
| 378 successful = readCppStyleComment(); | |
| 379 if ( !successful ) | |
| 380 return false; | |
| 381 | |
| 382 if ( collectComments_ ) | |
| 383 { | |
| 384 CommentPlacement placement = commentBefore; | |
| 385 if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) | |
| 386 { | |
| 387 if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) | |
| 388 placement = commentAfterOnSameLine; | |
| 389 } | |
| 390 | |
| 391 addComment( commentBegin, current_, placement ); | |
| 392 } | |
| 393 return true; | |
| 394 } | |
| 395 | |
| 396 | |
| 397 void | |
| 398 Reader::addComment( Location begin, | |
| 399 Location end, | |
| 400 CommentPlacement placement ) | |
| 401 { | |
| 402 assert( collectComments_ ); | |
| 403 if ( placement == commentAfterOnSameLine ) | |
| 404 { | |
| 405 assert( lastValue_ != 0 ); | |
| 406 lastValue_->setComment( std::string( begin, end ), placement ); | |
| 407 } | |
| 408 else | |
| 409 { | |
| 410 if ( !commentsBefore_.empty() ) | |
| 411 commentsBefore_ += "\n"; | |
| 412 commentsBefore_ += std::string( begin, end ); | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 | |
| 417 bool | |
| 418 Reader::readCStyleComment() | |
| 419 { | |
| 420 while ( current_ != end_ ) | |
| 421 { | |
| 422 Char c = getNextChar(); | |
| 423 if ( c == '*' && *current_ == '/' ) | |
| 424 break; | |
| 425 } | |
| 426 return getNextChar() == '/'; | |
| 427 } | |
| 428 | |
| 429 | |
| 430 bool | |
| 431 Reader::readCppStyleComment() | |
| 432 { | |
| 433 while ( current_ != end_ ) | |
| 434 { | |
| 435 Char c = getNextChar(); | |
| 436 if ( c == '\r' || c == '\n' ) | |
| 437 break; | |
| 438 } | |
| 439 return true; | |
| 440 } | |
| 441 | |
| 442 | |
| 443 void | |
| 444 Reader::readNumber() | |
| 445 { | |
| 446 while ( current_ != end_ ) | |
| 447 { | |
| 448 if ( !(*current_ >= '0' && *current_ <= '9') && | |
| 449 !in( *current_, '.', 'e', 'E', '+', '-' ) ) | |
| 450 break; | |
| 451 ++current_; | |
| 452 } | |
| 453 } | |
| 454 | |
| 455 bool | |
| 456 Reader::readString() | |
| 457 { | |
| 458 Char c = 0; | |
| 459 while ( current_ != end_ ) | |
| 460 { | |
| 461 c = getNextChar(); | |
| 462 if ( c == '\\' ) | |
| 463 getNextChar(); | |
| 464 else if ( c == '"' ) | |
| 465 break; | |
| 466 } | |
| 467 return c == '"'; | |
| 468 } | |
| 469 | |
| 470 | |
| 471 bool | |
| 472 Reader::readObject( Token &/*tokenStart*/ ) | |
| 473 { | |
| 474 Token tokenName; | |
| 475 std::string name; | |
| 476 currentValue() = Value( objectValue ); | |
| 477 while ( readToken( tokenName ) ) | |
| 478 { | |
| 479 bool initialTokenOk = true; | |
| 480 while ( tokenName.type_ == tokenComment && initialTokenOk ) | |
| 481 initialTokenOk = readToken( tokenName ); | |
| 482 if ( !initialTokenOk ) | |
| 483 break; | |
| 484 if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty objec t | |
| 485 return true; | |
| 486 if ( tokenName.type_ != tokenString ) | |
| 487 break; | |
| 488 | |
| 489 name = ""; | |
| 490 if ( !decodeString( tokenName, name ) ) | |
| 491 return recoverFromError( tokenObjectEnd ); | |
| 492 | |
| 493 Token colon; | |
| 494 if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) | |
| 495 { | |
| 496 return addErrorAndRecover( "Missing ':' after object member name", | |
| 497 colon, | |
| 498 tokenObjectEnd ); | |
| 499 } | |
| 500 Value &value = currentValue()[ name ]; | |
| 501 nodes_.push( &value ); | |
| 502 bool ok = readValue(); | |
| 503 nodes_.pop(); | |
| 504 if ( !ok ) // error already set | |
| 505 return recoverFromError( tokenObjectEnd ); | |
| 506 | |
| 507 Token comma; | |
| 508 if ( !readToken( comma ) | |
| 509 || ( comma.type_ != tokenObjectEnd && | |
| 510 comma.type_ != tokenArraySeparator && | |
| 511 comma.type_ != tokenComment ) ) | |
| 512 { | |
| 513 return addErrorAndRecover( "Missing ',' or '}' in object declaration", | |
| 514 comma, | |
| 515 tokenObjectEnd ); | |
| 516 } | |
| 517 bool finalizeTokenOk = true; | |
| 518 while ( comma.type_ == tokenComment && | |
| 519 finalizeTokenOk ) | |
| 520 finalizeTokenOk = readToken( comma ); | |
| 521 if ( comma.type_ == tokenObjectEnd ) | |
| 522 return true; | |
| 523 } | |
| 524 return addErrorAndRecover( "Missing '}' or object member name", | |
| 525 tokenName, | |
| 526 tokenObjectEnd ); | |
| 527 } | |
| 528 | |
| 529 | |
| 530 bool | |
| 531 Reader::readArray( Token &/*tokenStart*/ ) | |
| 532 { | |
| 533 currentValue() = Value( arrayValue ); | |
| 534 skipSpaces(); | |
| 535 if ( *current_ == ']' ) // empty array | |
| 536 { | |
| 537 Token endArray; | |
| 538 readToken( endArray ); | |
| 539 return true; | |
| 540 } | |
| 541 int index = 0; | |
| 542 for (;;) | |
| 543 { | |
| 544 Value &value = currentValue()[ index++ ]; | |
| 545 nodes_.push( &value ); | |
| 546 bool ok = readValue(); | |
| 547 nodes_.pop(); | |
| 548 if ( !ok ) // error already set | |
| 549 return recoverFromError( tokenArrayEnd ); | |
| 550 | |
| 551 Token token; | |
| 552 // Accept Comment after last item in the array. | |
| 553 ok = readToken( token ); | |
| 554 while ( token.type_ == tokenComment && ok ) | |
| 555 { | |
| 556 ok = readToken( token ); | |
| 557 } | |
| 558 bool badTokenType = ( token.type_ != tokenArraySeparator && | |
| 559 token.type_ != tokenArrayEnd ); | |
| 560 if ( !ok || badTokenType ) | |
| 561 { | |
| 562 return addErrorAndRecover( "Missing ',' or ']' in array declaration", | |
| 563 token, | |
| 564 tokenArrayEnd ); | |
| 565 } | |
| 566 if ( token.type_ == tokenArrayEnd ) | |
| 567 break; | |
| 568 } | |
| 569 return true; | |
| 570 } | |
| 571 | |
| 572 | |
| 573 bool | |
| 574 Reader::decodeNumber( Token &token ) | |
| 575 { | |
| 576 bool isDouble = false; | |
| 577 for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) | |
| 578 { | |
| 579 isDouble = isDouble | |
| 580 || in( *inspect, '.', 'e', 'E', '+' ) | |
| 581 || ( *inspect == '-' && inspect != token.start_ ); | |
| 582 } | |
| 583 if ( isDouble ) | |
| 584 return decodeDouble( token ); | |
| 585 // Attempts to parse the number as an integer. If the number is | |
| 586 // larger than the maximum supported value of an integer then | |
| 587 // we decode the number as a double. | |
| 588 Location current = token.start_; | |
| 589 bool isNegative = *current == '-'; | |
| 590 if ( isNegative ) | |
| 591 ++current; | |
| 592 Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value:: minLargestInt) | |
| 593 : Value::maxLargestUInt; | |
| 594 Value::LargestUInt threshold = maxIntegerValue / 10; | |
| 595 Value::LargestUInt value = 0; | |
| 596 while ( current < token.end_ ) | |
| 597 { | |
| 598 Char c = *current++; | |
| 599 if ( c < '0' || c > '9' ) | |
| 600 return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); | |
| 601 Value::UInt digit(c - '0'); | |
| 602 if ( value >= threshold ) | |
| 603 { | |
| 604 // We've hit or exceeded the max value divided by 10 (rounded down). If | |
| 605 // a) we've only just touched the limit, b) this is the last digit, and | |
| 606 // c) it's small enough to fit in that rounding delta, we're okay. | |
| 607 // Otherwise treat this number as a double to avoid overflow. | |
| 608 if (value > threshold || | |
| 609 current != token.end_ || | |
| 610 digit > maxIntegerValue % 10) | |
| 611 { | |
| 612 return decodeDouble( token ); | |
| 613 } | |
| 614 } | |
| 615 value = value * 10 + digit; | |
| 616 } | |
| 617 if ( isNegative ) | |
| 618 currentValue() = -Value::LargestInt( value ); | |
| 619 else if ( value <= Value::LargestUInt(Value::maxInt) ) | |
| 620 currentValue() = Value::LargestInt( value ); | |
| 621 else | |
| 622 currentValue() = value; | |
| 623 return true; | |
| 624 } | |
| 625 | |
| 626 | |
| 627 bool | |
| 628 Reader::decodeDouble( Token &token ) | |
| 629 { | |
| 630 double value = 0; | |
| 631 const int bufferSize = 32; | |
| 632 int count; | |
| 633 int length = int(token.end_ - token.start_); | |
| 634 | |
| 635 // Sanity check to avoid buffer overflow exploits. | |
| 636 if (length < 0) { | |
| 637 return addError( "Unable to parse token length", token ); | |
| 638 } | |
| 639 | |
| 640 // Avoid using a string constant for the format control string given to | |
| 641 // sscanf, as this can cause hard to debug crashes on OS X. See here for more | |
| 642 // info: | |
| 643 // | |
| 644 // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/g cc-4.0.1/gcc/Incompatibilities.html | |
| 645 char format[] = "%lf"; | |
| 646 | |
| 647 if ( length <= bufferSize ) | |
| 648 { | |
| 649 Char buffer[bufferSize+1]; | |
| 650 memcpy( buffer, token.start_, length ); | |
| 651 buffer[length] = 0; | |
| 652 count = sscanf( buffer, format, &value ); | |
| 653 } | |
| 654 else | |
| 655 { | |
| 656 std::string buffer( token.start_, token.end_ ); | |
| 657 count = sscanf( buffer.c_str(), format, &value ); | |
| 658 } | |
| 659 | |
| 660 if ( count != 1 ) | |
| 661 return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); | |
| 662 currentValue() = value; | |
| 663 return true; | |
| 664 } | |
| 665 | |
| 666 | |
| 667 bool | |
| 668 Reader::decodeString( Token &token ) | |
| 669 { | |
| 670 std::string decoded; | |
| 671 if ( !decodeString( token, decoded ) ) | |
| 672 return false; | |
| 673 currentValue() = decoded; | |
| 674 return true; | |
| 675 } | |
| 676 | |
| 677 | |
| 678 bool | |
| 679 Reader::decodeString( Token &token, std::string &decoded ) | |
| 680 { | |
| 681 decoded.reserve( token.end_ - token.start_ - 2 ); | |
| 682 Location current = token.start_ + 1; // skip '"' | |
| 683 Location end = token.end_ - 1; // do not include '"' | |
| 684 while ( current != end ) | |
| 685 { | |
| 686 Char c = *current++; | |
| 687 if ( c == '"' ) | |
| 688 break; | |
| 689 else if ( c == '\\' ) | |
| 690 { | |
| 691 if ( current == end ) | |
| 692 return addError( "Empty escape sequence in string", token, current ) ; | |
| 693 Char escape = *current++; | |
| 694 switch ( escape ) | |
| 695 { | |
| 696 case '"': decoded += '"'; break; | |
| 697 case '/': decoded += '/'; break; | |
| 698 case '\\': decoded += '\\'; break; | |
| 699 case 'b': decoded += '\b'; break; | |
| 700 case 'f': decoded += '\f'; break; | |
| 701 case 'n': decoded += '\n'; break; | |
| 702 case 'r': decoded += '\r'; break; | |
| 703 case 't': decoded += '\t'; break; | |
| 704 case 'u': | |
| 705 { | |
| 706 unsigned int unicode; | |
| 707 if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) | |
| 708 return false; | |
| 709 decoded += codePointToUTF8(unicode); | |
| 710 } | |
| 711 break; | |
| 712 default: | |
| 713 return addError( "Bad escape sequence in string", token, current ); | |
| 714 } | |
| 715 } | |
| 716 else | |
| 717 { | |
| 718 decoded += c; | |
| 719 } | |
| 720 } | |
| 721 return true; | |
| 722 } | |
| 723 | |
| 724 bool | |
| 725 Reader::decodeUnicodeCodePoint( Token &token, | |
| 726 Location ¤t, | |
| 727 Location end, | |
| 728 unsigned int &unicode ) | |
| 729 { | |
| 730 | |
| 731 if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) | |
| 732 return false; | |
| 733 if (unicode >= 0xD800 && unicode <= 0xDBFF) | |
| 734 { | |
| 735 // surrogate pairs | |
| 736 if (end - current < 6) | |
| 737 return addError( "additional six characters expected to parse unicode s urrogate pair.", token, current ); | |
| 738 unsigned int surrogatePair; | |
| 739 if (*(current++) == '\\' && *(current++)== 'u') | |
| 740 { | |
| 741 if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) | |
| 742 { | |
| 743 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3 FF); | |
| 744 } | |
| 745 else | |
| 746 return false; | |
| 747 } | |
| 748 else | |
| 749 return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); | |
| 750 } | |
| 751 return true; | |
| 752 } | |
| 753 | |
| 754 bool | |
| 755 Reader::decodeUnicodeEscapeSequence( Token &token, | |
| 756 Location ¤t, | |
| 757 Location end, | |
| 758 unsigned int &unicode ) | |
| 759 { | |
| 760 if ( end - current < 4 ) | |
| 761 return addError( "Bad unicode escape sequence in string: four digits expec ted.", token, current ); | |
| 762 unicode = 0; | |
| 763 for ( int index =0; index < 4; ++index ) | |
| 764 { | |
| 765 Char c = *current++; | |
| 766 unicode *= 16; | |
| 767 if ( c >= '0' && c <= '9' ) | |
| 768 unicode += c - '0'; | |
| 769 else if ( c >= 'a' && c <= 'f' ) | |
| 770 unicode += c - 'a' + 10; | |
| 771 else if ( c >= 'A' && c <= 'F' ) | |
| 772 unicode += c - 'A' + 10; | |
| 773 else | |
| 774 return addError( "Bad unicode escape sequence in string: hexadecimal di git expected.", token, current ); | |
| 775 } | |
| 776 return true; | |
| 777 } | |
| 778 | |
| 779 | |
| 780 bool | |
| 781 Reader::addError( const std::string &message, | |
| 782 Token &token, | |
| 783 Location extra ) | |
| 784 { | |
| 785 ErrorInfo info; | |
| 786 info.token_ = token; | |
| 787 info.message_ = message; | |
| 788 info.extra_ = extra; | |
| 789 errors_.push_back( info ); | |
| 790 return false; | |
| 791 } | |
| 792 | |
| 793 | |
| 794 bool | |
| 795 Reader::recoverFromError( TokenType skipUntilToken ) | |
| 796 { | |
| 797 int errorCount = int(errors_.size()); | |
| 798 Token skip; | |
| 799 for (;;) | |
| 800 { | |
| 801 if ( !readToken(skip) ) | |
| 802 errors_.resize( errorCount ); // discard errors caused by recovery | |
| 803 if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) | |
| 804 break; | |
| 805 } | |
| 806 errors_.resize( errorCount ); | |
| 807 return false; | |
| 808 } | |
| 809 | |
| 810 | |
| 811 bool | |
| 812 Reader::addErrorAndRecover( const std::string &message, | |
| 813 Token &token, | |
| 814 TokenType skipUntilToken ) | |
| 815 { | |
| 816 addError( message, token ); | |
| 817 return recoverFromError( skipUntilToken ); | |
| 818 } | |
| 819 | |
| 820 | |
| 821 Value & | |
| 822 Reader::currentValue() | |
| 823 { | |
| 824 return *(nodes_.top()); | |
| 825 } | |
| 826 | |
| 827 | |
| 828 Reader::Char | |
| 829 Reader::getNextChar() | |
| 830 { | |
| 831 if ( current_ == end_ ) | |
| 832 return 0; | |
| 833 return *current_++; | |
| 834 } | |
| 835 | |
| 836 | |
| 837 void | |
| 838 Reader::getLocationLineAndColumn( Location location, | |
| 839 int &line, | |
| 840 int &column ) const | |
| 841 { | |
| 842 Location current = begin_; | |
| 843 Location lastLineStart = current; | |
| 844 line = 0; | |
| 845 while ( current < location && current != end_ ) | |
| 846 { | |
| 847 Char c = *current++; | |
| 848 if ( c == '\r' ) | |
| 849 { | |
| 850 if ( *current == '\n' ) | |
| 851 ++current; | |
| 852 lastLineStart = current; | |
| 853 ++line; | |
| 854 } | |
| 855 else if ( c == '\n' ) | |
| 856 { | |
| 857 lastLineStart = current; | |
| 858 ++line; | |
| 859 } | |
| 860 } | |
| 861 // column & line start at 1 | |
| 862 column = int(location - lastLineStart) + 1; | |
| 863 ++line; | |
| 864 } | |
| 865 | |
| 866 | |
| 867 std::string | |
| 868 Reader::getLocationLineAndColumn( Location location ) const | |
| 869 { | |
| 870 int line, column; | |
| 871 getLocationLineAndColumn( location, line, column ); | |
| 872 char buffer[18+16+16+1]; | |
| 873 sprintf( buffer, "Line %d, Column %d", line, column ); | |
| 874 return buffer; | |
| 875 } | |
| 876 | |
| 877 | |
| 878 // Deprecated. Preserved for backward compatibility | |
| 879 std::string | |
| 880 Reader::getFormatedErrorMessages() const | |
| 881 { | |
| 882 return getFormattedErrorMessages(); | |
| 883 } | |
| 884 | |
| 885 | |
| 886 std::string | |
| 887 Reader::getFormattedErrorMessages() const | |
| 888 { | |
| 889 std::string formattedMessage; | |
| 890 for ( Errors::const_iterator itError = errors_.begin(); | |
| 891 itError != errors_.end(); | |
| 892 ++itError ) | |
| 893 { | |
| 894 const ErrorInfo &error = *itError; | |
| 895 formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; | |
| 896 formattedMessage += " " + error.message_ + "\n"; | |
| 897 if ( error.extra_ ) | |
| 898 formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; | |
| 899 } | |
| 900 return formattedMessage; | |
| 901 } | |
| 902 | |
| 903 | |
| 904 std::istream& operator>>( std::istream &sin, Value &root ) | |
| 905 { | |
| 906 Json::Reader reader; | |
| 907 bool ok = reader.parse(sin, root, true); | |
| 908 if (!ok) { | |
| 909 fprintf( | |
| 910 stderr, | |
| 911 "Error from reader: %s", | |
| 912 reader.getFormattedErrorMessages().c_str()); | |
| 913 | |
| 914 JSON_FAIL_MESSAGE("reader error"); | |
| 915 } | |
| 916 return sin; | |
| 917 } | |
| 918 | |
| 919 | |
| 920 } // namespace Json | |
| OLD | NEW |