Branch data Line data Source code
1 : : /* JSON parsing
2 : : Copyright (C) 2017-2024 Free Software Foundation, Inc.
3 : : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it under
8 : : the terms of the GNU General Public License as published by the Free
9 : : Software Foundation; either version 3, or (at your option) any later
10 : : version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #include "config.h"
22 : : #include "system.h"
23 : : #include "coretypes.h"
24 : : #include "json-parsing.h"
25 : : #include "pretty-print.h"
26 : : #include "math.h"
27 : : #include "make-unique.h"
28 : : #include "selftest.h"
29 : :
30 : : using namespace json;
31 : :
32 : : /* Declarations relating to parsing JSON, all within an
33 : : anonymous namespace. */
34 : :
35 : : namespace {
36 : :
37 : : /* A typedef representing a single unicode character. */
38 : :
39 : : typedef unsigned unichar;
40 : :
41 : : /* An enum for discriminating different kinds of JSON token. */
42 : :
43 : : enum token_id
44 : : {
45 : : TOK_ERROR,
46 : :
47 : : TOK_EOF,
48 : :
49 : : /* Punctuation. */
50 : : TOK_OPEN_SQUARE,
51 : : TOK_OPEN_CURLY,
52 : : TOK_CLOSE_SQUARE,
53 : : TOK_CLOSE_CURLY,
54 : : TOK_COLON,
55 : : TOK_COMMA,
56 : :
57 : : /* Literal names. */
58 : : TOK_TRUE,
59 : : TOK_FALSE,
60 : : TOK_NULL,
61 : :
62 : : TOK_STRING,
63 : : TOK_FLOAT_NUMBER,
64 : : TOK_INTEGER_NUMBER
65 : : };
66 : :
67 : : /* Human-readable descriptions of enum token_id. */
68 : :
69 : : static const char *token_id_name[] = {
70 : : "error",
71 : : "EOF",
72 : : "'['",
73 : : "'{'",
74 : : "']'",
75 : : "'}'",
76 : : "':'",
77 : : "','",
78 : : "'true'",
79 : : "'false'",
80 : : "'null'",
81 : : "string",
82 : : "number",
83 : : "number"
84 : : };
85 : :
86 : : /* Tokens within the JSON lexer. */
87 : :
88 : : struct token
89 : : {
90 : : /* The kind of token. */
91 : : enum token_id id;
92 : :
93 : : /* The location of this token within the unicode
94 : : character stream. */
95 : : location_map::range range;
96 : :
97 : : union
98 : : {
99 : : /* Value for TOK_ERROR and TOK_STRING. */
100 : : char *string;
101 : :
102 : : /* Value for TOK_FLOAT_NUMBER. */
103 : : double float_number;
104 : :
105 : : /* Value for TOK_INTEGER_NUMBER. */
106 : : long integer_number;
107 : : } u;
108 : : };
109 : :
110 : : /* A class for lexing JSON. */
111 : :
112 : : class lexer
113 : : {
114 : : public:
115 : : lexer (bool support_comments);
116 : : ~lexer ();
117 : :
118 : : std::unique_ptr<error> add_utf8 (size_t length, const char *utf8_buf);
119 : :
120 : : const token *peek ();
121 : :
122 : : void consume ();
123 : :
124 : : private:
125 : : bool get_char (unichar &out_char, location_map::point *out_point);
126 : : void unget_char ();
127 : : location_map::point get_next_point () const;
128 : : static void dump_token (FILE *outf, const token *tok);
129 : : void lex_token (token *out);
130 : : void lex_string (token *out);
131 : : void lex_number (token *out, unichar first_char);
132 : : bool rest_of_literal (token *out, const char *suffix);
133 : : std::unique_ptr<error> make_error (const char *msg);
134 : : bool consume_single_line_comment (token *out);
135 : : bool consume_multiline_comment (token *out);
136 : :
137 : : private:
138 : : auto_vec<unichar> m_buffer;
139 : : int m_next_char_idx;
140 : : int m_next_char_line;
141 : : int m_next_char_column;
142 : : int m_prev_line_final_column; /* for handling unget_char after a '\n'. */
143 : :
144 : : static const int MAX_TOKENS = 1;
145 : : token m_next_tokens[MAX_TOKENS];
146 : : int m_num_next_tokens;
147 : :
148 : : bool m_support_comments;
149 : : };
150 : :
151 : : /* A class for parsing JSON. */
152 : :
153 : : class parser
154 : : {
155 : : public:
156 : : parser (location_map *out_loc_map,
157 : : bool support_comments);
158 : : ~parser ();
159 : :
160 : : std::unique_ptr<error>
161 : : add_utf8 (size_t length, const char *utf8_buf);
162 : :
163 : : parser_result_t parse_value (int depth);
164 : : parser_result_t parse_object (int depth);
165 : : parser_result_t parse_array (int depth);
166 : :
167 : : std::unique_ptr<error>
168 : : require_eof ();
169 : :
170 : : private:
171 : : location_map::point get_next_token_start ();
172 : : location_map::point get_next_token_end ();
173 : :
174 : : std::unique_ptr<error>
175 : : require (enum token_id tok_id);
176 : :
177 : : result<enum token_id, std::unique_ptr<error>>
178 : : require_one_of (enum token_id tok_id_a, enum token_id tok_id_b);
179 : :
180 : : std::unique_ptr<error>
181 : : error_at (const location_map::range &r,
182 : : const char *fmt, ...) ATTRIBUTE_PRINTF_3;
183 : :
184 : : void maybe_record_range (json::value *jv, const location_map::range &r);
185 : : void maybe_record_range (json::value *jv,
186 : : const location_map::point &start,
187 : : const location_map::point &end);
188 : :
189 : : private:
190 : : lexer m_lexer;
191 : : location_map *m_loc_map;
192 : : };
193 : :
194 : : } // anonymous namespace for parsing implementation
195 : :
196 : : /* Parser implementation. */
197 : :
198 : : /* lexer's ctor. */
199 : :
200 : 112 : lexer::lexer (bool support_comments)
201 : 112 : : m_buffer (), m_next_char_idx (0),
202 : 112 : m_next_char_line (1), m_next_char_column (0),
203 : 112 : m_prev_line_final_column (-1),
204 : 112 : m_num_next_tokens (0),
205 : 112 : m_support_comments (support_comments)
206 : : {
207 : 0 : }
208 : :
209 : : /* lexer's dtor. */
210 : :
211 : 112 : lexer::~lexer ()
212 : : {
213 : 132 : while (m_num_next_tokens > 0)
214 : 20 : consume ();
215 : 112 : }
216 : :
217 : : /* Peek the next token. */
218 : :
219 : : const token *
220 : 688 : lexer::peek ()
221 : : {
222 : 688 : if (m_num_next_tokens == 0)
223 : : {
224 : 496 : lex_token (&m_next_tokens[0]);
225 : 496 : m_num_next_tokens++;
226 : : }
227 : 688 : return &m_next_tokens[0];
228 : : }
229 : :
230 : : /* Consume the next token. */
231 : :
232 : : void
233 : 496 : lexer::consume ()
234 : : {
235 : 496 : if (m_num_next_tokens == 0)
236 : 0 : peek ();
237 : :
238 : 496 : gcc_assert (m_num_next_tokens > 0);
239 : 496 : gcc_assert (m_num_next_tokens <= MAX_TOKENS);
240 : :
241 : 496 : if (0)
242 : : {
243 : : fprintf (stderr, "consuming token: ");
244 : : dump_token (stderr, &m_next_tokens[0]);
245 : : fprintf (stderr, "\n");
246 : : }
247 : :
248 : 496 : if (m_next_tokens[0].id == TOK_ERROR
249 : 496 : || m_next_tokens[0].id == TOK_STRING)
250 : 80 : free (m_next_tokens[0].u.string);
251 : :
252 : 496 : m_num_next_tokens--;
253 : 496 : memmove (&m_next_tokens[0], &m_next_tokens[1],
254 : 496 : sizeof (token) * m_num_next_tokens);
255 : 496 : }
256 : :
257 : : /* Add LENGTH bytes of UTF-8 encoded text from UTF8_BUF to this lexer's
258 : : buffer.
259 : : Return null if successful, or the error if there was a problem. */
260 : :
261 : : std::unique_ptr<error>
262 : 112 : lexer::add_utf8 (size_t length, const char *utf8_buf)
263 : : {
264 : : /* Adapted from charset.c:one_utf8_to_cppchar. */
265 : 112 : static const uchar masks[6] = { 0x7F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
266 : 112 : static const uchar patns[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
267 : :
268 : 112 : const uchar *inbuf = (const unsigned char *) (utf8_buf);
269 : 112 : const uchar **inbufp = &inbuf;
270 : 112 : size_t *inbytesleftp = &length;
271 : :
272 : 2684 : while (length > 0)
273 : : {
274 : 2572 : unichar c;
275 : 2572 : const uchar *inbuf = *inbufp;
276 : 2572 : size_t nbytes, i;
277 : :
278 : 2572 : c = *inbuf;
279 : 2572 : if (c < 0x80)
280 : : {
281 : 2556 : m_buffer.safe_push (c);
282 : 2556 : *inbytesleftp -= 1;
283 : 2556 : *inbufp += 1;
284 : 2556 : continue;
285 : : }
286 : :
287 : : /* The number of leading 1-bits in the first byte indicates how many
288 : : bytes follow. */
289 : 32 : for (nbytes = 2; nbytes < 7; nbytes++)
290 : 32 : if ((c & ~masks[nbytes-1]) == patns[nbytes-1])
291 : 16 : goto found;
292 : 0 : return make_error ("ill-formed UTF-8 sequence");
293 : 16 : found:
294 : :
295 : 16 : if (*inbytesleftp < nbytes)
296 : 0 : return make_error ("ill-formed UTF-8 sequence");
297 : :
298 : 16 : c = (c & masks[nbytes-1]);
299 : 16 : inbuf++;
300 : 48 : for (i = 1; i < nbytes; i++)
301 : : {
302 : 32 : unichar n = *inbuf++;
303 : 32 : if ((n & 0xC0) != 0x80)
304 : 0 : return make_error ("ill-formed UTF-8 sequence");
305 : 32 : c = ((c << 6) + (n & 0x3F));
306 : : }
307 : :
308 : : /* Make sure the shortest possible encoding was used. */
309 : 16 : if (( c <= 0x7F && nbytes > 1)
310 : 16 : || (c <= 0x7FF && nbytes > 2)
311 : 16 : || (c <= 0xFFFF && nbytes > 3)
312 : 16 : || (c <= 0x1FFFFF && nbytes > 4)
313 : 16 : || (c <= 0x3FFFFFF && nbytes > 5))
314 : 0 : return make_error ("ill-formed UTF-8:"
315 : 0 : " shortest possible encoding not used");
316 : :
317 : : /* Make sure the character is valid. */
318 : 16 : if (c > 0x7FFFFFFF || (c >= 0xD800 && c <= 0xDFFF))
319 : 0 : return make_error ("ill-formed UTF-8: invalid character");
320 : :
321 : 16 : m_buffer.safe_push (c);
322 : 16 : *inbufp = inbuf;
323 : 16 : *inbytesleftp -= nbytes;
324 : : }
325 : 112 : return nullptr;
326 : : }
327 : :
328 : : /* Attempt to get the next unicode character from this lexer's buffer.
329 : : If successful, write it to OUT_CHAR, and its location to *OUT_POINT,
330 : : and return true.
331 : : Otherwise, return false. */
332 : :
333 : : bool
334 : 2472 : lexer::get_char (unichar &out_char, location_map::point *out_point)
335 : : {
336 : 4940 : if (m_next_char_idx >= (int)m_buffer.length ())
337 : : return false;
338 : :
339 : 2344 : if (out_point)
340 : 1944 : *out_point = get_next_point ();
341 : 2344 : out_char = m_buffer[m_next_char_idx++];
342 : :
343 : 2344 : if (out_char == '\n')
344 : : {
345 : 64 : m_next_char_line++;
346 : 64 : m_prev_line_final_column = m_next_char_column;
347 : 64 : m_next_char_column = 0;
348 : : }
349 : : else
350 : 2280 : m_next_char_column++;
351 : :
352 : : return true;
353 : : }
354 : :
355 : : /* Undo the last successful get_char. */
356 : :
357 : : void
358 : 368 : lexer::unget_char ()
359 : : {
360 : 368 : --m_next_char_idx;
361 : 368 : if (m_next_char_column > 0)
362 : 352 : --m_next_char_column;
363 : : else
364 : : {
365 : 16 : m_next_char_line--;
366 : 16 : m_next_char_column = m_prev_line_final_column;
367 : : /* We don't support more than one unget_char in a row. */
368 : 16 : gcc_assert (m_prev_line_final_column != -1);
369 : 16 : m_prev_line_final_column = -1;
370 : : }
371 : 368 : }
372 : :
373 : : /* Get the location of the next char. */
374 : :
375 : : location_map::point
376 : 2028 : lexer::get_next_point () const
377 : : {
378 : 2028 : location_map::point result;
379 : 2028 : result.m_unichar_idx = m_next_char_idx;
380 : 2028 : result.m_line = m_next_char_line;
381 : 2028 : result.m_column = m_next_char_column;
382 : 1944 : return result;
383 : : }
384 : :
385 : : /* Print a textual representation of TOK to OUTF.
386 : : This is intended for debugging the lexer and parser,
387 : : rather than for user-facing output. */
388 : :
389 : : void
390 : 0 : lexer::dump_token (FILE *outf, const token *tok)
391 : : {
392 : 0 : switch (tok->id)
393 : : {
394 : 0 : case TOK_ERROR:
395 : 0 : fprintf (outf, "TOK_ERROR (\"%s\")", tok->u.string);
396 : 0 : break;
397 : :
398 : 0 : case TOK_EOF:
399 : 0 : fprintf (outf, "TOK_EOF");
400 : 0 : break;
401 : :
402 : 0 : case TOK_OPEN_SQUARE:
403 : 0 : fprintf (outf, "TOK_OPEN_SQUARE");
404 : 0 : break;
405 : :
406 : 0 : case TOK_OPEN_CURLY:
407 : 0 : fprintf (outf, "TOK_OPEN_CURLY");
408 : 0 : break;
409 : :
410 : 0 : case TOK_CLOSE_SQUARE:
411 : 0 : fprintf (outf, "TOK_CLOSE_SQUARE");
412 : 0 : break;
413 : :
414 : 0 : case TOK_CLOSE_CURLY:
415 : 0 : fprintf (outf, "TOK_CLOSE_CURLY");
416 : 0 : break;
417 : :
418 : 0 : case TOK_COLON:
419 : 0 : fprintf (outf, "TOK_COLON");
420 : 0 : break;
421 : :
422 : 0 : case TOK_COMMA:
423 : 0 : fprintf (outf, "TOK_COMMA");
424 : 0 : break;
425 : :
426 : 0 : case TOK_TRUE:
427 : 0 : fprintf (outf, "TOK_TRUE");
428 : 0 : break;
429 : :
430 : 0 : case TOK_FALSE:
431 : 0 : fprintf (outf, "TOK_FALSE");
432 : 0 : break;
433 : :
434 : 0 : case TOK_NULL:
435 : 0 : fprintf (outf, "TOK_NULL");
436 : 0 : break;
437 : :
438 : 0 : case TOK_STRING:
439 : 0 : fprintf (outf, "TOK_STRING (\"%s\")", tok->u.string);
440 : 0 : break;
441 : :
442 : 0 : case TOK_FLOAT_NUMBER:
443 : 0 : fprintf (outf, "TOK_FLOAT_NUMBER (%f)", tok->u.float_number);
444 : 0 : break;
445 : :
446 : 0 : case TOK_INTEGER_NUMBER:
447 : 0 : fprintf (outf, "TOK_INTEGER_NUMBER (%ld)", tok->u.integer_number);
448 : 0 : break;
449 : :
450 : 0 : default:
451 : 0 : gcc_unreachable ();
452 : 0 : break;
453 : : }
454 : 0 : }
455 : :
456 : : /* Treat "//" as a comment to the end of the line.
457 : :
458 : : This isn't compliant with the JSON spec,
459 : : but is very handy for writing DejaGnu tests.
460 : :
461 : : Return true if EOF and populate *OUT, false otherwise. */
462 : :
463 : : bool
464 : 16 : lexer::consume_single_line_comment (token *out)
465 : : {
466 : 208 : while (1)
467 : : {
468 : 112 : unichar next_char;
469 : 112 : if (!get_char (next_char, nullptr))
470 : : {
471 : 0 : out->id = TOK_EOF;
472 : 0 : location_map::point p = get_next_point ();
473 : 0 : out->range.m_start = p;
474 : 0 : out->range.m_end = p;
475 : 0 : return true;
476 : : }
477 : 112 : if (next_char == '\n')
478 : : return false;
479 : 96 : }
480 : : }
481 : :
482 : : /* Treat '/' '*' as a multiline comment until the next closing '*' '/'.
483 : :
484 : : This isn't compliant with the JSON spec,
485 : : but is very handy for writing DejaGnu tests.
486 : :
487 : : Return true if EOF and populate *OUT, false otherwise. */
488 : :
489 : : bool
490 : 12 : lexer::consume_multiline_comment (token *out)
491 : : {
492 : 276 : while (1)
493 : : {
494 : 276 : unichar next_char;
495 : 276 : if (!get_char (next_char, nullptr))
496 : : {
497 : 0 : out->id = TOK_ERROR;
498 : 0 : gcc_unreachable (); // TODO
499 : : location_map::point p = get_next_point ();
500 : : out->range.m_start = p;
501 : : out->range.m_end = p;
502 : : return true;
503 : : }
504 : 276 : if (next_char != '*')
505 : 264 : continue;
506 : 12 : if (!get_char (next_char, nullptr))
507 : : {
508 : 0 : out->id = TOK_ERROR;
509 : 0 : gcc_unreachable (); // TODO
510 : : location_map::point p = get_next_point ();
511 : : out->range.m_start = p;
512 : : out->range.m_end = p;
513 : : return true;
514 : : }
515 : 12 : if (next_char == '/')
516 : 12 : return false;
517 : : }
518 : : }
519 : :
520 : : /* Attempt to lex the input buffer, writing the next token to OUT.
521 : : On errors, TOK_ERROR (or TOK_EOF) is written to OUT. */
522 : :
523 : : void
524 : 496 : lexer::lex_token (token *out)
525 : : {
526 : : /* Skip to next non-whitespace char. */
527 : 940 : unichar next_char;
528 : 940 : location_map::point start_point;
529 : 940 : while (1)
530 : : {
531 : 940 : if (!get_char (next_char, &start_point))
532 : : {
533 : 84 : out->id = TOK_EOF;
534 : 84 : location_map::point p = get_next_point ();
535 : 84 : out->range.m_start = p;
536 : 84 : out->range.m_end = p;
537 : 84 : return;
538 : : }
539 : 856 : if (m_support_comments)
540 : 160 : if (next_char == '/')
541 : : {
542 : 28 : location_map::point point;
543 : 28 : unichar next_next_char;
544 : 28 : if (get_char (next_next_char, &point))
545 : : {
546 : 28 : switch (next_next_char)
547 : : {
548 : 16 : case '/':
549 : 16 : if (consume_single_line_comment (out))
550 : 0 : return;
551 : 28 : continue;
552 : 12 : case '*':
553 : 12 : if (consume_multiline_comment (out))
554 : : return;
555 : 12 : continue;
556 : 0 : default:
557 : : /* A stray single '/'. Break out of loop, so that we
558 : : handle it below as an unexpected character. */
559 : 0 : goto non_whitespace;
560 : : }
561 : : }
562 : : }
563 : 828 : if (next_char != ' '
564 : 828 : && next_char != '\t'
565 : 432 : && next_char != '\n'
566 : 412 : && next_char != '\r')
567 : : break;
568 : : }
569 : :
570 : 412 : non_whitespace:
571 : :
572 : 412 : out->range.m_start = start_point;
573 : 412 : out->range.m_end = start_point;
574 : :
575 : 412 : switch (next_char)
576 : : {
577 : 20 : case '[':
578 : 20 : out->id = TOK_OPEN_SQUARE;
579 : 20 : break;
580 : :
581 : 20 : case '{':
582 : 20 : out->id = TOK_OPEN_CURLY;
583 : 20 : break;
584 : :
585 : 16 : case ']':
586 : 16 : out->id = TOK_CLOSE_SQUARE;
587 : 16 : break;
588 : :
589 : 16 : case '}':
590 : 16 : out->id = TOK_CLOSE_CURLY;
591 : 16 : break;
592 : :
593 : 28 : case ':':
594 : 28 : out->id = TOK_COLON;
595 : 28 : break;
596 : :
597 : 64 : case ',':
598 : 64 : out->id = TOK_COMMA;
599 : 64 : break;
600 : :
601 : 64 : case '"':
602 : 64 : lex_string (out);
603 : 64 : break;
604 : :
605 : 140 : case '-':
606 : 140 : case '0':
607 : 140 : case '1':
608 : 140 : case '2':
609 : 140 : case '3':
610 : 140 : case '4':
611 : 140 : case '5':
612 : 140 : case '6':
613 : 140 : case '7':
614 : 140 : case '8':
615 : 140 : case '9':
616 : 140 : lex_number (out, next_char);
617 : 140 : break;
618 : :
619 : 8 : case 't':
620 : : /* Handle literal "true". */
621 : 8 : if (rest_of_literal (out, "rue"))
622 : : {
623 : 8 : out->id = TOK_TRUE;
624 : 8 : break;
625 : : }
626 : : else
627 : 0 : goto err;
628 : :
629 : 8 : case 'f':
630 : : /* Handle literal "false". */
631 : 8 : if (rest_of_literal (out, "alse"))
632 : : {
633 : 8 : out->id = TOK_FALSE;
634 : 8 : break;
635 : : }
636 : : else
637 : 0 : goto err;
638 : :
639 : 16 : case 'n':
640 : : /* Handle literal "null". */
641 : 16 : if (rest_of_literal (out, "ull"))
642 : : {
643 : 12 : out->id = TOK_NULL;
644 : 12 : break;
645 : : }
646 : : else
647 : 4 : goto err;
648 : :
649 : 16 : err:
650 : 16 : default:
651 : 16 : out->id = TOK_ERROR;
652 : 16 : out->u.string = xasprintf ("unexpected character: '%c'", next_char);
653 : 16 : break;
654 : : }
655 : : }
656 : :
657 : : /* Having consumed an open-quote character from the lexer's buffer, attempt
658 : : to lex the rest of a JSON string, writing the result to OUT (or TOK_ERROR)
659 : : if an error occurred.
660 : : (ECMA-404 section 9; RFC 7159 section 7). */
661 : :
662 : : void
663 : 64 : lexer::lex_string (token *out)
664 : : {
665 : 64 : auto_vec<unichar> content;
666 : 64 : bool still_going = true;
667 : 456 : while (still_going)
668 : : {
669 : 392 : unichar uc;
670 : 392 : if (!get_char (uc, &out->range.m_end))
671 : : {
672 : 0 : out->id = TOK_ERROR;
673 : 0 : out->range.m_end = get_next_point ();
674 : 0 : out->u.string = xstrdup ("EOF within string");
675 : 0 : return;
676 : : }
677 : 392 : switch (uc)
678 : : {
679 : : case '"':
680 : : still_going = false;
681 : : break;
682 : 24 : case '\\':
683 : 24 : {
684 : 24 : unichar next_char;
685 : 24 : if (!get_char (next_char, &out->range.m_end))
686 : : {
687 : 0 : out->id = TOK_ERROR;
688 : 0 : out->range.m_end = get_next_point ();
689 : 0 : out->u.string = xstrdup ("EOF within string");;
690 : 0 : return;
691 : : }
692 : 24 : switch (next_char)
693 : : {
694 : 8 : case '"':
695 : 8 : case '\\':
696 : 8 : case '/':
697 : 8 : content.safe_push (next_char);
698 : 8 : break;
699 : :
700 : 0 : case 'b':
701 : 0 : content.safe_push ('\b');
702 : 0 : break;
703 : :
704 : 0 : case 'f':
705 : 0 : content.safe_push ('\f');
706 : 0 : break;
707 : :
708 : 0 : case 'n':
709 : 0 : content.safe_push ('\n');
710 : 0 : break;
711 : :
712 : 0 : case 'r':
713 : 0 : content.safe_push ('\r');
714 : 0 : break;
715 : :
716 : 0 : case 't':
717 : 0 : content.safe_push ('\t');
718 : 0 : break;
719 : :
720 : 16 : case 'u':
721 : 16 : {
722 : 16 : unichar result = 0;
723 : 80 : for (int i = 0; i < 4; i++)
724 : : {
725 : 64 : unichar hexdigit;
726 : 64 : if (!get_char (hexdigit, &out->range.m_end))
727 : : {
728 : 0 : out->id = TOK_ERROR;
729 : 0 : out->range.m_end = get_next_point ();
730 : 0 : out->u.string = xstrdup ("EOF within string");
731 : 0 : return;
732 : : }
733 : 64 : result <<= 4;
734 : 64 : if (hexdigit >= '0' && hexdigit <= '9')
735 : 60 : result += hexdigit - '0';
736 : 4 : else if (hexdigit >= 'a' && hexdigit <= 'f')
737 : 4 : result += (hexdigit - 'a') + 10;
738 : 0 : else if (hexdigit >= 'A' && hexdigit <= 'F')
739 : 0 : result += (hexdigit - 'A') + 10;
740 : : else
741 : : {
742 : 0 : out->id = TOK_ERROR;
743 : 0 : out->range.m_start = out->range.m_end;
744 : 0 : out->u.string = xstrdup ("bogus hex char");
745 : 0 : return;
746 : : }
747 : : }
748 : 16 : content.safe_push (result);
749 : : }
750 : 16 : break;
751 : :
752 : 0 : default:
753 : 0 : out->id = TOK_ERROR;
754 : 0 : out->u.string = xstrdup ("unrecognized escape char");
755 : 0 : return;
756 : : }
757 : : }
758 : 24 : break;
759 : :
760 : 304 : default:
761 : : /* Reject unescaped control characters U+0000 through U+001F
762 : : (ECMA-404 section 9 para 1; RFC 7159 section 7 para 1). */
763 : 304 : if (uc <= 0x1f)
764 : : {
765 : 0 : out->id = TOK_ERROR;
766 : 0 : out->range.m_start = out->range.m_end;
767 : 0 : out->u.string = xstrdup ("unescaped control char");
768 : 0 : return;
769 : : }
770 : :
771 : : /* Otherwise, add regular unicode code point. */
772 : 304 : content.safe_push (uc);
773 : 304 : break;
774 : : }
775 : : }
776 : :
777 : 64 : out->id = TOK_STRING;
778 : :
779 : 128 : auto_vec<char> utf8_buf;
780 : : // Adapted from libcpp/charset.c:one_cppchar_to_utf8
781 : 392 : for (unsigned i = 0; i < content.length (); i++)
782 : : {
783 : 328 : static const uchar masks[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
784 : 328 : static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
785 : 328 : size_t nbytes;
786 : 328 : uchar buf[6], *p = &buf[6];
787 : 328 : unichar c = content[i];
788 : :
789 : 328 : nbytes = 1;
790 : 328 : if (c < 0x80)
791 : 296 : *--p = c;
792 : : else
793 : : {
794 : 64 : do
795 : : {
796 : 64 : *--p = ((c & 0x3F) | 0x80);
797 : 64 : c >>= 6;
798 : 64 : nbytes++;
799 : : }
800 : 64 : while (c >= 0x3F || (c & limits[nbytes-1]));
801 : 32 : *--p = (c | masks[nbytes-1]);
802 : : }
803 : :
804 : 720 : while (p < &buf[6])
805 : 392 : utf8_buf.safe_push (*p++);
806 : : }
807 : :
808 : 128 : out->u.string = XNEWVEC (char, utf8_buf.length () + 1);
809 : 456 : for (unsigned i = 0; i < utf8_buf.length (); i++)
810 : 392 : out->u.string[i] = utf8_buf[i];
811 : 128 : out->u.string[utf8_buf.length ()] = '\0';
812 : 64 : }
813 : :
814 : : /* Having consumed FIRST_CHAR, an initial digit or '-' character from
815 : : the lexer's buffer attempt to lex the rest of a JSON number, writing
816 : : the result to OUT (or TOK_ERROR) if an error occurred.
817 : : (ECMA-404 section 8; RFC 7159 section 6). */
818 : :
819 : : void
820 : 140 : lexer::lex_number (token *out, unichar first_char)
821 : : {
822 : 140 : bool negate = false;
823 : 140 : double value = 0.0;
824 : 140 : if (first_char == '-')
825 : : {
826 : 12 : negate = true;
827 : 12 : if (!get_char (first_char, &out->range.m_end))
828 : : {
829 : 0 : out->id = TOK_ERROR;
830 : 0 : out->range.m_start = out->range.m_end;
831 : 0 : out->u.string = xstrdup ("expected digit");
832 : 0 : return;
833 : : }
834 : : }
835 : :
836 : 140 : if (first_char == '0')
837 : : value = 0.0;
838 : 132 : else if (!ISDIGIT (first_char))
839 : : {
840 : 0 : out->id = TOK_ERROR;
841 : 0 : out->range.m_start = out->range.m_end;
842 : 0 : out->u.string = xstrdup ("expected digit");
843 : 0 : return;
844 : : }
845 : : else
846 : : {
847 : : /* Got a nonzero digit; expect zero or more digits. */
848 : 132 : value = first_char - '0';
849 : 364 : while (1)
850 : : {
851 : 248 : unichar uc;
852 : 248 : location_map::point point;
853 : 248 : if (!get_char (uc, &point))
854 : : break;
855 : 240 : if (ISDIGIT (uc))
856 : : {
857 : 116 : value *= 10;
858 : 116 : value += uc -'0';
859 : 116 : out->range.m_end = point;
860 : 116 : continue;
861 : : }
862 : : else
863 : : {
864 : 124 : unget_char ();
865 : 124 : break;
866 : : }
867 : : }
868 : : }
869 : :
870 : : /* Optional '.', followed by one or more decimals. */
871 : 140 : unichar next_char;
872 : 140 : location_map::point point;
873 : 140 : if (get_char (next_char, &point))
874 : : {
875 : 132 : if (next_char == '.')
876 : : {
877 : : /* Parse decimal digits. */
878 : : bool had_digit = false;
879 : : double digit_factor = 0.1;
880 : 44 : while (get_char (next_char, &point))
881 : : {
882 : 40 : if (!ISDIGIT (next_char))
883 : : {
884 : 8 : unget_char ();
885 : 8 : break;
886 : : }
887 : 32 : value += (next_char - '0') * digit_factor;
888 : 32 : digit_factor *= 0.1;
889 : 32 : had_digit = true;
890 : 32 : out->range.m_end = point;
891 : : }
892 : 12 : if (!had_digit)
893 : : {
894 : 0 : out->id = TOK_ERROR;
895 : 0 : out->range.m_start = point;
896 : 0 : out->range.m_start = point;
897 : 0 : out->u.string = xstrdup ("expected digit");
898 : 0 : return;
899 : : }
900 : : }
901 : : else
902 : 120 : unget_char ();
903 : : }
904 : :
905 : : /* Parse 'e' and 'E'. */
906 : 140 : unichar exponent_char;
907 : 140 : if (get_char (exponent_char, &point))
908 : : {
909 : 128 : if (exponent_char == 'e' || exponent_char == 'E')
910 : : {
911 : : /* Optional +/-. */
912 : 16 : unichar sign_char;
913 : 16 : int exponent = 0;
914 : 16 : bool negate_exponent = false;
915 : 16 : bool had_exponent_digit = false;
916 : 16 : if (!get_char (sign_char, &point))
917 : : {
918 : 0 : out->id = TOK_ERROR;
919 : 0 : out->range.m_start = point;
920 : 0 : out->range.m_start = point;
921 : 0 : out->u.string = xstrdup ("EOF within exponent");
922 : 0 : return;
923 : : }
924 : 16 : if (sign_char == '-')
925 : : negate_exponent = true;
926 : 12 : else if (sign_char == '+')
927 : : ;
928 : 8 : else if (ISDIGIT (sign_char))
929 : : {
930 : 8 : exponent = sign_char - '0';
931 : 8 : had_exponent_digit = true;
932 : : }
933 : : else
934 : : {
935 : 0 : out->id = TOK_ERROR;
936 : 0 : out->range.m_start = point;
937 : 0 : out->range.m_start = point;
938 : 0 : out->u.string
939 : 0 : = xstrdup ("expected '-','+' or digit within exponent");
940 : 0 : return;
941 : : }
942 : 16 : out->range.m_end = point;
943 : :
944 : : /* One or more digits (we might have seen the digit above,
945 : : though). */
946 : 32 : while (1)
947 : : {
948 : 24 : unichar uc;
949 : 24 : location_map::point point;
950 : 24 : if (!get_char (uc, &point))
951 : : break;
952 : 12 : if (ISDIGIT (uc))
953 : : {
954 : 8 : exponent *= 10;
955 : 8 : exponent += uc -'0';
956 : 8 : had_exponent_digit = true;
957 : 8 : out->range.m_end = point;
958 : 8 : continue;
959 : : }
960 : : else
961 : : {
962 : 4 : unget_char ();
963 : 4 : break;
964 : : }
965 : : }
966 : 16 : if (!had_exponent_digit)
967 : : {
968 : 0 : out->id = TOK_ERROR;
969 : 0 : out->range.m_start = point;
970 : 0 : out->range.m_start = point;
971 : 0 : out->u.string = xstrdup ("expected digit within exponent");
972 : 0 : return;
973 : : }
974 : 16 : if (negate_exponent)
975 : 4 : exponent = -exponent;
976 : 16 : value = value * pow (10, exponent);
977 : : }
978 : : else
979 : 112 : unget_char ();
980 : : }
981 : :
982 : 140 : if (negate)
983 : 12 : value = -value;
984 : :
985 : 140 : if (value == (long)value)
986 : : {
987 : 124 : out->id = TOK_INTEGER_NUMBER;
988 : 124 : out->u.integer_number = value;
989 : : }
990 : : else
991 : : {
992 : 16 : out->id = TOK_FLOAT_NUMBER;
993 : 16 : out->u.float_number = value;
994 : : }
995 : : }
996 : :
997 : : /* Determine if the next characters to be lexed match SUFFIX.
998 : : SUFFIX must be pure ASCII and not contain newlines.
999 : : If so, consume the characters and return true.
1000 : : Otherwise, return false. */
1001 : :
1002 : : bool
1003 : 32 : lexer::rest_of_literal (token *out, const char *suffix)
1004 : : {
1005 : 32 : int suffix_idx = 0;
1006 : 32 : int buf_idx = m_next_char_idx;
1007 : 216 : while (1)
1008 : : {
1009 : 124 : if (suffix[suffix_idx] == '\0')
1010 : : {
1011 : 28 : m_next_char_idx += suffix_idx;
1012 : 28 : m_next_char_column += suffix_idx;
1013 : 28 : out->range.m_end.m_unichar_idx += suffix_idx;
1014 : 28 : out->range.m_end.m_column += suffix_idx;
1015 : 28 : return true;
1016 : : }
1017 : 192 : if (buf_idx >= (int)m_buffer.length ())
1018 : : return false;
1019 : : /* This assumes that suffix is ASCII. */
1020 : 96 : if (m_buffer[buf_idx] != (unichar)suffix[suffix_idx])
1021 : : return false;
1022 : 92 : buf_idx++;
1023 : 92 : suffix_idx++;
1024 : : }
1025 : : }
1026 : :
1027 : : /* Create a new error instance for MSG, using the location of the next
1028 : : character for the location of the error. */
1029 : :
1030 : : std::unique_ptr<error>
1031 : 0 : lexer::make_error (const char *msg)
1032 : : {
1033 : 0 : location_map::point p;
1034 : 0 : p.m_unichar_idx = m_next_char_idx;
1035 : 0 : p.m_line = m_next_char_line;
1036 : 0 : p.m_column = m_next_char_column;
1037 : 0 : location_map::range r;
1038 : 0 : r.m_start = p;
1039 : 0 : r.m_end = p;
1040 : 0 : return ::make_unique<error> (r, xstrdup (msg));
1041 : : }
1042 : :
1043 : : /* parser's ctor. */
1044 : :
1045 : 92 : parser::parser (location_map *out_loc_map,
1046 : 92 : bool support_comments)
1047 : 92 : : m_lexer (support_comments), m_loc_map (out_loc_map)
1048 : : {
1049 : 0 : }
1050 : :
1051 : : /* parser's dtor. */
1052 : :
1053 : 92 : parser::~parser ()
1054 : : {
1055 : 92 : if (m_loc_map)
1056 : 92 : m_loc_map->on_finished_parsing ();
1057 : 92 : }
1058 : :
1059 : : /* Add LENGTH bytes of UTF-8 encoded text from UTF8_BUF to this parser's
1060 : : lexer's buffer. */
1061 : :
1062 : : std::unique_ptr<error>
1063 : 92 : parser::add_utf8 (size_t length, const char *utf8_buf)
1064 : : {
1065 : 0 : return m_lexer.add_utf8 (length, utf8_buf);
1066 : : }
1067 : :
1068 : : /* Parse a JSON value (object, array, number, string, or literal).
1069 : : (ECMA-404 section 5; RFC 7159 section 3). */
1070 : :
1071 : : parser_result_t
1072 : 184 : parser::parse_value (int depth)
1073 : : {
1074 : 184 : const token *tok = m_lexer.peek ();
1075 : :
1076 : : /* Avoid stack overflow with deeply-nested inputs; RFC 7159 section 9
1077 : : states: "An implementation may set limits on the maximum depth
1078 : : of nesting.".
1079 : :
1080 : : Ideally we'd avoid this limit (e.g. by rewriting parse_value,
1081 : : parse_object, and parse_array into a single function with a vec of
1082 : : state). */
1083 : 184 : const int MAX_DEPTH = 100;
1084 : 184 : if (depth >= MAX_DEPTH)
1085 : 0 : return error_at (tok->range, "maximum nesting depth exceeded: %i",
1086 : 0 : MAX_DEPTH);
1087 : :
1088 : 184 : switch (tok->id)
1089 : : {
1090 : 16 : case TOK_OPEN_CURLY:
1091 : 16 : return parse_object (depth);
1092 : :
1093 : 28 : case TOK_STRING:
1094 : 28 : {
1095 : 28 : auto val = ::make_unique<string> (tok->u.string);
1096 : 28 : m_lexer.consume ();
1097 : 28 : maybe_record_range (val.get (), tok->range);
1098 : 28 : return parser_result_t (std::move (val));
1099 : 28 : }
1100 : :
1101 : 16 : case TOK_OPEN_SQUARE:
1102 : 16 : return parse_array (depth);
1103 : :
1104 : 12 : case TOK_FLOAT_NUMBER:
1105 : 12 : {
1106 : 12 : auto val = ::make_unique<float_number> (tok->u.float_number);
1107 : 12 : m_lexer.consume ();
1108 : 12 : maybe_record_range (val.get (), tok->range);
1109 : 12 : return parser_result_t (std::move (val));
1110 : 12 : }
1111 : :
1112 : 84 : case TOK_INTEGER_NUMBER:
1113 : 84 : {
1114 : 84 : auto val = ::make_unique<integer_number> (tok->u.integer_number);
1115 : 84 : m_lexer.consume ();
1116 : 84 : maybe_record_range (val.get (), tok->range);
1117 : 84 : return parser_result_t (std::move (val));
1118 : 84 : }
1119 : :
1120 : 4 : case TOK_TRUE:
1121 : 4 : {
1122 : 4 : auto val = ::make_unique<literal> (JSON_TRUE);
1123 : 4 : m_lexer.consume ();
1124 : 4 : maybe_record_range (val.get (), tok->range);
1125 : 4 : return parser_result_t (std::move (val));
1126 : 4 : }
1127 : :
1128 : 4 : case TOK_FALSE:
1129 : 4 : {
1130 : 4 : auto val = ::make_unique<literal> (JSON_FALSE);
1131 : 4 : m_lexer.consume ();
1132 : 4 : maybe_record_range (val.get (), tok->range);
1133 : 4 : return parser_result_t (std::move (val));
1134 : 4 : }
1135 : :
1136 : 8 : case TOK_NULL:
1137 : 8 : {
1138 : 8 : auto val = ::make_unique<literal> (JSON_NULL);
1139 : 8 : m_lexer.consume ();
1140 : 8 : maybe_record_range (val.get (), tok->range);
1141 : 8 : return parser_result_t (std::move (val));
1142 : 8 : }
1143 : :
1144 : 8 : case TOK_ERROR:
1145 : 8 : return error_at (tok->range, "invalid JSON token: %s", tok->u.string);
1146 : :
1147 : 4 : default:
1148 : 4 : return error_at (tok->range, "expected a JSON value but got %s",
1149 : 4 : token_id_name[tok->id]);
1150 : : }
1151 : : }
1152 : :
1153 : : /* Parse a JSON object.
1154 : : (ECMA-404 section 6; RFC 7159 section 4). */
1155 : :
1156 : : parser_result_t
1157 : 16 : parser::parse_object (int depth)
1158 : : {
1159 : 32 : location_map::point start = get_next_token_start ();
1160 : :
1161 : 16 : require (TOK_OPEN_CURLY);
1162 : :
1163 : 16 : auto obj = ::make_unique<object> ();
1164 : :
1165 : 16 : const token *tok = m_lexer.peek ();
1166 : 16 : if (tok->id == TOK_CLOSE_CURLY)
1167 : : {
1168 : 8 : location_map::point end = get_next_token_end ();
1169 : 4 : maybe_record_range (obj.get (), start, end);
1170 : 4 : if (auto err = require (TOK_CLOSE_CURLY))
1171 : 4 : return parser_result_t (std::move (err));
1172 : 4 : return parser_result_t (std::move (obj));
1173 : : }
1174 : 12 : if (tok->id != TOK_STRING)
1175 : 0 : return error_at (tok->range,
1176 : : "expected string for object key after '{'; got %s",
1177 : 0 : token_id_name[tok->id]);
1178 : 16 : while (true)
1179 : : {
1180 : 28 : tok = m_lexer.peek ();
1181 : 28 : if (tok->id != TOK_STRING)
1182 : 0 : return error_at (tok->range,
1183 : : "expected string for object key after ','; got %s",
1184 : 0 : token_id_name[tok->id]);
1185 : 28 : label_text key = label_text::take (xstrdup (tok->u.string));
1186 : 28 : m_lexer.consume ();
1187 : :
1188 : 28 : if (auto err = require (TOK_COLON))
1189 : 28 : return parser_result_t (std::move (err));
1190 : :
1191 : 28 : parser_result_t r = parse_value (depth + 1);
1192 : 28 : if (r.m_err)
1193 : 0 : return r;
1194 : 28 : if (!r.m_val)
1195 : 0 : return parser_result_t (std::move (obj));
1196 : :
1197 : : /* We don't enforce uniqueness for keys. */
1198 : 28 : obj->set (key.get (), std::move (r.m_val));
1199 : :
1200 : 56 : location_map::point end = get_next_token_end ();
1201 : 28 : result<enum token_id, std::unique_ptr<error>> result
1202 : 28 : (require_one_of (TOK_COMMA, TOK_CLOSE_CURLY));
1203 : 28 : if (result.m_err)
1204 : 4 : return parser_result_t (std::move (result.m_err));
1205 : 24 : if (result.m_val == TOK_COMMA)
1206 : 16 : continue;
1207 : : else
1208 : : {
1209 : : /* TOK_CLOSE_CURLY. */
1210 : 8 : maybe_record_range (obj.get (), start, end);
1211 : 8 : return parser_result_t (std::move (obj));
1212 : : }
1213 : 56 : }
1214 : 16 : }
1215 : :
1216 : : /* Parse a JSON array.
1217 : : (ECMA-404 section 7; RFC 7159 section 5). */
1218 : :
1219 : : parser_result_t
1220 : 16 : parser::parse_array (int depth)
1221 : : {
1222 : 32 : location_map::point start = get_next_token_start ();
1223 : 16 : if (auto err = require (TOK_OPEN_SQUARE))
1224 : 16 : return parser_result_t (std::move (err));
1225 : :
1226 : 16 : auto arr = ::make_unique<array> ();
1227 : :
1228 : 16 : const token *tok = m_lexer.peek ();
1229 : 16 : if (tok->id == TOK_CLOSE_SQUARE)
1230 : : {
1231 : 0 : location_map::point end = get_next_token_end ();
1232 : 0 : maybe_record_range (arr.get (), start, end);
1233 : 0 : m_lexer.consume ();
1234 : 0 : return parser_result_t (std::move (arr));
1235 : : }
1236 : :
1237 : 112 : while (true)
1238 : : {
1239 : 64 : parser_result_t r = parse_value (depth + 1);
1240 : 64 : if (r.m_err)
1241 : 0 : return r;
1242 : :
1243 : 64 : arr->append (std::move (r.m_val));
1244 : :
1245 : 128 : location_map::point end = get_next_token_end ();
1246 : 64 : result<enum token_id, std::unique_ptr<error>> result
1247 : 64 : (require_one_of (TOK_COMMA, TOK_CLOSE_SQUARE));
1248 : 64 : if (result.m_err)
1249 : 4 : return parser_result_t (std::move (result.m_err));
1250 : 60 : if (result.m_val == TOK_COMMA)
1251 : 48 : continue;
1252 : : else
1253 : : {
1254 : : /* TOK_CLOSE_SQUARE. */
1255 : 12 : maybe_record_range (arr.get (), start, end);
1256 : 12 : return parser_result_t (std::move (arr));
1257 : : }
1258 : 128 : }
1259 : 16 : }
1260 : :
1261 : : /* Get the start point of the next token. */
1262 : :
1263 : : location_map::point
1264 : 32 : parser::get_next_token_start ()
1265 : : {
1266 : 32 : const token *tok = m_lexer.peek ();
1267 : 32 : return tok->range.m_start;
1268 : : }
1269 : :
1270 : : /* Get the end point of the next token. */
1271 : :
1272 : : location_map::point
1273 : 96 : parser::get_next_token_end ()
1274 : : {
1275 : 96 : const token *tok = m_lexer.peek ();
1276 : 96 : return tok->range.m_end;
1277 : : }
1278 : :
1279 : : /* Require an EOF, or fail if there is surplus input. */
1280 : :
1281 : : std::unique_ptr<error>
1282 : 72 : parser::require_eof ()
1283 : : {
1284 : 0 : return require (TOK_EOF);
1285 : : }
1286 : :
1287 : : /* Consume the next token, issuing an error if it is not of kind TOK_ID. */
1288 : :
1289 : : std::unique_ptr<error>
1290 : 136 : parser::require (enum token_id tok_id)
1291 : : {
1292 : 136 : const token *tok = m_lexer.peek ();
1293 : 136 : if (tok->id != tok_id)
1294 : : {
1295 : 0 : if (tok->id == TOK_ERROR)
1296 : 0 : return error_at (tok->range,
1297 : : "expected %s; got bad token: %s",
1298 : 0 : token_id_name[tok_id], tok->u.string);
1299 : : else
1300 : 0 : return error_at (tok->range,
1301 : 0 : "expected %s; got %s", token_id_name[tok_id],
1302 : 0 : token_id_name[tok->id]);
1303 : : }
1304 : 136 : m_lexer.consume ();
1305 : 136 : return nullptr;
1306 : : }
1307 : :
1308 : : /* Consume the next token, issuing an error if it is not of
1309 : : kind TOK_ID_A or TOK_ID_B.
1310 : : Return which kind it was. */
1311 : :
1312 : : result<enum token_id, std::unique_ptr<error>>
1313 : 92 : parser::require_one_of (enum token_id tok_id_a, enum token_id tok_id_b)
1314 : : {
1315 : 92 : const token *tok = m_lexer.peek ();
1316 : 92 : if ((tok->id != tok_id_a)
1317 : 28 : && (tok->id != tok_id_b))
1318 : : {
1319 : 8 : if (tok->id == TOK_ERROR)
1320 : 0 : return error_at (tok->range, "expected %s or %s; got bad token: %s",
1321 : 0 : token_id_name[tok_id_a], token_id_name[tok_id_b],
1322 : 0 : tok->u.string);
1323 : : else
1324 : 8 : return error_at (tok->range, "expected %s or %s; got %s",
1325 : 8 : token_id_name[tok_id_a], token_id_name[tok_id_b],
1326 : 8 : token_id_name[tok->id]);
1327 : : }
1328 : 84 : enum token_id id = tok->id;
1329 : 84 : m_lexer.consume ();
1330 : 84 : return result<enum token_id, std::unique_ptr<error>> (id);
1331 : : }
1332 : :
1333 : : /* Genarate a parsing error. */
1334 : :
1335 : : std::unique_ptr<error>
1336 : 20 : parser::error_at (const location_map::range &r, const char *fmt, ...)
1337 : : {
1338 : 20 : va_list ap;
1339 : 20 : va_start (ap, fmt);
1340 : 20 : char *formatted_msg = xvasprintf (fmt, ap);
1341 : 20 : va_end (ap);
1342 : :
1343 : 20 : return ::make_unique<error> (r, formatted_msg);
1344 : : }
1345 : :
1346 : : /* Record that JV has range R within the input file. */
1347 : :
1348 : : void
1349 : 140 : parser::maybe_record_range (json::value *jv, const location_map::range &r)
1350 : : {
1351 : 140 : if (m_loc_map)
1352 : 140 : m_loc_map->record_range_for_value (jv, r);
1353 : 140 : }
1354 : :
1355 : : /* Record that JV has range START to END within the input file. */
1356 : :
1357 : : void
1358 : 24 : parser::maybe_record_range (json::value *jv,
1359 : : const location_map::point &start,
1360 : : const location_map::point &end)
1361 : : {
1362 : 24 : if (m_loc_map)
1363 : : {
1364 : 24 : location_map::range r;
1365 : 24 : r.m_start = start;
1366 : 24 : r.m_end = end;
1367 : 24 : m_loc_map->record_range_for_value (jv, r);
1368 : : }
1369 : 24 : }
1370 : :
1371 : : /* Attempt to parse the UTF-8 encoded buffer at UTF8_BUF
1372 : : of the given LENGTH.
1373 : : If ALLOW_COMMENTS is true, then allow C and C++ style-comments in the
1374 : : buffer, as an extension to JSON, otherwise forbid them.
1375 : : If successful, return an json::value in the result.
1376 : : if there was a problem, return a json::error in the result.
1377 : : If OUT_LOC_MAP is non-NULL, notify *OUT_LOC_MAP about
1378 : : source locations of nodes seen during parsing. */
1379 : :
1380 : : parser_result_t
1381 : 92 : json::parse_utf8_string (size_t length,
1382 : : const char *utf8_buf,
1383 : : bool allow_comments,
1384 : : location_map *out_loc_map)
1385 : : {
1386 : 92 : parser p (out_loc_map, allow_comments);
1387 : 92 : if (auto err = p.add_utf8 (length, utf8_buf))
1388 : 92 : return parser_result_t (std::move (err));
1389 : 92 : parser_result_t r = p.parse_value (0);
1390 : 92 : if (r.m_err)
1391 : 20 : return r;
1392 : 72 : if (auto err = p.require_eof ())
1393 : 72 : return parser_result_t (std::move (err));
1394 : 72 : return r;
1395 : 92 : }
1396 : :
1397 : : /* Attempt to parse the nil-terminated UTF-8 encoded buffer at
1398 : : UTF8_BUF.
1399 : : If ALLOW_COMMENTS is true, then allow C and C++ style-comments in the
1400 : : buffer, as an extension to JSON, otherwise forbid them.
1401 : : If successful, return a non-NULL json::value *.
1402 : : if there was a problem, return NULL and write an error
1403 : : message to err_out, which must be deleted by the caller.
1404 : : If OUT_LOC_MAP is non-NULL, notify *OUT_LOC_MAP about
1405 : : source locations of nodes seen during parsing. */
1406 : :
1407 : : json::parser_result_t
1408 : 92 : json::parse_utf8_string (const char *utf8,
1409 : : bool allow_comments,
1410 : : location_map *out_loc_map)
1411 : : {
1412 : 92 : return parse_utf8_string (strlen (utf8), utf8, allow_comments,
1413 : 92 : out_loc_map);
1414 : : }
1415 : :
1416 : :
1417 : : #if CHECKING_P
1418 : :
1419 : : namespace selftest {
1420 : :
1421 : : /* Selftests. */
1422 : :
1423 : : #define ASSERT_PRINT_EQ(JV, FORMATTED, EXPECTED_JSON) \
1424 : : assert_print_eq (SELFTEST_LOCATION, JV, FORMATTED, EXPECTED_JSON)
1425 : :
1426 : : /* Implementation detail of ASSERT_RANGE_EQ. */
1427 : :
1428 : : static void
1429 : 424 : assert_point_eq (const location &loc,
1430 : : const location_map::point &actual_point,
1431 : : size_t exp_unichar_idx, int exp_line, int exp_column)
1432 : : {
1433 : 424 : ASSERT_EQ_AT (loc, actual_point.m_unichar_idx, exp_unichar_idx);
1434 : 424 : ASSERT_EQ_AT (loc, actual_point.m_line, exp_line);
1435 : 424 : ASSERT_EQ_AT (loc, actual_point.m_column, exp_column);
1436 : 424 : }
1437 : :
1438 : : /* Implementation detail of ASSERT_RANGE_EQ. */
1439 : :
1440 : : static void
1441 : 212 : assert_range_eq (const location &loc,
1442 : : const location_map::range &actual_range,
1443 : : /* Expected location. */
1444 : : size_t start_unichar_idx, int start_line, int start_column,
1445 : : size_t end_unichar_idx, int end_line, int end_column)
1446 : : {
1447 : 212 : assert_point_eq (loc, actual_range.m_start,
1448 : : start_unichar_idx, start_line, start_column);
1449 : 212 : assert_point_eq (loc, actual_range.m_end,
1450 : : end_unichar_idx, end_line, end_column);
1451 : 212 : }
1452 : :
1453 : : /* Assert that ACTUAL_RANGE starts at
1454 : : (START_UNICHAR_IDX, START_LINE, START_COLUMN)
1455 : : and ends at (END_UNICHAR_IDX, END_LINE, END_COLUMN). */
1456 : :
1457 : : #define ASSERT_RANGE_EQ(ACTUAL_RANGE, \
1458 : : START_UNICHAR_IDX, START_LINE, START_COLUMN, \
1459 : : END_UNICHAR_IDX, END_LINE, END_COLUMN) \
1460 : : assert_range_eq ((SELFTEST_LOCATION), (ACTUAL_RANGE), \
1461 : : (START_UNICHAR_IDX), (START_LINE), (START_COLUMN), \
1462 : : (END_UNICHAR_IDX), (END_LINE), (END_COLUMN))
1463 : :
1464 : : /* Implementation detail of ASSERT_ERR_EQ. */
1465 : :
1466 : : static void
1467 : 16 : assert_err_eq (const location &loc,
1468 : : const json::error *actual_err,
1469 : : /* Expected location. */
1470 : : size_t start_unichar_idx, int start_line, int start_column,
1471 : : size_t end_unichar_idx, int end_line, int end_column,
1472 : : const char *expected_msg)
1473 : : {
1474 : 16 : ASSERT_TRUE_AT (loc, actual_err);
1475 : 16 : const location_map::range &actual_range = actual_err->get_range ();
1476 : 16 : ASSERT_EQ_AT (loc, actual_range.m_start.m_unichar_idx, start_unichar_idx);
1477 : 16 : ASSERT_EQ_AT (loc, actual_range.m_start.m_line, start_line);
1478 : 16 : ASSERT_EQ_AT (loc, actual_range.m_start.m_column, start_column);
1479 : 16 : ASSERT_EQ_AT (loc, actual_range.m_end.m_unichar_idx, end_unichar_idx);
1480 : 16 : ASSERT_EQ_AT (loc, actual_range.m_end.m_line, end_line);
1481 : 16 : ASSERT_EQ_AT (loc, actual_range.m_end.m_column, end_column);
1482 : 16 : ASSERT_STREQ_AT (loc, actual_err->get_msg (), expected_msg);
1483 : 16 : }
1484 : :
1485 : : /* Assert that ACTUAL_ERR is a non-NULL json::error *,
1486 : : with message EXPECTED_MSG, and that its location starts
1487 : : at (START_UNICHAR_IDX, START_LINE, START_COLUMN)
1488 : : and ends at (END_UNICHAR_IDX, END_LINE, END_COLUMN). */
1489 : :
1490 : : #define ASSERT_ERR_EQ(ACTUAL_ERR, \
1491 : : START_UNICHAR_IDX, START_LINE, START_COLUMN, \
1492 : : END_UNICHAR_IDX, END_LINE, END_COLUMN, \
1493 : : EXPECTED_MSG) \
1494 : : assert_err_eq ((SELFTEST_LOCATION), (ACTUAL_ERR), \
1495 : : (START_UNICHAR_IDX), (START_LINE), (START_COLUMN), \
1496 : : (END_UNICHAR_IDX), (END_LINE), (END_COLUMN), \
1497 : : (EXPECTED_MSG))
1498 : :
1499 : : /* Verify that the JSON lexer works as expected. */
1500 : :
1501 : : static void
1502 : 4 : test_lexer ()
1503 : : {
1504 : 4 : lexer l (false);
1505 : 4 : const char *str
1506 : : /* 0 1 2 3 4 . */
1507 : : /* 01234567890123456789012345678901234567890123456789. */
1508 : : = (" 1066 -1 \n"
1509 : : " -273.15 1e6\n"
1510 : : " [ ] null true false { } \"foo\" \n");
1511 : 4 : auto err = l.add_utf8 (strlen (str), str);
1512 : 4 : ASSERT_EQ (err, nullptr);
1513 : :
1514 : : /* Line 1. */
1515 : 4 : {
1516 : 4 : const size_t line_offset = 0;
1517 : :
1518 : : /* Expect token: "1066" in columns 4-7. */
1519 : 4 : {
1520 : 4 : const token *tok = l.peek ();
1521 : 4 : ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
1522 : 4 : ASSERT_EQ (tok->u.integer_number, 1066);
1523 : 4 : ASSERT_RANGE_EQ (tok->range,
1524 : : line_offset + 4, 1, 4,
1525 : : line_offset + 7, 1, 7);
1526 : 4 : l.consume ();
1527 : : }
1528 : : /* Expect token: "-1" in columns 11-12. */
1529 : 4 : {
1530 : 4 : const token *tok = l.peek ();
1531 : 4 : ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
1532 : 4 : ASSERT_EQ (tok->u.integer_number, -1);
1533 : 4 : ASSERT_RANGE_EQ (tok->range,
1534 : : line_offset + 11, 1, 11,
1535 : : line_offset + 12, 1, 12);
1536 : 4 : l.consume ();
1537 : : }
1538 : : }
1539 : :
1540 : : /* Line 2. */
1541 : 4 : {
1542 : 4 : const size_t line_offset = 16;
1543 : :
1544 : : /* Expect token: "-273.15" in columns 4-10. */
1545 : 4 : {
1546 : 4 : const token *tok = l.peek ();
1547 : 4 : ASSERT_EQ (tok->id, TOK_FLOAT_NUMBER);
1548 : 4 : ASSERT_EQ (int(tok->u.float_number), int(-273.15));
1549 : 4 : ASSERT_RANGE_EQ (tok->range,
1550 : : line_offset + 4, 2, 4,
1551 : : line_offset + 10, 2, 10);
1552 : 4 : l.consume ();
1553 : : }
1554 : : /* Expect token: "1e6" in columns 12-14. */
1555 : 4 : {
1556 : 4 : const token *tok = l.peek ();
1557 : 4 : ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
1558 : 4 : ASSERT_EQ (tok->u.integer_number, 1000000);
1559 : 4 : ASSERT_RANGE_EQ (tok->range,
1560 : : line_offset + 12, 2, 12,
1561 : : line_offset + 14, 2, 14);
1562 : 4 : l.consume ();
1563 : : }
1564 : : }
1565 : :
1566 : : /* Line 3. */
1567 : 4 : {
1568 : 4 : const size_t line_offset = 32;
1569 : :
1570 : : /* Expect token: "[". */
1571 : 4 : {
1572 : 4 : const token *tok = l.peek ();
1573 : 4 : ASSERT_EQ (tok->id, TOK_OPEN_SQUARE);
1574 : 4 : ASSERT_RANGE_EQ (tok->range,
1575 : : line_offset + 2, 3, 2,
1576 : : line_offset + 2, 3, 2);
1577 : 4 : l.consume ();
1578 : : }
1579 : : /* Expect token: "]". */
1580 : 4 : {
1581 : 4 : const token *tok = l.peek ();
1582 : 4 : ASSERT_EQ (tok->id, TOK_CLOSE_SQUARE);
1583 : 4 : ASSERT_RANGE_EQ (tok->range,
1584 : : line_offset + 6, 3, 6,
1585 : : line_offset + 6, 3, 6);
1586 : 4 : l.consume ();
1587 : : }
1588 : : /* Expect token: "null". */
1589 : 4 : {
1590 : 4 : const token *tok = l.peek ();
1591 : 4 : ASSERT_EQ (tok->id, TOK_NULL);
1592 : 4 : ASSERT_RANGE_EQ (tok->range,
1593 : : line_offset + 8, 3, 8,
1594 : : line_offset + 11, 3, 11);
1595 : 4 : l.consume ();
1596 : : }
1597 : : /* Expect token: "true". */
1598 : 4 : {
1599 : 4 : const token *tok = l.peek ();
1600 : 4 : ASSERT_EQ (tok->id, TOK_TRUE);
1601 : 4 : ASSERT_RANGE_EQ (tok->range,
1602 : : line_offset + 15, 3, 15,
1603 : : line_offset + 18, 3, 18);
1604 : 4 : l.consume ();
1605 : : }
1606 : : /* Expect token: "false". */
1607 : 4 : {
1608 : 4 : const token *tok = l.peek ();
1609 : 4 : ASSERT_EQ (tok->id, TOK_FALSE);
1610 : 4 : ASSERT_RANGE_EQ (tok->range,
1611 : : line_offset + 21, 3, 21,
1612 : : line_offset + 25, 3, 25);
1613 : 4 : l.consume ();
1614 : : }
1615 : : /* Expect token: "{". */
1616 : 4 : {
1617 : 4 : const token *tok = l.peek ();
1618 : 4 : ASSERT_EQ (tok->id, TOK_OPEN_CURLY);
1619 : 4 : ASSERT_RANGE_EQ (tok->range,
1620 : : line_offset + 28, 3, 28,
1621 : : line_offset + 28, 3, 28);
1622 : 4 : l.consume ();
1623 : : }
1624 : : /* Expect token: "}". */
1625 : 4 : {
1626 : 4 : const token *tok = l.peek ();
1627 : 4 : ASSERT_EQ (tok->id, TOK_CLOSE_CURLY);
1628 : 4 : ASSERT_RANGE_EQ (tok->range,
1629 : : line_offset + 31, 3, 31,
1630 : : line_offset + 31, 3, 31);
1631 : 4 : l.consume ();
1632 : : }
1633 : : /* Expect token: "\"foo\"". */
1634 : 4 : {
1635 : 4 : const token *tok = l.peek ();
1636 : 4 : ASSERT_EQ (tok->id, TOK_STRING);
1637 : 4 : ASSERT_RANGE_EQ (tok->range,
1638 : : line_offset + 34, 3, 34,
1639 : : line_offset + 38, 3, 38);
1640 : 4 : l.consume ();
1641 : : }
1642 : : }
1643 : 4 : }
1644 : :
1645 : : /* Verify that the JSON lexer complains about single-line comments
1646 : : when comments are disabled. */
1647 : :
1648 : : static void
1649 : 4 : test_lexing_unsupported_single_line_comment ()
1650 : : {
1651 : 4 : lexer l (false);
1652 : 4 : const char *str
1653 : : /* 0 1 2 3 4 . */
1654 : : /* 01234567890123456789012345678901234567890123456789. */
1655 : : = (" 1066 // Hello world\n");
1656 : 4 : auto err = l.add_utf8 (strlen (str), str);
1657 : 4 : ASSERT_EQ (err, nullptr);
1658 : :
1659 : : /* Line 1. */
1660 : 4 : {
1661 : 4 : const size_t line_offset = 0;
1662 : 4 : const int line_1 = 1;
1663 : :
1664 : : /* Expect token: "1066" in columns 4-7. */
1665 : 4 : {
1666 : 4 : const token *tok = l.peek ();
1667 : 4 : ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
1668 : 4 : ASSERT_EQ (tok->u.integer_number, 1066);
1669 : 4 : ASSERT_RANGE_EQ (tok->range,
1670 : : line_offset + 4, line_1, 4,
1671 : : line_offset + 7, line_1, 7);
1672 : 4 : l.consume ();
1673 : : }
1674 : :
1675 : : /* Expect error. */
1676 : 4 : {
1677 : 4 : const token *tok = l.peek ();
1678 : 4 : ASSERT_EQ (tok->id, TOK_ERROR);
1679 : 4 : ASSERT_STREQ (tok->u.string, "unexpected character: '/'");
1680 : 4 : ASSERT_RANGE_EQ (tok->range,
1681 : : line_offset + 11, line_1, 11,
1682 : : line_offset + 11, line_1, 11);
1683 : 4 : l.consume ();
1684 : : }
1685 : : }
1686 : 4 : }
1687 : :
1688 : : /* Verify that the JSON lexer complains about multiline comments
1689 : : when comments are disabled. */
1690 : :
1691 : : static void
1692 : 4 : test_lexing_unsupported_multiline_comment ()
1693 : : {
1694 : 4 : lexer l (false);
1695 : 4 : const char *str
1696 : : /* 0 1 2 3 4 . */
1697 : : /* 01234567890123456789012345678901234567890123456789. */
1698 : : = (" 1066 /* Hello world\n"
1699 : : " continuation of comment\n"
1700 : : " end of comment */ 42\n");
1701 : 4 : auto err = l.add_utf8 (strlen (str), str);
1702 : 4 : ASSERT_EQ (err, nullptr);
1703 : :
1704 : : /* Line 1. */
1705 : 4 : {
1706 : 4 : const size_t line_offset = 0;
1707 : 4 : const int line_1 = 1;
1708 : :
1709 : : /* Expect token: "1066" in line 1, columns 4-7. */
1710 : 4 : {
1711 : 4 : const token *tok = l.peek ();
1712 : 4 : ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
1713 : 4 : ASSERT_EQ (tok->u.integer_number, 1066);
1714 : 4 : ASSERT_RANGE_EQ (tok->range,
1715 : : line_offset + 4, line_1, 4,
1716 : : line_offset + 7, line_1, 7);
1717 : 4 : l.consume ();
1718 : : }
1719 : :
1720 : : /* Expect error. */
1721 : 4 : {
1722 : 4 : const token *tok = l.peek ();
1723 : 4 : ASSERT_EQ (tok->id, TOK_ERROR);
1724 : 4 : ASSERT_STREQ (tok->u.string, "unexpected character: '/'");
1725 : 4 : ASSERT_RANGE_EQ (tok->range,
1726 : : line_offset + 11, line_1, 11,
1727 : : line_offset + 11, line_1, 11);
1728 : 4 : l.consume ();
1729 : : }
1730 : : }
1731 : 4 : }
1732 : :
1733 : : /* Verify that the JSON lexer handles single-line comments
1734 : : when comments are enabled. */
1735 : :
1736 : : static void
1737 : 4 : test_lexing_supported_single_line_comment ()
1738 : : {
1739 : 4 : lexer l (true);
1740 : 4 : const char *str
1741 : : /* 0 1 2 3 4 . */
1742 : : /* 01234567890123456789012345678901234567890123456789. */
1743 : : = (" 1066 // Hello world\n"
1744 : : " 42 // etc\n");
1745 : 4 : auto err = l.add_utf8 (strlen (str), str);
1746 : 4 : ASSERT_EQ (err, nullptr);
1747 : :
1748 : 4 : const size_t line_1_offset = 0;
1749 : 4 : const size_t line_2_offset = 26;
1750 : 4 : const size_t line_3_offset = line_2_offset + 17;
1751 : :
1752 : : /* Expect token: "1066" in line 1, columns 4-7. */
1753 : 4 : {
1754 : 4 : const int line_1 = 1;
1755 : 4 : const token *tok = l.peek ();
1756 : 4 : ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
1757 : 4 : ASSERT_EQ (tok->u.integer_number, 1066);
1758 : 4 : ASSERT_RANGE_EQ (tok->range,
1759 : : line_1_offset + 4, line_1, 4,
1760 : : line_1_offset + 7, line_1, 7);
1761 : 4 : l.consume ();
1762 : : }
1763 : :
1764 : : /* Expect token: "42" in line 2, columns 5-6. */
1765 : 4 : {
1766 : 4 : const int line_2 = 2;
1767 : 4 : const token *tok = l.peek ();
1768 : 4 : ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
1769 : 4 : ASSERT_EQ (tok->u.integer_number, 42);
1770 : 4 : ASSERT_RANGE_EQ (tok->range,
1771 : : line_2_offset + 5, line_2, 5,
1772 : : line_2_offset + 6, line_2, 6);
1773 : 4 : l.consume ();
1774 : : }
1775 : :
1776 : : /* Expect EOF. */
1777 : 4 : {
1778 : 4 : const int line_3 = 3;
1779 : 4 : const token *tok = l.peek ();
1780 : 4 : ASSERT_EQ (tok->id, TOK_EOF);
1781 : 4 : ASSERT_RANGE_EQ (tok->range,
1782 : : line_3_offset + 0, line_3, 0,
1783 : : line_3_offset + 0, line_3, 0);
1784 : 4 : l.consume ();
1785 : : }
1786 : 4 : }
1787 : :
1788 : : /* Verify that the JSON lexer handles multiline comments
1789 : : when comments are enabled. */
1790 : :
1791 : : static void
1792 : 4 : test_lexing_supported_multiline_comment ()
1793 : : {
1794 : 4 : lexer l (true);
1795 : 4 : const char *str
1796 : : /* 0 1 2 3 4 . */
1797 : : /* 01234567890123456789012345678901234567890123456789. */
1798 : : = (" 1066 /* Hello world\n"
1799 : : " continuation of comment\n"
1800 : : " end of comment */ 42\n");
1801 : 4 : auto err = l.add_utf8 (strlen (str), str);
1802 : 4 : ASSERT_EQ (err, nullptr);
1803 : :
1804 : 4 : const size_t line_1_offset = 0;
1805 : 4 : const size_t line_2_offset = 26;
1806 : 4 : const size_t line_3_offset = line_2_offset + 25;
1807 : 4 : const size_t line_4_offset = line_3_offset + 23;
1808 : :
1809 : : /* Expect token: "1066" in line 1, columns 4-7. */
1810 : 4 : {
1811 : 4 : const int line_1 = 1;
1812 : 4 : const token *tok = l.peek ();
1813 : 4 : ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
1814 : 4 : ASSERT_EQ (tok->u.integer_number, 1066);
1815 : 4 : ASSERT_RANGE_EQ (tok->range,
1816 : : line_1_offset + 4, line_1, 4,
1817 : : line_1_offset + 7, line_1, 7);
1818 : 4 : l.consume ();
1819 : : }
1820 : :
1821 : : /* Expect token: "42" in line 3, columns 20-21. */
1822 : 4 : {
1823 : 4 : const int line_3 = 3;
1824 : 4 : const token *tok = l.peek ();
1825 : 4 : ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
1826 : 4 : ASSERT_EQ (tok->u.integer_number, 42);
1827 : 4 : ASSERT_RANGE_EQ (tok->range,
1828 : : line_3_offset + 20, line_3, 20,
1829 : : line_3_offset + 21, line_3, 21);
1830 : 4 : l.consume ();
1831 : : }
1832 : :
1833 : : /* Expect EOF. */
1834 : 4 : {
1835 : 4 : const int line_4 = 4;
1836 : 4 : const token *tok = l.peek ();
1837 : 4 : ASSERT_EQ (tok->id, TOK_EOF);
1838 : 4 : ASSERT_RANGE_EQ (tok->range,
1839 : : line_4_offset + 0, line_4, 0,
1840 : : line_4_offset + 0, line_4, 0);
1841 : 4 : l.consume ();
1842 : : }
1843 : 4 : }
1844 : :
1845 : : /* Helper class for writing JSON parsing testcases.
1846 : : Attempts to parse a string in ctor, and captures the result (either
1847 : : a json::value or a json::error), and a location map. */
1848 : :
1849 : : struct parser_testcase
1850 : : {
1851 : : public:
1852 : 92 : parser_testcase (const char *utf8_string, bool allow_comments = false)
1853 : 92 : : m_loc_map (),
1854 : 92 : m_result (parse_utf8_string (utf8_string, allow_comments, &m_loc_map))
1855 : : {
1856 : 92 : }
1857 : :
1858 : 92 : const json::value *get_value () const { return m_result.m_val.get (); }
1859 : 96 : const json::error *get_error () const { return m_result.m_err.get (); }
1860 : :
1861 : : const location_map::range *
1862 : 124 : get_range_for_value (const json::value *jv) const
1863 : : {
1864 : 248 : return m_loc_map.get_range_for_value (jv);
1865 : : }
1866 : :
1867 : : private:
1868 : : /* Concrete implementation of location_map for use in
1869 : : JSON parsing selftests. */
1870 : : class test_location_map : public location_map
1871 : : {
1872 : : public:
1873 : 164 : void record_range_for_value (json::value *jv, const range &r) final override
1874 : : {
1875 : 164 : m_map.put (jv, r);
1876 : 164 : }
1877 : :
1878 : 124 : range *get_range_for_value (const json::value *jv) const
1879 : : {
1880 : 124 : return const_cast<hash_map<const json::value *, range> &> (m_map)
1881 : 124 : .get (jv);
1882 : : }
1883 : :
1884 : : private:
1885 : : hash_map<const json::value *, range> m_map;
1886 : : };
1887 : :
1888 : : test_location_map m_loc_map;
1889 : : json::parser_result_t m_result;
1890 : : };
1891 : :
1892 : : /* Verify that parse_utf8_string works as expected. */
1893 : :
1894 : : static void
1895 : 4 : test_parse_string ()
1896 : : {
1897 : 4 : const int line_1 = 1;
1898 : :
1899 : 4 : {
1900 : 4 : parser_testcase tc ("\"foo\"");
1901 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
1902 : 4 : const json::value *jv = tc.get_value ();
1903 : 4 : ASSERT_EQ (jv->get_kind (), JSON_STRING);
1904 : 4 : ASSERT_STREQ (as_a <const json::string *> (jv)->get_string (), "foo");
1905 : 4 : ASSERT_PRINT_EQ (*jv, true, "\"foo\"");
1906 : 4 : auto range = tc.get_range_for_value (jv);
1907 : 4 : ASSERT_TRUE (range);
1908 : 4 : ASSERT_RANGE_EQ (*range,
1909 : : 0, line_1, 0,
1910 : : 4, line_1, 4);
1911 : 4 : }
1912 : :
1913 : 4 : {
1914 : 4 : const char *contains_quotes = "\"before \\\"quoted\\\" after\"";
1915 : 4 : parser_testcase tc (contains_quotes);
1916 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
1917 : 4 : const json::value *jv = tc.get_value ();
1918 : 4 : ASSERT_EQ (jv->get_kind (), JSON_STRING);
1919 : 4 : ASSERT_STREQ (as_a <const json::string *> (jv)->get_string (),
1920 : : "before \"quoted\" after");
1921 : 4 : ASSERT_PRINT_EQ (*jv, true, contains_quotes);
1922 : 4 : auto range = tc.get_range_for_value (jv);
1923 : 4 : ASSERT_TRUE (range);
1924 : 4 : ASSERT_RANGE_EQ (*range,
1925 : : 0, line_1, 0,
1926 : : 24, line_1, 24);
1927 : 4 : }
1928 : :
1929 : : /* Test of non-ASCII input. This string is the Japanese word "mojibake",
1930 : : written as C octal-escaped UTF-8. */
1931 : 4 : const char *mojibake = (/* Opening quote. */
1932 : : "\""
1933 : : /* U+6587 CJK UNIFIED IDEOGRAPH-6587
1934 : : UTF-8: 0xE6 0x96 0x87
1935 : : C octal escaped UTF-8: \346\226\207. */
1936 : : "\346\226\207"
1937 : : /* U+5B57 CJK UNIFIED IDEOGRAPH-5B57
1938 : : UTF-8: 0xE5 0xAD 0x97
1939 : : C octal escaped UTF-8: \345\255\227. */
1940 : : "\345\255\227"
1941 : : /* U+5316 CJK UNIFIED IDEOGRAPH-5316
1942 : : UTF-8: 0xE5 0x8C 0x96
1943 : : C octal escaped UTF-8: \345\214\226. */
1944 : : "\345\214\226"
1945 : : /* U+3051 HIRAGANA LETTER KE
1946 : : UTF-8: 0xE3 0x81 0x91
1947 : : C octal escaped UTF-8: \343\201\221. */
1948 : : "\343\201\221"
1949 : : /* Closing quote. */
1950 : : "\"");
1951 : 4 : {
1952 : 4 : parser_testcase tc (mojibake);
1953 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
1954 : 4 : const json::value *jv = tc.get_value ();
1955 : 4 : ASSERT_EQ (jv->get_kind (), JSON_STRING);
1956 : : /* Result of get_string should be UTF-8 encoded, without quotes. */
1957 : 4 : ASSERT_STREQ (as_a <const json::string *> (jv)->get_string (),
1958 : : "\346\226\207" "\345\255\227" "\345\214\226" "\343\201\221");
1959 : : /* Result of dump should be UTF-8 encoded, with quotes. */
1960 : 4 : ASSERT_PRINT_EQ (*jv, false, mojibake);
1961 : 4 : auto range = tc.get_range_for_value (jv);
1962 : 4 : ASSERT_TRUE (range);
1963 : 4 : ASSERT_RANGE_EQ (*range,
1964 : : 0, line_1, 0,
1965 : : 5, line_1, 5);
1966 : 4 : }
1967 : :
1968 : : /* Test of \u-escaped unicode. This is "mojibake" again, as above. */
1969 : 4 : {
1970 : 4 : const char *escaped_unicode = "\"\\u6587\\u5b57\\u5316\\u3051\"";
1971 : 4 : parser_testcase tc (escaped_unicode);
1972 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
1973 : 4 : const json::value *jv = tc.get_value ();
1974 : 4 : ASSERT_EQ (jv->get_kind (), JSON_STRING);
1975 : : /* Result of get_string should be UTF-8 encoded, without quotes. */
1976 : 4 : ASSERT_STREQ (as_a <const json::string *> (jv)->get_string (),
1977 : : "\346\226\207" "\345\255\227" "\345\214\226" "\343\201\221");
1978 : : /* Result of dump should be UTF-8 encoded, with quotes. */
1979 : 4 : ASSERT_PRINT_EQ (*jv, false, mojibake);
1980 : 4 : auto range = tc.get_range_for_value (jv);
1981 : 4 : ASSERT_TRUE (range);
1982 : 4 : ASSERT_RANGE_EQ (*range,
1983 : : 0, line_1, 0,
1984 : : 25, line_1, 25);
1985 : 4 : }
1986 : 4 : }
1987 : :
1988 : : /* Verify that we can parse various kinds of JSON numbers. */
1989 : :
1990 : : static void
1991 : 4 : test_parse_number ()
1992 : : {
1993 : 4 : const int line_1 = 1;
1994 : :
1995 : 4 : {
1996 : 4 : parser_testcase tc ("42");
1997 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
1998 : 4 : const json::value *jv = tc.get_value ();
1999 : 4 : ASSERT_EQ (jv->get_kind (), JSON_INTEGER);
2000 : 4 : ASSERT_EQ (as_a <const json::integer_number *> (jv)->get (), 42.0);
2001 : 4 : ASSERT_PRINT_EQ (*jv, true, "42");
2002 : 4 : auto range = tc.get_range_for_value (jv);
2003 : 4 : ASSERT_TRUE (range);
2004 : 4 : ASSERT_RANGE_EQ (*range,
2005 : : 0, line_1, 0,
2006 : : 1, line_1, 1);
2007 : 4 : }
2008 : :
2009 : : /* Negative number. */
2010 : 4 : {
2011 : 4 : parser_testcase tc ("-17");
2012 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2013 : 4 : const json::value *jv = tc.get_value ();
2014 : 4 : ASSERT_EQ (jv->get_kind (), JSON_INTEGER);
2015 : 4 : ASSERT_EQ (as_a<const json::integer_number *> (jv)->get (), -17.0);
2016 : 4 : ASSERT_PRINT_EQ (*jv, true, "-17");
2017 : 4 : auto range = tc.get_range_for_value (jv);
2018 : 4 : ASSERT_TRUE (range);
2019 : 4 : ASSERT_RANGE_EQ (*range,
2020 : : 0, line_1, 0,
2021 : : 2, line_1, 2);
2022 : 4 : }
2023 : :
2024 : : /* Decimal. */
2025 : 4 : {
2026 : 4 : parser_testcase tc ("3.141");
2027 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2028 : 4 : const json::value *jv = tc.get_value ();
2029 : 4 : ASSERT_EQ (JSON_FLOAT, jv->get_kind ());
2030 : 4 : ASSERT_NEAR (3.141, ((const json::float_number *)jv)->get (), 0.001);
2031 : 4 : auto range = tc.get_range_for_value (jv);
2032 : 4 : ASSERT_TRUE (range);
2033 : 4 : ASSERT_RANGE_EQ (*range,
2034 : : 0, line_1, 0,
2035 : : 4, line_1, 4);
2036 : 4 : }
2037 : :
2038 : : /* Exponents. */
2039 : 4 : {
2040 : 4 : {
2041 : 4 : parser_testcase tc ("3.141e+0");
2042 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2043 : 4 : const json::value *jv = tc.get_value ();
2044 : 4 : ASSERT_EQ (jv->get_kind (), JSON_FLOAT);
2045 : 4 : ASSERT_NEAR (as_a <const json::float_number *> (jv)->get (), 3.141, 0.1);
2046 : 4 : auto range = tc.get_range_for_value (jv);
2047 : 4 : ASSERT_TRUE (range);
2048 : 4 : ASSERT_RANGE_EQ (*range,
2049 : : 0, line_1, 0,
2050 : : 7, line_1, 7);
2051 : 4 : }
2052 : 4 : {
2053 : 4 : parser_testcase tc ("42e2");
2054 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2055 : 4 : const json::value *jv = tc.get_value ();
2056 : 4 : ASSERT_EQ (jv->get_kind (), JSON_INTEGER);
2057 : 4 : ASSERT_EQ (as_a <const json::integer_number *> (jv)->get (), 4200);
2058 : 4 : ASSERT_PRINT_EQ (*jv, true, "4200");
2059 : 4 : auto range = tc.get_range_for_value (jv);
2060 : 4 : ASSERT_TRUE (range);
2061 : 4 : ASSERT_RANGE_EQ (*range,
2062 : : 0, line_1, 0,
2063 : : 3, line_1, 3);
2064 : 4 : }
2065 : 4 : {
2066 : 4 : parser_testcase tc ("42e-1");
2067 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2068 : 4 : const json::value *jv = tc.get_value ();
2069 : 4 : ASSERT_EQ (jv->get_kind (), JSON_FLOAT);
2070 : 4 : ASSERT_NEAR (as_a <const json::float_number *> (jv)->get (), 4.2, 0.1);
2071 : 4 : auto range = tc.get_range_for_value (jv);
2072 : 4 : ASSERT_TRUE (range);
2073 : 4 : ASSERT_RANGE_EQ (*range,
2074 : : 0, line_1, 0,
2075 : : 4, line_1, 4);
2076 : 4 : }
2077 : : }
2078 : 4 : }
2079 : :
2080 : : /* Verify that JSON array parsing works. */
2081 : :
2082 : : static void
2083 : 4 : test_parse_array ()
2084 : : {
2085 : 4 : const int line_1 = 1;
2086 : :
2087 : 4 : parser_testcase tc ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
2088 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2089 : 4 : const json::value *jv = tc.get_value ();
2090 : 4 : ASSERT_EQ (jv->get_kind (), JSON_ARRAY);
2091 : 4 : const json::array *arr = as_a <const json::array *> (jv);
2092 : 4 : ASSERT_EQ (arr->length (), 10);
2093 : 4 : auto range = tc.get_range_for_value (jv);
2094 : 4 : ASSERT_TRUE (range);
2095 : 4 : ASSERT_RANGE_EQ (*range,
2096 : : 0, line_1, 0,
2097 : : 29, line_1, 29);
2098 : 44 : for (int i = 0; i < 10; i++)
2099 : : {
2100 : 40 : json::value *element = arr->get (i);
2101 : 40 : ASSERT_EQ (element->get_kind (), JSON_INTEGER);
2102 : 40 : ASSERT_EQ (as_a <json::integer_number *> (element)->get (), i);
2103 : 40 : range = tc.get_range_for_value (element);
2104 : 40 : ASSERT_TRUE (range);
2105 : 40 : const int offset = 1 + (i * 3);
2106 : 40 : ASSERT_RANGE_EQ (*range,
2107 : : offset, line_1, offset,
2108 : : offset, line_1, offset);
2109 : : }
2110 : 4 : ASSERT_PRINT_EQ (*jv, false, "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
2111 : 4 : }
2112 : :
2113 : : /* Verify that JSON object parsing works. */
2114 : :
2115 : : static void
2116 : 4 : test_parse_object ()
2117 : : {
2118 : 4 : const int line_1 = 1;
2119 : 4 : std::unique_ptr<error> err;
2120 : : /* 0 1 2 3 . */
2121 : : /* 01 2345 678 9012 345 6789 0123456789012. */
2122 : 4 : parser_testcase tc ("{\"foo\": \"bar\", \"baz\": [42, null]}");
2123 : :
2124 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2125 : 4 : const json::value *jv = tc.get_value ();
2126 : 4 : ASSERT_NE (jv, nullptr);
2127 : 4 : ASSERT_EQ (jv->get_kind (), JSON_OBJECT);
2128 : 4 : auto range = tc.get_range_for_value (jv);
2129 : 4 : ASSERT_TRUE (range);
2130 : 4 : ASSERT_RANGE_EQ (*range,
2131 : : 0, line_1, 0,
2132 : : 32, line_1, 32);
2133 : 4 : const json::object *jo = static_cast <const json::object *> (jv);
2134 : :
2135 : 4 : json::value *foo_value = jo->get ("foo");
2136 : 4 : ASSERT_NE (foo_value, nullptr);
2137 : 4 : ASSERT_EQ (foo_value->get_kind (), JSON_STRING);
2138 : 4 : ASSERT_STREQ (as_a <json::string *> (foo_value)->get_string (), "bar");
2139 : 4 : range = tc.get_range_for_value (foo_value);
2140 : 4 : ASSERT_TRUE (range);
2141 : 4 : ASSERT_RANGE_EQ (*range,
2142 : : 8, line_1, 8,
2143 : : 12, line_1, 12);
2144 : :
2145 : 4 : json::value *baz_value = jo->get ("baz");
2146 : 4 : ASSERT_NE (baz_value, nullptr);
2147 : 4 : ASSERT_EQ (baz_value->get_kind (), JSON_ARRAY);
2148 : 4 : range = tc.get_range_for_value (baz_value);
2149 : 4 : ASSERT_TRUE (range);
2150 : 4 : ASSERT_RANGE_EQ (*range,
2151 : : 22, line_1, 22,
2152 : : 31, line_1, 31);
2153 : :
2154 : 4 : json::array *baz_array = as_a <json::array *> (baz_value);
2155 : 4 : ASSERT_EQ (baz_array->length (), 2);
2156 : :
2157 : 4 : json::value *element0 = baz_array->get (0);
2158 : 4 : ASSERT_EQ (as_a <json::integer_number *> (element0)->get (), 42);
2159 : 4 : range = tc.get_range_for_value (element0);
2160 : 4 : ASSERT_TRUE (range);
2161 : 4 : ASSERT_RANGE_EQ (*range,
2162 : : 23, line_1, 23,
2163 : : 24, line_1, 24);
2164 : :
2165 : 4 : json::value *element1 = baz_array->get (1);
2166 : 4 : ASSERT_EQ (element1->get_kind (), JSON_NULL);
2167 : 4 : range = tc.get_range_for_value (element1);
2168 : 4 : ASSERT_TRUE (range);
2169 : 4 : ASSERT_RANGE_EQ (*range,
2170 : : 27, line_1, 27,
2171 : : 30, line_1, 30);
2172 : 4 : }
2173 : :
2174 : : /* Verify that the JSON literals "true", "false" and "null" are parsed
2175 : : correctly. */
2176 : :
2177 : : static void
2178 : 4 : test_parse_literals ()
2179 : : {
2180 : 4 : const int line_1 = 1;
2181 : 4 : {
2182 : 4 : parser_testcase tc ("true");
2183 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2184 : 4 : const json::value *jv = tc.get_value ();
2185 : 4 : ASSERT_NE (jv, nullptr);
2186 : 4 : ASSERT_EQ (jv->get_kind (), JSON_TRUE);
2187 : 4 : ASSERT_PRINT_EQ (*jv, false, "true");
2188 : 4 : auto range = tc.get_range_for_value (jv);
2189 : 4 : ASSERT_TRUE (range);
2190 : 4 : ASSERT_RANGE_EQ (*range,
2191 : : 0, line_1, 0,
2192 : : 3, line_1, 3);
2193 : 4 : }
2194 : :
2195 : 4 : {
2196 : 4 : parser_testcase tc ("false");
2197 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2198 : 4 : const json::value *jv = tc.get_value ();
2199 : 4 : ASSERT_NE (jv, nullptr);
2200 : 4 : ASSERT_EQ (jv->get_kind (), JSON_FALSE);
2201 : 4 : ASSERT_PRINT_EQ (*jv, false, "false");
2202 : 4 : auto range = tc.get_range_for_value (jv);
2203 : 4 : ASSERT_TRUE (range);
2204 : 4 : ASSERT_RANGE_EQ (*range,
2205 : : 0, line_1, 0,
2206 : : 4, line_1, 4);
2207 : 4 : }
2208 : :
2209 : 4 : {
2210 : 4 : parser_testcase tc ("null");
2211 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2212 : 4 : const json::value *jv = tc.get_value ();
2213 : 4 : ASSERT_NE (jv, nullptr);
2214 : 4 : ASSERT_EQ (jv->get_kind (), JSON_NULL);
2215 : 4 : ASSERT_PRINT_EQ (*jv, false, "null");
2216 : 4 : auto range = tc.get_range_for_value (jv);
2217 : 4 : ASSERT_TRUE (range);
2218 : 4 : ASSERT_RANGE_EQ (*range,
2219 : : 0, line_1, 0,
2220 : : 3, line_1, 3);
2221 : 4 : }
2222 : 4 : }
2223 : :
2224 : : /* Verify that we can parse a simple JSON-RPC request. */
2225 : :
2226 : : static void
2227 : 4 : test_parse_jsonrpc ()
2228 : : {
2229 : 4 : std::unique_ptr<error> err;
2230 : 4 : const char *request
2231 : : /* 0 1 2 3 4. */
2232 : : /* 01 23456789 012 3456 789 0123456 789 012345678 90. */
2233 : : = ("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\",\n"
2234 : : /* 0 1 2 3 4. */
2235 : : /* 0 1234567 8901234567890 1234 56789012345678 90. */
2236 : : " \"params\": [42, 23], \"id\": 1}");
2237 : 4 : const int line_1 = 1;
2238 : 4 : const int line_2 = 2;
2239 : 4 : const size_t line_2_offset = 41;
2240 : 4 : parser_testcase tc (request);
2241 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2242 : 4 : const json::value *jv = tc.get_value ();
2243 : 4 : ASSERT_NE (jv, nullptr);
2244 : 4 : auto range = tc.get_range_for_value (jv);
2245 : 4 : ASSERT_TRUE (range);
2246 : 4 : ASSERT_RANGE_EQ (*range,
2247 : : 0, line_1, 0,
2248 : : line_2_offset + 28, line_2, 28);
2249 : 4 : }
2250 : :
2251 : : /* Verify that we can parse an empty JSON object. */
2252 : :
2253 : : static void
2254 : 4 : test_parse_empty_object ()
2255 : : {
2256 : 4 : const int line_1 = 1;
2257 : 4 : std::unique_ptr<error> err;
2258 : 4 : parser_testcase tc ("{}");
2259 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2260 : 4 : const json::value *jv = tc.get_value ();
2261 : 4 : ASSERT_NE (jv, nullptr);
2262 : 4 : ASSERT_EQ (jv->get_kind (), JSON_OBJECT);
2263 : 4 : ASSERT_PRINT_EQ (*jv, true, "{}");
2264 : 4 : auto range = tc.get_range_for_value (jv);
2265 : 4 : ASSERT_TRUE (range);
2266 : 4 : ASSERT_RANGE_EQ (*range,
2267 : : 0, line_1, 0,
2268 : : 1, line_1, 1);
2269 : 4 : }
2270 : :
2271 : : /* Verify that comment-parsing can be enabled or disabled. */
2272 : :
2273 : : static void
2274 : 4 : test_parsing_comments ()
2275 : : {
2276 : 4 : const char *str = ("// foo\n"
2277 : : "/*...\n"
2278 : : "...*/ 42 // bar\n"
2279 : : "/* etc */\n");
2280 : :
2281 : : /* Parsing with comment support disabled. */
2282 : 4 : {
2283 : 4 : parser_testcase tc (str);
2284 : 4 : ASSERT_NE (tc.get_error (), nullptr);
2285 : 4 : ASSERT_STREQ (tc.get_error ()->get_msg (),
2286 : : "invalid JSON token: unexpected character: '/'");
2287 : 4 : ASSERT_EQ (tc.get_value (), nullptr);
2288 : 4 : }
2289 : :
2290 : : /* Parsing with comment support enabled. */
2291 : 4 : {
2292 : 4 : parser_testcase tc (str, true);
2293 : 4 : ASSERT_EQ (tc.get_error (), nullptr);
2294 : 4 : const json::value *jv = tc.get_value ();
2295 : 4 : ASSERT_NE (jv, nullptr);
2296 : 4 : ASSERT_EQ (jv->get_kind (), JSON_INTEGER);
2297 : 4 : ASSERT_EQ (((const json::integer_number *)jv)->get (), 42);
2298 : 4 : }
2299 : 4 : }
2300 : :
2301 : : /* Verify that we can parse an empty JSON string. */
2302 : :
2303 : : static void
2304 : 4 : test_error_empty_string ()
2305 : : {
2306 : 4 : const int line_1 = 1;
2307 : 4 : parser_testcase tc ("");
2308 : 4 : ASSERT_ERR_EQ (tc.get_error (),
2309 : : 0, line_1, 0,
2310 : : 0, line_1, 0,
2311 : : "expected a JSON value but got EOF");
2312 : 4 : ASSERT_EQ (tc.get_value (), nullptr);
2313 : 4 : }
2314 : :
2315 : : /* Verify that JSON parsing gracefully handles an invalid token. */
2316 : :
2317 : : static void
2318 : 4 : test_error_bad_token ()
2319 : : {
2320 : 4 : const int line_1 = 1;
2321 : 4 : parser_testcase tc (" not valid ");
2322 : 4 : ASSERT_ERR_EQ (tc.get_error (),
2323 : : 2, line_1, 2,
2324 : : 2, line_1, 2,
2325 : : "invalid JSON token: unexpected character: 'n'");
2326 : 4 : ASSERT_EQ (tc.get_value (), nullptr);
2327 : 4 : }
2328 : :
2329 : : /* Verify that JSON parsing gracefully handles a missing comma
2330 : : within an object. */
2331 : :
2332 : : static void
2333 : 4 : test_error_object_with_missing_comma ()
2334 : : {
2335 : 4 : const int line_1 = 1;
2336 : : /* 0 1 2. */
2337 : : /* 01 2345 6789012 3456 7890. */
2338 : 4 : const char *json = "{\"foo\" : 42 \"bar\"";
2339 : 4 : parser_testcase tc (json);
2340 : 4 : ASSERT_ERR_EQ (tc.get_error (),
2341 : : 12, line_1, 12,
2342 : : 16, line_1, 16,
2343 : : "expected ',' or '}'; got string");
2344 : 4 : ASSERT_EQ (tc.get_value (), nullptr);
2345 : 4 : }
2346 : :
2347 : : /* Verify that JSON parsing gracefully handles a missing comma
2348 : : within an array. */
2349 : :
2350 : : static void
2351 : 4 : test_error_array_with_missing_comma ()
2352 : : {
2353 : 4 : const int line_1 = 1;
2354 : : /* 01234567. */
2355 : 4 : const char *json = "[0, 1 42]";
2356 : 4 : parser_testcase tc (json);
2357 : 4 : ASSERT_ERR_EQ (tc.get_error (),
2358 : : 6, line_1, 6,
2359 : : 7, line_1, 7,
2360 : : "expected ',' or ']'; got number");
2361 : 4 : ASSERT_EQ (tc.get_value (), nullptr);
2362 : 4 : }
2363 : :
2364 : : /* Run all of the selftests within this file. */
2365 : :
2366 : : void
2367 : 4 : json_parser_cc_tests ()
2368 : : {
2369 : 4 : test_lexer ();
2370 : 4 : test_lexing_unsupported_single_line_comment ();
2371 : 4 : test_lexing_unsupported_multiline_comment ();
2372 : 4 : test_lexing_supported_single_line_comment ();
2373 : 4 : test_lexing_supported_multiline_comment ();
2374 : 4 : test_parse_string ();
2375 : 4 : test_parse_number ();
2376 : 4 : test_parse_array ();
2377 : 4 : test_parse_object ();
2378 : 4 : test_parse_literals ();
2379 : 4 : test_parse_jsonrpc ();
2380 : 4 : test_parse_empty_object ();
2381 : 4 : test_parsing_comments ();
2382 : 4 : test_error_empty_string ();
2383 : 4 : test_error_bad_token ();
2384 : 4 : test_error_object_with_missing_comma ();
2385 : 4 : test_error_array_with_missing_comma ();
2386 : 4 : }
2387 : :
2388 : : } // namespace selftest
2389 : :
2390 : : #endif /* #if CHECKING_P */
|