LCOV - code coverage report
Current view: top level - gcc - json-parsing.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 85.4 % 1191 1017
Test Date: 2025-06-21 16:26:05 Functions: 83.6 % 55 46
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

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

Generated by: LCOV version 2.1-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.