Make jv_invalid() first-class values capable of holding an error.
This commit is contained in:
1 parent
5c25c90657
commit
649cac791a
6 files changed
+70
-30
No files matched your search
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in new issue
Block a user