return x.kind;
}
-static const jv JV_INVALID = {JV_KIND_INVALID, {0}};
static const jv JV_NULL = {JV_KIND_NULL, {0}};
static const jv JV_FALSE = {JV_KIND_FALSE, {0}};
static const jv JV_TRUE = {JV_KIND_TRUE, {0}};
-jv jv_invalid() {
- return JV_INVALID;
-}
-
jv jv_true() {
return JV_TRUE;
}
return x ? JV_TRUE : JV_FALSE;
}
+/*
+ * Invalid objects, with optional error messages
+ */
+
+typedef struct {
+ jv_refcnt refcnt;
+ jv errmsg;
+} jvp_invalid;
+
+jv jv_invalid_with_msg(jv err) {
+ jv x;
+ x.kind = JV_KIND_INVALID;
+ x.val.complex.i[0] = x.val.complex.i[1] = 0;
+ jvp_invalid* i = malloc(sizeof(jvp_invalid));
+ x.val.complex.ptr = &i->refcnt;
+ i->refcnt.count = 1;
+ i->errmsg = err;
+ return x;
+}
+
+jv jv_invalid() {
+ return jv_invalid_with_msg(jv_null());
+}
+
+jv jv_invalid_get_message(jv inv) {
+ jv x = ((jvp_invalid*)inv.val.complex.ptr)->errmsg;
+ jv_free(inv);
+ return x;
+}
+
+static void jvp_invalid_free(jv_complex* x) {
+ if (jvp_refcnt_dec(x)) {
+ jv_free(((jvp_invalid*)x->ptr)->errmsg);
+ free(x->ptr);
+ }
+}
+
/*
* Numbers
*/
jv jv_copy(jv j) {
if (jv_get_kind(j) == JV_KIND_ARRAY ||
jv_get_kind(j) == JV_KIND_STRING ||
- jv_get_kind(j) == JV_KIND_OBJECT) {
+ jv_get_kind(j) == JV_KIND_OBJECT ||
+ jv_get_kind(j) == JV_KIND_INVALID) {
jvp_refcnt_inc(&j.val.complex);
}
return j;
jvp_string_free(&j.val.complex);
} else if (jv_get_kind(j) == JV_KIND_OBJECT) {
jvp_object_free(&j.val.complex);
+ } else if (jv_get_kind(j) == JV_KIND_INVALID) {
+ jvp_invalid_free(&j.val.complex);
}
}
void jv_parser_init(struct jv_parser* p) {
p->stack = 0;
p->stacklen = p->stackpos = 0;
- p->hasnext = 0;
- p->next = jv_invalid(); //FIXME: jv_invalid
+ p->next = jv_invalid();
p->tokenbuf = 0;
p->tokenlen = p->tokenpos = 0;
p->st = JV_PARSER_NORMAL;
}
void jv_parser_free(struct jv_parser* p) {
- if (p->hasnext) jv_free(p->next);
+ jv_free(p->next);
free(p->stack);
free(p->tokenbuf);
jvp_dtoa_context_free(&p->dtoa);
}
static pfunc value(struct jv_parser* p, jv val) {
- if (p->hasnext) return "Expected separator between values";
- p->hasnext = 1;
+ if (jv_is_valid(p->next)) return "Expected separator between values";
+ jv_free(p->next);
p->next = val;
return 0;
}
static pfunc token(struct jv_parser* p, char ch) {
switch (ch) {
case '[':
- if (p->hasnext) return "Expected separator between values";
+ if (jv_is_valid(p->next)) return "Expected separator between values";
push(p, jv_array());
break;
case '{':
- if (p->hasnext) return "Expected separator between values";
+ if (jv_is_valid(p->next)) return "Expected separator between values";
push(p, jv_object());
break;
case ':':
- if (!p->hasnext)
+ if (!jv_is_valid(p->next))
return "Expected string key before ':'";
if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)
return "':' not as part of an object";
if (jv_get_kind(p->next) != JV_KIND_STRING)
return "Object keys must be strings";
push(p, p->next);
- p->hasnext = 0;
+ p->next = jv_invalid();
break;
case ',':
- if (!p->hasnext)
+ if (!jv_is_valid(p->next))
return "Expected value before ','";
if (p->stackpos == 0)
return "',' not as part of an object or array";
if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_ARRAY) {
p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);
- p->hasnext = 0;
+ p->next = jv_invalid();
} else if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_STRING) {
assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
p->stack[p->stackpos-1], p->next);
p->stackpos--;
- p->hasnext = 0;
+ p->next = jv_invalid();
} else {
// this case hits on input like {"a", "b"}
return "Objects must consist of key:value pairs";
case ']':
if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_ARRAY)
return "Unmatched ']'";
- if (p->hasnext) {
+ if (jv_is_valid(p->next)) {
p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);
- p->hasnext = 0;
+ p->next = jv_invalid();
} else {
if (jv_array_length(jv_copy(p->stack[p->stackpos-1])) != 0) {
// this case hits on input like [1,2,3,]
return "Expected another array element";
}
}
- p->hasnext = 1;
+ jv_free(p->next);
p->next = p->stack[--p->stackpos];
break;
case '}':
if (p->stackpos == 0)
return "Unmatched '}'";
- if (p->hasnext) {
+ if (jv_is_valid(p->next)) {
if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_STRING)
return "Objects must consist of key:value pairs";
assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
p->stack[p->stackpos-1], p->next);
p->stackpos--;
- p->hasnext = 0;
+ p->next = jv_invalid();
} else {
if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)
return "Unmatched '}'";
if (jv_object_length(jv_copy(p->stack[p->stackpos-1])) != 0)
return "Expected another key-value pair";
}
- p->hasnext = 1;
+ jv_free(p->next);
p->next = p->stack[--p->stackpos];
break;
}
return "Unfinished JSON term";
// this will happen on the empty string
- if (!p->hasnext)
+ if (!jv_is_valid(p->next))
return "Expected JSON value";
return 0;