1 /* 2 * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/> 3 * (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com> 4 * 5 * This file is part of lsp-plugins 6 * Created on: 16 окт. 2019 г. 7 * 8 * lsp-plugins is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * any later version. 12 * 13 * lsp-plugins is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public License 19 * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 22 #include <core/io/InStringSequence.h> 23 #include <core/io/InSequence.h> 24 #include <core/io/InFileStream.h> 25 #include <core/files/json/Parser.h> 26 27 namespace lsp 28 { 29 namespace json 30 { 31 Parser()32 Parser::Parser() 33 { 34 pTokenizer = NULL; 35 pSequence = NULL; 36 nWFlags = 0; 37 enVersion = JSON_LEGACY; 38 sState.mode = READ_ROOT; 39 sState.flags = 0; 40 sCurrent.type = JE_UNKNOWN; 41 } 42 ~Parser()43 Parser::~Parser() 44 { 45 close(); 46 } 47 open(const char * path,json_version_t version,const char * charset)48 status_t Parser::open(const char *path, json_version_t version, const char *charset) 49 { 50 if (pTokenizer != NULL) 51 return STATUS_BAD_STATE; 52 else if (path == NULL) 53 return STATUS_BAD_ARGUMENTS; 54 55 io::InFileStream *ifs = new io::InFileStream(); 56 if (ifs == NULL) 57 return STATUS_NO_MEM; 58 status_t res = ifs->open(path); 59 if (res == STATUS_OK) 60 { 61 res = wrap(ifs, version, WRAP_CLOSE | WRAP_DELETE, charset); 62 if (res == STATUS_OK) 63 return res; 64 ifs->close(); 65 } 66 delete ifs; 67 68 return res; 69 } 70 open(const LSPString * path,json_version_t version,const char * charset)71 status_t Parser::open(const LSPString *path, json_version_t version, const char *charset) 72 { 73 if (pTokenizer != NULL) 74 return STATUS_BAD_STATE; 75 else if (path == NULL) 76 return STATUS_BAD_ARGUMENTS; 77 78 io::InFileStream *ifs = new io::InFileStream(); 79 if (ifs == NULL) 80 return STATUS_NO_MEM; 81 status_t res = ifs->open(path); 82 if (res == STATUS_OK) 83 { 84 res = wrap(ifs, version, WRAP_CLOSE | WRAP_DELETE, charset); 85 if (res == STATUS_OK) 86 return res; 87 ifs->close(); 88 } 89 delete ifs; 90 91 return res; 92 } 93 open(const io::Path * path,json_version_t version,const char * charset)94 status_t Parser::open(const io::Path *path, json_version_t version, const char *charset) 95 { 96 if (pTokenizer != NULL) 97 return STATUS_BAD_STATE; 98 else if (path == NULL) 99 return STATUS_BAD_ARGUMENTS; 100 101 io::InFileStream *ifs = new io::InFileStream(); 102 if (ifs == NULL) 103 return STATUS_NO_MEM; 104 status_t res = ifs->open(path); 105 if (res == STATUS_OK) 106 { 107 res = wrap(ifs, version, WRAP_CLOSE | WRAP_DELETE, charset); 108 if (res == STATUS_OK) 109 return res; 110 ifs->close(); 111 } 112 delete ifs; 113 114 return res; 115 } 116 wrap(const char * str,json_version_t version,const char * charset)117 status_t Parser::wrap(const char *str, json_version_t version, const char *charset) 118 { 119 if (pTokenizer != NULL) 120 return STATUS_BAD_STATE; 121 else if (str == NULL) 122 return STATUS_BAD_ARGUMENTS; 123 124 io::InStringSequence *seq = new io::InStringSequence(); 125 if (seq == NULL) 126 return STATUS_NO_MEM; 127 128 status_t res = seq->wrap(str, charset); 129 if (res == STATUS_OK) 130 { 131 if ((res = wrap(seq, version, WRAP_CLOSE | WRAP_DELETE)) == STATUS_OK) 132 return res; 133 seq->close(); 134 } 135 136 delete seq; 137 return res; 138 } 139 wrap(const LSPString * str,json_version_t version)140 status_t Parser::wrap(const LSPString *str, json_version_t version) 141 { 142 if (pTokenizer != NULL) 143 return STATUS_BAD_STATE; 144 else if (str == NULL) 145 return STATUS_BAD_ARGUMENTS; 146 147 io::InStringSequence *seq = new io::InStringSequence(); 148 if (seq == NULL) 149 return STATUS_NO_MEM; 150 151 status_t res = seq->wrap(str); 152 if (res == STATUS_OK) 153 { 154 if ((res = wrap(seq, version, WRAP_CLOSE | WRAP_DELETE)) == STATUS_OK) 155 return res; 156 seq->close(); 157 } 158 159 delete seq; 160 return res; 161 } 162 wrap(io::IInStream * is,json_version_t version,size_t flags,const char * charset)163 status_t Parser::wrap(io::IInStream *is, json_version_t version, size_t flags, const char *charset) 164 { 165 if (pTokenizer != NULL) 166 return STATUS_BAD_STATE; 167 else if (is == NULL) 168 return STATUS_BAD_ARGUMENTS; 169 170 io::InSequence *seq = new io::InSequence(); 171 if (seq == NULL) 172 return STATUS_NO_MEM; 173 174 status_t res = seq->wrap(is, flags, charset); 175 if (res == STATUS_OK) 176 { 177 if ((res = wrap(seq, version, WRAP_CLOSE | WRAP_DELETE)) == STATUS_OK) 178 return res; 179 seq->close(); 180 } 181 182 delete seq; 183 return res; 184 } 185 wrap(io::IInSequence * seq,json_version_t version,size_t flags)186 status_t Parser::wrap(io::IInSequence *seq, json_version_t version, size_t flags) 187 { 188 if (pTokenizer != NULL) 189 return STATUS_BAD_STATE; 190 else if (seq == NULL) 191 return STATUS_BAD_ARGUMENTS; 192 193 Tokenizer *tok = new Tokenizer(seq); 194 if (tok == NULL) 195 return STATUS_NO_MEM; 196 197 pTokenizer = tok; 198 pSequence = seq; 199 nWFlags = flags; 200 enVersion = version; 201 sState.mode = READ_ROOT; 202 sState.flags = 0; 203 204 return STATUS_OK; 205 } 206 close()207 status_t Parser::close() 208 { 209 status_t res = STATUS_OK; 210 211 if (pTokenizer != NULL) 212 { 213 delete pTokenizer; 214 pTokenizer = NULL; 215 } 216 217 if (pSequence != NULL) 218 { 219 if (nWFlags & WRAP_CLOSE) 220 { 221 status_t xres = pSequence->close(); 222 if (res == STATUS_OK) 223 res = xres; 224 } 225 226 if (nWFlags & WRAP_DELETE) 227 delete pSequence; 228 229 pSequence = NULL; 230 } 231 232 sCurrent.type = JE_UNKNOWN; 233 sCurrent.sValue.truncate(); 234 sStack.flush(); 235 236 return res; 237 } 238 push_state(pmode_t mode)239 status_t Parser::push_state(pmode_t mode) 240 { 241 if (!sStack.add(&sState)) 242 return STATUS_NO_MEM; 243 244 sState.mode = mode; 245 sState.flags = 0; 246 return STATUS_OK; 247 } 248 pop_state()249 status_t Parser::pop_state() 250 { 251 state_t *st = sStack.last(); 252 if (st == NULL) 253 return STATUS_BAD_STATE; 254 sState = *st; 255 return (sStack.remove_last()) ? STATUS_OK : STATUS_BAD_STATE; 256 } 257 lookup_token()258 token_t Parser::lookup_token() 259 { 260 return pTokenizer->get_token(true); 261 } 262 get_current(event_t * ev)263 status_t Parser::get_current(event_t *ev) 264 { 265 if (pTokenizer == NULL) 266 return STATUS_BAD_STATE; 267 if (ev == NULL) 268 return STATUS_BAD_ARGUMENTS; 269 270 switch (sCurrent.type) 271 { 272 case JE_OBJECT_START: 273 case JE_OBJECT_END: 274 case JE_ARRAY_START: 275 case JE_ARRAY_END: 276 case JE_UNKNOWN: 277 case JE_NULL: 278 break; 279 280 case JE_PROPERTY: 281 case JE_STRING: 282 if (!ev->sValue.set(&sCurrent.sValue)) 283 return STATUS_NO_MEM; 284 break; 285 case JE_INTEGER: 286 ev->iValue = sCurrent.iValue; 287 break; 288 case JE_DOUBLE: 289 ev->fValue = sCurrent.fValue; 290 break; 291 case JE_BOOL: 292 ev->bValue = sCurrent.bValue; 293 break; 294 295 default: 296 return STATUS_BAD_STATE; 297 } 298 299 ev->type = sCurrent.type; 300 return STATUS_OK; 301 } 302 read_primitive(token_t tok)303 status_t Parser::read_primitive(token_t tok) 304 { 305 switch (tok) 306 { 307 case JT_DQ_STRING: 308 if (!sCurrent.sValue.set(pTokenizer->text_value())) 309 return STATUS_NO_MEM; 310 sCurrent.type = JE_STRING; 311 break; 312 313 case JT_TRUE: 314 case JT_FALSE: 315 sCurrent.bValue = (tok == JT_TRUE); 316 sCurrent.type = JE_BOOL; 317 break; 318 319 case JT_NULL: 320 sCurrent.type = JE_NULL; 321 break; 322 323 case JT_DECIMAL: 324 sCurrent.iValue = pTokenizer->int_value(); 325 sCurrent.type = JE_INTEGER; 326 break; 327 328 case JT_DOUBLE: 329 sCurrent.fValue = pTokenizer->float_value(); 330 sCurrent.type = JE_DOUBLE; 331 break; 332 333 case JT_SQ_STRING: // Single-quoted strings are allowed since JSON5 334 case JT_IDENTIFIER: 335 if (enVersion < JSON_VERSION5) 336 return STATUS_BAD_TOKEN; 337 if (!sCurrent.sValue.set(pTokenizer->text_value())) 338 return STATUS_NO_MEM; 339 sCurrent.type = JE_STRING; 340 break; 341 342 case JT_HEXADECIMAL: // Hexadecimals are allowed since JSON5 343 if (enVersion < JSON_VERSION5) 344 return STATUS_BAD_TOKEN; 345 sCurrent.iValue = pTokenizer->int_value(); 346 sCurrent.type = JE_INTEGER; 347 break; 348 349 default: 350 return STATUS_BAD_TOKEN; 351 } 352 353 return STATUS_OK; 354 } 355 read_root()356 status_t Parser::read_root() 357 { 358 while (true) 359 { 360 // Get token 361 token_t tok = lookup_token(); 362 363 // Analyze token 364 switch (tok) 365 { 366 case JT_EOF: // End of input 367 return STATUS_EOF; 368 case JT_ERROR: // Error input 369 return pTokenizer->error(); 370 371 case JT_SL_COMMENT: // Skip comments 372 case JT_ML_COMMENT: 373 if (enVersion < JSON_VERSION5) // Only JSON5 allows comments 374 return STATUS_BAD_TOKEN; 375 break; 376 377 case JT_LQ_BRACE: // Start of array 378 if (sState.flags & PF_VALUE) 379 return STATUS_BAD_TOKEN; 380 sCurrent.type = JE_ARRAY_START; 381 sState.flags |= PF_VALUE; 382 return push_state(READ_ARRAY); 383 384 case JT_LC_BRACE: // Start of object 385 if (sState.flags & PF_VALUE) 386 return STATUS_BAD_TOKEN; 387 sCurrent.type = JE_OBJECT_START; 388 sState.flags |= PF_VALUE; 389 return push_state(READ_OBJECT); 390 391 case JT_SQ_STRING: 392 case JT_DQ_STRING: 393 case JT_TRUE: 394 case JT_FALSE: 395 case JT_NULL: 396 case JT_DECIMAL: 397 case JT_DOUBLE: 398 case JT_HEXADECIMAL: 399 if (sState.flags & PF_VALUE) 400 return STATUS_BAD_TOKEN; 401 sState.flags |= PF_VALUE; 402 return read_primitive(tok); 403 404 default: 405 return STATUS_BAD_TOKEN; 406 } 407 } 408 } 409 read_array()410 status_t Parser::read_array() 411 { 412 while (true) 413 { 414 // Get token 415 token_t tok = lookup_token(); 416 417 // Analyze token 418 switch (tok) 419 { 420 case JT_EOF: // End of input 421 return STATUS_CORRUPTED; 422 case JT_ERROR: // Error input 423 return pTokenizer->error(); 424 425 case JT_SL_COMMENT: // Skip comments 426 case JT_ML_COMMENT: 427 if (enVersion < JSON_VERSION5) // Only JSON5 allows comments 428 return STATUS_BAD_TOKEN; 429 break; 430 431 case JT_COMMA: // Comma 432 if ((sState.flags & PF_ARRAY_ALL) != PF_VALUE) 433 return STATUS_BAD_TOKEN; 434 sState.flags |= PF_COMMA; // Add comma flag 435 break; 436 437 case JT_RQ_BRACE: // End of current array 438 // Closing brace after comma is allowed only since JSON5 439 if ((sState.flags & PF_COMMA) && (enVersion < JSON_VERSION5)) 440 return STATUS_BAD_TOKEN; 441 sCurrent.type = JE_ARRAY_END; 442 return pop_state(); 443 444 case JT_LQ_BRACE: // Start of sub-array 445 { 446 size_t flags = sState.flags & PF_ARRAY_ALL; 447 if ((flags != 0) && (flags != (PF_VALUE | PF_COMMA))) 448 return STATUS_BAD_TOKEN; 449 sState.flags = PF_VALUE; 450 sCurrent.type = JE_ARRAY_START; 451 return push_state(READ_ARRAY); 452 } 453 454 case JT_LC_BRACE: // Start of nested object 455 { 456 size_t flags = sState.flags & PF_ARRAY_ALL; 457 if ((flags != 0) && (flags != (PF_VALUE | PF_COMMA))) 458 return STATUS_BAD_TOKEN; 459 sState.flags = PF_VALUE; 460 sCurrent.type = JE_OBJECT_START; 461 return push_state(READ_OBJECT); 462 } 463 464 case JT_SQ_STRING: 465 case JT_DQ_STRING: 466 case JT_TRUE: 467 case JT_FALSE: 468 case JT_NULL: 469 case JT_DECIMAL: 470 case JT_DOUBLE: 471 case JT_HEXADECIMAL: // Read primitive type 472 { 473 size_t flags = sState.flags & PF_ARRAY_ALL; 474 if ((flags != 0) && (flags != (PF_VALUE | PF_COMMA))) 475 return STATUS_BAD_TOKEN; 476 sState.flags = PF_VALUE; 477 return read_primitive(tok); 478 } 479 480 default: 481 return STATUS_BAD_TOKEN; 482 } 483 } 484 } 485 read_object()486 status_t Parser::read_object() 487 { 488 status_t res; 489 490 while (true) 491 { 492 // Get token 493 token_t tok = lookup_token(); 494 495 // Analyze token 496 switch (tok) 497 { 498 case JT_EOF: // End of input 499 return STATUS_CORRUPTED; 500 case JT_ERROR: // Error input 501 return pTokenizer->error(); 502 503 case JT_SL_COMMENT: // Skip comments 504 case JT_ML_COMMENT: 505 if (enVersion < JSON_VERSION5) // Only JSON5 allows comments 506 return STATUS_BAD_TOKEN; 507 break; 508 509 case JT_COMMA: // Comma 510 if ((sState.flags & PF_OBJECT_ALL) != (PF_PROPERTY | PF_COLON | PF_VALUE)) 511 return STATUS_BAD_TOKEN; 512 sState.flags |= PF_COMMA; // Add comma flag 513 break; 514 515 case JT_COLON: // Colon 516 if ((sState.flags & PF_OBJECT_ALL) != PF_PROPERTY) 517 return STATUS_BAD_TOKEN; 518 sState.flags |= PF_COLON; 519 break; 520 521 case JT_RC_BRACE: // End of current object 522 { 523 size_t flags = sState.flags & PF_OBJECT_ALL; 524 525 // If property is present, there should be a corresponding value 526 // JSON5 allows a comma before closing brace 527 if (flags == PF_OBJECT_ALL) 528 { 529 if (enVersion < JSON_VERSION5) 530 return STATUS_BAD_TOKEN; 531 } 532 else if ((flags != 0) && (flags != (PF_PROPERTY | PF_COLON | PF_VALUE))) 533 return STATUS_BAD_TOKEN; 534 535 sCurrent.type = JE_OBJECT_END; 536 return pop_state(); 537 } 538 539 case JT_LQ_BRACE: // Start of array value 540 // Property should be defined first 541 if ((sState.flags & PF_OBJECT_ALL) != (PF_PROPERTY | PF_COLON)) 542 return STATUS_BAD_TOKEN; 543 sCurrent.type = JE_ARRAY_START; 544 sState.flags |= PF_VALUE; 545 return push_state(READ_ARRAY); 546 547 case JT_LC_BRACE: // Start of object value 548 // Property should be defined first 549 if ((sState.flags & PF_OBJECT_ALL) != (PF_PROPERTY | PF_COLON)) 550 return STATUS_BAD_TOKEN; 551 sCurrent.type = JE_OBJECT_START; 552 sState.flags |= PF_VALUE; 553 return push_state(READ_OBJECT); 554 555 case JT_SQ_STRING: 556 case JT_DQ_STRING: 557 case JT_IDENTIFIER: // Property name 558 { 559 size_t flags = sState.flags & PF_OBJECT_ALL; 560 if ((flags == 0) || (flags == PF_OBJECT_ALL)) // Property name? 561 { 562 if ((res = read_primitive(tok)) == STATUS_OK) 563 { 564 sState.flags = PF_PROPERTY; 565 sCurrent.type = JE_PROPERTY; // Override type of event 566 } 567 } 568 else if (flags == (PF_PROPERTY | PF_COLON)) // Value? 569 { 570 if ((res = read_primitive(tok)) == STATUS_OK) 571 sState.flags |= PF_VALUE; 572 } 573 else 574 res = STATUS_BAD_STATE; 575 576 return res; 577 } 578 579 case JT_TRUE: 580 case JT_FALSE: 581 case JT_NULL: 582 case JT_DECIMAL: 583 case JT_DOUBLE: 584 case JT_HEXADECIMAL: // Read primitive type 585 { 586 if ((sState.flags & PF_OBJECT_ALL) != (PF_PROPERTY | PF_COLON)) 587 return STATUS_BAD_TOKEN; 588 sState.flags |= PF_VALUE; 589 return read_primitive(tok); 590 } 591 592 default: 593 return STATUS_BAD_TOKEN; 594 } 595 } 596 } 597 read_next(event_t * ev)598 status_t Parser::read_next(event_t *ev) 599 { 600 if (pTokenizer == NULL) 601 return STATUS_BAD_STATE; 602 603 status_t res; 604 605 // Root position? 606 switch (sState.mode) 607 { 608 case READ_ROOT: 609 res = read_root(); 610 break; 611 612 case READ_ARRAY: 613 res = read_array(); 614 break; 615 616 case READ_OBJECT: 617 res = read_object(); 618 break; 619 620 default: 621 return STATUS_BAD_STATE; 622 } 623 624 // Deploy result if there is place to deploy 625 if ((res == STATUS_OK) && (ev != NULL)) 626 res = get_current(ev); 627 628 return res; 629 } 630 read_next_type(event_type_t * type)631 status_t Parser::read_next_type(event_type_t *type) 632 { 633 event_t ev; 634 status_t res = read_next(&ev); 635 if ((res == STATUS_OK) && (type != NULL)) 636 *type = ev.type; 637 return res; 638 } 639 read_string(LSPString * dst)640 status_t Parser::read_string(LSPString *dst) 641 { 642 event_t ev; 643 status_t res = read_next(&ev); 644 if (res != STATUS_OK) 645 return res; 646 switch (ev.type) 647 { 648 case JE_STRING: break; 649 case JE_NULL: return STATUS_NULL; 650 default: return STATUS_BAD_TYPE; 651 } 652 if (dst != NULL) 653 dst->swap(&ev.sValue); 654 return STATUS_OK; 655 } 656 read_double(double * dst)657 status_t Parser::read_double(double *dst) 658 { 659 event_t ev; 660 status_t res = read_next(&ev); 661 if (res != STATUS_OK) 662 return res; 663 switch (ev.type) 664 { 665 case JE_DOUBLE: break; 666 case JE_NULL: return STATUS_NULL; 667 default: return STATUS_BAD_TYPE; 668 } 669 if (dst != NULL) 670 *dst = ev.fValue; 671 return STATUS_OK; 672 } 673 read_int(ssize_t * dst)674 status_t Parser::read_int(ssize_t *dst) 675 { 676 event_t ev; 677 status_t res = read_next(&ev); 678 if (res != STATUS_OK) 679 return res; 680 switch (ev.type) 681 { 682 case JE_INTEGER: break; 683 case JE_NULL: return STATUS_NULL; 684 default: return STATUS_BAD_TYPE; 685 } 686 if (dst != NULL) 687 *dst = ev.iValue; 688 return STATUS_OK; 689 } 690 read_bool(bool * dst)691 status_t Parser::read_bool(bool *dst) 692 { 693 event_t ev; 694 status_t res = read_next(&ev); 695 if (res != STATUS_OK) 696 return res; 697 switch (ev.type) 698 { 699 case JE_BOOL: break; 700 case JE_NULL: return STATUS_NULL; 701 default: return STATUS_BAD_TYPE; 702 } 703 if (dst != NULL) 704 *dst = ev.bValue; 705 return STATUS_OK; 706 } 707 get_string(LSPString * dst)708 status_t Parser::get_string(LSPString *dst) 709 { 710 if (pTokenizer == NULL) 711 return STATUS_BAD_STATE; 712 switch (sCurrent.type) 713 { 714 case JE_STRING: break; 715 case JE_NULL: return STATUS_NULL; 716 default: return STATUS_BAD_TYPE; 717 } 718 if (dst != NULL) 719 return (dst->set(&sCurrent.sValue)) ? STATUS_OK : STATUS_NO_MEM; 720 return STATUS_OK; 721 } 722 get_double(double * dst)723 status_t Parser::get_double(double *dst) 724 { 725 if (pTokenizer == NULL) 726 return STATUS_BAD_STATE; 727 switch (sCurrent.type) 728 { 729 case JE_DOUBLE: break; 730 case JE_NULL: return STATUS_NULL; 731 default: return STATUS_BAD_TYPE; 732 } 733 if (dst != NULL) 734 *dst = sCurrent.fValue; 735 return STATUS_OK; 736 } 737 get_int(ssize_t * dst)738 status_t Parser::get_int(ssize_t *dst) 739 { 740 if (pTokenizer == NULL) 741 return STATUS_BAD_STATE; 742 switch (sCurrent.type) 743 { 744 case JE_INTEGER: break; 745 case JE_NULL: return STATUS_NULL; 746 default: return STATUS_BAD_TYPE; 747 } 748 if (dst != NULL) 749 *dst = sCurrent.iValue; 750 return STATUS_OK; 751 } 752 get_bool(bool * dst)753 status_t Parser::get_bool(bool *dst) 754 { 755 if (pTokenizer == NULL) 756 return STATUS_BAD_STATE; 757 switch (sCurrent.type) 758 { 759 case JE_BOOL: break; 760 case JE_NULL: return STATUS_NULL; 761 default: return STATUS_BAD_TYPE; 762 } 763 if (dst != NULL) 764 *dst = sCurrent.bValue; 765 return STATUS_OK; 766 } 767 skip_next()768 status_t Parser::skip_next() 769 { 770 status_t res = read_next(NULL); 771 if (res == STATUS_OK) 772 res = skip_current(); 773 return res; 774 } 775 skip_current()776 status_t Parser::skip_current() 777 { 778 status_t res; 779 780 switch (sCurrent.type) 781 { 782 case JE_OBJECT_START: 783 while (true) 784 { 785 // Get next event 786 if ((res = read_next(NULL)) != STATUS_OK) 787 return res; 788 789 // Analyze event type 790 if (sCurrent.type != JE_PROPERTY) 791 return (sCurrent.type == JE_OBJECT_END) ? STATUS_OK : STATUS_BAD_TOKEN; 792 793 // Skip the value after property 794 if ((res = read_next(NULL)) != STATUS_OK) 795 return res; 796 if ((res = skip_current()) != STATUS_OK) 797 return res; 798 } 799 break; 800 801 case JE_ARRAY_START: 802 while (true) 803 { 804 // Get next event 805 if ((res = read_next(NULL)) != STATUS_OK) 806 return res; 807 808 // Analyze event type 809 if (sCurrent.type == JE_ARRAY_END) 810 return STATUS_OK; 811 812 // Skip the value after property 813 if ((res = skip_current()) != STATUS_OK) 814 return res; 815 } 816 break; 817 818 case JE_PROPERTY: 819 // Skip the value after property 820 if ((res = read_next(NULL)) != STATUS_OK) 821 return res; 822 if ((res = skip_current()) != STATUS_OK) 823 return res; 824 break; 825 826 default: 827 break; 828 } 829 830 return STATUS_OK; 831 } 832 833 } /* namespace json */ 834 } /* namespace lsp */ 835