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