Make jv_invalid() first-class values capable of holding an error.

This commit is contained in:
Stephen Dolan committed 2012-09-10 15:04:19 +01:00
1 parent 5c25c90657
commit 649cac791a
6 files changed
+70 -30

No files matched your search

+1
View File
@@ -466,6 +466,7 @@ void run_program(struct bytecode* bc) {
jv_dump(result); jv_dump(result);
printf("\n"); printf("\n");
} }
jv_free(result);
#if JQ_DEBUG #if JQ_DEBUG
printf("end of results\n"); printf("end of results\n");
#endif #endif
+41 -6
View File
@@ -37,15 +37,10 @@ jv_kind jv_get_kind(jv x) {
return x.kind; 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_NULL = {JV_KIND_NULL, {0}};
static const jv JV_FALSE = {JV_KIND_FALSE, {0}}; static const jv JV_FALSE = {JV_KIND_FALSE, {0}};
static const jv JV_TRUE = {JV_KIND_TRUE, {0}}; static const jv JV_TRUE = {JV_KIND_TRUE, {0}};
jv jv_invalid() {
return JV_INVALID;
}
jv jv_true() { jv jv_true() {
return JV_TRUE; return JV_TRUE;
} }
@@ -62,6 +57,43 @@ jv jv_bool(int x) {
return x ? JV_TRUE : JV_FALSE; 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 * Numbers
*/ */
@@ -789,7 +821,8 @@ jv jv_object_iter_value(jv object, int iter) {
jv jv_copy(jv j) { jv jv_copy(jv j) {
if (jv_get_kind(j) == JV_KIND_ARRAY || if (jv_get_kind(j) == JV_KIND_ARRAY ||
jv_get_kind(j) == JV_KIND_STRING || 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); jvp_refcnt_inc(&j.val.complex);
} }
return j; return j;
@@ -802,6 +835,8 @@ void jv_free(jv j) {
jvp_string_free(&j.val.complex); jvp_string_free(&j.val.complex);
} else if (jv_get_kind(j) == JV_KIND_OBJECT) { } else if (jv_get_kind(j) == JV_KIND_OBJECT) {
jvp_object_free(&j.val.complex); jvp_object_free(&j.val.complex);
} else if (jv_get_kind(j) == JV_KIND_INVALID) {
jvp_invalid_free(&j.val.complex);
} }
} }
+7 -4
View File
@@ -14,8 +14,8 @@ typedef enum {
JV_KIND_TRUE, JV_KIND_TRUE,
JV_KIND_NUMBER, JV_KIND_NUMBER,
JV_KIND_STRING, JV_KIND_STRING,
JV_KIND_OBJECT, JV_KIND_ARRAY,
JV_KIND_ARRAY JV_KIND_OBJECT
} jv_kind; } jv_kind;
typedef struct { typedef struct {
@@ -49,6 +49,9 @@ void jv_free(jv);
int jv_equal(jv, jv); int jv_equal(jv, jv);
jv jv_invalid(); jv jv_invalid();
jv jv_invalid_with_msg(jv);
jv jv_invalid_get_msg(jv);
jv jv_null(); jv jv_null();
jv jv_true(); jv jv_true();
jv jv_false(); jv jv_false();
@@ -99,19 +102,19 @@ jv jv_parse(const char* string);
jv jv_parse_sized(const char* string, int length); jv jv_parse_sized(const char* string, int length);
static jv jv_lookup(jv t, jv k) { static jv jv_lookup(jv t, jv k) {
jv v; jv v;
if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) {
v = jv_object_get(t, k); v = jv_object_get(t, k);
if (!jv_is_valid(v)) { if (!jv_is_valid(v)) {
jv_free(v);
v = jv_null(); v = jv_null();
} }
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) { } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) {
// FIXME: don't do lookup for noninteger index // FIXME: don't do lookup for noninteger index
v = jv_array_get(t, (int)jv_number_value(k)); v = jv_array_get(t, (int)jv_number_value(k));
if (!jv_is_valid(v)) { if (!jv_is_valid(v)) {
jv_free(v);
v = jv_null(); v = jv_null();
} }
} else if (jv_get_kind(t) == JV_KIND_NULL && } else if (jv_get_kind(t) == JV_KIND_NULL &&
+18 -19
View File
@@ -18,8 +18,7 @@ typedef const char* presult;
void jv_parser_init(struct jv_parser* p) { void jv_parser_init(struct jv_parser* p) {
p->stack = 0; p->stack = 0;
p->stacklen = p->stackpos = 0; p->stacklen = p->stackpos = 0;
p->hasnext = 0; p->next = jv_invalid();
p->next = jv_invalid(); //FIXME: jv_invalid
p->tokenbuf = 0; p->tokenbuf = 0;
p->tokenlen = p->tokenpos = 0; p->tokenlen = p->tokenpos = 0;
p->st = JV_PARSER_NORMAL; p->st = JV_PARSER_NORMAL;
@@ -27,15 +26,15 @@ void jv_parser_init(struct jv_parser* p) {
} }
void jv_parser_free(struct jv_parser* p) { void jv_parser_free(struct jv_parser* p) {
if (p->hasnext) jv_free(p->next); jv_free(p->next);
free(p->stack); free(p->stack);
free(p->tokenbuf); free(p->tokenbuf);
jvp_dtoa_context_free(&p->dtoa); jvp_dtoa_context_free(&p->dtoa);
} }
static pfunc value(struct jv_parser* p, jv val) { static pfunc value(struct jv_parser* p, jv val) {
if (p->hasnext) return "Expected separator between values"; if (jv_is_valid(p->next)) return "Expected separator between values";
p->hasnext = 1; jv_free(p->next);
p->next = val; p->next = val;
return 0; return 0;
} }
@@ -53,40 +52,40 @@ static void push(struct jv_parser* p, jv v) {
static pfunc token(struct jv_parser* p, char ch) { static pfunc token(struct jv_parser* p, char ch) {
switch (ch) { switch (ch) {
case '[': case '[':
if (p->hasnext) return "Expected separator between values"; if (jv_is_valid(p->next)) return "Expected separator between values";
push(p, jv_array()); push(p, jv_array());
break; break;
case '{': case '{':
if (p->hasnext) return "Expected separator between values"; if (jv_is_valid(p->next)) return "Expected separator between values";
push(p, jv_object()); push(p, jv_object());
break; break;
case ':': case ':':
if (!p->hasnext) if (!jv_is_valid(p->next))
return "Expected string key before ':'"; return "Expected string key before ':'";
if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT) if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)
return "':' not as part of an object"; return "':' not as part of an object";
if (jv_get_kind(p->next) != JV_KIND_STRING) if (jv_get_kind(p->next) != JV_KIND_STRING)
return "Object keys must be strings"; return "Object keys must be strings";
push(p, p->next); push(p, p->next);
p->hasnext = 0; p->next = jv_invalid();
break; break;
case ',': case ',':
if (!p->hasnext) if (!jv_is_valid(p->next))
return "Expected value before ','"; return "Expected value before ','";
if (p->stackpos == 0) if (p->stackpos == 0)
return "',' not as part of an object or array"; return "',' not as part of an object or array";
if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_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->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) { } 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); 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-2] = jv_object_set(p->stack[p->stackpos-2],
p->stack[p->stackpos-1], p->next); p->stack[p->stackpos-1], p->next);
p->stackpos--; p->stackpos--;
p->hasnext = 0; p->next = jv_invalid();
} else { } else {
// this case hits on input like {"a", "b"} // this case hits on input like {"a", "b"}
return "Objects must consist of key:value pairs"; return "Objects must consist of key:value pairs";
@@ -96,37 +95,37 @@ static pfunc token(struct jv_parser* p, char ch) {
case ']': case ']':
if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_ARRAY) if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_ARRAY)
return "Unmatched ']'"; 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->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);
p->hasnext = 0; p->next = jv_invalid();
} else { } else {
if (jv_array_length(jv_copy(p->stack[p->stackpos-1])) != 0) { if (jv_array_length(jv_copy(p->stack[p->stackpos-1])) != 0) {
// this case hits on input like [1,2,3,] // this case hits on input like [1,2,3,]
return "Expected another array element"; return "Expected another array element";
} }
} }
p->hasnext = 1; jv_free(p->next);
p->next = p->stack[--p->stackpos]; p->next = p->stack[--p->stackpos];
break; break;
case '}': case '}':
if (p->stackpos == 0) if (p->stackpos == 0)
return "Unmatched '}'"; return "Unmatched '}'";
if (p->hasnext) { if (jv_is_valid(p->next)) {
if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_STRING) if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_STRING)
return "Objects must consist of key:value pairs"; return "Objects must consist of key:value pairs";
assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT); 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-2] = jv_object_set(p->stack[p->stackpos-2],
p->stack[p->stackpos-1], p->next); p->stack[p->stackpos-1], p->next);
p->stackpos--; p->stackpos--;
p->hasnext = 0; p->next = jv_invalid();
} else { } else {
if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT) if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)
return "Unmatched '}'"; return "Unmatched '}'";
if (jv_object_length(jv_copy(p->stack[p->stackpos-1])) != 0) if (jv_object_length(jv_copy(p->stack[p->stackpos-1])) != 0)
return "Expected another key-value pair"; return "Expected another key-value pair";
} }
p->hasnext = 1; jv_free(p->next);
p->next = p->stack[--p->stackpos]; p->next = p->stack[--p->stackpos];
break; break;
} }
@@ -320,7 +319,7 @@ static pfunc finish(struct jv_parser* p) {
return "Unfinished JSON term"; return "Unfinished JSON term";
// this will happen on the empty string // this will happen on the empty string
if (!p->hasnext) if (!jv_is_valid(p->next))
return "Expected JSON value"; return "Expected JSON value";
return 0; return 0;
-1
View File
@@ -4,7 +4,6 @@ struct jv_parser {
int stackpos; int stackpos;
int stacklen; int stacklen;
jv next; jv next;
int hasnext;
char* tokenbuf; char* tokenbuf;
int tokenpos; int tokenpos;
+3
View File
@@ -49,6 +49,7 @@ void run_tests() {
assert(jv_is_valid(expected)); assert(jv_is_valid(expected));
jv actual = jq_next(); jv actual = jq_next();
if (!jv_is_valid(actual)) { if (!jv_is_valid(actual)) {
jv_free(actual);
printf("Insufficient results\n"); printf("Insufficient results\n");
pass = 0; pass = 0;
break; break;
@@ -70,6 +71,8 @@ void run_tests() {
jv_dump(extra); jv_dump(extra);
printf("\n"); printf("\n");
pass = 0; pass = 0;
} else {
jv_free(extra);
} }
} }
jq_teardown(); jq_teardown();