From f08c9b5afa60f82394cde3edeb15029e6e1c769d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 17 May 2017 15:08:04 -0400
Subject: [PATCH 01/22] FIXME: initial version of json parsing

---
 gcc/Makefile.in          |   1 +
 gcc/json.c               | 750 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 4 files changed, 753 insertions(+)
 create mode 100644 gcc/json.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index f675e07..3625c20 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1361,6 +1361,7 @@ OBJS = \
 	ira-color.o \
 	ira-emit.o \
 	ira-lives.o \
+	json.o \
 	jump.o \
 	langhooks.o \
 	lcm.o \
diff --git a/gcc/json.c b/gcc/json.c
new file mode 100644
index 0000000..16e51c4
--- /dev/null
+++ b/gcc/json.c
@@ -0,0 +1,750 @@
+/* JSON parsing
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "selftest.h"
+
+namespace json
+{
+
+#if 0
+class context
+{
+};
+#endif
+
+enum kind
+{
+  JSON_OBJECT,
+  JSON_ARRAY,
+  JSON_NUMBER,
+  JSON_STRING,
+  JSON_TRUE,
+  JSON_FALSE,
+  JSON_NULL
+};
+
+/* Base class of JSON value.  */
+
+class value
+{
+ public:
+  virtual enum kind get_kind () const = 0;
+};
+
+/* Subclass of value for objects: key/value pairs.  */
+
+class object : public value
+{
+ public:
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT; }
+
+  value *get (const char *key) const;
+  void set (const char *key, value *v);
+
+ private:
+  hash_map <const char *, value *,
+    simple_hashmap_traits<nofree_string_hash, value *> > m_map;
+};
+
+/* Subclass of value for arrays.  */
+
+class array : public value
+{
+ public:
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY; }
+
+  unsigned get_length () const { return m_elements.length (); }
+  value *get (int idx) const { return m_elements[idx]; }
+  void append (value *v) { m_elements.safe_push (v); }
+
+ private:
+  auto_vec<value *> m_elements;
+};
+
+/* Subclass of value for numbers.  */
+
+class number : public value
+{
+ public:
+  number (double value) : m_value (value) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER; }
+
+  double get () const { return m_value; }
+
+ private:
+  double m_value;
+};
+
+/* Subclass of value for strings.  */
+
+class string : public value
+{
+ public:
+  string (const char *utf8) : m_utf8 (utf8) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING; }
+
+  const char *get_string () const { return m_utf8; }
+
+ private:
+  const char *m_utf8;
+};
+
+/* Subclass of value for the literals "true", "false", and "null".  */
+
+class literal : public value
+{
+ public:
+  literal (enum kind kind) : m_kind (kind) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
+
+ private:
+  enum kind m_kind;
+};
+
+/* Implemenation.  */
+
+value *
+object::get (const char *key) const
+{
+  value **slot = const_cast <object*> (this)->m_map.get (key);
+  if (slot)
+    return *slot;
+  return NULL;
+}
+
+void
+object::set (const char *key, value *v)
+{
+  m_map.put (key, v);
+}
+
+/* Parsing decls.  */
+
+extern value *parse_utf8_string (const char *utf8);
+
+typedef unsigned unichar;
+
+enum token_id
+{
+  TOK_ERROR,
+
+  TOK_EOF,
+
+  /* Punctuation.  */
+  TOK_OPEN_SQUARE,
+  TOK_OPEN_CURLY,
+  TOK_CLOSE_SQUARE,
+  TOK_CLOSE_CURLY,
+  TOK_COLON,
+  TOK_COMMA,
+
+  /* Literal names.  */
+  TOK_TRUE,
+  TOK_FALSE,
+  TOK_NULL,
+
+  TOK_STRING,
+  TOK_NUMBER
+};
+
+struct token
+{
+  enum token_id id;
+  int index;
+  int length;
+  union
+  {
+    char *string;
+    double number;
+  } u;
+};
+
+class lexer
+{
+ public:
+  lexer ();
+  void add_utf8 (const char *utf8);
+
+  const token *peek ();
+  void consume ();
+
+ private:
+  bool get_char (unichar &out);
+  void unget_char ();
+  static void dump_token (FILE *outf, const token *tok);
+  void lex_token (token *out);
+  void lex_string (token *out);
+  void lex_number (token *out, unichar first_char);
+
+ private:
+  auto_vec<unichar> m_buffer;
+  int m_next_char_idx;
+
+  static const int MAX_TOKENS = 1;
+  token m_next_tokens[MAX_TOKENS];
+  int m_num_next_tokens;
+};
+
+class parser
+{
+ public:
+  parser ();
+  void add_utf8 (const char *utf8);
+  value *parse_value ();
+  object *parse_object ();
+  array *parse_array ();
+
+  bool seen_error_p () const { return m_first_error; }
+
+ private:
+  void require (enum token_id tok_id);
+  void error (const char *);
+
+ private:
+  lexer m_lexer;
+  const char *m_first_error;
+};
+
+} // namespace json
+
+/* Parser implementation.  */
+
+using namespace json;
+
+lexer::lexer ()
+: m_buffer (), m_next_char_idx (0), m_num_next_tokens (0)
+{
+}
+
+const token *
+lexer::peek ()
+{
+  if (m_num_next_tokens == 0)
+    {
+      lex_token (&m_next_tokens[0]);
+      m_num_next_tokens++;
+    }
+  return &m_next_tokens[0];
+}
+
+void
+lexer::consume ()
+{
+  if (m_num_next_tokens == 0)
+    peek ();
+
+  gcc_assert (m_num_next_tokens > 0);
+  gcc_assert (m_num_next_tokens <= MAX_TOKENS);
+
+  fprintf (stderr, "consuming token: ");
+  dump_token (stderr, &m_next_tokens[0]);
+  fprintf (stderr, "\n");
+
+  memmove (&m_next_tokens[0], &m_next_tokens[1],
+	   sizeof (token) * m_num_next_tokens);
+  m_num_next_tokens--;
+}
+
+void
+lexer::add_utf8 (const char *utf8)
+{
+  // FIXME: this assumes Latin-1.
+  for (const char *ptr = utf8; *ptr; ptr++)
+    m_buffer.safe_push (*ptr);
+}
+
+bool
+lexer::get_char (unichar &out)
+{
+  if (m_next_char_idx >= (int)m_buffer.length ())
+    return false;
+
+  out = m_buffer[m_next_char_idx++];
+  return true;
+}
+
+void
+lexer::unget_char ()
+{
+  --m_next_char_idx;
+}
+
+void
+lexer::dump_token (FILE *outf, const token *tok)
+{
+  switch (tok->id)
+    {
+    case TOK_ERROR:
+      fprintf (outf, "TOK_ERROR");
+      break;
+
+    case TOK_EOF:
+      fprintf (outf, "TOK_EOF");
+      break;
+
+    case TOK_OPEN_SQUARE:
+      fprintf (outf, "TOK_OPEN_SQUARE");
+      break;
+
+    case TOK_OPEN_CURLY:
+      fprintf (outf, "TOK_OPEN_CURLY");
+      break;
+
+    case TOK_CLOSE_SQUARE:
+      fprintf (outf, "TOK_CLOSE_SQUARE");
+      break;
+
+    case TOK_CLOSE_CURLY:
+      fprintf (outf, "TOK_CLOSE_CURLY");
+      break;
+
+    case TOK_COLON:
+      fprintf (outf, "TOK_COLON");
+      break;
+
+    case TOK_COMMA:
+      fprintf (outf, "TOK_COMMA");
+      break;
+
+    case TOK_TRUE:
+      fprintf (outf, "TOK_TRUE");
+      break;
+
+    case TOK_FALSE:
+      fprintf (outf, "TOK_FALSE");
+      break;
+
+    case TOK_NULL:
+      fprintf (outf, "TOK_NULL");
+      break;
+
+    case TOK_STRING:
+      fprintf (outf, "TOK_STRING (\"%s\")", tok->u.string);
+      break;
+
+    case TOK_NUMBER:
+      fprintf (outf, "TOK_NUMBER (%f)", tok->u.number);
+      break;
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+}
+
+void
+lexer::lex_token (token *out)
+{
+  out->index = m_next_char_idx;
+
+  /* Skip to next non-whitespace char.  */
+  unichar next_char;
+  while (1)
+    {
+      if (!get_char (next_char))
+	{
+	  out->id = TOK_EOF;
+	  out->length = 1;
+	  return;
+	}
+      if (next_char != ' ' && next_char != '\t')
+	break;
+    }
+
+  switch (next_char)
+    {
+    case '[':
+      out->id = TOK_OPEN_SQUARE;
+      out->length = 1;
+      break;
+
+    case '{':
+      out->id = TOK_OPEN_CURLY;
+      out->length = 1;
+      break;
+
+    case ']':
+      out->id = TOK_CLOSE_SQUARE;
+      out->length = 1;
+      break;
+
+    case '}':
+      out->id = TOK_CLOSE_CURLY;
+      out->length = 1;
+      break;
+
+    case ':':
+      out->id = TOK_COLON;
+      out->length = 1;
+      break;
+
+    case ',':
+      out->id = TOK_COMMA;
+      out->length = 1;
+      break;
+
+    case '"':
+      lex_string (out);
+      break;
+
+    case '-':
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      lex_number (out, next_char);
+      break;
+
+    default:
+      //error ();
+      out->id = TOK_ERROR;
+      out->length = 1;
+      break;
+    }
+}
+
+void
+lexer::lex_string (token *out)
+{
+  auto_vec<unichar> content;
+  bool still_going = true;
+  while (still_going)
+    {
+      unichar uc;
+      if (!get_char (uc))
+	{
+	  out->id = TOK_ERROR;
+	  return;
+	}
+      switch (uc)
+	{
+	case '"':
+	  still_going = false;
+	  break;
+#if 0
+	case '\\':
+	  {
+	    unichar next = m_buffer[m_next_char_idx++];
+	    switch (next)
+	      {
+	      }
+	  }
+	  break;
+#endif
+
+	default:
+	  content.safe_push (uc);
+	  break;
+	}
+    }
+
+  out->id = TOK_STRING;
+
+  // FIXME: this assume it's all Latin-1
+  out->u.string = XNEWVEC (char, content.length () + 1);
+  for (unsigned i = 0; i < content.length (); i++)
+    out->u.string[i] = content[i];
+  out->u.string[content.length ()] = '\0';
+
+  // FIXME: leaks?  have a json_context do the allocation
+
+  //out->length = out->m_next_char_idx;
+}
+
+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))
+	{
+	  value *= 10;
+	  value += uc -'0';
+	  continue;
+	}
+      else
+	{
+	  unget_char ();
+	  break;
+	}
+      // FIXME '.' and 'e'/'E'
+    }
+
+  if (negate)
+    value = -value;
+
+  out->id = TOK_NUMBER;
+  out->u.number = value;
+}
+
+parser::parser ()
+: m_lexer (), m_first_error (NULL)
+{
+}
+
+void
+parser::add_utf8 (const char *utf8)
+{
+  m_lexer.add_utf8 (utf8);
+}
+
+value *
+parser::parse_value ()
+{
+  const token *tok = m_lexer.peek ();
+  if (tok->id == TOK_OPEN_CURLY)
+    return parse_object ();
+  else if (tok->id == TOK_STRING)
+    {
+      string *result = new json::string (tok->u.string);
+      m_lexer.consume ();
+      return result;
+    }
+  else if (tok->id == TOK_OPEN_SQUARE)
+    return parse_array ();
+  else if (tok->id == TOK_NUMBER)
+    {
+      number *result = new json::number (tok->u.number);
+      m_lexer.consume ();
+      return result;
+    }
+
+  // FIXME
+  error ("error in parse_value");
+  return NULL; // FIXME
+}
+
+object *
+parser::parse_object ()
+{
+  require (TOK_OPEN_CURLY);
+
+  object *result = new object ();
+
+  const token *tok = m_lexer.peek ();
+  if (tok->id == TOK_CLOSE_CURLY)
+    {
+      require (TOK_CLOSE_CURLY);
+      return result;
+    }
+  if (tok->id != TOK_STRING)
+    {
+      error ("expected string for object key");
+      return result;
+    }
+  while (!seen_error_p ())
+    {
+      tok = m_lexer.peek ();
+      if (tok->id != TOK_STRING)
+	error ("expected string for object key");
+      const char *key = tok->u.string;
+      m_lexer.consume ();
+
+      require (TOK_COLON);
+
+      value *v = parse_value ();
+      result->set (key, v);
+
+      tok = m_lexer.peek ();
+      if (tok->id == TOK_COMMA)
+	{
+	  m_lexer.consume ();
+	  continue;
+	}
+      else
+	{
+	  require (TOK_CLOSE_CURLY);
+	  break;
+	}
+    }
+  return result;
+}
+
+array *
+parser::parse_array ()
+{
+  require (TOK_OPEN_SQUARE);
+
+  array *result = new array ();
+
+  const token *tok = m_lexer.peek ();
+  if (tok->id == TOK_CLOSE_SQUARE)
+    {
+      m_lexer.consume ();
+      return result;
+    }
+
+  while (!seen_error_p ())
+    {
+      value *v = parse_value ();
+      result->append (v);
+
+      tok = m_lexer.peek ();
+      if (tok->id == TOK_COMMA)
+	{
+	  m_lexer.consume ();
+	  continue;
+	}
+      else
+	{
+	  require (TOK_CLOSE_SQUARE);
+	  break;
+	}
+    }
+
+  return result;
+}
+
+void
+parser::require (enum token_id tok_id)
+{
+  const token *tok = m_lexer.peek ();
+  if (tok->id != tok_id)
+    error ("expected FIXME");
+  m_lexer.consume ();
+}
+
+void
+parser::error (const char *msg)
+{
+  m_first_error = msg;
+  fprintf (stderr, "parsing error: %s\n", msg);
+}
+
+
+value *
+json::parse_utf8_string (const char *utf8)
+{
+  parser p;
+  p.add_utf8 (utf8);
+  return p.parse_value ();
+}
+
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+static void
+test_parse_string ()
+{
+  json::value *jv = parse_utf8_string ("\"foo\"");
+  ASSERT_EQ (JSON_STRING, jv->get_kind ());
+  ASSERT_STREQ ("foo", ((json::string *)jv)->get_string ());
+}
+
+static void
+test_parse_number ()
+{
+  json::value *jv = parse_utf8_string ("42");
+  ASSERT_EQ (JSON_NUMBER, jv->get_kind ());
+  ASSERT_EQ (42.0, ((json::number *)jv)->get ());
+}
+
+static void
+test_parse_array ()
+{
+  json::value *jv = parse_utf8_string ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
+  ASSERT_EQ (JSON_ARRAY, jv->get_kind ());
+  json::array *arr = static_cast <json::array *> (jv);
+  ASSERT_EQ (10, arr->get_length ());
+  for (int i = 0; i < 10; i++)
+    {
+      json::value *element = arr->get (i);
+      ASSERT_EQ (JSON_NUMBER, element->get_kind ());
+      ASSERT_EQ (i, ((json::number *)element)->get ());
+    }
+}
+
+static void
+test_parse_object ()
+{
+  json::value *jv = parse_utf8_string ("{\"foo\": \"bar\", \"baz\": [42, 43]}");
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_OBJECT, jv->get_kind ());
+  json::object *jo = static_cast <json::object *> (jv);
+
+  json::value *foo_value = jo->get ("foo");
+  ASSERT_TRUE (foo_value != NULL);
+  ASSERT_EQ (JSON_STRING, foo_value->get_kind ());
+  ASSERT_STREQ ("bar", ((json::string *)foo_value)->get_string ());
+
+  json::value *baz_value = jo->get ("baz");
+  ASSERT_TRUE (baz_value != NULL);
+  ASSERT_EQ (JSON_ARRAY, baz_value->get_kind ());
+
+  json::array *baz_array = (json::array *)baz_value;
+  ASSERT_EQ (2, baz_array->get_length ());
+
+  // etc, verify numbers
+  // etc, true, false, null
+
+  // TODO: error-handling
+  // TODO: partial document
+}
+
+static void
+test_parse_empty_object ()
+{
+  json::value *jv = parse_utf8_string ("{}");
+  ASSERT_TRUE (jv != NULL);
+  ASSERT_EQ (JSON_OBJECT, jv->get_kind ());
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+json_c_tests ()
+{
+  test_parse_string ();
+  test_parse_number ();
+  test_parse_array ();
+  test_parse_object ();
+  test_parse_empty_object ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index f62bc72..b8fe4a1 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -66,6 +66,7 @@ selftest::run_tests ()
   sreal_c_tests ();
   fibonacci_heap_c_tests ();
   typed_splay_tree_c_tests ();
+  json_c_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index dad53e9..6b03e39 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -183,6 +183,7 @@ extern void ggc_tests_c_tests ();
 extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
+extern void json_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
 extern void rtl_tests_c_tests ();
-- 
1.8.5.3

