annotate src/share/vm/utilities/json.cpp @ 12823:b756e7a2ec33

Added tag jdk-9+181 for changeset 4a443796f6f5
author prr
date Thu, 03 Aug 2017 18:56:57 +0000
parents b82a370a474e
children
rev   line source
neliasso@9191 1 /*
ppunegov@12088 2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
neliasso@9191 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
neliasso@9191 4 *
neliasso@9191 5 * This code is free software; you can redistribute it and/or modify it
neliasso@9191 6 * under the terms of the GNU General Public License version 2 only, as
neliasso@9191 7 * published by the Free Software Foundation.
neliasso@9191 8 *
neliasso@9191 9 * This code is distributed in the hope that it will be useful, but WITHOUT
neliasso@9191 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
neliasso@9191 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
neliasso@9191 12 * version 2 for more details (a copy is included in the LICENSE file that
neliasso@9191 13 * accompanied this code).
neliasso@9191 14 *
neliasso@9191 15 * You should have received a copy of the GNU General Public License version
neliasso@9191 16 * 2 along with this work; if not, write to the Free Software Foundation,
neliasso@9191 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
neliasso@9191 18 *
neliasso@9191 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
neliasso@9191 20 * or visit www.oracle.com if you need additional information or have any
neliasso@9191 21 * questions.
neliasso@9191 22 *
neliasso@9191 23 */
neliasso@9191 24
neliasso@9191 25 /*
neliasso@9191 26 * This is not really json in the state it is now.
neliasso@9191 27 * Some differences:
neliasso@9191 28 * - Double quotes around the key in an object is not enforced.
neliasso@9191 29 * i.e you can write: { foo : "bar" } instead of { "foo" : "bar" }.
neliasso@9191 30 * - Comments are allowed.
neliasso@9191 31 * - The last element in an object or array can have an ending comma.
neliasso@9191 32 */
neliasso@9191 33
neliasso@9191 34 #include "precompiled.hpp"
neliasso@9191 35 #include "utilities/json.hpp"
neliasso@9191 36 #include "utilities/ostream.hpp"
neliasso@9191 37 #include <math.h>
neliasso@9191 38
neliasso@9191 39 const char* strchrnul_(const char *s, int c) {
neliasso@9191 40 const char* tmp = strchr(s, c);
neliasso@9191 41 return tmp == NULL ? s + strlen(s) : tmp;
neliasso@9191 42 }
neliasso@9191 43
neliasso@9191 44 JSON::JSON(const char* text, bool silent, outputStream* st)
neliasso@9191 45 : start(text), pos(text), mark(text),
neliasso@9191 46 level(0), line(1), column(0), silent(silent), _valid(true), _st(st)
neliasso@9191 47 {
neliasso@9191 48 }
neliasso@9191 49
neliasso@9191 50 void JSON::parse() {
neliasso@9191 51 assert(start != NULL, "Need something to parse");
neliasso@9191 52 if (start == NULL) {
neliasso@9191 53 _valid = false;
neliasso@9191 54 error(INTERNAL_ERROR, "JSON parser was called with a string that was NULL.");
neliasso@9191 55 } else {
neliasso@9191 56 _valid = parse_json_value();
neliasso@9191 57 }
neliasso@9191 58 }
neliasso@9191 59
neliasso@9191 60 bool JSON::valid() {
neliasso@9191 61 return _valid;
neliasso@9191 62 }
neliasso@9191 63
neliasso@9191 64 bool JSON::parse_json_value() {
neliasso@9191 65 int c;
neliasso@9191 66
neliasso@9191 67 c = skip_to_token();
neliasso@9191 68 if (c == -1) {
neliasso@9191 69 return false;
neliasso@9191 70 }
neliasso@9191 71
neliasso@9191 72 // Must start with object or array
neliasso@9191 73 if (level == 0) {
neliasso@9191 74
neliasso@9191 75 switch (c) {
neliasso@9191 76 case '{':
neliasso@9191 77 if (parse_json_object() == false) {
neliasso@9191 78 return false;
neliasso@9191 79 }
neliasso@9191 80 c = skip_to_token();
neliasso@9191 81 if (c > 0) {
neliasso@9191 82 mark_pos();
neliasso@9191 83 error(SYNTAX_ERROR, "Only one top level object/array is allowed.");
neliasso@9191 84 return false;
neliasso@9191 85 } else if (c < 0) {
neliasso@9191 86 return false;
neliasso@9191 87 }
neliasso@9191 88 return true;
neliasso@9191 89
neliasso@9191 90 case '[':
neliasso@9191 91 if (parse_json_array() == false) {
neliasso@9191 92 return false;
neliasso@9191 93 }
neliasso@9191 94 c = skip_to_token();
neliasso@9191 95 if (c > 0) {
neliasso@9191 96 mark_pos();
neliasso@9191 97 error(SYNTAX_ERROR, "Only one top level object/array is allowed.");
neliasso@9191 98 return false;
neliasso@9191 99 } else if (c < 0) {
neliasso@9191 100 return false;
neliasso@9191 101 }
neliasso@9191 102 return true;
neliasso@9191 103
neliasso@9191 104 case 0:
neliasso@9191 105 error(SYNTAX_ERROR, "EOS was encountered before any json declarations");
neliasso@9191 106 return false;
neliasso@9191 107
neliasso@9191 108 default:
neliasso@9191 109 error(SYNTAX_ERROR, "Json must start with an object or an array.");
neliasso@9191 110 return false;
neliasso@9191 111 }
neliasso@9191 112 } else { // level > 0
neliasso@9191 113 switch (c) {
neliasso@9191 114 case '{':
neliasso@9191 115 return parse_json_object();
neliasso@9191 116
neliasso@9191 117 case '[':
neliasso@9191 118 return parse_json_array();
neliasso@9191 119
neliasso@9191 120 case '"':
neliasso@9191 121 return parse_json_string();
neliasso@9191 122
neliasso@9191 123 case '-': case '0':
neliasso@9191 124 case '1': case '2': case '3':
neliasso@9191 125 case '4': case '5': case '6':
neliasso@9191 126 case '7': case '8': case '9':
neliasso@9191 127 return parse_json_number();
neliasso@9191 128
neliasso@9191 129 case 't':
neliasso@9191 130 return parse_json_symbol("true", JSON_TRUE);
neliasso@9191 131
neliasso@9191 132 case 'f':
neliasso@9191 133 return parse_json_symbol("false", JSON_FALSE);
neliasso@9191 134
neliasso@9191 135 case 'n':
neliasso@9191 136 return parse_json_symbol("null", JSON_NULL);
neliasso@9191 137
neliasso@9191 138 case 0:
neliasso@9191 139 error(SYNTAX_ERROR, "EOS was encountered when expecting a json value.");
neliasso@9191 140 return false;
neliasso@9191 141
neliasso@9191 142 default:
neliasso@9191 143 error(SYNTAX_ERROR, "Could not parse as a json value (did you forget to quote your strings?).");
neliasso@9191 144 return false;
neliasso@9191 145 }
neliasso@9191 146 }
neliasso@9191 147 }
neliasso@9191 148
neliasso@9191 149 // Should only be called when we actually have the start of an object
neliasso@9191 150 // Otherwise it is an internal error
neliasso@9191 151 bool JSON::parse_json_object() {
neliasso@9191 152 NOT_PRODUCT(const char* prev_pos);
neliasso@9191 153 int c;
neliasso@9191 154
neliasso@9191 155 mark_pos();
neliasso@9191 156 // Check that we are not called in error
neliasso@9191 157 if (expect_any("{", "object start", INTERNAL_ERROR) <= 0) {
neliasso@9191 158 return false;
neliasso@9191 159 }
neliasso@9191 160
neliasso@9191 161 if (!callback(JSON_OBJECT_BEGIN, NULL, level++)) {
neliasso@9191 162 return false;
neliasso@9191 163 }
neliasso@9191 164
neliasso@9191 165 for (;;) {
neliasso@9191 166 mark_pos();
neliasso@9191 167 c = skip_to_token();
neliasso@9191 168 if (c == 0) {
neliasso@9191 169 error(SYNTAX_ERROR, "EOS when expecting an object key or object end");
neliasso@9191 170 return false;
neliasso@9191 171 } else if (c < 0) {
neliasso@9191 172 return false;
neliasso@9191 173 } else if (c == '}') {
neliasso@9191 174 // We got here from either empty object "{}" or ending comma "{a:1,}"
neliasso@9191 175 next();
neliasso@9191 176 break;
neliasso@9191 177 }
neliasso@9191 178
neliasso@9191 179 NOT_PRODUCT(prev_pos = pos);
neliasso@9191 180 if (parse_json_key() == false) {
neliasso@9191 181 return false;
neliasso@9191 182 }
neliasso@9191 183 assert(pos > prev_pos, "parsing stalled");
neliasso@9191 184
neliasso@9191 185 skip_to_token();
neliasso@9191 186 mark_pos();
neliasso@9191 187 if (expect_any(":", "object key-value separator") <= 0) {
neliasso@9191 188 return false;
neliasso@9191 189 }
neliasso@9191 190
neliasso@9191 191 skip_to_token();
neliasso@9191 192 mark_pos();
neliasso@9191 193 NOT_PRODUCT(prev_pos = pos);
neliasso@9191 194 if (parse_json_value() == false) {
neliasso@9191 195 return false;
neliasso@9191 196 }
neliasso@9191 197 assert(pos > prev_pos, "parsing stalled");
neliasso@9191 198
neliasso@9191 199 c = skip_to_token();
neliasso@9191 200 mark_pos();
neliasso@9191 201 if (expect_any(",}", "value separator or object end") <= 0) {
neliasso@9191 202 return false;
neliasso@9191 203 }
neliasso@9191 204 if (c == '}') {
neliasso@9191 205 break;
neliasso@9191 206 }
neliasso@9191 207 }
neliasso@9191 208
neliasso@9191 209 assert(c == '}', "array parsing ended without object end token ('}')");
neliasso@9191 210 return callback(JSON_OBJECT_END, NULL, --level);
neliasso@9191 211 }
neliasso@9191 212
neliasso@9191 213 // Should only be called when we actually have the start of an array
neliasso@9191 214 // Otherwise it is an internal error
neliasso@9191 215 bool JSON::parse_json_array() {
neliasso@9191 216 NOT_PRODUCT(const char* prev_pos);
neliasso@9191 217 int c;
neliasso@9191 218
neliasso@9191 219 mark_pos();
neliasso@9191 220 // Check that we are not called in error
neliasso@9191 221 if (expect_any("[", "array start character", INTERNAL_ERROR) <= 0) {
neliasso@9191 222 return false;
neliasso@9191 223 }
neliasso@9191 224
neliasso@9191 225 if (!callback(JSON_ARRAY_BEGIN, NULL, level++)) {
neliasso@9191 226 return false;
neliasso@9191 227 }
neliasso@9191 228
neliasso@9191 229 for (;;) {
neliasso@9191 230 mark_pos();
neliasso@9191 231 c = skip_to_token();
neliasso@9191 232 if (c == 0) {
neliasso@9191 233 error(SYNTAX_ERROR, "EOS when expecting a json value or array end");
neliasso@9191 234 return false;
neliasso@9191 235 } else if (c < 0) {
neliasso@9191 236 return false;
neliasso@9191 237 } else if (c == ']') {
neliasso@9191 238 // We got here from either empty array "[]" or ending comma "[1,]"
neliasso@9191 239 next();
neliasso@9191 240 break;
neliasso@9191 241 }
neliasso@9191 242
neliasso@9191 243 mark_pos();
neliasso@9191 244 NOT_PRODUCT(prev_pos = pos);
neliasso@9191 245 if (parse_json_value() == false) {
neliasso@9191 246 return false;
neliasso@9191 247 }
neliasso@9191 248 assert(pos > prev_pos, "parsing stalled");
neliasso@9191 249
neliasso@9191 250 c = skip_to_token();
neliasso@9191 251 mark_pos();
neliasso@9191 252 if (expect_any(",]", "value separator or array end") <= 0) {
neliasso@9191 253 return false;
neliasso@9191 254 }
neliasso@9191 255 if (c == ']') {
neliasso@9191 256 break;
neliasso@9191 257 }
neliasso@9191 258 }
neliasso@9191 259
neliasso@9191 260 assert(c == ']', "array parsing ended without array end token (']')");
neliasso@9191 261 return callback(JSON_ARRAY_END, NULL, --level);
neliasso@9191 262 }
neliasso@9191 263
neliasso@9191 264 bool JSON::parse_json_string(bool key) {
neliasso@9191 265 const char* end;
neliasso@9191 266 JSON_VAL v;
neliasso@9191 267
neliasso@9191 268 mark_pos();
neliasso@9191 269 if (expect_any("\"", "string start character", INTERNAL_ERROR) <= 0) {
neliasso@9191 270 return false;
neliasso@9191 271 }
neliasso@9191 272
neliasso@9191 273 end = strchr(pos, '"'); // TODO: escapes
neliasso@9191 274 if (end == NULL) {
neliasso@9191 275 error(SYNTAX_ERROR, "String started here never ended. Expected \'\"\' before EOS.");
neliasso@9191 276 return false;
neliasso@9191 277 }
neliasso@9191 278
neliasso@9191 279 v.str.start = pos;
neliasso@9191 280 v.str.length = end - pos;
neliasso@9191 281 skip(end - pos);
neliasso@9191 282
neliasso@9191 283 if (expect_any("\"", "string end character", INTERNAL_ERROR) <= 0) {
neliasso@9191 284 return false;
neliasso@9191 285 }
neliasso@9191 286
neliasso@9191 287 if (key == true) {
neliasso@9191 288 return callback(JSON_KEY, &v, level);
neliasso@9191 289 } else {
neliasso@9191 290 return callback(JSON_STRING, &v, level);
neliasso@9191 291 }
neliasso@9191 292 }
neliasso@9191 293
neliasso@9191 294 // TODO: hotspot equivalents?
neliasso@9191 295 static bool is_alpha(u_char c) {
neliasso@9191 296 return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
neliasso@9191 297 }
neliasso@9191 298 static bool is_numeric(u_char c) {
neliasso@9191 299 return (c >= '0' && c <= '9');
neliasso@9191 300 }
neliasso@9191 301 static bool is_alnum(u_char c) {
neliasso@9191 302 return is_alpha(c) || is_numeric(c);
neliasso@9191 303 }
neliasso@9191 304 static bool is_word(u_char c) {
neliasso@9191 305 return c == '_' || is_alnum(c);
neliasso@9191 306 }
neliasso@9191 307
neliasso@9191 308 // Allow object keys to be without quotation,
neliasso@9191 309 // but then restrict to ([a-zA-Z0-9_])+
neliasso@9191 310 bool JSON::parse_json_key() {
neliasso@9191 311 const char* begin;
neliasso@9191 312 JSON_VAL v;
neliasso@9191 313 u_char c;
neliasso@9191 314
neliasso@9191 315 mark_pos();
neliasso@9191 316 c = peek();
neliasso@9191 317 if (c == '"') {
neliasso@9191 318 return parse_json_string(true);
neliasso@9191 319 }
neliasso@9191 320
neliasso@9191 321 begin = pos;
neliasso@9191 322 c = peek();
neliasso@9191 323 if (c == 0) {
neliasso@9191 324 error(SYNTAX_ERROR, "Got EOS when expecting an object key.");
neliasso@9191 325 return false;
neliasso@9191 326 } else if (is_word(c) == false) {
neliasso@9191 327 error(SYNTAX_ERROR, "Expected an object key, which can be a double-quoted (\") string or a simple string (only alphanumeric characters and underscore, separated by whitespace) that doesn't need to be quoted.");
neliasso@9191 328 return false;
neliasso@9191 329 }
neliasso@9191 330
neliasso@9191 331 for (;;) {
neliasso@9191 332 c = peek();
neliasso@9191 333 // Allow the key to be delimited by control characters and the object key-value separator ':'
neliasso@9191 334 if (c <= ' ' || c == ':') {
neliasso@9191 335 break;
neliasso@9191 336 } else if (is_word(c) == false) {
neliasso@9191 337 error(SYNTAX_ERROR, "Object key need to be quoted, or consist entirely of alphanumeric characters and underscores.");
neliasso@9191 338 return false;
neliasso@9191 339 }
neliasso@9191 340 next();
neliasso@9191 341 }
neliasso@9191 342
neliasso@9191 343 v.str.start = begin;
neliasso@9191 344 v.str.length = pos - begin;
neliasso@9191 345 return callback(JSON_KEY, &v, level);
neliasso@9191 346 }
neliasso@9191 347
neliasso@9191 348 bool JSON::parse_json_number() {
neliasso@9191 349 double double_value;
neliasso@9191 350 int tokens, read;
neliasso@9191 351 JSON_VAL v;
neliasso@9191 352
neliasso@9191 353 mark_pos();
neliasso@9191 354
neliasso@9191 355 // Parsing number - for simplicity ints are limited to 2**53
neliasso@9191 356 // sscanf as a double and check if part is 0.
neliasso@9191 357 tokens = sscanf(pos, "%lf%n", &double_value, &read);
neliasso@9191 358 assert(tokens <= 1, "scanf implementation that counts as a token, parsing json numbers will always fail");
neliasso@9191 359 if (tokens == 1) {
neliasso@9191 360 assert(read > 0, "sanity");
neliasso@9191 361
neliasso@9191 362 if (floor(double_value) == double_value) {
neliasso@9191 363 // No exponent - treat as an int
neliasso@9191 364 v.int_value = (int)double_value;
neliasso@9191 365 if (!callback(JSON_NUMBER_INT, &v, level)) {
neliasso@9191 366 return false;
neliasso@9191 367 }
neliasso@9191 368 } else {
neliasso@9191 369 v.double_value = double_value;
neliasso@9191 370 if (!callback(JSON_NUMBER_FLOAT, &v, level)) {
neliasso@9191 371 return false;
neliasso@9191 372 }
neliasso@9191 373 }
neliasso@9191 374 skip(read);
neliasso@9191 375 return true;
neliasso@9191 376 }
neliasso@9191 377
neliasso@9191 378 error(SYNTAX_ERROR, "Couldn't parse json number (note that exponents are not supported).");
neliasso@9191 379 return false;
neliasso@9191 380 }
neliasso@9191 381
neliasso@9191 382 bool JSON::parse_json_symbol(const char* name, JSON_TYPE symbol) {
neliasso@9191 383 if (expect_string(name, "maybe you forgot to quote your strings?") == false) {
neliasso@9191 384 mark_pos();
neliasso@9191 385 return false;
neliasso@9191 386 }
neliasso@9191 387 return callback(symbol, NULL, level);
neliasso@9191 388 }
neliasso@9191 389
neliasso@9191 390 void JSON::mark_pos() {
neliasso@9191 391 assert((mark == start || *(mark - 1)) != 0, "buffer overrun");
neliasso@9191 392 assert(mark <= pos, "mark runahead");
neliasso@9191 393
neliasso@9191 394 u_char c;
neliasso@9191 395
neliasso@9191 396 while (mark < pos) {
neliasso@9191 397 c = *mark;
neliasso@9191 398 assert(c != 0, "pos buffer overrun?");
neliasso@9191 399 if (c != 0) {
neliasso@9191 400 mark++;
neliasso@9191 401 column++;
neliasso@9191 402 }
neliasso@9191 403 if (c == '\n') {
neliasso@9191 404 line++;
neliasso@9191 405 column = 0;
neliasso@9191 406 }
neliasso@9191 407 }
neliasso@9191 408
neliasso@9191 409 assert(mark <= pos, "mark runahead");
neliasso@9191 410 }
neliasso@9191 411
neliasso@9191 412 u_char JSON::next() {
neliasso@9191 413 assert((pos == start || *(pos - 1)) != 0, "buffer overrun");
neliasso@9191 414
neliasso@9191 415 u_char c = *pos;
neliasso@9191 416 if (c != 0) {
neliasso@9191 417 pos++;
neliasso@9191 418 }
neliasso@9191 419 return c;
neliasso@9191 420 }
neliasso@9191 421
neliasso@9191 422 u_char JSON::peek() {
neliasso@9191 423 return *pos;
neliasso@9191 424 }
neliasso@9191 425
neliasso@9191 426 // Peek ahead i chars (0 is same as peek())
neliasso@9191 427 u_char JSON::peek(size_t i) {
neliasso@9191 428 u_char c;
neliasso@9191 429 const char* p;
neliasso@9191 430
neliasso@9191 431 p = pos;
neliasso@9191 432 c = *p;
neliasso@9191 433 while (i > 0 && c != 0) {
neliasso@9191 434 i--;
neliasso@9191 435 p++;
neliasso@9191 436 c = *p;
neliasso@9191 437 }
neliasso@9191 438 return c;
neliasso@9191 439 }
neliasso@9191 440
neliasso@9191 441 /*
neliasso@9191 442 * Check that one of the expected characters is next in the stream.
neliasso@9191 443 * If not, it is an error.
neliasso@9191 444 * Returns 0 if EOS is encountered.
neliasso@9191 445 * Returns -1 if the next character was not one of the expected.
neliasso@9191 446 * Otherwise consumes and returns the expected character that was encountered.
neliasso@9191 447 */
neliasso@9191 448 int JSON::expect_any(const char* valid_chars, const char* error_msg, JSON_ERROR e) {
neliasso@9191 449 size_t len;
neliasso@9191 450 u_char c;
neliasso@9191 451
neliasso@9191 452 len = strlen(valid_chars);
neliasso@9191 453 assert(len > 0, "need non-empty string");
neliasso@9191 454
neliasso@9191 455 c = peek();
neliasso@9191 456 if (c == 0) {
neliasso@9191 457 error(e, "Got EOS when expecting %s (%s\'%s\').", error_msg, len > 1 ? "one of " : "", valid_chars);
neliasso@9191 458 return 0;
neliasso@9191 459 }
neliasso@9191 460 for (size_t i = 0; i < len; i++) {
neliasso@9191 461 if (c == valid_chars[i]) {
neliasso@9191 462 return next();
neliasso@9191 463 }
neliasso@9191 464 }
neliasso@9191 465 error(e, "Expected %s (%s\'%s\').", error_msg, len > 1 ? "one of " : "", valid_chars);
neliasso@9191 466 return -1;
neliasso@9191 467 }
neliasso@9191 468
neliasso@9191 469 /*
neliasso@9191 470 * Check that the expected string is next in the stream.
neliasso@9191 471 * If not, it is an error.
neliasso@9191 472 * Consumes the expected characters if they are present.
neliasso@9191 473 * Returns true if the expected characters were present, otherwise false.
neliasso@9191 474 */
neliasso@9191 475 bool JSON::expect_string(const char* expected_string, const char* error_msg, JSON_ERROR e) {
neliasso@9191 476 u_char c, expected_char;
neliasso@9191 477 size_t len;
neliasso@9191 478
neliasso@9191 479 assert(expected_string != NULL, "need non-null string");
neliasso@9191 480 len = strlen(expected_string);
neliasso@9191 481 assert(len > 0, "need non-empty string");
neliasso@9191 482
neliasso@9191 483 for (size_t i = 0; i < len; i++) {
neliasso@9191 484 expected_char = expected_string[i];
neliasso@9191 485 assert(expected_char > ' ', "not sane for control characters");
neliasso@9191 486 if (expected_char <= ' ') {
neliasso@9191 487 error(INTERNAL_ERROR, "expect got a control char");
neliasso@9191 488 }
neliasso@9191 489 c = pos[i];
neliasso@9191 490 if (c == 0) {
neliasso@9191 491 error(e, "EOS encountered when expecting %s (\"%s\")", error_msg, expected_string);
neliasso@9191 492 return false;
neliasso@9191 493 } else if (c != expected_char) {
neliasso@9191 494 error(e, "Expected \"%s\" (%s)", expected_string, error_msg);
neliasso@9191 495 return false;
neliasso@9191 496 }
neliasso@9191 497 }
neliasso@9191 498 skip(len);
neliasso@9191 499 return true;
neliasso@9191 500 }
neliasso@9191 501
neliasso@9191 502 /*
neliasso@9191 503 * Skip i characters.
neliasso@9191 504 * Returns number of characters skipped.
neliasso@9191 505 */
neliasso@9191 506 size_t JSON::skip(size_t i) {
neliasso@9191 507 u_char c;
neliasso@9191 508 size_t j;
neliasso@9191 509
neliasso@9191 510 c = peek();
neliasso@9191 511 for (j = i; c != 0 && j > 0; j--) {
neliasso@9191 512 c = next();
neliasso@9191 513 }
neliasso@9191 514 return i - j;
neliasso@9191 515 }
neliasso@9191 516
neliasso@9191 517 /*
neliasso@9191 518 * Skip whitespace and comments.
neliasso@9191 519 * Returns the first token after whitespace/comments without consuming it
neliasso@9191 520 * Returns 0 if EOS is encountered.
neliasso@9191 521 * Returns -1 if there is an error
neliasso@9191 522 */
neliasso@9191 523 int JSON::skip_to_token() {
neliasso@9191 524 for (;;) {
neliasso@9191 525 int c = peek(0);
neliasso@9191 526 if (c == '/') {
neliasso@9191 527 u_char c2 = peek(1);
neliasso@9191 528 if (c2 == '/') {
neliasso@9191 529 c = skip_line_comment();
neliasso@9191 530 } else if (c2 == '*') {
neliasso@9191 531 c = skip_block_comment();
neliasso@9191 532 if (c < 0) {
neliasso@9191 533 return -1;
neliasso@9191 534 }
neliasso@9191 535 }
neliasso@9191 536 // Fall through to keep checking if there
neliasso@9191 537 // are more whitespace / comments to skip
neliasso@9191 538 }
neliasso@9191 539 if (c == 0 || c > ' ') {
neliasso@9191 540 return c;
neliasso@9191 541 }
neliasso@9191 542 next();
neliasso@9191 543 }
neliasso@9191 544 return 0;
neliasso@9191 545 }
neliasso@9191 546
neliasso@9191 547 /*
neliasso@9191 548 * Skip to, and return the wanted char without consuming it
neliasso@9191 549 * Returns 0 if EOS is encountered.
neliasso@9191 550 */
neliasso@9191 551 u_char JSON::skip_to(u_char want) {
neliasso@9191 552 // We want the bookkeeping done in next().
neliasso@9191 553 // Otherwise strchr could have been used.
neliasso@9191 554 u_char c;
neliasso@9191 555 for(;;) {
neliasso@9191 556 c = peek();
neliasso@9191 557 if (c == 0 || c == want) {
neliasso@9191 558 return c;
neliasso@9191 559 }
neliasso@9191 560 next();
neliasso@9191 561 }
neliasso@9191 562 }
neliasso@9191 563
neliasso@9191 564 /*
neliasso@9191 565 * Should only be called when we actually have a line comment to skip.
neliasso@9191 566 * Otherwise it is an internal error.
neliasso@9191 567 *
neliasso@9191 568 * Will return the first token after the line comment without consuming it.
neliasso@9191 569 * Returns 0 if EOS is encoutered.
neliasso@9191 570 */
neliasso@9191 571 u_char JSON::skip_line_comment() {
neliasso@9191 572 u_char c;
neliasso@9191 573
neliasso@9191 574 // Check that we are not called in error
neliasso@9191 575 expect_any("/", "line comment start", INTERNAL_ERROR);
neliasso@9191 576 expect_any("/", "line comment start", INTERNAL_ERROR);
neliasso@9191 577
neliasso@9191 578 c = skip_to('\n');
neliasso@9191 579 if (c == 0) {
neliasso@9191 580 return 0;
neliasso@9191 581 }
neliasso@9191 582 next();
neliasso@9191 583 return next();
neliasso@9191 584 }
neliasso@9191 585
neliasso@9191 586 /*
neliasso@9191 587 * Should only be called when we actually have a block comment to skip.
neliasso@9191 588 * Otherwise it is an internal error.
neliasso@9191 589 *
neliasso@9191 590 * Returns the first token after the block comment without consuming it.
neliasso@9191 591 * Returns -1 if EOS is encountered in the middle of a comment.
neliasso@9191 592 */
neliasso@9191 593 int JSON::skip_block_comment() {
neliasso@9191 594 const char* current;
neliasso@9191 595
neliasso@9191 596 // Check that we are not called in error.
neliasso@9191 597 if (peek() != '/' || peek(1) != '*') {
neliasso@9191 598 // Let expect handle EOS.
neliasso@9191 599 expect_string("/*", "block comment start", INTERNAL_ERROR);
neliasso@9191 600 return 0;
neliasso@9191 601 }
neliasso@9191 602
neliasso@9191 603 current = pos;
neliasso@9191 604 for (;;) {
neliasso@9191 605 current = strchrnul_(current, '*');
neliasso@9191 606
neliasso@9191 607 if (current[0] == 0 || current[1] == 0) {
neliasso@9191 608 // Advance error marker to start of block comment
neliasso@9191 609 mark_pos();
neliasso@9191 610 error(SYNTAX_ERROR, "Block comment started here never ended. Expected \"*/\" before EOS.");
neliasso@9191 611 return -1;
neliasso@9191 612 }
neliasso@9191 613
neliasso@9191 614 if (current[1] == '/') {
neliasso@9191 615 pos = current;
neliasso@9191 616 if (expect_string("*/", "block comment end", INTERNAL_ERROR) == false) {
neliasso@9191 617 return -1;
neliasso@9191 618 }
neliasso@9191 619 // Found block comment end
neliasso@9191 620 return peek();
neliasso@9191 621 }
neliasso@9191 622 current++;
neliasso@9191 623 }
neliasso@9191 624 }
neliasso@9191 625
neliasso@9191 626 const char* JSON::strerror(JSON_ERROR e) {
neliasso@9191 627 switch (e) {
neliasso@9191 628 case SYNTAX_ERROR:
neliasso@9191 629 return "Syntax error";
neliasso@9191 630 case INTERNAL_ERROR:
neliasso@9191 631 return "Internal error";
neliasso@9191 632 case KEY_ERROR:
neliasso@9191 633 return "Key error";
neliasso@9191 634 case VALUE_ERROR:
neliasso@9191 635 return "Value error";
neliasso@9191 636 default:
neliasso@9191 637 ShouldNotReachHere();
neliasso@9191 638 return "Unknown error";
neliasso@9191 639 }
neliasso@9191 640 }
neliasso@9191 641
neliasso@9191 642 void JSON::error(JSON_ERROR e, const char* format, ...) {
neliasso@9191 643 _valid = false;
neliasso@9191 644
neliasso@9191 645 if (!silent) {
neliasso@9191 646 const char* line_start;
neliasso@9191 647 const char* tmp;
neliasso@9191 648 size_t line_length;
neliasso@9191 649 va_list args;
neliasso@9191 650 u_char c;
neliasso@9191 651
neliasso@9191 652 _st->print("%s on line %u byte %u: ", JSON::strerror(e), line, column + 1);
neliasso@9191 653 va_start(args, format);
neliasso@9191 654 _st->vprint(format, args);
neliasso@9191 655 _st->cr();
neliasso@9191 656 va_end(args);
neliasso@9191 657
neliasso@9191 658 line_start = mark - column;
neliasso@9191 659 assert(line_start >= start, "out of bounds");
neliasso@9191 660 assert(line_start <= mark, "out of bounds");
neliasso@9191 661 assert(line_start == start || line_start[-1] == '\n', "line counting error");
neliasso@9191 662
neliasso@9191 663 c = *pos;
neliasso@9191 664 if (c == 0) {
neliasso@9191 665 _st->print(" Got ");
neliasso@9191 666 _st->print_cr("EOS.");
neliasso@9191 667 }
neliasso@9191 668 tmp = mark;
neliasso@9191 669 c = *tmp;
neliasso@9191 670 if (c > ' ') {
neliasso@9191 671 _st->print(" At ");
neliasso@9191 672 _st->print("'");
neliasso@9191 673 while (c > ' ') {
neliasso@9191 674 _st->print("%c", c);
neliasso@9191 675 tmp++;
neliasso@9191 676 c = *tmp;
neliasso@9191 677 }
neliasso@9191 678 _st->print_cr("'.");
neliasso@9191 679 }
neliasso@9191 680
neliasso@9191 681 // Skip to newline or EOS
neliasso@9191 682 tmp = strchrnul_(mark, '\n');
neliasso@9191 683 line_length = tmp - line_start;
neliasso@9191 684
neliasso@9191 685 _st->print_cr("%s", line_start);
neliasso@9191 686 }
neliasso@9191 687 }
neliasso@9191 688