json: add json parsing support

This patch implements JSON parsing support.

It's based on the parsing parts of the patch I posted here:
https://gcc.gnu.org/legacy-ml/gcc-patches/2017-08/msg00417.html
with the parsing moved to a separate source file and header, heavily
rewritten to capture source location information for JSON values, and
to capture errors via a result template.

I also added optional support for C and C++ style comments, which is
extremely useful in DejaGnu tests.

gcc/ChangeLog:
	* Makefile.in (OBJS-libcommon): Add json-parsing.o.
	* json-parsing.cc: New file.
	* json-parsing.h: New file.
	* json.cc (selftest::assert_print_eq): Remove "static".
	* json.h (json::array::begin): New.
	(json::array::end): New.
	(json::array::length): New.
	(json::array::get): New.
	(is_a_helper <json::value *>::test): New.
	(is_a_helper <const json::value *>::test): New.
	(is_a_helper <json::object *>::test): New.
	(is_a_helper <const json::object *>::test): New.
	(is_a_helper <json::array *>::test): New.
	(is_a_helper <const json::array *>::test): New.
	(is_a_helper <json::float_number *>::test): New.
	(is_a_helper <const json::float_number *>::test): New.
	(is_a_helper <json::integer_number *>::test): New.
	(is_a_helper <const json::integer_number *>::test): New.
	(is_a_helper <json::string *>::test): New.
	(is_a_helper <const json::string *>::test): New.
	(selftest::assert_print_eq): New decl.
	* selftest-run-tests.cc (selftest::run_tests): Call
	selftest::json_parser_cc_tests.
	* selftest.h (selftest::json_parser_cc_tests): New decl.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2024-11-18 17:08:36 -05:00
parent 99a909aba3
commit 66e6e4f131
7 changed files with 2631 additions and 4 deletions

View File

@ -1838,7 +1838,7 @@ OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \
diagnostic-show-locus.o \ diagnostic-show-locus.o \
edit-context.o \ edit-context.o \
pretty-print.o intl.o \ pretty-print.o intl.o \
json.o \ json.o json-parsing.o \
sbitmap.o \ sbitmap.o \
vec.o input.o hash-table.o ggc-none.o memory-block.o \ vec.o input.o hash-table.o ggc-none.o memory-block.o \
selftest.o selftest-diagnostic.o sort.o \ selftest.o selftest-diagnostic.o sort.o \

2394
gcc/json-parsing.cc Normal file

File diff suppressed because it is too large Load Diff

113
gcc/json-parsing.h Normal file
View File

@ -0,0 +1,113 @@
/* JSON parsing
Copyright (C) 2017-2022 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
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/>. */
#ifndef GCC_JSON_PARSING_H
#define GCC_JSON_PARSING_H
#include "json.h"
namespace json
{
/* Declarations for parsing JSON to a json::value * tree. */
/* Abstract base class for recording what the locations of JSON values
were as they parsed. */
class location_map
{
public:
/* A point within the JSON input file. */
struct point
{
size_t m_unichar_idx; /* zero-based. */
int m_line; /* one-based. */
int m_column; /* zero-based unichar count. */
};
/* A range of points within the JSON input file.
Both endpoints are part of the range. */
struct range
{
point m_start;
point m_end;
};
virtual ~location_map () {}
virtual void record_range_for_value (json::value *jv, const range &r) = 0;
virtual void on_finished_parsing () {}
};
/* Class for recording an error within a JSON file. */
class error
{
public:
error (const location_map::range &r, char *msg)
: m_range (r), m_msg (msg)
{
}
~error ()
{
free (m_msg);
}
const location_map::range &get_range () const { return m_range; }
const char *get_msg () const { return m_msg; }
private:
location_map::range m_range;
char *m_msg;
};
/* Class for the result of an operation: either a value or an error
(or both null for the case of "successful nullptr").
The types must be default-constructible. */
template <typename ValueType, typename ErrorType>
struct result
{
result (ValueType val) : m_val (std::move (val)), m_err () {}
result (ErrorType err) : m_val (), m_err (std::move (err)) {}
ValueType m_val;
ErrorType m_err;
};
/* Typedef for the result of parsing JSON: ownership of either a
json::value * or of a json::error *. */
typedef result<std::unique_ptr<value>,
std::unique_ptr<error>> parser_result_t;
/* Functions for parsing JSON buffers. */
extern parser_result_t
parse_utf8_string (size_t length,
const char *utf8_buf,
bool allow_comments,
location_map *out_loc_map);
extern parser_result_t
parse_utf8_string (const char *utf8,
bool allow_comments,
location_map *out_loc_map);
} // namespace json
#endif /* GCC_JSON_PARSING_H */

View File

@ -383,7 +383,7 @@ namespace selftest {
/* Verify that JV->print () prints EXPECTED_JSON. */ /* Verify that JV->print () prints EXPECTED_JSON. */
static void void
assert_print_eq (const location &loc, assert_print_eq (const location &loc,
const json::value &jv, const json::value &jv,
bool formatted, bool formatted,

View File

@ -36,8 +36,8 @@ along with GCC; see the file COPYING3. If not see
and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
and https://tools.ietf.org/html/rfc7159 and https://tools.ietf.org/html/rfc7159
Supports creating a DOM-like tree of json::value *, and then dumping Supports parsing text into a DOM-like tree of json::value *, directly
json::value * to text. */ creating such trees, and dumping json::value * to text. */
/* TODO: `libcpp/mkdeps.cc` wants JSON writing support for p1689r5 output; /* TODO: `libcpp/mkdeps.cc` wants JSON writing support for p1689r5 output;
extract this code and move to libiberty. */ extract this code and move to libiberty. */
@ -175,6 +175,13 @@ class array : public value
size_t size () const { return m_elements.length (); } size_t size () const { return m_elements.length (); }
value *operator[] (size_t i) const { return m_elements[i]; } value *operator[] (size_t i) const { return m_elements[i]; }
value **begin () { return m_elements.begin (); }
value **end () { return m_elements.end (); }
const value * const *begin () const { return m_elements.begin (); }
const value * const *end () const { return m_elements.end (); }
size_t length () const { return m_elements.length (); }
value *get (size_t idx) const { return m_elements[idx]; }
private: private:
auto_vec<value *> m_elements; auto_vec<value *> m_elements;
}; };
@ -252,4 +259,115 @@ class literal : public value
} // namespace json } // namespace json
template <>
template <>
inline bool
is_a_helper <json::value *>::test (json::value *)
{
return true;
}
template <>
template <>
inline bool
is_a_helper <const json::value *>::test (const json::value *)
{
return true;
}
template <>
template <>
inline bool
is_a_helper <json::object *>::test (json::value *jv)
{
return jv->get_kind () == json::JSON_OBJECT;
}
template <>
template <>
inline bool
is_a_helper <const json::object *>::test (const json::value *jv)
{
return jv->get_kind () == json::JSON_OBJECT;
}
template <>
template <>
inline bool
is_a_helper <json::array *>::test (json::value *jv)
{
return jv->get_kind () == json::JSON_ARRAY;
}
template <>
template <>
inline bool
is_a_helper <const json::array *>::test (const json::value *jv)
{
return jv->get_kind () == json::JSON_ARRAY;
}
template <>
template <>
inline bool
is_a_helper <json::float_number *>::test (json::value *jv)
{
return jv->get_kind () == json::JSON_FLOAT;
}
template <>
template <>
inline bool
is_a_helper <const json::float_number *>::test (const json::value *jv)
{
return jv->get_kind () == json::JSON_FLOAT;
}
template <>
template <>
inline bool
is_a_helper <json::integer_number *>::test (json::value *jv)
{
return jv->get_kind () == json::JSON_INTEGER;
}
template <>
template <>
inline bool
is_a_helper <const json::integer_number *>::test (const json::value *jv)
{
return jv->get_kind () == json::JSON_INTEGER;
}
template <>
template <>
inline bool
is_a_helper <json::string *>::test (json::value *jv)
{
return jv->get_kind () == json::JSON_STRING;
}
template <>
template <>
inline bool
is_a_helper <const json::string *>::test (const json::value *jv)
{
return jv->get_kind () == json::JSON_STRING;
}
#if CHECKING_P
namespace selftest {
class location;
extern void assert_print_eq (const location &loc,
const json::value &jv,
bool formatted,
const char *expected_json);
} // namespace selftest
#endif /* #if CHECKING_P */
#endif /* GCC_JSON_H */ #endif /* GCC_JSON_H */

View File

@ -75,6 +75,7 @@ selftest::run_tests ()
opt_suggestions_cc_tests (); opt_suggestions_cc_tests ();
opts_cc_tests (); opts_cc_tests ();
json_cc_tests (); json_cc_tests ();
json_parser_cc_tests ();
cgraph_cc_tests (); cgraph_cc_tests ();
optinfo_emit_json_cc_tests (); optinfo_emit_json_cc_tests ();
ordered_hash_map_tests_cc_tests (); ordered_hash_map_tests_cc_tests ();

View File

@ -240,6 +240,7 @@ extern void hash_set_tests_cc_tests ();
extern void input_cc_tests (); extern void input_cc_tests ();
extern void json_cc_tests (); extern void json_cc_tests ();
extern void lazy_diagnostic_path_cc_tests (); extern void lazy_diagnostic_path_cc_tests ();
extern void json_parser_cc_tests ();
extern void optinfo_emit_json_cc_tests (); extern void optinfo_emit_json_cc_tests ();
extern void opts_cc_tests (); extern void opts_cc_tests ();
extern void opts_diagnostic_cc_tests (); extern void opts_diagnostic_cc_tests ();