From 7318bb9ae27b0c53e33248e739c69d9eb8eabe2e Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 30 May 2017 16:10:06 -0400
Subject: [PATCH 29/38] FIXME: json.c/h fixes

---
 gcc/json.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------
 gcc/json.h |   1 +
 2 files changed, 181 insertions(+), 26 deletions(-)

diff --git a/gcc/json.c b/gcc/json.c
index 73bf95f..064bf13 100644
--- a/gcc/json.c
+++ b/gcc/json.c
@@ -593,6 +593,7 @@ class parser
   array *parse_array ();
 
   bool seen_error_p () const { return *m_err_out; }
+  void require_eof ();
 
  private:
   void require (enum token_id tok_id);
@@ -693,7 +694,10 @@ lexer::add_utf8 (size_t length, const char *utf8_buf, char **err_out)
     found:
 
       if (*inbytesleftp < nbytes)
-	return false;
+	{
+	  *err_out = xstrdup ("ill-formed UTF-8 sequence");
+	  return false;
+	}
 
       c = (c & masks[nbytes-1]);
       inbuf++;
@@ -701,7 +705,10 @@ lexer::add_utf8 (size_t length, const char *utf8_buf, char **err_out)
 	{
 	  unichar n = *inbuf++;
 	  if ((n & 0xC0) != 0x80)
-	    return false;
+	    {
+	      *err_out = xstrdup ("ill-formed UTF-8 sequence");
+	      return false;
+	    }
 	  c = ((c << 6) + (n & 0x3F));
 	}
 
@@ -927,7 +934,8 @@ lexer::lex_token (token *out)
 
 /* Having consumed an open-quote character from the lexer's buffer, attempt
    to lex the rest of a JSON string, writing the result to OUT (or TOK_ERROR)
-   if an error occurred.  */
+   if an error occurred.
+   (ECMA-404 section 9; RFC 7159 section 7).  */
 
 void
 lexer::lex_string (token *out)
@@ -1026,6 +1034,16 @@ lexer::lex_string (token *out)
 	  break;
 
 	default:
+	  /* Reject unescaped control characters U+0000 through U+001F
+	     (ECMA-404 section 9 para 1; RFC 7159 section 7 para 1).  */
+	  if (uc <= 0x1f)
+	    {
+		out->id = TOK_ERROR;
+		out->u.string = xasprintf ("unescaped control char");
+		return;
+	    }
+
+	  /* Otherwise, add regular unicode code point.  */
 	  content.safe_push (uc);
 	  break;
 	}
@@ -1072,44 +1090,64 @@ lexer::lex_string (token *out)
 
 /* Having consumed FIRST_CHAR, an initial digit or '-' character from
    the lexer's buffer attempt to lex the rest of a JSON number, writing
-   the result to OUT (or TOK_ERROR) if an error occurred.  */
+   the result to OUT (or TOK_ERROR) if an error occurred.
+   (ECMA-404 section 8; RFC 7159 section 6).  */
 
 void
 lexer::lex_number (token *out, unichar first_char)
 {
-  // FIXME: proper lexing here
-
   bool negate = false;
   double value = 0.0;
   if (first_char == '-')
-    negate = true;
-  else if (first_char != '0')
-    value = (first_char - '0');
-
-  while (1)
     {
-      unichar uc;
-      if (!get_char (uc))
-	break;
-      if (ISDIGIT (uc))
+      negate = true;
+      if (!get_char (first_char))
 	{
-	  value *= 10;
-	  value += uc -'0';
-	  continue;
+	  out->id = TOK_ERROR;
+	  out->u.string = xasprintf ("expected digit");
+	  return;
 	}
-      else
+    }
+
+  if (first_char == '0')
+    value = 0.0;
+  else if (!ISDIGIT (first_char))
+    {
+      out->id = TOK_ERROR;
+      out->u.string = xasprintf ("expected digit");
+      return;
+    }
+  else
+    {
+      /* Got a nonzero digit; expect zero or more digits.  */
+      value = first_char - '0';
+      while (1)
 	{
-	  unget_char ();
-	  break;
+	  unichar uc;
+	  if (!get_char (uc))
+	    break;
+	  if (ISDIGIT (uc))
+	    {
+	      value *= 10;
+	      value += uc -'0';
+	      continue;
+	    }
+	  else
+	    {
+	      unget_char ();
+	      break;
+	    }
 	}
     }
 
+  /* Optional '.', followed by one or more decimals.  */
   unichar next_char;
   if (get_char (next_char))
     {
       if (next_char == '.')
 	{
 	  /* Parse decimal digits.  */
+	  bool had_digit = false;
 	  // FIXME: does this lose too much precision?
 	  double digit_factor = 0.1;
 	  while (get_char (next_char))
@@ -1121,13 +1159,87 @@ lexer::lex_number (token *out, unichar first_char)
 		}
 	      value += (next_char - '0') * digit_factor;
 	      digit_factor *= 0.1;
+	      had_digit = true;
+	    }
+	  if (!had_digit)
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string = xasprintf ("expected digit");
+	      return;
 	    }
 	}
       else
 	unget_char ();
     }
 
-  /* TODO: parse 'e' and 'E'.  */
+  /* Parse 'e' and 'E'.  */
+  unichar exponent_char;
+  if (get_char (exponent_char))
+    {
+      if (exponent_char == 'e' || exponent_char == 'E')
+	{
+	  /* Optional +/-.  */
+	  unichar sign_char;
+	  int exponent = 0;
+	  bool negate_exponent = false;
+	  bool had_exponent_digit = false;
+	  if (!get_char (sign_char))
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string = xasprintf ("EOF within exponent");
+	      return;
+	    }
+	  if (sign_char == '-')
+	    negate_exponent = true;
+	  else if (sign_char == '+')
+	    ;
+	  else if (ISDIGIT (sign_char))
+	    {
+	      exponent = sign_char - '0';
+	      had_exponent_digit = true;
+	    }
+	  else
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string
+		= xasprintf ("expected '-','+' or digit within exponent");
+	      return;
+	    }
+
+	  /* One or more digits (we might have seen the digit above,
+	     though).  */
+	  while (1)
+	    {
+	      unichar uc;
+	      if (!get_char (uc))
+		break;
+	      if (ISDIGIT (uc))
+		{
+		  exponent *= 10;
+		  exponent += uc -'0';
+		  had_exponent_digit = true;
+		  continue;
+		}
+	      else
+		{
+		  unget_char ();
+		  break;
+		}
+	    }
+	  if (!had_exponent_digit)
+	    {
+	      out->id = TOK_ERROR;
+	      out->u.string = xasprintf ("expected digit within exponent");
+	      return;
+	    }
+	  if (negate_exponent)
+	    exponent = -exponent;
+	  /* FIXME: better way to do this?  */
+	  value = value * pow (10, exponent);
+	}
+      else
+	unget_char ();
+    }
 
   if (negate)
     value = -value;
@@ -1153,6 +1265,8 @@ lexer::rest_of_literal (const char *suffix)
 	  m_next_char_idx += suffix_idx;
 	  return true;
 	}
+      if (buf_idx >= (int)m_buffer.length ())
+	return false;
       /* This assumes that suffix is ASCII.  */
       if (m_buffer[buf_idx] != (unichar)suffix[suffix_idx])
 	return false;
@@ -1180,7 +1294,8 @@ parser::add_utf8 (size_t length, const char *utf8_buf, char **err_out)
   return m_lexer.add_utf8 (length, utf8_buf, err_out);
 }
 
-/* FIXME.  */
+/* Parse a JSON value (object, array, number, string, or literal).
+   (ECMA-404 section 5; RFC 7159 section 3).  */
 
 value *
 parser::parse_value ()
@@ -1235,7 +1350,8 @@ parser::parse_value ()
     }
 }
 
-/* FIXME.  */
+/* Parse a JSON object.
+   (ECMA-404 section 6; RFC 7159 section 4).  */
 
 object *
 parser::parse_object ()
@@ -1259,7 +1375,10 @@ parser::parse_object ()
     {
       tok = m_lexer.peek ();
       if (tok->id != TOK_STRING)
-	error_at (tok->index, "expected string for object key");
+	{
+	  error_at (tok->index, "expected string for object key");
+	  return result;
+	}
       char *key = xstrdup (tok->u.string);
       m_lexer.consume ();
 
@@ -1271,6 +1390,7 @@ parser::parse_object ()
 	  free (key);
 	  return result;
 	}
+      /* We don't enforce uniqueness for keys.  */
       result->set (key, v);
       free (key);
 
@@ -1289,7 +1409,8 @@ parser::parse_object ()
   return result;
 }
 
-/* FIXME.  */
+/* Parse a JSON array.
+   (ECMA-404 section 7; RFC 7159 section 5).  */
 
 array *
 parser::parse_array ()
@@ -1329,6 +1450,14 @@ parser::parse_array ()
   return result;
 }
 
+/* Require an EOF, or fail if there is surplus input.  */
+
+void
+parser::require_eof ()
+{
+  require (TOK_EOF);
+}
+
 /* Consume the next token, issuing an error if it is not of kind TOK_ID.  */
 
 void
@@ -1391,6 +1520,8 @@ json::parse_utf8_string (size_t length, const char *utf8_buf,
   if (!p.add_utf8 (length, utf8_buf, err_out))
     return NULL;
   value *result = p.parse_value ();
+  if (!p.seen_error_p ())
+    p.require_eof ();
   if (p.seen_error_p ())
     {
       gcc_assert (*err_out);
@@ -1534,6 +1665,29 @@ test_parse_number ()
   ASSERT_EQ (3.141, ((json::number *)jv)->get ());
   assert_to_str_eq ("3.141", jv);
   delete jv;
+
+  /* Exponents.  */
+  jv = parse_utf8_string ("3.141e+0", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (3.141, ((json::number *)jv)->get ());
+  assert_to_str_eq ("3.141", jv);
+  delete jv;
+
+  jv = parse_utf8_string ("42e2", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (4200, ((json::number *)jv)->get ());
+  assert_to_str_eq ("4200", jv);
+  delete jv;
+
+  jv = parse_utf8_string ("42e-1", &err);
+  ASSERT_EQ (NULL, err);
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (4.2, ((json::number *)jv)->get ());
+  assert_to_str_eq ("4.2", jv);
+  delete jv;
+
 }
 
 /* FIXME.  */
diff --git a/gcc/json.h b/gcc/json.h
index e97d384..aedf84a 100644
--- a/gcc/json.h
+++ b/gcc/json.h
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 
    See http://www.json.org/
    and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
+   and https://tools.ietf.org/html/rfc7159
 
    Supports parsing text into a DOM-like tree of json::value *, dumping
    json::value * to text.  */
-- 
1.8.5.3

