From 5c6e3ef1f8d9129bd7577c6df28a26e85603b771 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 22 May 2017 16:47:11 -0400 Subject: [PATCH 29/31] FIXME: json.c cleanups --- gcc/json.c | 397 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------- gcc/json.h | 44 +++++-- gcc/lsp.c | 2 +- 3 files changed, 379 insertions(+), 64 deletions(-) diff --git a/gcc/json.c b/gcc/json.c index 5cd06d5..c9b3d97 100644 --- a/gcc/json.c +++ b/gcc/json.c @@ -26,7 +26,9 @@ along with GCC; see the file COPYING3. If not see using namespace json; -/* Implemenation. */ +/* class json::value. */ + +/* FIXME. */ char * value::to_str () const @@ -36,6 +38,8 @@ value::to_str () const return xstrdup (pp_formatted_text (&pp)); } +/* FIXME. */ + void value::dump (FILE *outf) const { @@ -45,6 +49,8 @@ value::dump (FILE *outf) const pp_flush (&pp); } +/* FIXME. */ + const object * value::as_object () const { @@ -53,6 +59,8 @@ value::as_object () const return static_cast (this); } +/* FIXME. */ + const array * value::as_array () const { @@ -61,6 +69,8 @@ value::as_array () const return static_cast (this); } +/* FIXME. */ + const number * value::as_number () const { @@ -69,6 +79,8 @@ value::as_number () const return static_cast (this); } +/* FIXME. */ + const string * value::as_string () const { @@ -77,7 +89,12 @@ value::as_string () const return static_cast (this); } -/* FIXME. */ +/* Attempt to get lookup the value of a key/value pair from this value + as if it is an object. + + On success, return true and write the value to OUT_VALUE. + On failure, return false and write an error message to OUT_ERR + (which must be freed by the caller). */ bool value::get_value_by_key (const char *name, const value *&out_value, @@ -99,7 +116,8 @@ value::get_value_by_key (const char *name, const value *&out_value, return true; } -/* FIXME. */ +/* As value::get_value_by_key, but the value must be a number; + if successful, write it as an int to OUT_VALUE. */ bool value::get_int_by_key (const char *name, int &out_value, char *&out_err) const @@ -117,7 +135,8 @@ value::get_int_by_key (const char *name, int &out_value, char *&out_err) const return true; } -/* FIXME. */ +/* As value::get_value_by_key, but the value must be a string; + if successful, write it as const char * to OUT_VALUE. */ bool value::get_string_by_key (const char *name, const char *&out_value, @@ -136,6 +155,10 @@ value::get_string_by_key (const char *name, const char *&out_value, return true; } +/* class json::object, a subclass of json::value. */ + +/* json:object's dtor. */ + object::~object () { for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it) @@ -145,6 +168,8 @@ object::~object () } } +/* Implementation of json::value::print for json::object. */ + void object::print (pretty_printer *pp) const { @@ -162,12 +187,23 @@ object::print (pretty_printer *pp) const pp_character (pp, '}'); } +/* Implementation of json::value::clone for json::object. */ + value * object::clone () const { - gcc_unreachable (); // FIXME + object *other = new object (); + for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it) + { + const char *key = const_cast ((*it).first); + value *value = (*it).second; + other->set (key, value->clone ()); + } + return other; } +/* Get the json::value * for KEY, or NULL if the key is not present. */ + value * object::get (const char *key) const { @@ -177,7 +213,8 @@ object::get (const char *key) const return NULL; } -/* Takes ownership of KEY and VALUE. */ +/* Set the json::value * for KEY, taking ownership of VALUE + (and taking a copy of KEY). */ void object::set (const char *key, value *v) @@ -185,6 +222,10 @@ object::set (const char *key, value *v) m_map.put (xstrdup (key), v); } +/* class json::array, a subclass of json::value. */ + +/* json::array's dtor. */ + array::~array () { unsigned i; @@ -193,6 +234,8 @@ array::~array () delete v; } +/* Implementation of json::value::print for json::array. */ + void array::print (pretty_printer *pp) const { @@ -208,6 +251,8 @@ array::print (pretty_printer *pp) const pp_character (pp, ']'); } +/* Implementation of json::value::clone for json::array. */ + value * array::clone () const { @@ -219,18 +264,26 @@ array::clone () const return other; } +/* class json::number, a subclass of json::value. */ + +/* Implementation of json::value::print for json::number. */ + void number::print (pretty_printer *pp) const { pp_printf (pp, "%i", (int)m_value); // FIXME } +/* Implementation of json::value::clone for json::number. */ + value * number::clone () const { return new number (m_value); } +/* class json::string, a subclass of json::value. */ + void string::print (pretty_printer *pp) const { @@ -256,30 +309,56 @@ string::print (pretty_printer *pp) const pp_character (pp, '"'); } +/* Implementation of json::value::clone for json::string. */ + value * string::clone () const { return new string (m_utf8); } +/* class json::literal, a subclass of json::value. */ + +/* Implementation of json::value::print for json::literal. */ + void literal::print (pretty_printer *pp) const { - pp_string (pp, "FIXME"); // FIXME + switch (m_kind) + { + case JSON_TRUE: + pp_string (pp, "true"); + break; + case JSON_FALSE: + pp_string (pp, "false"); + break; + case JSON_NULL: + pp_string (pp, "null"); + break; + default: + gcc_unreachable (); + } } +/* Implementation of json::value::clone for json::literal. */ + value * literal::clone () const { - gcc_unreachable (); // FIXME + return new literal (m_kind); } -/* Parsing decls. */ +/* Declarations relating to parsing JSON, all within an + anonymous namespace. */ + +namespace { -namespace json { +/* FIXME. */ typedef unsigned unichar; +/* FIXME. */ + enum token_id { TOK_ERROR, @@ -303,6 +382,8 @@ enum token_id TOK_NUMBER }; +/* FIXME. */ + static const char *token_id_name[] = { "error", "EOF", @@ -319,11 +400,12 @@ static const char *token_id_name[] = { "number" }; +/* FIXME. */ + struct token { enum token_id id; int index; - int length; union { char *string; @@ -331,11 +413,13 @@ struct token } u; }; +/* A class for lexing JSON. */ + class lexer { public: lexer (); - void add_utf8 (size_t sz, const char *utf8_buf); + void add_utf8 (size_t length, const char *utf8_buf); const token *peek (); void consume (); @@ -347,6 +431,7 @@ class lexer void lex_token (token *out); void lex_string (token *out); void lex_number (token *out, unichar first_char); + bool rest_of_literal_p (const char *suffix) const; private: auto_vec m_buffer; @@ -357,11 +442,13 @@ class lexer int m_num_next_tokens; }; +/* A class for parsing JSON. */ + class parser { public: parser (char **err_out); - void add_utf8 (size_t sz, const char *utf8_buf); + void add_utf8 (size_t length, const char *utf8_buf); value *parse_value (); object *parse_object (); array *parse_array (); @@ -377,17 +464,19 @@ class parser char **m_err_out; }; -} // namespace json +} // anonymous namespace for parsing implementation /* Parser implementation. */ -using namespace json; +/* FIXME. */ lexer::lexer () : m_buffer (), m_next_char_idx (0), m_num_next_tokens (0) { } +/* FIXME. */ + const token * lexer::peek () { @@ -399,6 +488,8 @@ lexer::peek () return &m_next_tokens[0]; } +/* FIXME. */ + void lexer::consume () { @@ -423,14 +514,18 @@ lexer::consume () sizeof (token) * m_num_next_tokens); } +/* FIXME. */ + void -lexer::add_utf8 (size_t sz, const char *utf8_buf) +lexer::add_utf8 (size_t length, const char *utf8_buf) { // FIXME: this assumes Latin-1. - for (size_t i = 0; i < sz; i++) + for (size_t i = 0; i < length; i++) m_buffer.safe_push (utf8_buf[i]); } +/* FIXME. */ + bool lexer::get_char (unichar &out) { @@ -441,12 +536,16 @@ lexer::get_char (unichar &out) return true; } +/* FIXME. */ + void lexer::unget_char () { --m_next_char_idx; } +/* FIXME. */ + void lexer::dump_token (FILE *outf, const token *tok) { @@ -510,6 +609,8 @@ lexer::dump_token (FILE *outf, const token *tok) } } +/* FIXME. */ + void lexer::lex_token (token *out) { @@ -521,7 +622,6 @@ lexer::lex_token (token *out) if (!get_char (next_char)) { out->id = TOK_EOF; - out->length = 1; return; } if (next_char != ' ' && next_char != '\t') @@ -532,32 +632,26 @@ lexer::lex_token (token *out) { 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 '"': @@ -578,14 +672,45 @@ lexer::lex_token (token *out) lex_number (out, next_char); break; + case 't': + /* Handle literal "true". */ + if (rest_of_literal_p ("rue")) + { + out->id = TOK_TRUE; + break; + } + else + goto err; + + case 'f': + /* Handle literal "false". */ + if (rest_of_literal_p ("alse")) + { + out->id = TOK_FALSE; + break; + } + else + goto err; + + case 'n': + /* Handle literal "null". */ + if (rest_of_literal_p ("ull")) + { + out->id = TOK_NULL; + break; + } + else + goto err; + + err: default: - //error (); out->id = TOK_ERROR; - out->length = 1; break; } } +/* FIXME. */ + void lexer::lex_string (token *out) { @@ -610,7 +735,6 @@ lexer::lex_string (token *out) if (!get_char (next_char)) { out->id = TOK_ERROR; - out->length = 1; return; } switch (next_char) @@ -624,7 +748,6 @@ lexer::lex_string (token *out) default: out->id = TOK_ERROR; - out->length = 1; return; } } @@ -645,10 +768,10 @@ lexer::lex_string (token *out) out->u.string[content.length ()] = '\0'; // FIXME: leaks? have a json_context do the allocation - - //out->length = out->m_next_char_idx; } +/* FIXME. */ + void lexer::lex_number (token *out, unichar first_char) { @@ -687,6 +810,27 @@ lexer::lex_number (token *out, unichar first_char) out->u.number = value; } +/* Determine if the next characters to be lexed match SUFFIX. */ + +bool +lexer::rest_of_literal_p (const char *suffix) const +{ + int suffix_idx = 0; + int buf_idx = m_next_char_idx; + while (1) + { + if (suffix[suffix_idx] == '\0') + return true; + /* FIXME: this assumes ASCII. */ + if (m_buffer[buf_idx] != (unichar)suffix[suffix_idx]) + return false; + buf_idx++; + suffix_idx++; + } +} + +/* FIXME. */ + parser::parser (char **err_out) : m_lexer (), m_err_out (err_out) { @@ -695,37 +839,71 @@ parser::parser (char **err_out) *err_out = NULL; } +/* FIXME. */ + void -parser::add_utf8 (size_t sz, const char *utf8_buf) +parser::add_utf8 (size_t length, const char *utf8_buf) { - m_lexer.add_utf8 (sz, utf8_buf); + m_lexer.add_utf8 (length, utf8_buf); } +/* FIXME. */ + 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 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) + switch (tok->id) { - number *result = new number (tok->u.number); - m_lexer.consume (); - return result; - } + case TOK_OPEN_CURLY: + return parse_object (); + + case TOK_STRING: + { + string *result = new string (tok->u.string); + m_lexer.consume (); + return result; + } + + case TOK_OPEN_SQUARE: + return parse_array (); - error_at (tok->index, "unexpected token: %s", token_id_name[tok->id]); - return NULL; // FIXME + case TOK_NUMBER: + { + number *result = new number (tok->u.number); + m_lexer.consume (); + return result; + } + + case TOK_TRUE: + { + literal *result = new literal (JSON_TRUE); + m_lexer.consume (); + return result; + } + + case TOK_FALSE: + { + literal *result = new literal (JSON_FALSE); + m_lexer.consume (); + return result; + } + + case TOK_NULL: + { + literal *result = new literal (JSON_NULL); + m_lexer.consume (); + return result; + } + + default: + error_at (tok->index, "unexpected token: %s", token_id_name[tok->id]); + return NULL; + } } +/* FIXME. */ + object * parser::parse_object () { @@ -755,6 +933,11 @@ parser::parse_object () require (TOK_COLON); value *v = parse_value (); + if (!v) + { + free (key); + return result; + } result->set (key, v); free (key); @@ -773,6 +956,8 @@ parser::parse_object () return result; } +/* FIXME. */ + array * parser::parse_array () { @@ -790,6 +975,9 @@ parser::parse_array () while (!seen_error_p ()) { value *v = parse_value (); + if (!v) + return result; + result->append (v); tok = m_lexer.peek (); @@ -808,6 +996,8 @@ parser::parse_array () return result; } +/* FIXME. */ + void parser::require (enum token_id tok_id) { @@ -818,6 +1008,8 @@ parser::require (enum token_id tok_id) m_lexer.consume (); } +/* FIXME. */ + void parser::error_at (int index, const char *fmt, ...) { @@ -830,23 +1022,29 @@ parser::error_at (int index, const char *fmt, ...) index, formatted); free (formatted); - fprintf (stderr, "%s\n", msg_with_index); + if (0) + fprintf (stderr, "%s\n", msg_with_index); if (*m_err_out == NULL) *m_err_out = msg_with_index; else free (msg_with_index); } +/* Attempt to parse the UTF-8 encoded buffer at UTF8_BUF + of the given LENGTH. + If successful, return a non-NULL json::value *. + if there was a problem, return NULL and write an error + message to err_out, which must be freed by the caller. */ value * -json::parse_utf8_string (size_t sz, const char *utf8_buf, +json::parse_utf8_string (size_t length, const char *utf8_buf, char **err_out) { gcc_assert (err_out); gcc_assert (*err_out == NULL); parser p (err_out); - p.add_utf8 (sz, utf8_buf); + p.add_utf8 (length, utf8_buf); value *result = p.parse_value (); if (p.seen_error_p ()) { @@ -857,6 +1055,12 @@ json::parse_utf8_string (size_t sz, const char *utf8_buf, return result; } +/* Attempt to parse the nil-terminated UTF-8 encoded buffer at + UTF8_BUF. + If successful, return a non-NULL json::value *. + if there was a problem, return NULL and write an error + message to err_out, which must be freed by the caller. */ + value * json::parse_utf8_string (const char *utf8, char **err_out) { @@ -880,6 +1084,8 @@ assert_to_str_eq (const char *expected_json, json::value *jv) free (json); } +/* FIXME. */ + static void test_parse_string () { @@ -900,23 +1106,34 @@ test_parse_string () delete jv; } +/* FIXME. */ + static void test_parse_number () { + json::value *jv, *clone; + char *err = NULL; - json::value *jv = parse_utf8_string ("42", &err); + jv = parse_utf8_string ("42", &err); ASSERT_EQ (NULL, err); ASSERT_EQ (JSON_NUMBER, jv->get_kind ()); ASSERT_EQ (42.0, ((json::number *)jv)->get ()); assert_to_str_eq ("42", jv); + clone = jv->clone (); + ASSERT_EQ (JSON_NUMBER, clone->get_kind ()); + delete clone; delete jv; } +/* FIXME. */ + static void test_parse_array () { + json::value *jv, *clone; + char *err = NULL; - json::value *jv = parse_utf8_string ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", &err); + jv = parse_utf8_string ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", &err); ASSERT_EQ (NULL, err); ASSERT_EQ (JSON_ARRAY, jv->get_kind ()); json::array *arr = static_cast (jv); @@ -928,9 +1145,25 @@ test_parse_array () ASSERT_EQ (i, ((json::number *)element)->get ()); } assert_to_str_eq ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", jv); + + clone = jv->clone (); + ASSERT_EQ (JSON_ARRAY, clone->get_kind ()); + arr = static_cast (clone); + 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 ()); + } + assert_to_str_eq ("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", clone); + delete clone; + delete jv; } +/* FIXME. */ + static void test_parse_object () { @@ -955,16 +1188,61 @@ test_parse_object () ASSERT_EQ (2, baz_array->get_length ()); // etc, verify numbers - // etc, true, false, null // TODO: error-handling // TODO: partial document /* We can't use assert_to_str_eq since ordering is not guaranteed. */ + json::value *clone = jv->clone (); + ASSERT_EQ (JSON_OBJECT, clone->get_kind ()); + ASSERT_EQ (JSON_STRING, clone->as_object ()->get ("foo")->get_kind ()); + delete clone; + delete jv; } +/* Verify that the literals "true", "false" and "null" are parsed, + dumped, and are clonable. */ + +static void +test_parse_literals () +{ + json::value *jv, *clone; + char *err = NULL; + jv = parse_utf8_string ("true", &err); + ASSERT_EQ (NULL, err); + ASSERT_TRUE (jv != NULL); + ASSERT_EQ (JSON_TRUE, jv->get_kind ()); + assert_to_str_eq ("true", jv); + clone = jv->clone (); + ASSERT_EQ (JSON_TRUE, clone->get_kind ()); + delete clone; + delete jv; + + jv = parse_utf8_string ("false", &err); + ASSERT_EQ (NULL, err); + ASSERT_TRUE (jv != NULL); + ASSERT_EQ (JSON_FALSE, jv->get_kind ()); + assert_to_str_eq ("false", jv); + clone = jv->clone (); + ASSERT_EQ (JSON_FALSE, clone->get_kind ()); + delete clone; + delete jv; + + jv = parse_utf8_string ("null", &err); + ASSERT_EQ (NULL, err); + ASSERT_TRUE (jv != NULL); + ASSERT_EQ (JSON_NULL, jv->get_kind ()); + assert_to_str_eq ("null", jv); + clone = jv->clone (); + ASSERT_EQ (JSON_NULL, clone->get_kind ()); + delete clone; + delete jv; +} + +/* FIXME. */ + static void test_parse_jsonrpc () { @@ -978,6 +1256,8 @@ test_parse_jsonrpc () delete jv; } +/* FIXME. */ + static void test_parse_empty_object () { @@ -990,6 +1270,8 @@ test_parse_empty_object () delete jv; } +/* FIXME. */ + static void test_error_empty_string () { @@ -1000,6 +1282,8 @@ test_error_empty_string () free (err); } +/* FIXME. */ + static void test_error_missing_comma () { @@ -1023,6 +1307,7 @@ json_c_tests () test_parse_number (); test_parse_array (); test_parse_object (); + test_parse_literals (); test_parse_jsonrpc (); test_parse_empty_object (); test_error_empty_string (); @@ -1030,6 +1315,8 @@ json_c_tests () /* FIXME: tests for roundtripping (noting that we don't preserve object key ordering). */ + + /* FIXME: cloning. */ } } // namespace selftest diff --git a/gcc/json.h b/gcc/json.h index 7655d50..6e71beb 100644 --- a/gcc/json.h +++ b/gcc/json.h @@ -20,22 +20,45 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_JSON_H #define GCC_JSON_H +/* Implementation of JSON, a lightweight data-interchange format. + + See http://www.json.org/ + and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf + + Supports parsing text into a DOM-like tree of json::value *, dumping + json::value * to text. */ + namespace json { +/* Forward decls of json::value and its subclasses (using indentation + to denote inheritance. */ + class value; - class string; - class number; class object; class array; + class number; + class string; class literal; +/* An enum for discriminating the subclasses of json::value. */ + enum kind { + /* class json::object. */ JSON_OBJECT, + + /* class json::array. */ JSON_ARRAY, + + /* class json::number. */ JSON_NUMBER, + + /* class json::string. */ JSON_STRING, + + /* class json::literal uses these three values to identify the + particular literal. */ JSON_TRUE, JSON_FALSE, JSON_NULL @@ -49,18 +72,23 @@ class value virtual ~value () {} virtual enum kind get_kind () const = 0; virtual void print (pretty_printer *pp) const = 0; + + /* Create a deep copy of the value, returning a value which must be + deleted by the caller. */ virtual value *clone () const = 0; char *to_str () const; void dump (FILE *) const; + /* Methods for dynamically casting a value to one of the subclasses, + returning NULL if the value is of the wrong kind. */ const object *as_object () const; const array *as_array () const; const number *as_number () const; const string *as_string () const; - /* Convenience accessors for attempting to get a value from - this value as if it is an object. + /* Convenience accessors for attempting to perform key/value lookups + on this value as if it were an json::object. On success, return true and write the value to OUT_VALUE. On failure, return false and write an error message to OUT_ERR @@ -146,7 +174,8 @@ class string : public value char *m_utf8; }; -/* Subclass of value for the literals "true", "false", and "null". */ +/* Subclass of value for the three JSON literals "true", "false", + and "null". */ class literal : public value { @@ -161,13 +190,12 @@ class literal : public value enum kind m_kind; }; -/* Parsing decls. */ +/* Declarations for parsing JSON to a json::value * tree. */ -extern value *parse_utf8_string (size_t sz, const char *utf8_buf, +extern value *parse_utf8_string (size_t length, const char *utf8_buf, char **err_out); extern value *parse_utf8_string (const char *utf8, char **err_out); } // namespace json #endif /* GCC_JSON_H */ - diff --git a/gcc/lsp.c b/gcc/lsp.c index 9663ccc..ed5d68e 100644 --- a/gcc/lsp.c +++ b/gcc/lsp.c @@ -247,7 +247,7 @@ static void test_simple () { noop_server noop; - lsp::jsonrpc_server js (true, noop); + lsp::jsonrpc_server js (false, noop); const char *init_request = ("{\"jsonrpc\": \"2.0\", \"method\": \"initialize\"," " \"params\": [42, 23], \"id\": 1}"); // FIXME -- 1.8.5.3