LCOV - code coverage report
Current view: top level - gcc - json-parsing.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 85.7 % 1215 1041
Test Date: 2026-02-28 14:20:25 Functions: 96.5 % 57 55
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* JSON parsing
       2              :    Copyright (C) 2017-2026 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              : #define INCLUDE_MAP
      22              : #include "config.h"
      23              : #include "system.h"
      24              : #include "coretypes.h"
      25              : #include "json-parsing.h"
      26              : #include "pretty-print.h"
      27              : #include "math.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          116 : lexer::lexer (bool support_comments)
     201          116 : : m_buffer (), m_next_char_idx (0),
     202          116 :   m_next_char_line (1), m_next_char_column (0),
     203          116 :   m_prev_line_final_column (-1),
     204          116 :   m_num_next_tokens (0),
     205          116 :   m_support_comments (support_comments)
     206              : {
     207            0 : }
     208              : 
     209              : /* lexer's dtor.  */
     210              : 
     211          116 : lexer::~lexer ()
     212              : {
     213          136 :   while (m_num_next_tokens > 0)
     214           20 :     consume ();
     215          116 : }
     216              : 
     217              : /* Peek the next token.  */
     218              : 
     219              : const token *
     220          748 : lexer::peek ()
     221              : {
     222          748 :   if (m_num_next_tokens == 0)
     223              :     {
     224          536 :       lex_token (&m_next_tokens[0]);
     225          536 :       m_num_next_tokens++;
     226              :     }
     227          748 :   return &m_next_tokens[0];
     228              : }
     229              : 
     230              : /* Consume the next token.  */
     231              : 
     232              : void
     233          536 : lexer::consume ()
     234              : {
     235          536 :   if (m_num_next_tokens == 0)
     236            0 :     peek ();
     237              : 
     238          536 :   gcc_assert (m_num_next_tokens > 0);
     239          536 :   gcc_assert (m_num_next_tokens <= MAX_TOKENS);
     240              : 
     241          536 :   if (0)
     242              :     {
     243              :       fprintf (stderr, "consuming token: ");
     244              :       dump_token (stderr, &m_next_tokens[0]);
     245              :       fprintf (stderr, "\n");
     246              :     }
     247              : 
     248          536 :   if (m_next_tokens[0].id == TOK_ERROR
     249          536 :       || m_next_tokens[0].id == TOK_STRING)
     250           88 :     free (m_next_tokens[0].u.string);
     251              : 
     252          536 :   m_num_next_tokens--;
     253          536 :   memmove (&m_next_tokens[0], &m_next_tokens[1],
     254          536 :            sizeof (token) * m_num_next_tokens);
     255          536 : }
     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          116 : lexer::add_utf8 (size_t length, const char *utf8_buf)
     263              : {
     264              :   /* Adapted from charset.c:one_utf8_to_cppchar.  */
     265          116 :   static const uchar masks[6] = { 0x7F, 0x1F, 0x0F, 0x07, 0x03, 0x01 };
     266          116 :   static const uchar patns[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
     267              : 
     268          116 :   const uchar *inbuf = (const unsigned char *) (utf8_buf);
     269          116 :   const uchar **inbufp = &inbuf;
     270          116 :   size_t *inbytesleftp = &length;
     271              : 
     272         2860 :   while (length > 0)
     273              :     {
     274         2744 :       unichar c;
     275         2744 :       const uchar *inbuf = *inbufp;
     276         2744 :       size_t nbytes, i;
     277              : 
     278         2744 :       c = *inbuf;
     279         2744 :       if (c < 0x80)
     280              :         {
     281         2728 :           m_buffer.safe_push (c);
     282         2728 :           *inbytesleftp -= 1;
     283         2728 :           *inbufp += 1;
     284         2728 :           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          116 :   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         2672 : lexer::get_char (unichar &out_char, location_map::point *out_point)
     335              : {
     336         5340 :   if (m_next_char_idx >= (int)m_buffer.length ())
     337              :     return false;
     338              : 
     339         2540 :   if (out_point)
     340         2140 :     *out_point = get_next_point ();
     341         2540 :   out_char = m_buffer[m_next_char_idx++];
     342              : 
     343         2540 :   if (out_char == '\n')
     344              :     {
     345           92 :       m_next_char_line++;
     346           92 :       m_prev_line_final_column = m_next_char_column;
     347           92 :       m_next_char_column = 0;
     348              :     }
     349              :   else
     350         2448 :     m_next_char_column++;
     351              : 
     352              :   return true;
     353              : }
     354              : 
     355              : /* Undo the last successful get_char.  */
     356              : 
     357              : void
     358          392 : lexer::unget_char ()
     359              : {
     360          392 :   --m_next_char_idx;
     361          392 :   if (m_next_char_column > 0)
     362          364 :     --m_next_char_column;
     363              :   else
     364              :     {
     365           28 :       m_next_char_line--;
     366           28 :       m_next_char_column = m_prev_line_final_column;
     367              :       /* We don't support more than one unget_char in a row.  */
     368           28 :       gcc_assert (m_prev_line_final_column != -1);
     369           28 :       m_prev_line_final_column = -1;
     370              :     }
     371          392 : }
     372              : 
     373              : /* Get the location of the next char.  */
     374              : 
     375              : location_map::point
     376         2228 : lexer::get_next_point () const
     377              : {
     378         2228 :   location_map::point result;
     379         2228 :   result.m_unichar_idx = m_next_char_idx;
     380         2228 :   result.m_line = m_next_char_line;
     381         2228 :   result.m_column = m_next_char_column;
     382         2140 :   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          536 : lexer::lex_token (token *out)
     525              : {
     526              :   /* Skip to next non-whitespace char.  */
     527         1084 :   unichar next_char;
     528         1084 :   location_map::point start_point;
     529         1084 :   while (1)
     530              :     {
     531         1084 :       if (!get_char (next_char, &start_point))
     532              :         {
     533           88 :           out->id = TOK_EOF;
     534           88 :           location_map::point p = get_next_point ();
     535           88 :           out->range.m_start = p;
     536           88 :           out->range.m_end = p;
     537           88 :           return;
     538              :         }
     539          996 :       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          968 :       if (next_char != ' '
     564          968 :           && next_char != '\t'
     565          484 :           && next_char != '\n'
     566          448 :           && next_char != '\r')
     567              :         break;
     568              :     }
     569              : 
     570          448 :  non_whitespace:
     571              : 
     572          448 :   out->range.m_start = start_point;
     573          448 :   out->range.m_end = start_point;
     574              : 
     575          448 :   switch (next_char)
     576              :     {
     577           20 :     case '[':
     578           20 :       out->id = TOK_OPEN_SQUARE;
     579           20 :       break;
     580              : 
     581           24 :     case '{':
     582           24 :       out->id = TOK_OPEN_CURLY;
     583           24 :       break;
     584              : 
     585           16 :     case ']':
     586           16 :       out->id = TOK_CLOSE_SQUARE;
     587           16 :       break;
     588              : 
     589           20 :     case '}':
     590           20 :       out->id = TOK_CLOSE_CURLY;
     591           20 :       break;
     592              : 
     593           36 :     case ':':
     594           36 :       out->id = TOK_COLON;
     595           36 :       break;
     596              : 
     597           68 :     case ',':
     598           68 :       out->id = TOK_COMMA;
     599           68 :       break;
     600              : 
     601           72 :     case '"':
     602           72 :       lex_string (out);
     603           72 :       break;
     604              : 
     605          148 :     case '-':
     606          148 :     case '0':
     607          148 :     case '1':
     608          148 :     case '2':
     609          148 :     case '3':
     610          148 :     case '4':
     611          148 :     case '5':
     612          148 :     case '6':
     613          148 :     case '7':
     614          148 :     case '8':
     615          148 :     case '9':
     616          148 :       lex_number (out, next_char);
     617          148 :       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           72 : lexer::lex_string (token *out)
     664              : {
     665           72 :   auto_vec<unichar> content;
     666           72 :   bool still_going = true;
     667          496 :   while (still_going)
     668              :     {
     669          424 :       unichar uc;
     670          424 :       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          424 :       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          328 :         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          328 :           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          328 :           content.safe_push (uc);
     773          328 :           break;
     774              :         }
     775              :     }
     776              : 
     777           72 :   out->id = TOK_STRING;
     778              : 
     779          144 :   auto_vec<char> utf8_buf;
     780              :   // Adapted from libcpp/charset.c:one_cppchar_to_utf8
     781          424 :   for (unsigned i = 0; i < content.length (); i++)
     782              :     {
     783          352 :       static const uchar masks[6] =  { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
     784          352 :       static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
     785          352 :       size_t nbytes;
     786          352 :       uchar buf[6], *p = &buf[6];
     787          352 :       unichar c = content[i];
     788              : 
     789          352 :       nbytes = 1;
     790          352 :       if (c < 0x80)
     791          320 :         *--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          768 :       while (p < &buf[6])
     805          416 :         utf8_buf.safe_push (*p++);
     806              :     }
     807              : 
     808          144 :   out->u.string = XNEWVEC (char, utf8_buf.length () + 1);
     809          488 :   for (unsigned i = 0; i < utf8_buf.length (); i++)
     810          416 :     out->u.string[i] = utf8_buf[i];
     811          144 :   out->u.string[utf8_buf.length ()] = '\0';
     812           72 : }
     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          148 : lexer::lex_number (token *out, unichar first_char)
     821              : {
     822          148 :   bool negate = false;
     823          148 :   double value = 0.0;
     824          148 :   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          148 :   if (first_char == '0')
     837              :     value = 0.0;
     838          140 :   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          140 :       value = first_char - '0';
     849          372 :       while (1)
     850              :         {
     851          256 :           unichar uc;
     852          256 :           location_map::point point;
     853          256 :           if (!get_char (uc, &point))
     854              :             break;
     855          248 :           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          132 :               unget_char ();
     865          132 :               break;
     866              :             }
     867              :         }
     868              :     }
     869              : 
     870              :   /* Optional '.', followed by one or more decimals.  */
     871          148 :   unichar next_char;
     872          148 :   location_map::point point;
     873          148 :   if (get_char (next_char, &point))
     874              :     {
     875          140 :       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          128 :         unget_char ();
     903              :     }
     904              : 
     905              :   /* Parse 'e' and 'E'.  */
     906          148 :   unichar exponent_char;
     907          148 :   if (get_char (exponent_char, &point))
     908              :     {
     909          136 :       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          120 :         unget_char ();
     980              :     }
     981              : 
     982          148 :   if (negate)
     983           12 :     value = -value;
     984              : 
     985          148 :   if (value == (long)value)
     986              :     {
     987          132 :       out->id = TOK_INTEGER_NUMBER;
     988          132 :       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 std::make_unique<error> (r, xstrdup (msg));
    1041              : }
    1042              : 
    1043              : /* parser's ctor.  */
    1044              : 
    1045           96 : parser::parser (location_map *out_loc_map,
    1046           96 :                 bool support_comments)
    1047           96 : : m_lexer (support_comments), m_loc_map (out_loc_map)
    1048              : {
    1049            0 : }
    1050              : 
    1051              : /* parser's dtor.  */
    1052              : 
    1053           96 : parser::~parser ()
    1054              : {
    1055           96 :   if (m_loc_map)
    1056           96 :     m_loc_map->on_finished_parsing ();
    1057           96 : }
    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           96 : 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          196 : parser::parse_value (int depth)
    1073              : {
    1074          196 :   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          196 :   const int MAX_DEPTH = 100;
    1084          196 :   if (depth >= MAX_DEPTH)
    1085            0 :     return error_at (tok->range, "maximum nesting depth exceeded: %i",
    1086            0 :                      MAX_DEPTH);
    1087              : 
    1088          196 :   switch (tok->id)
    1089              :     {
    1090           20 :     case TOK_OPEN_CURLY:
    1091           20 :       return parse_object (depth);
    1092              : 
    1093           28 :     case TOK_STRING:
    1094           28 :       {
    1095           28 :         auto val = std::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 = std::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           92 :     case TOK_INTEGER_NUMBER:
    1113           92 :       {
    1114           92 :         auto val = std::make_unique<integer_number> (tok->u.integer_number);
    1115           92 :         m_lexer.consume ();
    1116           92 :         maybe_record_range (val.get (), tok->range);
    1117           92 :         return parser_result_t (std::move (val));
    1118           92 :       }
    1119              : 
    1120            4 :     case TOK_TRUE:
    1121            4 :       {
    1122            4 :         auto val = std::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 = std::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 = std::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           20 : parser::parse_object (int depth)
    1158              : {
    1159           40 :   location_map::point start = get_next_token_start ();
    1160              : 
    1161           20 :   require (TOK_OPEN_CURLY);
    1162              : 
    1163           20 :   auto obj = std::make_unique<object> ();
    1164              : 
    1165           20 :   const token *tok = m_lexer.peek ();
    1166           20 :   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           16 :   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           20 :   while (true)
    1179              :     {
    1180           36 :       tok = m_lexer.peek ();
    1181           36 :       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           36 :       label_text key = label_text::take (xstrdup (tok->u.string));
    1186           36 :       m_lexer.consume ();
    1187              : 
    1188           36 :       if (auto err = require (TOK_COLON))
    1189           36 :         return parser_result_t (std::move (err));
    1190              : 
    1191           36 :       parser_result_t r = parse_value (depth + 1);
    1192           36 :       if (r.m_err)
    1193            0 :         return r;
    1194           36 :       if (!r.m_val)
    1195            0 :         return parser_result_t (std::move (obj));
    1196              : 
    1197              :       /* We don't enforce uniqueness for keys.  */
    1198           36 :       obj->set (key.get (), std::move (r.m_val));
    1199              : 
    1200           72 :       location_map::point end = get_next_token_end ();
    1201           36 :       result<enum token_id, std::unique_ptr<error>> result
    1202           36 :         (require_one_of (TOK_COMMA, TOK_CLOSE_CURLY));
    1203           36 :       if (result.m_err)
    1204            4 :         return parser_result_t (std::move (result.m_err));
    1205           32 :       if (result.m_val == TOK_COMMA)
    1206           20 :         continue;
    1207              :       else
    1208              :         {
    1209              :           /* TOK_CLOSE_CURLY.  */
    1210           12 :           maybe_record_range (obj.get (), start, end);
    1211           12 :           return parser_result_t (std::move (obj));
    1212              :         }
    1213           72 :     }
    1214           20 : }
    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 = std::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           36 : parser::get_next_token_start ()
    1265              : {
    1266           36 :   const token *tok = m_lexer.peek ();
    1267           36 :   return tok->range.m_start;
    1268              : }
    1269              : 
    1270              : /* Get the end point of the next token.  */
    1271              : 
    1272              : location_map::point
    1273          104 : parser::get_next_token_end ()
    1274              : {
    1275          104 :   const token *tok = m_lexer.peek ();
    1276          104 :   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           76 : 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          152 : parser::require (enum token_id tok_id)
    1291              : {
    1292          152 :   const token *tok = m_lexer.peek ();
    1293          152 :   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          152 :   m_lexer.consume ();
    1305          152 :   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          100 : parser::require_one_of (enum token_id tok_id_a, enum token_id tok_id_b)
    1314              : {
    1315          100 :   const token *tok = m_lexer.peek ();
    1316          100 :   if ((tok->id != tok_id_a)
    1317           32 :       && (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           92 :   enum token_id id = tok->id;
    1329           92 :   m_lexer.consume ();
    1330           92 :   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 std::make_unique<error> (r, formatted_msg);
    1344              : }
    1345              : 
    1346              : /* Record that JV has range R within the input file.  */
    1347              : 
    1348              : void
    1349          148 : parser::maybe_record_range (json::value *jv, const location_map::range &r)
    1350              : {
    1351          148 :   if (m_loc_map)
    1352          148 :     m_loc_map->record_range_for_value (jv, r);
    1353          148 : }
    1354              : 
    1355              : /* Record that JV has range START to END within the input file.  */
    1356              : 
    1357              : void
    1358           28 : parser::maybe_record_range (json::value *jv,
    1359              :                             const location_map::point &start,
    1360              :                             const location_map::point &end)
    1361              : {
    1362           28 :   if (m_loc_map)
    1363              :     {
    1364           28 :       location_map::range r;
    1365           28 :       r.m_start = start;
    1366           28 :       r.m_end = end;
    1367           28 :       m_loc_map->record_range_for_value (jv, r);
    1368              :     }
    1369           28 : }
    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           96 : json::parse_utf8_string (size_t length,
    1382              :                          const char *utf8_buf,
    1383              :                          bool allow_comments,
    1384              :                          location_map *out_loc_map)
    1385              : {
    1386           96 :   parser p (out_loc_map, allow_comments);
    1387           96 :   if (auto err = p.add_utf8 (length, utf8_buf))
    1388           96 :     return parser_result_t (std::move (err));
    1389           96 :   parser_result_t r = p.parse_value (0);
    1390           96 :   if (r.m_err)
    1391           20 :     return r;
    1392           76 :   if (auto err = p.require_eof ())
    1393           76 :     return parser_result_t (std::move (err));
    1394           76 :   return r;
    1395           96 : }
    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           96 : json::parse_utf8_string (const char *utf8,
    1409              :                          bool allow_comments,
    1410              :                          location_map *out_loc_map)
    1411              : {
    1412           96 :   return parse_utf8_string (strlen (utf8), utf8, allow_comments,
    1413           96 :                             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              : /* Implementation detail of ASSERT_JSON_POINTER_EQ.  */
    1500              : 
    1501              : static void
    1502           28 : assert_json_pointer_eq (const location &loc,
    1503              :                         const json::value *jv,
    1504              :                         const char *expected_str)
    1505              : {
    1506           28 :   pretty_printer pp;
    1507           28 :   ASSERT_TRUE_AT (loc, jv);
    1508           28 :   jv->print_pointer (&pp);
    1509           28 :   ASSERT_STREQ_AT (loc, pp_formatted_text (&pp), expected_str);
    1510           28 : }
    1511              : 
    1512              : /* Assert that JV is a non-NULL json::value *, and that
    1513              :    jv->print_pointer prints EXPECTED_STR.  */
    1514              : 
    1515              : #define ASSERT_JSON_POINTER_EQ(JV, EXPECTED_STR) \
    1516              :   assert_json_pointer_eq ((SELFTEST_LOCATION), (JV), (EXPECTED_STR))
    1517              : 
    1518              : /* Verify that the JSON lexer works as expected.  */
    1519              : 
    1520              : static void
    1521            4 : test_lexer ()
    1522              : {
    1523            4 :   lexer l (false);
    1524            4 :   const char *str
    1525              :     /*  0         1         2         3         4         .  */
    1526              :     /*  01234567890123456789012345678901234567890123456789.  */
    1527              :     = ("    1066   -1  \n"
    1528              :        "    -273.15 1e6\n"
    1529              :        "  [   ] null   true  false  {  }  \"foo\" \n");
    1530            4 :   auto err = l.add_utf8 (strlen (str), str);
    1531            4 :   ASSERT_EQ (err, nullptr);
    1532              : 
    1533              :   /* Line 1.  */
    1534            4 :   {
    1535            4 :     const size_t line_offset = 0;
    1536              : 
    1537              :     /* Expect token: "1066" in columns 4-7.  */
    1538            4 :     {
    1539            4 :       const token *tok = l.peek ();
    1540            4 :       ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
    1541            4 :       ASSERT_EQ (tok->u.integer_number, 1066);
    1542            4 :       ASSERT_RANGE_EQ (tok->range,
    1543              :                        line_offset + 4, 1, 4,
    1544              :                        line_offset + 7, 1, 7);
    1545            4 :       l.consume ();
    1546              :     }
    1547              :     /* Expect token: "-1" in columns 11-12.  */
    1548            4 :     {
    1549            4 :       const token *tok = l.peek ();
    1550            4 :       ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
    1551            4 :       ASSERT_EQ (tok->u.integer_number, -1);
    1552            4 :       ASSERT_RANGE_EQ (tok->range,
    1553              :                        line_offset + 11, 1, 11,
    1554              :                        line_offset + 12, 1, 12);
    1555            4 :       l.consume ();
    1556              :     }
    1557              :   }
    1558              : 
    1559              :   /* Line 2.  */
    1560            4 :   {
    1561            4 :     const size_t line_offset = 16;
    1562              : 
    1563              :     /* Expect token: "-273.15" in columns 4-10.  */
    1564            4 :     {
    1565            4 :       const token *tok = l.peek ();
    1566            4 :       ASSERT_EQ (tok->id, TOK_FLOAT_NUMBER);
    1567            4 :       ASSERT_EQ (int(tok->u.float_number), int(-273.15));
    1568            4 :       ASSERT_RANGE_EQ (tok->range,
    1569              :                        line_offset + 4, 2, 4,
    1570              :                        line_offset + 10, 2, 10);
    1571            4 :       l.consume ();
    1572              :     }
    1573              :     /* Expect token: "1e6" in columns 12-14.  */
    1574            4 :     {
    1575            4 :       const token *tok = l.peek ();
    1576            4 :       ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
    1577            4 :       ASSERT_EQ (tok->u.integer_number, 1000000);
    1578            4 :       ASSERT_RANGE_EQ (tok->range,
    1579              :                        line_offset + 12, 2, 12,
    1580              :                        line_offset + 14, 2, 14);
    1581            4 :       l.consume ();
    1582              :     }
    1583              :   }
    1584              : 
    1585              :   /* Line 3.  */
    1586            4 :   {
    1587            4 :     const size_t line_offset = 32;
    1588              : 
    1589              :     /* Expect token: "[".  */
    1590            4 :     {
    1591            4 :       const token *tok = l.peek ();
    1592            4 :       ASSERT_EQ (tok->id, TOK_OPEN_SQUARE);
    1593            4 :       ASSERT_RANGE_EQ (tok->range,
    1594              :                        line_offset + 2, 3, 2,
    1595              :                        line_offset + 2, 3, 2);
    1596            4 :       l.consume ();
    1597              :     }
    1598              :     /* Expect token: "]".  */
    1599            4 :     {
    1600            4 :       const token *tok = l.peek ();
    1601            4 :       ASSERT_EQ (tok->id, TOK_CLOSE_SQUARE);
    1602            4 :       ASSERT_RANGE_EQ (tok->range,
    1603              :                        line_offset + 6, 3, 6,
    1604              :                        line_offset + 6, 3, 6);
    1605            4 :       l.consume ();
    1606              :     }
    1607              :     /* Expect token: "null".  */
    1608            4 :     {
    1609            4 :       const token *tok = l.peek ();
    1610            4 :       ASSERT_EQ (tok->id, TOK_NULL);
    1611            4 :       ASSERT_RANGE_EQ (tok->range,
    1612              :                        line_offset + 8, 3, 8,
    1613              :                        line_offset + 11, 3, 11);
    1614            4 :       l.consume ();
    1615              :     }
    1616              :     /* Expect token: "true".  */
    1617            4 :     {
    1618            4 :       const token *tok = l.peek ();
    1619            4 :       ASSERT_EQ (tok->id, TOK_TRUE);
    1620            4 :       ASSERT_RANGE_EQ (tok->range,
    1621              :                        line_offset + 15, 3, 15,
    1622              :                        line_offset + 18, 3, 18);
    1623            4 :       l.consume ();
    1624              :     }
    1625              :     /* Expect token: "false".  */
    1626            4 :     {
    1627            4 :       const token *tok = l.peek ();
    1628            4 :       ASSERT_EQ (tok->id, TOK_FALSE);
    1629            4 :       ASSERT_RANGE_EQ (tok->range,
    1630              :                        line_offset + 21, 3, 21,
    1631              :                        line_offset + 25, 3, 25);
    1632            4 :       l.consume ();
    1633              :     }
    1634              :     /* Expect token: "{".  */
    1635            4 :     {
    1636            4 :       const token *tok = l.peek ();
    1637            4 :       ASSERT_EQ (tok->id, TOK_OPEN_CURLY);
    1638            4 :       ASSERT_RANGE_EQ (tok->range,
    1639              :                        line_offset + 28, 3, 28,
    1640              :                        line_offset + 28, 3, 28);
    1641            4 :       l.consume ();
    1642              :     }
    1643              :     /* Expect token: "}".  */
    1644            4 :     {
    1645            4 :       const token *tok = l.peek ();
    1646            4 :       ASSERT_EQ (tok->id, TOK_CLOSE_CURLY);
    1647            4 :       ASSERT_RANGE_EQ (tok->range,
    1648              :                        line_offset + 31, 3, 31,
    1649              :                        line_offset + 31, 3, 31);
    1650            4 :       l.consume ();
    1651              :     }
    1652              :     /* Expect token: "\"foo\"".  */
    1653            4 :     {
    1654            4 :       const token *tok = l.peek ();
    1655            4 :       ASSERT_EQ (tok->id, TOK_STRING);
    1656            4 :       ASSERT_RANGE_EQ (tok->range,
    1657              :                        line_offset + 34, 3, 34,
    1658              :                        line_offset + 38, 3, 38);
    1659            4 :       l.consume ();
    1660              :     }
    1661              :   }
    1662            4 : }
    1663              : 
    1664              : /* Verify that the JSON lexer complains about single-line comments
    1665              :    when comments are disabled.  */
    1666              : 
    1667              : static void
    1668            4 : test_lexing_unsupported_single_line_comment ()
    1669              : {
    1670            4 :   lexer l (false);
    1671            4 :   const char *str
    1672              :     /*  0         1         2         3         4         .  */
    1673              :     /*  01234567890123456789012345678901234567890123456789.  */
    1674              :     = ("    1066   // Hello world\n");
    1675            4 :   auto err = l.add_utf8 (strlen (str), str);
    1676            4 :   ASSERT_EQ (err, nullptr);
    1677              : 
    1678              :   /* Line 1.  */
    1679            4 :   {
    1680            4 :     const size_t line_offset = 0;
    1681            4 :     const int line_1 = 1;
    1682              : 
    1683              :     /* Expect token: "1066" in columns 4-7.  */
    1684            4 :     {
    1685            4 :       const token *tok = l.peek ();
    1686            4 :       ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
    1687            4 :       ASSERT_EQ (tok->u.integer_number, 1066);
    1688            4 :       ASSERT_RANGE_EQ (tok->range,
    1689              :                        line_offset + 4, line_1, 4,
    1690              :                        line_offset + 7, line_1, 7);
    1691            4 :       l.consume ();
    1692              :     }
    1693              : 
    1694              :     /* Expect error.  */
    1695            4 :     {
    1696            4 :       const token *tok = l.peek ();
    1697            4 :       ASSERT_EQ (tok->id, TOK_ERROR);
    1698            4 :       ASSERT_STREQ (tok->u.string, "unexpected character: '/'");
    1699            4 :       ASSERT_RANGE_EQ (tok->range,
    1700              :                        line_offset + 11, line_1, 11,
    1701              :                        line_offset + 11, line_1, 11);
    1702            4 :       l.consume ();
    1703              :     }
    1704              :   }
    1705            4 : }
    1706              : 
    1707              : /* Verify that the JSON lexer complains about multiline comments
    1708              :    when comments are disabled.  */
    1709              : 
    1710              : static void
    1711            4 : test_lexing_unsupported_multiline_comment ()
    1712              : {
    1713            4 :   lexer l (false);
    1714            4 :   const char *str
    1715              :     /*  0         1         2         3         4         .  */
    1716              :     /*  01234567890123456789012345678901234567890123456789.  */
    1717              :     = ("    1066   /* Hello world\n"
    1718              :        " continuation of comment\n"
    1719              :        " end of comment */  42\n");
    1720            4 :   auto err = l.add_utf8 (strlen (str), str);
    1721            4 :   ASSERT_EQ (err, nullptr);
    1722              : 
    1723              :   /* Line 1.  */
    1724            4 :   {
    1725            4 :     const size_t line_offset = 0;
    1726            4 :     const int line_1 = 1;
    1727              : 
    1728              :     /* Expect token: "1066" in line 1, columns 4-7.  */
    1729            4 :     {
    1730            4 :       const token *tok = l.peek ();
    1731            4 :       ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
    1732            4 :       ASSERT_EQ (tok->u.integer_number, 1066);
    1733            4 :       ASSERT_RANGE_EQ (tok->range,
    1734              :                        line_offset + 4, line_1, 4,
    1735              :                        line_offset + 7, line_1, 7);
    1736            4 :       l.consume ();
    1737              :     }
    1738              : 
    1739              :     /* Expect error.  */
    1740            4 :     {
    1741            4 :       const token *tok = l.peek ();
    1742            4 :       ASSERT_EQ (tok->id, TOK_ERROR);
    1743            4 :       ASSERT_STREQ (tok->u.string, "unexpected character: '/'");
    1744            4 :       ASSERT_RANGE_EQ (tok->range,
    1745              :                        line_offset + 11, line_1, 11,
    1746              :                        line_offset + 11, line_1, 11);
    1747            4 :       l.consume ();
    1748              :     }
    1749              :   }
    1750            4 : }
    1751              : 
    1752              : /* Verify that the JSON lexer handles single-line comments
    1753              :    when comments are enabled.  */
    1754              : 
    1755              : static void
    1756            4 : test_lexing_supported_single_line_comment ()
    1757              : {
    1758            4 :   lexer l (true);
    1759            4 :   const char *str
    1760              :     /*  0         1         2         3         4         .  */
    1761              :     /*  01234567890123456789012345678901234567890123456789.  */
    1762              :     = ("    1066   // Hello world\n"
    1763              :        "     42   // etc\n");
    1764            4 :   auto err = l.add_utf8 (strlen (str), str);
    1765            4 :   ASSERT_EQ (err, nullptr);
    1766              : 
    1767            4 :   const size_t line_1_offset = 0;
    1768            4 :   const size_t line_2_offset = 26;
    1769            4 :   const size_t line_3_offset = line_2_offset + 17;
    1770              : 
    1771              :   /* Expect token: "1066" in line 1, columns 4-7.  */
    1772            4 :   {
    1773            4 :     const int line_1 = 1;
    1774            4 :     const token *tok = l.peek ();
    1775            4 :     ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
    1776            4 :     ASSERT_EQ (tok->u.integer_number, 1066);
    1777            4 :     ASSERT_RANGE_EQ (tok->range,
    1778              :                      line_1_offset + 4, line_1, 4,
    1779              :                      line_1_offset + 7, line_1, 7);
    1780            4 :     l.consume ();
    1781              :   }
    1782              : 
    1783              :   /* Expect token: "42" in line 2, columns 5-6.  */
    1784            4 :   {
    1785            4 :     const int line_2 = 2;
    1786            4 :     const token *tok = l.peek ();
    1787            4 :     ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
    1788            4 :     ASSERT_EQ (tok->u.integer_number, 42);
    1789            4 :     ASSERT_RANGE_EQ (tok->range,
    1790              :                      line_2_offset + 5, line_2, 5,
    1791              :                      line_2_offset + 6, line_2, 6);
    1792            4 :     l.consume ();
    1793              :   }
    1794              : 
    1795              :   /* Expect EOF.  */
    1796            4 :   {
    1797            4 :     const int line_3 = 3;
    1798            4 :     const token *tok = l.peek ();
    1799            4 :     ASSERT_EQ (tok->id, TOK_EOF);
    1800            4 :     ASSERT_RANGE_EQ (tok->range,
    1801              :                      line_3_offset + 0, line_3, 0,
    1802              :                      line_3_offset + 0, line_3, 0);
    1803            4 :     l.consume ();
    1804              :   }
    1805            4 : }
    1806              : 
    1807              : /* Verify that the JSON lexer handles multiline comments
    1808              :    when comments are enabled.  */
    1809              : 
    1810              : static void
    1811            4 : test_lexing_supported_multiline_comment ()
    1812              : {
    1813            4 :   lexer l (true);
    1814            4 :   const char *str
    1815              :     /*  0         1         2         3         4         .  */
    1816              :     /*  01234567890123456789012345678901234567890123456789.  */
    1817              :     = ("    1066   /* Hello world\n"
    1818              :        " continuation of comment\n"
    1819              :        " end of comment */  42\n");
    1820            4 :   auto err = l.add_utf8 (strlen (str), str);
    1821            4 :   ASSERT_EQ (err, nullptr);
    1822              : 
    1823            4 :   const size_t line_1_offset = 0;
    1824            4 :   const size_t line_2_offset = 26;
    1825            4 :   const size_t line_3_offset = line_2_offset + 25;
    1826            4 :   const size_t line_4_offset = line_3_offset + 23;
    1827              : 
    1828              :   /* Expect token: "1066" in line 1, columns 4-7.  */
    1829            4 :   {
    1830            4 :     const int line_1 = 1;
    1831            4 :     const token *tok = l.peek ();
    1832            4 :     ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
    1833            4 :     ASSERT_EQ (tok->u.integer_number, 1066);
    1834            4 :     ASSERT_RANGE_EQ (tok->range,
    1835              :                      line_1_offset + 4, line_1, 4,
    1836              :                      line_1_offset + 7, line_1, 7);
    1837            4 :     l.consume ();
    1838              :   }
    1839              : 
    1840              :   /* Expect token: "42" in line 3, columns 20-21.  */
    1841            4 :   {
    1842            4 :     const int line_3 = 3;
    1843            4 :     const token *tok = l.peek ();
    1844            4 :     ASSERT_EQ (tok->id, TOK_INTEGER_NUMBER);
    1845            4 :     ASSERT_EQ (tok->u.integer_number, 42);
    1846            4 :     ASSERT_RANGE_EQ (tok->range,
    1847              :                      line_3_offset + 20, line_3, 20,
    1848              :                      line_3_offset + 21, line_3, 21);
    1849            4 :     l.consume ();
    1850              :   }
    1851              : 
    1852              :   /* Expect EOF.  */
    1853            4 :   {
    1854            4 :     const int line_4 = 4;
    1855            4 :     const token *tok = l.peek ();
    1856            4 :     ASSERT_EQ (tok->id, TOK_EOF);
    1857            4 :     ASSERT_RANGE_EQ (tok->range,
    1858              :                      line_4_offset + 0, line_4, 0,
    1859              :                      line_4_offset + 0, line_4, 0);
    1860            4 :     l.consume ();
    1861              :   }
    1862            4 : }
    1863              : 
    1864              : /* Helper class for writing JSON parsing testcases.
    1865              :    Attempts to parse a string in ctor, and captures the result (either
    1866              :    a json::value or a json::error), and a location map.  */
    1867              : 
    1868              : struct parser_testcase
    1869              : {
    1870              : public:
    1871           96 :   parser_testcase (const char *utf8_string, bool allow_comments = false)
    1872           96 :   : m_loc_map (),
    1873           96 :     m_result (parse_utf8_string (utf8_string, allow_comments, &m_loc_map))
    1874              :   {
    1875           96 :   }
    1876              : 
    1877           96 :   const json::value *get_value () const { return m_result.m_val.get (); }
    1878          100 :   const json::error *get_error () const { return m_result.m_err.get (); }
    1879              : 
    1880              :   const location_map::range *
    1881          124 :   get_range_for_value (const json::value *jv) const
    1882              :   {
    1883          248 :     return m_loc_map.get_range_for_value (jv);
    1884              :   }
    1885              : 
    1886              : private:
    1887              :   /* Concrete implementation of location_map for use in
    1888              :      JSON parsing selftests.  */
    1889              :   class test_location_map : public location_map
    1890              :   {
    1891              :   public:
    1892          176 :     void record_range_for_value (json::value *jv, const range &r) final override
    1893              :     {
    1894          176 :       m_map.put (jv, r);
    1895          176 :     }
    1896              : 
    1897          124 :     range *get_range_for_value (const json::value *jv) const
    1898              :     {
    1899          124 :       return const_cast<hash_map<const json::value *, range> &> (m_map)
    1900          124 :         .get (jv);
    1901              :     }
    1902              : 
    1903              :   private:
    1904              :     hash_map<const json::value *, range> m_map;
    1905              :   };
    1906              : 
    1907              :   test_location_map m_loc_map;
    1908              :   json::parser_result_t m_result;
    1909              : };
    1910              : 
    1911              : /* Verify that parse_utf8_string works as expected.  */
    1912              : 
    1913              : static void
    1914            4 : test_parse_string ()
    1915              : {
    1916            4 :   const int line_1 = 1;
    1917              : 
    1918            4 :   {
    1919            4 :     parser_testcase tc ("\"foo\"");
    1920            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    1921            4 :     const json::value *jv = tc.get_value ();
    1922            4 :     ASSERT_EQ (jv->get_kind (), JSON_STRING);
    1923            4 :     ASSERT_STREQ (as_a <const json::string *> (jv)->get_string (), "foo");
    1924            4 :     ASSERT_PRINT_EQ (*jv, true, "\"foo\"");
    1925            4 :     auto range = tc.get_range_for_value (jv);
    1926            4 :     ASSERT_TRUE (range);
    1927            4 :     ASSERT_RANGE_EQ (*range,
    1928              :                      0, line_1, 0,
    1929              :                      4, line_1, 4);
    1930            4 :   }
    1931              : 
    1932            4 :   {
    1933            4 :     const char *contains_quotes = "\"before \\\"quoted\\\" after\"";
    1934            4 :     parser_testcase tc (contains_quotes);
    1935            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    1936            4 :     const json::value *jv = tc.get_value ();
    1937            4 :     ASSERT_EQ (jv->get_kind (), JSON_STRING);
    1938            4 :     ASSERT_STREQ (as_a <const json::string *> (jv)->get_string (),
    1939              :                   "before \"quoted\" after");
    1940            4 :     ASSERT_PRINT_EQ (*jv, true, contains_quotes);
    1941            4 :     auto range = tc.get_range_for_value (jv);
    1942            4 :     ASSERT_TRUE (range);
    1943            4 :     ASSERT_RANGE_EQ (*range,
    1944              :                      0, line_1, 0,
    1945              :                      24, line_1, 24);
    1946            4 :   }
    1947              : 
    1948              :   /* Test of non-ASCII input.  This string is the Japanese word "mojibake",
    1949              :      written as C octal-escaped UTF-8.  */
    1950            4 :   const char *mojibake = (/* Opening quote.  */
    1951              :                           "\""
    1952              :                           /* U+6587 CJK UNIFIED IDEOGRAPH-6587
    1953              :                              UTF-8: 0xE6 0x96 0x87
    1954              :                              C octal escaped UTF-8: \346\226\207.  */
    1955              :                           "\346\226\207"
    1956              :                           /* U+5B57 CJK UNIFIED IDEOGRAPH-5B57
    1957              :                              UTF-8: 0xE5 0xAD 0x97
    1958              :                              C octal escaped UTF-8: \345\255\227.  */
    1959              :                           "\345\255\227"
    1960              :                           /* U+5316 CJK UNIFIED IDEOGRAPH-5316
    1961              :                              UTF-8: 0xE5 0x8C 0x96
    1962              :                              C octal escaped UTF-8: \345\214\226.  */
    1963              :                           "\345\214\226"
    1964              :                           /* U+3051 HIRAGANA LETTER KE
    1965              :                              UTF-8: 0xE3 0x81 0x91
    1966              :                              C octal escaped UTF-8: \343\201\221.  */
    1967              :                           "\343\201\221"
    1968              :                           /* Closing quote.  */
    1969              :                           "\"");
    1970            4 :   {
    1971            4 :     parser_testcase tc (mojibake);
    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              :                      5, line_1, 5);
    1985            4 :   }
    1986              : 
    1987              :   /* Test of \u-escaped unicode.  This is "mojibake" again, as above.  */
    1988            4 :   {
    1989            4 :     const char *escaped_unicode = "\"\\u6587\\u5b57\\u5316\\u3051\"";
    1990            4 :     parser_testcase tc (escaped_unicode);
    1991            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    1992            4 :     const json::value *jv = tc.get_value ();
    1993            4 :     ASSERT_EQ (jv->get_kind (), JSON_STRING);
    1994              :     /* Result of get_string should be UTF-8 encoded, without quotes.  */
    1995            4 :     ASSERT_STREQ (as_a <const json::string *> (jv)->get_string (),
    1996              :                   "\346\226\207" "\345\255\227" "\345\214\226" "\343\201\221");
    1997              :     /* Result of dump should be UTF-8 encoded, with quotes.  */
    1998            4 :     ASSERT_PRINT_EQ (*jv, false, mojibake);
    1999            4 :     auto range = tc.get_range_for_value (jv);
    2000            4 :     ASSERT_TRUE (range);
    2001            4 :     ASSERT_RANGE_EQ (*range,
    2002              :                      0, line_1, 0,
    2003              :                      25, line_1, 25);
    2004            4 :   }
    2005            4 : }
    2006              : 
    2007              : /* Verify that we can parse various kinds of JSON numbers.  */
    2008              : 
    2009              : static void
    2010            4 : test_parse_number ()
    2011              : {
    2012            4 :   const int line_1 = 1;
    2013              : 
    2014            4 :   {
    2015            4 :     parser_testcase tc ("42");
    2016            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    2017            4 :     const json::value *jv = tc.get_value ();
    2018            4 :     ASSERT_EQ (jv->get_kind (), JSON_INTEGER);
    2019            4 :     ASSERT_EQ (as_a <const json::integer_number *> (jv)->get (), 42.0);
    2020            4 :     ASSERT_PRINT_EQ (*jv, true, "42");
    2021            4 :     auto range = tc.get_range_for_value (jv);
    2022            4 :     ASSERT_TRUE (range);
    2023            4 :     ASSERT_RANGE_EQ (*range,
    2024              :                      0, line_1, 0,
    2025              :                      1, line_1, 1);
    2026            4 :   }
    2027              : 
    2028              :   /* Negative number.  */
    2029            4 :   {
    2030            4 :     parser_testcase tc ("-17");
    2031            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    2032            4 :     const json::value *jv = tc.get_value ();
    2033            4 :     ASSERT_EQ (jv->get_kind (), JSON_INTEGER);
    2034            4 :     ASSERT_EQ (as_a<const json::integer_number *> (jv)->get (), -17.0);
    2035            4 :     ASSERT_PRINT_EQ (*jv, true, "-17");
    2036            4 :     auto range = tc.get_range_for_value (jv);
    2037            4 :     ASSERT_TRUE (range);
    2038            4 :     ASSERT_RANGE_EQ (*range,
    2039              :                      0, line_1, 0,
    2040              :                      2, line_1, 2);
    2041            4 :   }
    2042              : 
    2043              :   /* Decimal.  */
    2044            4 :   {
    2045            4 :     parser_testcase tc ("3.141");
    2046            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    2047            4 :     const json::value *jv = tc.get_value ();
    2048            4 :     ASSERT_EQ (JSON_FLOAT, jv->get_kind ());
    2049            4 :     ASSERT_NEAR (3.141, ((const json::float_number *)jv)->get (), 0.001);
    2050            4 :     auto range = tc.get_range_for_value (jv);
    2051            4 :     ASSERT_TRUE (range);
    2052            4 :     ASSERT_RANGE_EQ (*range,
    2053              :                      0, line_1, 0,
    2054              :                      4, line_1, 4);
    2055            4 :   }
    2056              : 
    2057              :   /* Exponents.  */
    2058            4 :   {
    2059            4 :     {
    2060            4 :       parser_testcase tc ("3.141e+0");
    2061            4 :       ASSERT_EQ (tc.get_error (), nullptr);
    2062            4 :       const json::value *jv = tc.get_value ();
    2063            4 :       ASSERT_EQ (jv->get_kind (), JSON_FLOAT);
    2064            4 :       ASSERT_NEAR (as_a <const json::float_number *> (jv)->get (), 3.141, 0.1);
    2065            4 :       auto range = tc.get_range_for_value (jv);
    2066            4 :       ASSERT_TRUE (range);
    2067            4 :       ASSERT_RANGE_EQ (*range,
    2068              :                        0, line_1, 0,
    2069              :                        7, line_1, 7);
    2070            4 :     }
    2071            4 :     {
    2072            4 :       parser_testcase tc ("42e2");
    2073            4 :       ASSERT_EQ (tc.get_error (), nullptr);
    2074            4 :       const json::value *jv = tc.get_value ();
    2075            4 :       ASSERT_EQ (jv->get_kind (), JSON_INTEGER);
    2076            4 :       ASSERT_EQ (as_a <const json::integer_number *> (jv)->get (), 4200);
    2077            4 :       ASSERT_PRINT_EQ (*jv, true, "4200");
    2078            4 :       auto range = tc.get_range_for_value (jv);
    2079            4 :       ASSERT_TRUE (range);
    2080            4 :       ASSERT_RANGE_EQ (*range,
    2081              :                        0, line_1, 0,
    2082              :                        3, line_1, 3);
    2083            4 :     }
    2084            4 :     {
    2085            4 :       parser_testcase tc ("42e-1");
    2086            4 :       ASSERT_EQ (tc.get_error (), nullptr);
    2087            4 :       const json::value *jv = tc.get_value ();
    2088            4 :       ASSERT_EQ (jv->get_kind (), JSON_FLOAT);
    2089            4 :       ASSERT_NEAR (as_a <const json::float_number *> (jv)->get (), 4.2, 0.1);
    2090            4 :       auto range = tc.get_range_for_value (jv);
    2091            4 :       ASSERT_TRUE (range);
    2092            4 :       ASSERT_RANGE_EQ (*range,
    2093              :                        0, line_1, 0,
    2094              :                        4, line_1, 4);
    2095            4 :     }
    2096              :   }
    2097            4 : }
    2098              : 
    2099              : /* Verify that JSON array parsing works.  */
    2100              : 
    2101              : static void
    2102            4 : test_parse_array ()
    2103              : {
    2104            4 :   const int line_1 = 1;
    2105              : 
    2106            4 :   parser_testcase tc ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
    2107            4 :   ASSERT_EQ (tc.get_error (), nullptr);
    2108            4 :   const json::value *jv = tc.get_value ();
    2109            4 :   ASSERT_EQ (jv->get_kind (), JSON_ARRAY);
    2110            4 :   const json::array *arr = as_a <const json::array *> (jv);
    2111            4 :   ASSERT_EQ (arr->length (), 10);
    2112            4 :   auto range = tc.get_range_for_value (jv);
    2113            4 :   ASSERT_TRUE (range);
    2114            4 :   ASSERT_RANGE_EQ (*range,
    2115              :                    0, line_1, 0,
    2116              :                    29, line_1, 29);
    2117           44 :   for (int i = 0; i < 10; i++)
    2118              :     {
    2119           40 :       json::value *element = arr->get (i);
    2120           40 :       ASSERT_EQ (element->get_kind (), JSON_INTEGER);
    2121           40 :       ASSERT_EQ (as_a <json::integer_number *> (element)->get (), i);
    2122           40 :       range = tc.get_range_for_value (element);
    2123           40 :       ASSERT_TRUE (range);
    2124           40 :       const int offset = 1 + (i * 3);
    2125           40 :       ASSERT_RANGE_EQ (*range,
    2126              :                        offset, line_1, offset,
    2127              :                        offset, line_1, offset);
    2128              :     }
    2129            4 :   ASSERT_PRINT_EQ (*jv, false, "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
    2130            4 : }
    2131              : 
    2132              : /* Verify that JSON object parsing works.  */
    2133              : 
    2134              : static void
    2135            4 : test_parse_object ()
    2136              : {
    2137            4 :   const int line_1 = 1;
    2138            4 :   std::unique_ptr<error> err;
    2139              :   /*                   0            1            2         3  .  */
    2140              :   /*                   01 2345 678 9012 345 6789 0123456789012.  */
    2141            4 :   parser_testcase tc ("{\"foo\": \"bar\", \"baz\": [42, null]}");
    2142              : 
    2143            4 :   ASSERT_EQ (tc.get_error (), nullptr);
    2144            4 :   const json::value *jv = tc.get_value ();
    2145            4 :   ASSERT_NE (jv, nullptr);
    2146            4 :   ASSERT_EQ (jv->get_kind (), JSON_OBJECT);
    2147            4 :   auto range = tc.get_range_for_value (jv);
    2148            4 :   ASSERT_TRUE (range);
    2149            4 :   ASSERT_RANGE_EQ (*range,
    2150              :                    0, line_1, 0,
    2151              :                    32, line_1, 32);
    2152            4 :   const json::object *jo = static_cast <const json::object *> (jv);
    2153            4 :   ASSERT_JSON_POINTER_EQ (jv, "");
    2154              : 
    2155            4 :   json::value *foo_value = jo->get ("foo");
    2156            4 :   ASSERT_NE (foo_value, nullptr);
    2157            4 :   ASSERT_EQ (foo_value->get_kind (), JSON_STRING);
    2158            4 :   ASSERT_STREQ (as_a <json::string *> (foo_value)->get_string (), "bar");
    2159            4 :   range = tc.get_range_for_value (foo_value);
    2160            4 :   ASSERT_TRUE (range);
    2161            4 :   ASSERT_RANGE_EQ (*range,
    2162              :                    8, line_1, 8,
    2163              :                    12, line_1, 12);
    2164            4 :   ASSERT_JSON_POINTER_EQ (foo_value, "/foo");
    2165              : 
    2166            4 :   json::value *baz_value = jo->get ("baz");
    2167            4 :   ASSERT_NE (baz_value, nullptr);
    2168            4 :   ASSERT_EQ (baz_value->get_kind (), JSON_ARRAY);
    2169            4 :   range = tc.get_range_for_value (baz_value);
    2170            4 :   ASSERT_TRUE (range);
    2171            4 :   ASSERT_RANGE_EQ (*range,
    2172              :                    22, line_1, 22,
    2173              :                    31, line_1, 31);
    2174            4 :   ASSERT_JSON_POINTER_EQ (baz_value, "/baz");
    2175              : 
    2176            4 :   json::array *baz_array = as_a <json::array *> (baz_value);
    2177            4 :   ASSERT_EQ (baz_array->length (), 2);
    2178              : 
    2179            4 :   json::value *element0 = baz_array->get (0);
    2180            4 :   ASSERT_EQ (as_a <json::integer_number *> (element0)->get (), 42);
    2181            4 :   range = tc.get_range_for_value (element0);
    2182            4 :   ASSERT_TRUE (range);
    2183            4 :   ASSERT_RANGE_EQ (*range,
    2184              :                    23, line_1, 23,
    2185              :                    24, line_1, 24);
    2186            4 :   ASSERT_JSON_POINTER_EQ (element0, "/baz/0");
    2187              : 
    2188            4 :   json::value *element1 = baz_array->get (1);
    2189            4 :   ASSERT_EQ (element1->get_kind (), JSON_NULL);
    2190            4 :   range = tc.get_range_for_value (element1);
    2191            4 :   ASSERT_TRUE (range);
    2192            4 :   ASSERT_RANGE_EQ (*range,
    2193              :                    27, line_1, 27,
    2194              :                    30, line_1, 30);
    2195            4 :   ASSERT_JSON_POINTER_EQ (element1, "/baz/1");
    2196            4 : }
    2197              : 
    2198              : /* Verify that the JSON literals "true", "false" and "null" are parsed
    2199              :    correctly.  */
    2200              : 
    2201              : static void
    2202            4 : test_parse_literals ()
    2203              : {
    2204            4 :   const int line_1 = 1;
    2205            4 :   {
    2206            4 :     parser_testcase tc ("true");
    2207            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    2208            4 :     const json::value *jv = tc.get_value ();
    2209            4 :     ASSERT_NE (jv, nullptr);
    2210            4 :     ASSERT_EQ (jv->get_kind (), JSON_TRUE);
    2211            4 :     ASSERT_PRINT_EQ (*jv, false, "true");
    2212            4 :     auto range = tc.get_range_for_value (jv);
    2213            4 :     ASSERT_TRUE (range);
    2214            4 :     ASSERT_RANGE_EQ (*range,
    2215              :                      0, line_1, 0,
    2216              :                      3, line_1, 3);
    2217            4 :   }
    2218              : 
    2219            4 :   {
    2220            4 :     parser_testcase tc ("false");
    2221            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    2222            4 :     const json::value *jv = tc.get_value ();
    2223            4 :     ASSERT_NE (jv, nullptr);
    2224            4 :     ASSERT_EQ (jv->get_kind (), JSON_FALSE);
    2225            4 :     ASSERT_PRINT_EQ (*jv, false, "false");
    2226            4 :     auto range = tc.get_range_for_value (jv);
    2227            4 :     ASSERT_TRUE (range);
    2228            4 :     ASSERT_RANGE_EQ (*range,
    2229              :                      0, line_1, 0,
    2230              :                      4, line_1, 4);
    2231            4 :   }
    2232              : 
    2233            4 :   {
    2234            4 :     parser_testcase tc ("null");
    2235            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    2236            4 :     const json::value *jv = tc.get_value ();
    2237            4 :     ASSERT_NE (jv, nullptr);
    2238            4 :     ASSERT_EQ (jv->get_kind (), JSON_NULL);
    2239            4 :     ASSERT_PRINT_EQ (*jv, false, "null");
    2240            4 :     auto range = tc.get_range_for_value (jv);
    2241            4 :     ASSERT_TRUE (range);
    2242            4 :     ASSERT_RANGE_EQ (*range,
    2243              :                      0, line_1, 0,
    2244              :                      3, line_1, 3);
    2245            4 :   }
    2246            4 : }
    2247              : 
    2248              : /* Verify that we can parse a simple JSON-RPC request.  */
    2249              : 
    2250              : static void
    2251            4 : test_parse_jsonrpc ()
    2252              : {
    2253            4 :   std::unique_ptr<error> err;
    2254            4 :   const char *request
    2255              :     /*  0           1            2           3          4.  */
    2256              :     /*  01 23456789 012 3456 789 0123456 789 012345678 90.  */
    2257              :     = ("{\"jsonrpc\": \"2.0\", \"method\": \"subtract\",\n"
    2258              :     /*  0           1         2           3          4.  */
    2259              :     /*  0 1234567 8901234567890 1234 56789012345678 90.  */
    2260              :        " \"params\": [42, 23], \"id\": 1}");
    2261            4 :   const int line_1 = 1;
    2262            4 :   const int line_2 = 2;
    2263            4 :   const size_t line_2_offset = 41;
    2264            4 :   parser_testcase tc (request);
    2265            4 :   ASSERT_EQ (tc.get_error (), nullptr);
    2266            4 :   const json::value *jv = tc.get_value ();
    2267            4 :   ASSERT_NE (jv, nullptr);
    2268            4 :   auto range = tc.get_range_for_value (jv);
    2269            4 :   ASSERT_TRUE (range);
    2270            4 :   ASSERT_RANGE_EQ (*range,
    2271              :                    0, line_1, 0,
    2272              :                    line_2_offset + 28, line_2, 28);
    2273            4 : }
    2274              : 
    2275              : /* Verify that we can parse an empty JSON object.  */
    2276              : 
    2277              : static void
    2278            4 : test_parse_empty_object ()
    2279              : {
    2280            4 :   const int line_1 = 1;
    2281            4 :   std::unique_ptr<error> err;
    2282            4 :   parser_testcase tc ("{}");
    2283            4 :   ASSERT_EQ (tc.get_error (), nullptr);
    2284            4 :   const json::value *jv = tc.get_value ();
    2285            4 :   ASSERT_NE (jv, nullptr);
    2286            4 :   ASSERT_EQ (jv->get_kind (), JSON_OBJECT);
    2287            4 :   ASSERT_PRINT_EQ (*jv, true, "{}");
    2288            4 :   auto range = tc.get_range_for_value (jv);
    2289            4 :   ASSERT_TRUE (range);
    2290            4 :   ASSERT_RANGE_EQ (*range,
    2291              :                    0, line_1, 0,
    2292              :                    1, line_1, 1);
    2293            4 : }
    2294              : 
    2295              : /* Verify that comment-parsing can be enabled or disabled.  */
    2296              : 
    2297              : static void
    2298            4 : test_parsing_comments ()
    2299              : {
    2300            4 :   const char *str = ("// foo\n"
    2301              :                      "/*...\n"
    2302              :                      "...*/ 42 // bar\n"
    2303              :                      "/* etc */\n");
    2304              : 
    2305              :   /* Parsing with comment support disabled.  */
    2306            4 :   {
    2307            4 :     parser_testcase tc (str);
    2308            4 :     ASSERT_NE (tc.get_error (), nullptr);
    2309            4 :     ASSERT_STREQ (tc.get_error ()->get_msg (),
    2310              :                   "invalid JSON token: unexpected character: '/'");
    2311            4 :     ASSERT_EQ (tc.get_value (), nullptr);
    2312            4 :   }
    2313              : 
    2314              :   /* Parsing with comment support enabled.  */
    2315            4 :   {
    2316            4 :     parser_testcase tc (str, true);
    2317            4 :     ASSERT_EQ (tc.get_error (), nullptr);
    2318            4 :     const json::value *jv = tc.get_value ();
    2319            4 :     ASSERT_NE (jv, nullptr);
    2320            4 :     ASSERT_EQ (jv->get_kind (), JSON_INTEGER);
    2321            4 :     ASSERT_EQ (((const json::integer_number *)jv)->get (), 42);
    2322            4 :   }
    2323            4 : }
    2324              : 
    2325              : /* Verify that JSON Pointers are correctly escaped.  */
    2326              : 
    2327              : static void
    2328            4 : test_pointer_escaping ()
    2329              : {
    2330            4 :   std::unique_ptr<error> err;
    2331              :   /* Example adapted from RFC 6901 section 5.  */
    2332            4 :   parser_testcase tc
    2333              :     ("     {\n"
    2334              :      "      \"a/b\": 1,\n"
    2335              :      "      \"m~n\": 8\n"
    2336            4 :      "   }\n");
    2337            4 :   ASSERT_EQ (tc.get_error (), nullptr);
    2338            4 :   const json::value *jv = tc.get_value ();
    2339            4 :   ASSERT_NE (jv, nullptr);
    2340            4 :   ASSERT_EQ (jv->get_kind (), JSON_OBJECT);
    2341              : 
    2342            4 :   auto jo = static_cast <const json::object *> (jv);
    2343            4 :   ASSERT_JSON_POINTER_EQ (jo->get ("a/b"), "/a~1b");
    2344            4 :   ASSERT_JSON_POINTER_EQ (jo->get ("m~n"), "/m~0n");
    2345            4 : }
    2346              : 
    2347              : /* Verify that we can parse an empty JSON string.  */
    2348              : 
    2349              : static void
    2350            4 : test_error_empty_string ()
    2351              : {
    2352            4 :   const int line_1 = 1;
    2353            4 :   parser_testcase tc ("");
    2354            4 :   ASSERT_ERR_EQ (tc.get_error (),
    2355              :                  0, line_1, 0,
    2356              :                  0, line_1, 0,
    2357              :                  "expected a JSON value but got EOF");
    2358            4 :   ASSERT_EQ (tc.get_value (), nullptr);
    2359            4 : }
    2360              : 
    2361              : /* Verify that JSON parsing gracefully handles an invalid token.  */
    2362              : 
    2363              : static void
    2364            4 : test_error_bad_token ()
    2365              : {
    2366            4 :   const int line_1 = 1;
    2367            4 :   parser_testcase tc ("  not valid ");
    2368            4 :   ASSERT_ERR_EQ (tc.get_error (),
    2369              :                  2, line_1, 2,
    2370              :                  2, line_1, 2,
    2371              :                  "invalid JSON token: unexpected character: 'n'");
    2372            4 :   ASSERT_EQ (tc.get_value (), nullptr);
    2373            4 : }
    2374              : 
    2375              : /* Verify that JSON parsing gracefully handles a missing comma
    2376              :    within an object.  */
    2377              : 
    2378              : static void
    2379            4 : test_error_object_with_missing_comma ()
    2380              : {
    2381            4 :   const int line_1 = 1;
    2382              :   /*                  0           1           2.  */
    2383              :   /*                  01 2345 6789012 3456 7890.  */
    2384            4 :   const char *json = "{\"foo\" : 42 \"bar\"";
    2385            4 :   parser_testcase tc (json);
    2386            4 :   ASSERT_ERR_EQ (tc.get_error (),
    2387              :                  12, line_1, 12,
    2388              :                  16, line_1, 16,
    2389              :                  "expected ',' or '}'; got string");
    2390            4 :   ASSERT_EQ (tc.get_value (), nullptr);
    2391            4 : }
    2392              : 
    2393              : /* Verify that JSON parsing gracefully handles a missing comma
    2394              :    within an array.  */
    2395              : 
    2396              : static void
    2397            4 : test_error_array_with_missing_comma ()
    2398              : {
    2399            4 :   const int line_1 = 1;
    2400              :   /*                  01234567.  */
    2401            4 :   const char *json = "[0, 1 42]";
    2402            4 :   parser_testcase tc (json);
    2403            4 :   ASSERT_ERR_EQ (tc.get_error (),
    2404              :                  6, line_1, 6,
    2405              :                  7, line_1, 7,
    2406              :                  "expected ',' or ']'; got number");
    2407            4 :   ASSERT_EQ (tc.get_value (), nullptr);
    2408            4 : }
    2409              : 
    2410              : /* Run all of the selftests within this file.  */
    2411              : 
    2412              : void
    2413            4 : json_parser_cc_tests ()
    2414              : {
    2415            4 :   test_lexer ();
    2416            4 :   test_lexing_unsupported_single_line_comment ();
    2417            4 :   test_lexing_unsupported_multiline_comment ();
    2418            4 :   test_lexing_supported_single_line_comment ();
    2419            4 :   test_lexing_supported_multiline_comment ();
    2420            4 :   test_parse_string ();
    2421            4 :   test_parse_number ();
    2422            4 :   test_parse_array ();
    2423            4 :   test_parse_object ();
    2424            4 :   test_parse_literals ();
    2425            4 :   test_parse_jsonrpc ();
    2426            4 :   test_parse_empty_object ();
    2427            4 :   test_parsing_comments ();
    2428            4 :   test_pointer_escaping ();
    2429            4 :   test_error_empty_string ();
    2430            4 :   test_error_bad_token ();
    2431            4 :   test_error_object_with_missing_comma ();
    2432            4 :   test_error_array_with_missing_comma ();
    2433            4 : }
    2434              : 
    2435              : } // namespace selftest
    2436              : 
    2437              : #endif /* #if CHECKING_P */
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.