]>
Commit | Line | Data |
---|---|---|
9dcf2a11 | 1 | /* JSON trees |
fbd26352 | 2 | Copyright (C) 2017-2019 Free Software Foundation, Inc. |
9dcf2a11 | 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.h" | |
25 | #include "pretty-print.h" | |
26 | #include "math.h" | |
27 | #include "selftest.h" | |
28 | ||
29 | using namespace json; | |
30 | ||
31 | /* class json::value. */ | |
32 | ||
33 | /* Dump this json::value tree to OUTF. | |
34 | No formatting is done. There are no guarantees about the order | |
35 | in which the key/value pairs of json::objects are printed. */ | |
36 | ||
37 | void | |
38 | value::dump (FILE *outf) const | |
39 | { | |
40 | pretty_printer pp; | |
41 | pp_buffer (&pp)->stream = outf; | |
42 | print (&pp); | |
43 | pp_flush (&pp); | |
44 | } | |
45 | ||
46 | /* class json::object, a subclass of json::value, representing | |
47 | an unordered collection of key/value pairs. */ | |
48 | ||
49 | /* json:object's dtor. */ | |
50 | ||
51 | object::~object () | |
52 | { | |
53 | for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it) | |
54 | { | |
55 | free (const_cast <char *>((*it).first)); | |
56 | delete ((*it).second); | |
57 | } | |
58 | } | |
59 | ||
60 | /* Implementation of json::value::print for json::object. */ | |
61 | ||
62 | void | |
63 | object::print (pretty_printer *pp) const | |
64 | { | |
65 | /* Note that the order is not guaranteed. */ | |
66 | pp_character (pp, '{'); | |
67 | for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it) | |
68 | { | |
69 | if (it != m_map.begin ()) | |
70 | pp_string (pp, ", "); | |
71 | const char *key = const_cast <char *>((*it).first); | |
72 | value *value = (*it).second; | |
73 | pp_printf (pp, "\"%s\": ", key); // FIXME: escaping? | |
74 | value->print (pp); | |
75 | } | |
76 | pp_character (pp, '}'); | |
77 | } | |
78 | ||
e4aaf333 | 79 | /* Set the json::value * for KEY, taking ownership of V |
9dcf2a11 | 80 | (and taking a copy of KEY if necessary). */ |
81 | ||
82 | void | |
83 | object::set (const char *key, value *v) | |
84 | { | |
e4aaf333 | 85 | gcc_assert (key); |
86 | gcc_assert (v); | |
87 | ||
9dcf2a11 | 88 | value **ptr = m_map.get (key); |
89 | if (ptr) | |
90 | { | |
91 | /* If the key is already present, delete the existing value | |
92 | and overwrite it. */ | |
93 | delete *ptr; | |
94 | *ptr = v; | |
95 | } | |
96 | else | |
97 | /* If the key wasn't already present, take a copy of the key, | |
98 | and store the value. */ | |
99 | m_map.put (xstrdup (key), v); | |
100 | } | |
101 | ||
102 | /* class json::array, a subclass of json::value, representing | |
103 | an ordered collection of values. */ | |
104 | ||
105 | /* json::array's dtor. */ | |
106 | ||
107 | array::~array () | |
108 | { | |
109 | unsigned i; | |
110 | value *v; | |
111 | FOR_EACH_VEC_ELT (m_elements, i, v) | |
112 | delete v; | |
113 | } | |
114 | ||
115 | /* Implementation of json::value::print for json::array. */ | |
116 | ||
117 | void | |
118 | array::print (pretty_printer *pp) const | |
119 | { | |
120 | pp_character (pp, '['); | |
121 | unsigned i; | |
122 | value *v; | |
123 | FOR_EACH_VEC_ELT (m_elements, i, v) | |
124 | { | |
125 | if (i) | |
126 | pp_string (pp, ", "); | |
127 | v->print (pp); | |
128 | } | |
129 | pp_character (pp, ']'); | |
130 | } | |
131 | ||
e4aaf333 | 132 | /* Append non-NULL value V to a json::array, taking ownership of V. */ |
133 | ||
134 | void | |
135 | array::append (value *v) | |
136 | { | |
137 | gcc_assert (v); | |
138 | m_elements.safe_push (v); | |
139 | } | |
140 | ||
9dcf2a11 | 141 | /* class json::number, a subclass of json::value, wrapping a double. */ |
142 | ||
143 | /* Implementation of json::value::print for json::number. */ | |
144 | ||
145 | void | |
146 | number::print (pretty_printer *pp) const | |
147 | { | |
148 | char tmp[1024]; | |
149 | snprintf (tmp, sizeof (tmp), "%g", m_value); | |
150 | pp_string (pp, tmp); | |
151 | } | |
152 | ||
153 | /* class json::string, a subclass of json::value. */ | |
154 | ||
e4aaf333 | 155 | /* json::string's ctor. */ |
156 | ||
157 | string::string (const char *utf8) | |
158 | { | |
159 | gcc_assert (utf8); | |
160 | m_utf8 = xstrdup (utf8); | |
161 | } | |
162 | ||
163 | /* Implementation of json::value::print for json::string. */ | |
164 | ||
9dcf2a11 | 165 | void |
166 | string::print (pretty_printer *pp) const | |
167 | { | |
168 | pp_character (pp, '"'); | |
169 | for (const char *ptr = m_utf8; *ptr; ptr++) | |
170 | { | |
171 | char ch = *ptr; | |
172 | switch (ch) | |
173 | { | |
174 | case '"': | |
175 | pp_string (pp, "\\\""); | |
176 | break; | |
177 | case '\\': | |
178 | pp_string (pp, "\\n"); | |
179 | break; | |
180 | case '\b': | |
181 | pp_string (pp, "\\b"); | |
182 | break; | |
183 | case '\f': | |
184 | pp_string (pp, "\\f"); | |
185 | break; | |
186 | case '\n': | |
187 | pp_string (pp, "\\n"); | |
188 | break; | |
189 | case '\r': | |
190 | pp_string (pp, "\\r"); | |
191 | break; | |
192 | case '\t': | |
193 | pp_string (pp, "\\t"); | |
194 | break; | |
195 | ||
196 | default: | |
197 | pp_character (pp, ch); | |
198 | } | |
199 | } | |
200 | pp_character (pp, '"'); | |
201 | } | |
202 | ||
203 | /* class json::literal, a subclass of json::value. */ | |
204 | ||
205 | /* Implementation of json::value::print for json::literal. */ | |
206 | ||
207 | void | |
208 | literal::print (pretty_printer *pp) const | |
209 | { | |
210 | switch (m_kind) | |
211 | { | |
212 | case JSON_TRUE: | |
213 | pp_string (pp, "true"); | |
214 | break; | |
215 | case JSON_FALSE: | |
216 | pp_string (pp, "false"); | |
217 | break; | |
218 | case JSON_NULL: | |
219 | pp_string (pp, "null"); | |
220 | break; | |
221 | default: | |
222 | gcc_unreachable (); | |
223 | } | |
224 | } | |
225 | ||
226 | \f | |
227 | #if CHECKING_P | |
228 | ||
229 | namespace selftest { | |
230 | ||
231 | /* Selftests. */ | |
232 | ||
233 | /* Verify that JV->print () prints EXPECTED_JSON. */ | |
234 | ||
235 | static void | |
236 | assert_print_eq (const json::value &jv, const char *expected_json) | |
237 | { | |
238 | pretty_printer pp; | |
239 | jv.print (&pp); | |
240 | ASSERT_STREQ (expected_json, pp_formatted_text (&pp)); | |
241 | } | |
242 | ||
243 | /* Verify that JSON objects are written correctly. We can't test more than | |
244 | one key/value pair, as we don't impose a guaranteed ordering. */ | |
245 | ||
246 | static void | |
247 | test_writing_objects () | |
248 | { | |
249 | object obj; | |
250 | obj.set ("foo", new json::string ("bar")); | |
251 | assert_print_eq (obj, "{\"foo\": \"bar\"}"); | |
252 | } | |
253 | ||
254 | /* Verify that JSON arrays are written correctly. */ | |
255 | ||
256 | static void | |
257 | test_writing_arrays () | |
258 | { | |
259 | array arr; | |
260 | assert_print_eq (arr, "[]"); | |
261 | ||
262 | arr.append (new json::string ("foo")); | |
263 | assert_print_eq (arr, "[\"foo\"]"); | |
264 | ||
265 | arr.append (new json::string ("bar")); | |
266 | assert_print_eq (arr, "[\"foo\", \"bar\"]"); | |
267 | } | |
268 | ||
269 | /* Verify that JSON numbers are written correctly. */ | |
270 | ||
271 | static void | |
272 | test_writing_numbers () | |
273 | { | |
274 | assert_print_eq (number (0), "0"); | |
275 | assert_print_eq (number (42), "42"); | |
276 | assert_print_eq (number (-100), "-100"); | |
277 | } | |
278 | ||
279 | /* Verify that JSON strings are written correctly. */ | |
280 | ||
281 | static void | |
282 | test_writing_strings () | |
283 | { | |
284 | string foo ("foo"); | |
285 | assert_print_eq (foo, "\"foo\""); | |
286 | ||
287 | string contains_quotes ("before \"quoted\" after"); | |
288 | assert_print_eq (contains_quotes, "\"before \\\"quoted\\\" after\""); | |
289 | } | |
290 | ||
4cd96a00 | 291 | /* Verify that JSON literals are written correctly. */ |
9dcf2a11 | 292 | |
293 | static void | |
294 | test_writing_literals () | |
295 | { | |
296 | assert_print_eq (literal (JSON_TRUE), "true"); | |
297 | assert_print_eq (literal (JSON_FALSE), "false"); | |
298 | assert_print_eq (literal (JSON_NULL), "null"); | |
8d11df62 | 299 | |
300 | assert_print_eq (literal (true), "true"); | |
301 | assert_print_eq (literal (false), "false"); | |
9dcf2a11 | 302 | } |
303 | ||
304 | /* Run all of the selftests within this file. */ | |
305 | ||
306 | void | |
307 | json_cc_tests () | |
308 | { | |
309 | test_writing_objects (); | |
310 | test_writing_arrays (); | |
311 | test_writing_numbers (); | |
312 | test_writing_strings (); | |
313 | test_writing_literals (); | |
314 | } | |
315 | ||
316 | } // namespace selftest | |
317 | ||
318 | #endif /* #if CHECKING_P */ |