xref: /openbmc/qemu/qobject/json-parser.c (revision 04ddcda6a2387274b3f31a501be3affd172aea3d)
1a372823aSPaolo Bonzini /*
2a372823aSPaolo Bonzini  * JSON Parser
3a372823aSPaolo Bonzini  *
4a372823aSPaolo Bonzini  * Copyright IBM, Corp. 2009
5a372823aSPaolo Bonzini  *
6a372823aSPaolo Bonzini  * Authors:
7a372823aSPaolo Bonzini  *  Anthony Liguori   <aliguori@us.ibm.com>
8a372823aSPaolo Bonzini  *
9a372823aSPaolo Bonzini  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10a372823aSPaolo Bonzini  * See the COPYING.LIB file in the top-level directory.
11a372823aSPaolo Bonzini  *
12a372823aSPaolo Bonzini  */
13a372823aSPaolo Bonzini 
14f2ad72b3SPeter Maydell #include "qemu/osdep.h"
15856dfd8aSMarkus Armbruster #include "qemu/ctype.h"
162bc7cfeaSMarc-André Lureau #include "qemu/cutils.h"
17e59f39d4SMarkus Armbruster #include "qemu/unicode.h"
18da34e65cSMarkus Armbruster #include "qapi/error.h"
196b673957SMarkus Armbruster #include "qapi/qmp/qbool.h"
20452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h"
2147e6b297SMarkus Armbruster #include "qapi/qmp/qlist.h"
2215280c36SMarkus Armbruster #include "qapi/qmp/qnull.h"
2315280c36SMarkus Armbruster #include "qapi/qmp/qnum.h"
246b673957SMarkus Armbruster #include "qapi/qmp/qstring.h"
2586cdf9ecSMarkus Armbruster #include "json-parser-int.h"
26a372823aSPaolo Bonzini 
27abe7c206SMarkus Armbruster struct JSONToken {
28abe7c206SMarkus Armbruster     JSONTokenType type;
29abe7c206SMarkus Armbruster     int x;
30abe7c206SMarkus Armbruster     int y;
31abe7c206SMarkus Armbruster     char str[];
32abe7c206SMarkus Armbruster };
33abe7c206SMarkus Armbruster 
345086c997SZhang Han typedef struct JSONParserContext {
35a372823aSPaolo Bonzini     Error *err;
369bada897SPaolo Bonzini     JSONToken *current;
3795385fe9SPaolo Bonzini     GQueue *buf;
38ada74c3bSMarkus Armbruster     va_list *ap;
39a372823aSPaolo Bonzini } JSONParserContext;
40a372823aSPaolo Bonzini 
41a372823aSPaolo Bonzini #define BUG_ON(cond) assert(!(cond))
42a372823aSPaolo Bonzini 
43a372823aSPaolo Bonzini /**
44a372823aSPaolo Bonzini  * TODO
45a372823aSPaolo Bonzini  *
46a372823aSPaolo Bonzini  * 0) make errors meaningful again
47a372823aSPaolo Bonzini  * 1) add geometry information to tokens
48a372823aSPaolo Bonzini  * 3) should we return a parsed size?
49a372823aSPaolo Bonzini  * 4) deal with premature EOI
50a372823aSPaolo Bonzini  */
51a372823aSPaolo Bonzini 
52ada74c3bSMarkus Armbruster static QObject *parse_value(JSONParserContext *ctxt);
53a372823aSPaolo Bonzini 
54a372823aSPaolo Bonzini /**
55a372823aSPaolo Bonzini  * Error handler
56a372823aSPaolo Bonzini  */
parse_error(JSONParserContext * ctxt,JSONToken * token,const char * msg,...)57*9edc6313SMarc-André Lureau static void G_GNUC_PRINTF(3, 4) parse_error(JSONParserContext *ctxt,
589bada897SPaolo Bonzini                                            JSONToken *token, const char *msg, ...)
59a372823aSPaolo Bonzini {
60a372823aSPaolo Bonzini     va_list ap;
61a372823aSPaolo Bonzini     char message[1024];
62574bf16fSMarkus Armbruster 
63574bf16fSMarkus Armbruster     if (ctxt->err) {
64574bf16fSMarkus Armbruster         return;
65574bf16fSMarkus Armbruster     }
66a372823aSPaolo Bonzini     va_start(ap, msg);
67a372823aSPaolo Bonzini     vsnprintf(message, sizeof(message), msg, ap);
68a372823aSPaolo Bonzini     va_end(ap);
69f231b88dSCole Robinson     error_setg(&ctxt->err, "JSON parse error, %s", message);
70a372823aSPaolo Bonzini }
71a372823aSPaolo Bonzini 
cvt4hex(const char * s)72dc45a07cSMarkus Armbruster static int cvt4hex(const char *s)
73a372823aSPaolo Bonzini {
74dc45a07cSMarkus Armbruster     int cp, i;
75dc45a07cSMarkus Armbruster 
76dc45a07cSMarkus Armbruster     cp = 0;
77dc45a07cSMarkus Armbruster     for (i = 0; i < 4; i++) {
78dc45a07cSMarkus Armbruster         if (!qemu_isxdigit(s[i])) {
79dc45a07cSMarkus Armbruster             return -1;
80a372823aSPaolo Bonzini         }
81dc45a07cSMarkus Armbruster         cp <<= 4;
82dc45a07cSMarkus Armbruster         if (s[i] >= '0' && s[i] <= '9') {
83dc45a07cSMarkus Armbruster             cp |= s[i] - '0';
84dc45a07cSMarkus Armbruster         } else if (s[i] >= 'a' && s[i] <= 'f') {
85dc45a07cSMarkus Armbruster             cp |= 10 + s[i] - 'a';
86dc45a07cSMarkus Armbruster         } else if (s[i] >= 'A' && s[i] <= 'F') {
87dc45a07cSMarkus Armbruster             cp |= 10 + s[i] - 'A';
88dc45a07cSMarkus Armbruster         } else {
89dc45a07cSMarkus Armbruster             return -1;
90dc45a07cSMarkus Armbruster         }
91dc45a07cSMarkus Armbruster     }
92dc45a07cSMarkus Armbruster     return cp;
93a372823aSPaolo Bonzini }
94a372823aSPaolo Bonzini 
95a372823aSPaolo Bonzini /**
96b2da4a4dSMarkus Armbruster  * parse_string(): Parse a JSON string
97a372823aSPaolo Bonzini  *
98b2da4a4dSMarkus Armbruster  * From RFC 8259 "The JavaScript Object Notation (JSON) Data
99b2da4a4dSMarkus Armbruster  * Interchange Format":
100b2da4a4dSMarkus Armbruster  *
101b2da4a4dSMarkus Armbruster  *    char = unescaped /
102b2da4a4dSMarkus Armbruster  *        escape (
103b2da4a4dSMarkus Armbruster  *            %x22 /          ; "    quotation mark  U+0022
104b2da4a4dSMarkus Armbruster  *            %x5C /          ; \    reverse solidus U+005C
105b2da4a4dSMarkus Armbruster  *            %x2F /          ; /    solidus         U+002F
106b2da4a4dSMarkus Armbruster  *            %x62 /          ; b    backspace       U+0008
107b2da4a4dSMarkus Armbruster  *            %x66 /          ; f    form feed       U+000C
108b2da4a4dSMarkus Armbruster  *            %x6E /          ; n    line feed       U+000A
109b2da4a4dSMarkus Armbruster  *            %x72 /          ; r    carriage return U+000D
110b2da4a4dSMarkus Armbruster  *            %x74 /          ; t    tab             U+0009
111b2da4a4dSMarkus Armbruster  *            %x75 4HEXDIG )  ; uXXXX                U+XXXX
112b2da4a4dSMarkus Armbruster  *    escape = %x5C              ; \
113b2da4a4dSMarkus Armbruster  *    quotation-mark = %x22      ; "
114b2da4a4dSMarkus Armbruster  *    unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
115b2da4a4dSMarkus Armbruster  *
116b2da4a4dSMarkus Armbruster  * Extensions over RFC 8259:
117b2da4a4dSMarkus Armbruster  * - Extra escape sequence in strings:
118b2da4a4dSMarkus Armbruster  *   0x27 (apostrophe) is recognized after escape, too
119b2da4a4dSMarkus Armbruster  * - Single-quoted strings:
120b2da4a4dSMarkus Armbruster  *   Like double-quoted strings, except they're delimited by %x27
121b2da4a4dSMarkus Armbruster  *   (apostrophe) instead of %x22 (quotation mark), and can't contain
122b2da4a4dSMarkus Armbruster  *   unescaped apostrophe, but can contain unescaped quotation mark.
123b2da4a4dSMarkus Armbruster  *
124b2da4a4dSMarkus Armbruster  * Note:
125b2da4a4dSMarkus Armbruster  * - Encoding is modified UTF-8.
126b2da4a4dSMarkus Armbruster  * - Invalid Unicode characters are rejected.
127b2da4a4dSMarkus Armbruster  * - Control characters \x00..\x1F are rejected by the lexer.
128a372823aSPaolo Bonzini  */
parse_string(JSONParserContext * ctxt,JSONToken * token)129b2da4a4dSMarkus Armbruster static QString *parse_string(JSONParserContext *ctxt, JSONToken *token)
130a372823aSPaolo Bonzini {
1319bada897SPaolo Bonzini     const char *ptr = token->str;
13268af4cc1SMarkus Armbruster     GString *str;
13300ea57faSMarkus Armbruster     char quote;
134dc45a07cSMarkus Armbruster     const char *beg;
135dc45a07cSMarkus Armbruster     int cp, trailing;
136e59f39d4SMarkus Armbruster     char *end;
137e59f39d4SMarkus Armbruster     ssize_t len;
138e59f39d4SMarkus Armbruster     char utf8_buf[5];
139a372823aSPaolo Bonzini 
14000ea57faSMarkus Armbruster     assert(*ptr == '"' || *ptr == '\'');
14100ea57faSMarkus Armbruster     quote = *ptr++;
14268af4cc1SMarkus Armbruster     str = g_string_new(NULL);
14300ea57faSMarkus Armbruster 
14400ea57faSMarkus Armbruster     while (*ptr != quote) {
14500ea57faSMarkus Armbruster         assert(*ptr);
14616a48599SMarkus Armbruster         switch (*ptr) {
14716a48599SMarkus Armbruster         case '\\':
148dc45a07cSMarkus Armbruster             beg = ptr++;
14900ea57faSMarkus Armbruster             switch (*ptr++) {
150a372823aSPaolo Bonzini             case '"':
15168af4cc1SMarkus Armbruster                 g_string_append_c(str, '"');
152a372823aSPaolo Bonzini                 break;
153a372823aSPaolo Bonzini             case '\'':
15468af4cc1SMarkus Armbruster                 g_string_append_c(str, '\'');
155a372823aSPaolo Bonzini                 break;
156a372823aSPaolo Bonzini             case '\\':
15768af4cc1SMarkus Armbruster                 g_string_append_c(str, '\\');
158a372823aSPaolo Bonzini                 break;
159a372823aSPaolo Bonzini             case '/':
16068af4cc1SMarkus Armbruster                 g_string_append_c(str, '/');
161a372823aSPaolo Bonzini                 break;
162a372823aSPaolo Bonzini             case 'b':
16368af4cc1SMarkus Armbruster                 g_string_append_c(str, '\b');
164a372823aSPaolo Bonzini                 break;
165a372823aSPaolo Bonzini             case 'f':
16668af4cc1SMarkus Armbruster                 g_string_append_c(str, '\f');
167a372823aSPaolo Bonzini                 break;
168a372823aSPaolo Bonzini             case 'n':
16968af4cc1SMarkus Armbruster                 g_string_append_c(str, '\n');
170a372823aSPaolo Bonzini                 break;
171a372823aSPaolo Bonzini             case 'r':
17268af4cc1SMarkus Armbruster                 g_string_append_c(str, '\r');
173a372823aSPaolo Bonzini                 break;
174a372823aSPaolo Bonzini             case 't':
17568af4cc1SMarkus Armbruster                 g_string_append_c(str, '\t');
176a372823aSPaolo Bonzini                 break;
177de6decfeSMarkus Armbruster             case 'u':
178dc45a07cSMarkus Armbruster                 cp = cvt4hex(ptr);
179dc45a07cSMarkus Armbruster                 ptr += 4;
180dc45a07cSMarkus Armbruster 
181dc45a07cSMarkus Armbruster                 /* handle surrogate pairs */
182dc45a07cSMarkus Armbruster                 if (cp >= 0xD800 && cp <= 0xDBFF
183dc45a07cSMarkus Armbruster                     && ptr[0] == '\\' && ptr[1] == 'u') {
184dc45a07cSMarkus Armbruster                     /* leading surrogate followed by \u */
185dc45a07cSMarkus Armbruster                     cp = 0x10000 + ((cp & 0x3FF) << 10);
186dc45a07cSMarkus Armbruster                     trailing = cvt4hex(ptr + 2);
187dc45a07cSMarkus Armbruster                     if (trailing >= 0xDC00 && trailing <= 0xDFFF) {
188dc45a07cSMarkus Armbruster                         /* followed by trailing surrogate */
189dc45a07cSMarkus Armbruster                         cp |= trailing & 0x3FF;
190dc45a07cSMarkus Armbruster                         ptr += 6;
191dc45a07cSMarkus Armbruster                     } else {
192dc45a07cSMarkus Armbruster                         cp = -1; /* invalid */
193a372823aSPaolo Bonzini                     }
194a372823aSPaolo Bonzini                 }
195a372823aSPaolo Bonzini 
19646a628b1SMarkus Armbruster                 if (mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp) < 0) {
19746a628b1SMarkus Armbruster                     parse_error(ctxt, token,
198dc45a07cSMarkus Armbruster                                 "%.*s is not a valid Unicode character",
199dc45a07cSMarkus Armbruster                                 (int)(ptr - beg), beg);
20046a628b1SMarkus Armbruster                     goto out;
20146a628b1SMarkus Armbruster                 }
20268af4cc1SMarkus Armbruster                 g_string_append(str, utf8_buf);
203de6decfeSMarkus Armbruster                 break;
204a372823aSPaolo Bonzini             default:
205a372823aSPaolo Bonzini                 parse_error(ctxt, token, "invalid escape sequence in string");
206a372823aSPaolo Bonzini                 goto out;
207a372823aSPaolo Bonzini             }
20816a48599SMarkus Armbruster             break;
20916a48599SMarkus Armbruster         case '%':
210bbc0586cSChristophe Fergeau             if (ctxt->ap) {
211bbc0586cSChristophe Fergeau                 if (ptr[1] != '%') {
21216a48599SMarkus Armbruster                     parse_error(ctxt, token, "can't interpolate into string");
21316a48599SMarkus Armbruster                     goto out;
21416a48599SMarkus Armbruster                 }
2158bca4613SMarkus Armbruster                 ptr++;
216bbc0586cSChristophe Fergeau             }
21716a48599SMarkus Armbruster             /* fall through */
21816a48599SMarkus Armbruster         default:
219e59f39d4SMarkus Armbruster             cp = mod_utf8_codepoint(ptr, 6, &end);
2204b1c0cd7SMarkus Armbruster             if (cp < 0) {
221e59f39d4SMarkus Armbruster                 parse_error(ctxt, token, "invalid UTF-8 sequence in string");
222e59f39d4SMarkus Armbruster                 goto out;
223e59f39d4SMarkus Armbruster             }
224e59f39d4SMarkus Armbruster             ptr = end;
225e59f39d4SMarkus Armbruster             len = mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp);
226e59f39d4SMarkus Armbruster             assert(len >= 0);
22768af4cc1SMarkus Armbruster             g_string_append(str, utf8_buf);
228a372823aSPaolo Bonzini         }
229a372823aSPaolo Bonzini     }
230a372823aSPaolo Bonzini 
23168af4cc1SMarkus Armbruster     return qstring_from_gstring(str);
232a372823aSPaolo Bonzini 
233a372823aSPaolo Bonzini out:
23468af4cc1SMarkus Armbruster     g_string_free(str, true);
235a372823aSPaolo Bonzini     return NULL;
236a372823aSPaolo Bonzini }
237a372823aSPaolo Bonzini 
2389bada897SPaolo Bonzini /* Note: the token object returned by parser_context_peek_token or
2399bada897SPaolo Bonzini  * parser_context_pop_token is deleted as soon as parser_context_pop_token
2409bada897SPaolo Bonzini  * is called again.
24195385fe9SPaolo Bonzini  */
parser_context_pop_token(JSONParserContext * ctxt)2429bada897SPaolo Bonzini static JSONToken *parser_context_pop_token(JSONParserContext *ctxt)
243a372823aSPaolo Bonzini {
2449bada897SPaolo Bonzini     g_free(ctxt->current);
24595385fe9SPaolo Bonzini     ctxt->current = g_queue_pop_head(ctxt->buf);
24695385fe9SPaolo Bonzini     return ctxt->current;
247a372823aSPaolo Bonzini }
248a372823aSPaolo Bonzini 
parser_context_peek_token(JSONParserContext * ctxt)2499bada897SPaolo Bonzini static JSONToken *parser_context_peek_token(JSONParserContext *ctxt)
250a372823aSPaolo Bonzini {
25195385fe9SPaolo Bonzini     return g_queue_peek_head(ctxt->buf);
252a372823aSPaolo Bonzini }
253a372823aSPaolo Bonzini 
254a372823aSPaolo Bonzini /**
255a372823aSPaolo Bonzini  * Parsing rules
256a372823aSPaolo Bonzini  */
parse_pair(JSONParserContext * ctxt,QDict * dict)257ada74c3bSMarkus Armbruster static int parse_pair(JSONParserContext *ctxt, QDict *dict)
258a372823aSPaolo Bonzini {
259922d42bbSAlex Chen     QObject *key_obj = NULL;
260922d42bbSAlex Chen     QString *key;
261532fb532SMax Reitz     QObject *value;
2629bada897SPaolo Bonzini     JSONToken *peek, *token;
263a372823aSPaolo Bonzini 
264a372823aSPaolo Bonzini     peek = parser_context_peek_token(ctxt);
265a372823aSPaolo Bonzini     if (peek == NULL) {
266a372823aSPaolo Bonzini         parse_error(ctxt, NULL, "premature EOI");
267a372823aSPaolo Bonzini         goto out;
268a372823aSPaolo Bonzini     }
269a372823aSPaolo Bonzini 
270922d42bbSAlex Chen     key_obj = parse_value(ctxt);
271922d42bbSAlex Chen     key = qobject_to(QString, key_obj);
272532fb532SMax Reitz     if (!key) {
273a372823aSPaolo Bonzini         parse_error(ctxt, peek, "key is not a string in object");
274a372823aSPaolo Bonzini         goto out;
275a372823aSPaolo Bonzini     }
276a372823aSPaolo Bonzini 
277a372823aSPaolo Bonzini     token = parser_context_pop_token(ctxt);
278a372823aSPaolo Bonzini     if (token == NULL) {
279a372823aSPaolo Bonzini         parse_error(ctxt, NULL, "premature EOI");
280a372823aSPaolo Bonzini         goto out;
281a372823aSPaolo Bonzini     }
282a372823aSPaolo Bonzini 
2839bada897SPaolo Bonzini     if (token->type != JSON_COLON) {
284a372823aSPaolo Bonzini         parse_error(ctxt, token, "missing : in object pair");
285a372823aSPaolo Bonzini         goto out;
286a372823aSPaolo Bonzini     }
287a372823aSPaolo Bonzini 
288ada74c3bSMarkus Armbruster     value = parse_value(ctxt);
289a372823aSPaolo Bonzini     if (value == NULL) {
290a372823aSPaolo Bonzini         parse_error(ctxt, token, "Missing value in dict");
291a372823aSPaolo Bonzini         goto out;
292a372823aSPaolo Bonzini     }
293a372823aSPaolo Bonzini 
29400382fa8SMarkus Armbruster     if (qdict_haskey(dict, qstring_get_str(key))) {
29500382fa8SMarkus Armbruster         parse_error(ctxt, token, "duplicate key");
29600382fa8SMarkus Armbruster         goto out;
29700382fa8SMarkus Armbruster     }
29800382fa8SMarkus Armbruster 
299532fb532SMax Reitz     qdict_put_obj(dict, qstring_get_str(key), value);
300a372823aSPaolo Bonzini 
301922d42bbSAlex Chen     qobject_unref(key_obj);
302a372823aSPaolo Bonzini     return 0;
303a372823aSPaolo Bonzini 
304a372823aSPaolo Bonzini out:
305922d42bbSAlex Chen     qobject_unref(key_obj);
306a372823aSPaolo Bonzini     return -1;
307a372823aSPaolo Bonzini }
308a372823aSPaolo Bonzini 
parse_object(JSONParserContext * ctxt)309ada74c3bSMarkus Armbruster static QObject *parse_object(JSONParserContext *ctxt)
310a372823aSPaolo Bonzini {
311a372823aSPaolo Bonzini     QDict *dict = NULL;
3129bada897SPaolo Bonzini     JSONToken *token, *peek;
313a372823aSPaolo Bonzini 
314a372823aSPaolo Bonzini     token = parser_context_pop_token(ctxt);
3159bada897SPaolo Bonzini     assert(token && token->type == JSON_LCURLY);
316a372823aSPaolo Bonzini 
317a372823aSPaolo Bonzini     dict = qdict_new();
318a372823aSPaolo Bonzini 
319a372823aSPaolo Bonzini     peek = parser_context_peek_token(ctxt);
320a372823aSPaolo Bonzini     if (peek == NULL) {
321a372823aSPaolo Bonzini         parse_error(ctxt, NULL, "premature EOI");
322a372823aSPaolo Bonzini         goto out;
323a372823aSPaolo Bonzini     }
324a372823aSPaolo Bonzini 
3259bada897SPaolo Bonzini     if (peek->type != JSON_RCURLY) {
326ada74c3bSMarkus Armbruster         if (parse_pair(ctxt, dict) == -1) {
327a372823aSPaolo Bonzini             goto out;
328a372823aSPaolo Bonzini         }
329a372823aSPaolo Bonzini 
330a372823aSPaolo Bonzini         token = parser_context_pop_token(ctxt);
331a372823aSPaolo Bonzini         if (token == NULL) {
332a372823aSPaolo Bonzini             parse_error(ctxt, NULL, "premature EOI");
333a372823aSPaolo Bonzini             goto out;
334a372823aSPaolo Bonzini         }
335a372823aSPaolo Bonzini 
3369bada897SPaolo Bonzini         while (token->type != JSON_RCURLY) {
3379bada897SPaolo Bonzini             if (token->type != JSON_COMMA) {
338a372823aSPaolo Bonzini                 parse_error(ctxt, token, "expected separator in dict");
339a372823aSPaolo Bonzini                 goto out;
340a372823aSPaolo Bonzini             }
341a372823aSPaolo Bonzini 
342ada74c3bSMarkus Armbruster             if (parse_pair(ctxt, dict) == -1) {
343a372823aSPaolo Bonzini                 goto out;
344a372823aSPaolo Bonzini             }
345a372823aSPaolo Bonzini 
346a372823aSPaolo Bonzini             token = parser_context_pop_token(ctxt);
347a372823aSPaolo Bonzini             if (token == NULL) {
348a372823aSPaolo Bonzini                 parse_error(ctxt, NULL, "premature EOI");
349a372823aSPaolo Bonzini                 goto out;
350a372823aSPaolo Bonzini             }
351a372823aSPaolo Bonzini         }
352a372823aSPaolo Bonzini     } else {
353a491af47SGonglei         (void)parser_context_pop_token(ctxt);
354a372823aSPaolo Bonzini     }
355a372823aSPaolo Bonzini 
356a372823aSPaolo Bonzini     return QOBJECT(dict);
357a372823aSPaolo Bonzini 
358a372823aSPaolo Bonzini out:
359cb3e7f08SMarc-André Lureau     qobject_unref(dict);
360a372823aSPaolo Bonzini     return NULL;
361a372823aSPaolo Bonzini }
362a372823aSPaolo Bonzini 
parse_array(JSONParserContext * ctxt)363ada74c3bSMarkus Armbruster static QObject *parse_array(JSONParserContext *ctxt)
364a372823aSPaolo Bonzini {
365a372823aSPaolo Bonzini     QList *list = NULL;
3669bada897SPaolo Bonzini     JSONToken *token, *peek;
367a372823aSPaolo Bonzini 
368a372823aSPaolo Bonzini     token = parser_context_pop_token(ctxt);
3699bada897SPaolo Bonzini     assert(token && token->type == JSON_LSQUARE);
370a372823aSPaolo Bonzini 
371a372823aSPaolo Bonzini     list = qlist_new();
372a372823aSPaolo Bonzini 
373a372823aSPaolo Bonzini     peek = parser_context_peek_token(ctxt);
374a372823aSPaolo Bonzini     if (peek == NULL) {
375a372823aSPaolo Bonzini         parse_error(ctxt, NULL, "premature EOI");
376a372823aSPaolo Bonzini         goto out;
377a372823aSPaolo Bonzini     }
378a372823aSPaolo Bonzini 
3799bada897SPaolo Bonzini     if (peek->type != JSON_RSQUARE) {
380a372823aSPaolo Bonzini         QObject *obj;
381a372823aSPaolo Bonzini 
382ada74c3bSMarkus Armbruster         obj = parse_value(ctxt);
383a372823aSPaolo Bonzini         if (obj == NULL) {
384a372823aSPaolo Bonzini             parse_error(ctxt, token, "expecting value");
385a372823aSPaolo Bonzini             goto out;
386a372823aSPaolo Bonzini         }
387a372823aSPaolo Bonzini 
388a372823aSPaolo Bonzini         qlist_append_obj(list, obj);
389a372823aSPaolo Bonzini 
390a372823aSPaolo Bonzini         token = parser_context_pop_token(ctxt);
391a372823aSPaolo Bonzini         if (token == NULL) {
392a372823aSPaolo Bonzini             parse_error(ctxt, NULL, "premature EOI");
393a372823aSPaolo Bonzini             goto out;
394a372823aSPaolo Bonzini         }
395a372823aSPaolo Bonzini 
3969bada897SPaolo Bonzini         while (token->type != JSON_RSQUARE) {
3979bada897SPaolo Bonzini             if (token->type != JSON_COMMA) {
398a372823aSPaolo Bonzini                 parse_error(ctxt, token, "expected separator in list");
399a372823aSPaolo Bonzini                 goto out;
400a372823aSPaolo Bonzini             }
401a372823aSPaolo Bonzini 
402ada74c3bSMarkus Armbruster             obj = parse_value(ctxt);
403a372823aSPaolo Bonzini             if (obj == NULL) {
404a372823aSPaolo Bonzini                 parse_error(ctxt, token, "expecting value");
405a372823aSPaolo Bonzini                 goto out;
406a372823aSPaolo Bonzini             }
407a372823aSPaolo Bonzini 
408a372823aSPaolo Bonzini             qlist_append_obj(list, obj);
409a372823aSPaolo Bonzini 
410a372823aSPaolo Bonzini             token = parser_context_pop_token(ctxt);
411a372823aSPaolo Bonzini             if (token == NULL) {
412a372823aSPaolo Bonzini                 parse_error(ctxt, NULL, "premature EOI");
413a372823aSPaolo Bonzini                 goto out;
414a372823aSPaolo Bonzini             }
415a372823aSPaolo Bonzini         }
416a372823aSPaolo Bonzini     } else {
417a491af47SGonglei         (void)parser_context_pop_token(ctxt);
418a372823aSPaolo Bonzini     }
419a372823aSPaolo Bonzini 
420a372823aSPaolo Bonzini     return QOBJECT(list);
421a372823aSPaolo Bonzini 
422a372823aSPaolo Bonzini out:
423cb3e7f08SMarc-André Lureau     qobject_unref(list);
424a372823aSPaolo Bonzini     return NULL;
425a372823aSPaolo Bonzini }
426a372823aSPaolo Bonzini 
parse_keyword(JSONParserContext * ctxt)427a372823aSPaolo Bonzini static QObject *parse_keyword(JSONParserContext *ctxt)
428a372823aSPaolo Bonzini {
4299bada897SPaolo Bonzini     JSONToken *token;
430a372823aSPaolo Bonzini 
431a372823aSPaolo Bonzini     token = parser_context_pop_token(ctxt);
4329bada897SPaolo Bonzini     assert(token && token->type == JSON_KEYWORD);
43350e2a467SMarkus Armbruster 
4349bada897SPaolo Bonzini     if (!strcmp(token->str, "true")) {
435d538b255SMarkus Armbruster         return QOBJECT(qbool_from_bool(true));
4369bada897SPaolo Bonzini     } else if (!strcmp(token->str, "false")) {
437d538b255SMarkus Armbruster         return QOBJECT(qbool_from_bool(false));
4389bada897SPaolo Bonzini     } else if (!strcmp(token->str, "null")) {
439006ca09fSMarkus Armbruster         return QOBJECT(qnull());
440a372823aSPaolo Bonzini     }
4419bada897SPaolo Bonzini     parse_error(ctxt, token, "invalid keyword '%s'", token->str);
442a372823aSPaolo Bonzini     return NULL;
443a372823aSPaolo Bonzini }
444a372823aSPaolo Bonzini 
parse_interpolation(JSONParserContext * ctxt)445ada74c3bSMarkus Armbruster static QObject *parse_interpolation(JSONParserContext *ctxt)
446a372823aSPaolo Bonzini {
4479bada897SPaolo Bonzini     JSONToken *token;
448a372823aSPaolo Bonzini 
449a372823aSPaolo Bonzini     token = parser_context_pop_token(ctxt);
45061030280SMarkus Armbruster     assert(token && token->type == JSON_INTERP);
4516b9606f6SMarkus Armbruster 
4529bada897SPaolo Bonzini     if (!strcmp(token->str, "%p")) {
453ada74c3bSMarkus Armbruster         return va_arg(*ctxt->ap, QObject *);
4549bada897SPaolo Bonzini     } else if (!strcmp(token->str, "%i")) {
455ada74c3bSMarkus Armbruster         return QOBJECT(qbool_from_bool(va_arg(*ctxt->ap, int)));
4569bada897SPaolo Bonzini     } else if (!strcmp(token->str, "%d")) {
457ada74c3bSMarkus Armbruster         return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int)));
4589bada897SPaolo Bonzini     } else if (!strcmp(token->str, "%ld")) {
459ada74c3bSMarkus Armbruster         return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long)));
46053a0d616SMarkus Armbruster     } else if (!strcmp(token->str, "%lld")) {
461ada74c3bSMarkus Armbruster         return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long long)));
46253a0d616SMarkus Armbruster     } else if (!strcmp(token->str, "%" PRId64)) {
463ada74c3bSMarkus Armbruster         return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int64_t)));
4642bc7cfeaSMarc-André Lureau     } else if (!strcmp(token->str, "%u")) {
465ada74c3bSMarkus Armbruster         return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned int)));
4662bc7cfeaSMarc-André Lureau     } else if (!strcmp(token->str, "%lu")) {
467ada74c3bSMarkus Armbruster         return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long)));
46853a0d616SMarkus Armbruster     } else if (!strcmp(token->str, "%llu")) {
469ada74c3bSMarkus Armbruster         return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long long)));
47053a0d616SMarkus Armbruster     } else if (!strcmp(token->str, "%" PRIu64)) {
471ada74c3bSMarkus Armbruster         return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, uint64_t)));
4729bada897SPaolo Bonzini     } else if (!strcmp(token->str, "%s")) {
473ada74c3bSMarkus Armbruster         return QOBJECT(qstring_from_str(va_arg(*ctxt->ap, const char *)));
4749bada897SPaolo Bonzini     } else if (!strcmp(token->str, "%f")) {
475ada74c3bSMarkus Armbruster         return QOBJECT(qnum_from_double(va_arg(*ctxt->ap, double)));
476a372823aSPaolo Bonzini     }
477f7617d45SMarkus Armbruster     parse_error(ctxt, token, "invalid interpolation '%s'", token->str);
478a372823aSPaolo Bonzini     return NULL;
479a372823aSPaolo Bonzini }
480a372823aSPaolo Bonzini 
parse_literal(JSONParserContext * ctxt)481a372823aSPaolo Bonzini static QObject *parse_literal(JSONParserContext *ctxt)
482a372823aSPaolo Bonzini {
4839bada897SPaolo Bonzini     JSONToken *token;
484a372823aSPaolo Bonzini 
485a372823aSPaolo Bonzini     token = parser_context_pop_token(ctxt);
486d538b255SMarkus Armbruster     assert(token);
487a372823aSPaolo Bonzini 
4889bada897SPaolo Bonzini     switch (token->type) {
489a372823aSPaolo Bonzini     case JSON_STRING:
490b2da4a4dSMarkus Armbruster         return QOBJECT(parse_string(ctxt, token));
4913d5b3ec6SMichael Roth     case JSON_INTEGER: {
49201b2ffceSMarc-André Lureau         /*
49301b2ffceSMarc-André Lureau          * Represent JSON_INTEGER as QNUM_I64 if possible, else as
4942bc7cfeaSMarc-André Lureau          * QNUM_U64, else as QNUM_DOUBLE.  Note that qemu_strtoi64()
4952bc7cfeaSMarc-André Lureau          * and qemu_strtou64() fail with ERANGE when it's not
4962bc7cfeaSMarc-André Lureau          * possible.
4973d5b3ec6SMichael Roth          *
49801b2ffceSMarc-André Lureau          * qnum_get_int() will then work for any signed 64-bit
4992bc7cfeaSMarc-André Lureau          * JSON_INTEGER, qnum_get_uint() for any unsigned 64-bit
5002bc7cfeaSMarc-André Lureau          * integer, and qnum_get_double() both for any JSON_INTEGER
5012bc7cfeaSMarc-André Lureau          * and any JSON_FLOAT (with precision loss for integers beyond
5022bc7cfeaSMarc-André Lureau          * 53 bits)
5033d5b3ec6SMichael Roth          */
5042bc7cfeaSMarc-André Lureau         int ret;
5053d5b3ec6SMichael Roth         int64_t value;
5062bc7cfeaSMarc-André Lureau         uint64_t uvalue;
5073d5b3ec6SMichael Roth 
5082bc7cfeaSMarc-André Lureau         ret = qemu_strtoi64(token->str, NULL, 10, &value);
5092bc7cfeaSMarc-André Lureau         if (!ret) {
51001b2ffceSMarc-André Lureau             return QOBJECT(qnum_from_int(value));
5113d5b3ec6SMichael Roth         }
5122bc7cfeaSMarc-André Lureau         assert(ret == -ERANGE);
5132bc7cfeaSMarc-André Lureau 
5142bc7cfeaSMarc-André Lureau         if (token->str[0] != '-') {
5152bc7cfeaSMarc-André Lureau             ret = qemu_strtou64(token->str, NULL, 10, &uvalue);
5162bc7cfeaSMarc-André Lureau             if (!ret) {
5172bc7cfeaSMarc-André Lureau                 return QOBJECT(qnum_from_uint(uvalue));
5182bc7cfeaSMarc-André Lureau             }
5192bc7cfeaSMarc-André Lureau             assert(ret == -ERANGE);
5202bc7cfeaSMarc-André Lureau         }
5213d5b3ec6SMichael Roth     }
5226f0dd6c5SPhilippe Mathieu-Daudé     /* fall through to JSON_FLOAT */
523a372823aSPaolo Bonzini     case JSON_FLOAT:
5246e8e5cb9SEric Blake         /* FIXME dependent on locale; a pervasive issue in QEMU */
52537aded92SMarkus Armbruster         /* FIXME our lexer matches RFC 8259 in forbidding Inf or NaN,
5266e8e5cb9SEric Blake          * but those might be useful extensions beyond JSON */
52701b2ffceSMarc-André Lureau         return QOBJECT(qnum_from_double(strtod(token->str, NULL)));
528a372823aSPaolo Bonzini     default:
529d538b255SMarkus Armbruster         abort();
530a372823aSPaolo Bonzini     }
531a372823aSPaolo Bonzini }
532a372823aSPaolo Bonzini 
parse_value(JSONParserContext * ctxt)533ada74c3bSMarkus Armbruster static QObject *parse_value(JSONParserContext *ctxt)
534a372823aSPaolo Bonzini {
5359bada897SPaolo Bonzini     JSONToken *token;
536a372823aSPaolo Bonzini 
537d538b255SMarkus Armbruster     token = parser_context_peek_token(ctxt);
538d538b255SMarkus Armbruster     if (token == NULL) {
539d538b255SMarkus Armbruster         parse_error(ctxt, NULL, "premature EOI");
540d538b255SMarkus Armbruster         return NULL;
541a372823aSPaolo Bonzini     }
542a372823aSPaolo Bonzini 
5439bada897SPaolo Bonzini     switch (token->type) {
544d538b255SMarkus Armbruster     case JSON_LCURLY:
545ada74c3bSMarkus Armbruster         return parse_object(ctxt);
546d538b255SMarkus Armbruster     case JSON_LSQUARE:
547ada74c3bSMarkus Armbruster         return parse_array(ctxt);
54861030280SMarkus Armbruster     case JSON_INTERP:
549ada74c3bSMarkus Armbruster         return parse_interpolation(ctxt);
550d538b255SMarkus Armbruster     case JSON_INTEGER:
551d538b255SMarkus Armbruster     case JSON_FLOAT:
552d538b255SMarkus Armbruster     case JSON_STRING:
553d538b255SMarkus Armbruster         return parse_literal(ctxt);
554d538b255SMarkus Armbruster     case JSON_KEYWORD:
555d538b255SMarkus Armbruster         return parse_keyword(ctxt);
556d538b255SMarkus Armbruster     default:
557d538b255SMarkus Armbruster         parse_error(ctxt, token, "expecting value");
558d538b255SMarkus Armbruster         return NULL;
559d538b255SMarkus Armbruster     }
560a372823aSPaolo Bonzini }
561a372823aSPaolo Bonzini 
json_token(JSONTokenType type,int x,int y,GString * tokstr)562abe7c206SMarkus Armbruster JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr)
563abe7c206SMarkus Armbruster {
564abe7c206SMarkus Armbruster     JSONToken *token = g_malloc(sizeof(JSONToken) + tokstr->len + 1);
565abe7c206SMarkus Armbruster 
566abe7c206SMarkus Armbruster     token->type = type;
567abe7c206SMarkus Armbruster     memcpy(token->str, tokstr->str, tokstr->len);
568abe7c206SMarkus Armbruster     token->str[tokstr->len] = 0;
569abe7c206SMarkus Armbruster     token->x = x;
570abe7c206SMarkus Armbruster     token->y = y;
571abe7c206SMarkus Armbruster     return token;
572abe7c206SMarkus Armbruster }
573abe7c206SMarkus Armbruster 
json_parser_parse(GQueue * tokens,va_list * ap,Error ** errp)57462815d85SMarkus Armbruster QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp)
575a372823aSPaolo Bonzini {
576ada74c3bSMarkus Armbruster     JSONParserContext ctxt = { .buf = tokens, .ap = ap };
577a372823aSPaolo Bonzini     QObject *result;
578a372823aSPaolo Bonzini 
579ada74c3bSMarkus Armbruster     result = parse_value(&ctxt);
5805d50113cSMarkus Armbruster     assert(ctxt.err || g_queue_is_empty(ctxt.buf));
581a372823aSPaolo Bonzini 
582e8b19d7dSMarc-André Lureau     error_propagate(errp, ctxt.err);
583a372823aSPaolo Bonzini 
584e8b19d7dSMarc-André Lureau     while (!g_queue_is_empty(ctxt.buf)) {
585e8b19d7dSMarc-André Lureau         parser_context_pop_token(&ctxt);
586e8b19d7dSMarc-André Lureau     }
587e8b19d7dSMarc-André Lureau     g_free(ctxt.current);
588a372823aSPaolo Bonzini 
589a372823aSPaolo Bonzini     return result;
590a372823aSPaolo Bonzini }
591