*
*/
-#include <stdbool.h>
+#include <stdarg.h>
#include "qemu-common.h"
#include "qstring.h"
#include "qbool.h"
#include "json-parser.h"
#include "json-lexer.h"
+#include "qerror.h"
typedef struct JSONParserContext
{
+ Error *err;
} JSONParserContext;
#define BUG_ON(cond) assert(!(cond))
/**
* Error handler
*/
-static void parse_error(JSONParserContext *ctxt, QObject *token, const char *msg, ...)
+static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt,
+ QObject *token, const char *msg, ...)
{
- fprintf(stderr, "parse error: %s\n", msg);
+ va_list ap;
+ char message[1024];
+ va_start(ap, msg);
+ vsnprintf(message, sizeof(message), msg, ap);
+ va_end(ap);
+ if (ctxt->err) {
+ error_free(ctxt->err);
+ ctxt->err = NULL;
+ }
+ error_set(&ctxt->err, QERR_JSON_PARSE_ERROR, message);
}
/**
qstring_append(str, "\b");
ptr++;
break;
+ case 'f':
+ qstring_append(str, "\f");
+ ptr++;
+ break;
case 'n':
qstring_append(str, "\n");
ptr++;
*/
static int parse_pair(JSONParserContext *ctxt, QDict *dict, QList **tokens, va_list *ap)
{
- QObject *key, *token = NULL, *value, *peek;
+ QObject *key = NULL, *token = NULL, *value, *peek;
QList *working = qlist_copy(*tokens);
peek = qlist_peek(working);
+ if (peek == NULL) {
+ parse_error(ctxt, NULL, "premature EOI");
+ goto out;
+ }
+
key = parse_value(ctxt, &working, ap);
- if (qobject_type(key) != QTYPE_QSTRING) {
+ if (!key || qobject_type(key) != QTYPE_QSTRING) {
parse_error(ctxt, peek, "key is not a string in object");
goto out;
}
token = qlist_pop(working);
+ if (token == NULL) {
+ parse_error(ctxt, NULL, "premature EOI");
+ goto out;
+ }
+
if (!token_is_operator(token, ':')) {
parse_error(ctxt, token, "missing : in object pair");
goto out;
QList *working = qlist_copy(*tokens);
token = qlist_pop(working);
+ if (token == NULL) {
+ goto out;
+ }
+
if (!token_is_operator(token, '{')) {
goto out;
}
dict = qdict_new();
peek = qlist_peek(working);
+ if (peek == NULL) {
+ parse_error(ctxt, NULL, "premature EOI");
+ goto out;
+ }
+
if (!token_is_operator(peek, '}')) {
if (parse_pair(ctxt, dict, &working, ap) == -1) {
goto out;
}
token = qlist_pop(working);
+ if (token == NULL) {
+ parse_error(ctxt, NULL, "premature EOI");
+ goto out;
+ }
+
while (!token_is_operator(token, '}')) {
if (!token_is_operator(token, ',')) {
parse_error(ctxt, token, "expected separator in dict");
}
token = qlist_pop(working);
+ if (token == NULL) {
+ parse_error(ctxt, NULL, "premature EOI");
+ goto out;
+ }
}
qobject_decref(token);
token = NULL;
QList *working = qlist_copy(*tokens);
token = qlist_pop(working);
+ if (token == NULL) {
+ goto out;
+ }
+
if (!token_is_operator(token, '[')) {
goto out;
}
list = qlist_new();
peek = qlist_peek(working);
+ if (peek == NULL) {
+ parse_error(ctxt, NULL, "premature EOI");
+ goto out;
+ }
+
if (!token_is_operator(peek, ']')) {
QObject *obj;
qlist_append_obj(list, obj);
token = qlist_pop(working);
+ if (token == NULL) {
+ parse_error(ctxt, NULL, "premature EOI");
+ goto out;
+ }
+
while (!token_is_operator(token, ']')) {
if (!token_is_operator(token, ',')) {
parse_error(ctxt, token, "expected separator in list");
qlist_append_obj(list, obj);
token = qlist_pop(working);
+ if (token == NULL) {
+ parse_error(ctxt, NULL, "premature EOI");
+ goto out;
+ }
}
qobject_decref(token);
QList *working = qlist_copy(*tokens);
token = qlist_pop(working);
+ if (token == NULL) {
+ goto out;
+ }
if (token_get_type(token) != JSON_KEYWORD) {
goto out;
}
token = qlist_pop(working);
+ if (token == NULL) {
+ goto out;
+ }
if (token_is_escape(token, "%p")) {
obj = va_arg(*ap, QObject *);
QList *working = qlist_copy(*tokens);
token = qlist_pop(working);
+ if (token == NULL) {
+ goto out;
+ }
+
switch (token_get_type(token)) {
case JSON_STRING:
obj = QOBJECT(qstring_from_escaped_str(ctxt, token));
}
QObject *json_parser_parse(QList *tokens, va_list *ap)
+{
+ return json_parser_parse_err(tokens, ap, NULL);
+}
+
+QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp)
{
JSONParserContext ctxt = {};
- QList *working = qlist_copy(tokens);
+ QList *working;
QObject *result;
+ if (!tokens) {
+ return NULL;
+ }
+ working = qlist_copy(tokens);
result = parse_value(&ctxt, &working, ap);
QDECREF(working);
+ error_propagate(errp, ctxt.err);
+
return result;
}