]> granicus.if.org Git - jq/commitdiff
Make jv_invalid() first-class values capable of holding an error.
authorStephen Dolan <mu@netsoc.tcd.ie>
Mon, 10 Sep 2012 14:04:19 +0000 (15:04 +0100)
committerStephen Dolan <mu@netsoc.tcd.ie>
Mon, 10 Sep 2012 14:04:19 +0000 (15:04 +0100)
c/execute.c
c/jv.c
c/jv.h
c/jv_parse.c
c/jv_parse.h
c/main.c

index 62984ca4b28245ed8f5483877976493205c9ff07..c8505bf6cbe34011e46f9e9313d53d3a52dc5697 100644 (file)
@@ -466,6 +466,7 @@ void run_program(struct bytecode* bc) {
     jv_dump(result);
     printf("\n");
   }
+  jv_free(result);
   #if JQ_DEBUG
   printf("end of results\n");
   #endif
diff --git a/c/jv.c b/c/jv.c
index 35ade78961950e7dbd252f7ffd805e53080d4dcd..be50c6f6c64ac7f5df763e2efc7d56a8c3c5d1e6 100644 (file)
--- a/c/jv.c
+++ b/c/jv.c
@@ -37,15 +37,10 @@ jv_kind jv_get_kind(jv x) {
   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;
 }
@@ -62,6 +57,43 @@ jv jv_bool(int x) {
   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
  */
@@ -789,7 +821,8 @@ jv jv_object_iter_value(jv object, int iter) {
 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;
@@ -802,6 +835,8 @@ void jv_free(jv 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);
   }
 }
 
diff --git a/c/jv.h b/c/jv.h
index 2ec1862986f95a77854652d968ae5e39cfbe96c9..a3a5e79496323c12e8af6f6104193208157d690f 100644 (file)
--- a/c/jv.h
+++ b/c/jv.h
@@ -14,8 +14,8 @@ typedef enum {
   JV_KIND_TRUE,
   JV_KIND_NUMBER,
   JV_KIND_STRING,
-  JV_KIND_OBJECT,
-  JV_KIND_ARRAY
+  JV_KIND_ARRAY,
+  JV_KIND_OBJECT
 } jv_kind;
 
 typedef struct {
@@ -49,6 +49,9 @@ void jv_free(jv);
 int jv_equal(jv, jv);
 
 jv jv_invalid();
+jv jv_invalid_with_msg(jv);
+jv jv_invalid_get_msg(jv);
+
 jv jv_null();
 jv jv_true();
 jv jv_false();
@@ -99,19 +102,19 @@ jv jv_parse(const char* string);
 jv jv_parse_sized(const char* string, int length);
 
 
-
-
 static jv jv_lookup(jv t, jv k) {
   jv v;
   if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) {
     v = jv_object_get(t, k);
     if (!jv_is_valid(v)) {
+      jv_free(v);
       v = jv_null();
     }
   } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) {
     // FIXME: don't do lookup for noninteger index
     v = jv_array_get(t, (int)jv_number_value(k));
     if (!jv_is_valid(v)) {
+      jv_free(v);
       v = jv_null();
     }
   } else if (jv_get_kind(t) == JV_KIND_NULL && 
index 7a8c811e48d30a5495a198caf3d69721e3c34de5..9300f311fea4b4ba09aa25f70ebf5614ebbc77ef 100644 (file)
@@ -18,8 +18,7 @@ typedef const char* presult;
 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;
@@ -27,15 +26,15 @@ void jv_parser_init(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->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;
 }
@@ -53,40 +52,40 @@ static void push(struct jv_parser* p, jv v) {
 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";
@@ -96,37 +95,37 @@ static pfunc token(struct jv_parser* p, char ch) {
   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;
   }
@@ -320,7 +319,7 @@ static pfunc finish(struct jv_parser* p) {
     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;
index 4dddcf6f451d13edb968952be0c9970599638f6e..102707667bfe3693f79d31ce35ee9f3bd7a1ec3c 100644 (file)
@@ -4,7 +4,6 @@ struct jv_parser {
   int stackpos;
   int stacklen;
   jv next;
-  int hasnext;
   
   char* tokenbuf;
   int tokenpos;
index 7e274c4a151b7fe4609fa1334373ad2e720d708c..7c341d8d8c45b7edbcba16b355f1aa155e401764 100644 (file)
--- a/c/main.c
+++ b/c/main.c
@@ -49,6 +49,7 @@ void run_tests() {
       assert(jv_is_valid(expected));
       jv actual = jq_next();
       if (!jv_is_valid(actual)) {
+        jv_free(actual);
         printf("Insufficient results\n");
         pass = 0;
         break;
@@ -70,6 +71,8 @@ void run_tests() {
         jv_dump(extra);
         printf("\n");
         pass = 0;
+      } else {
+        jv_free(extra);
       }
     }
     jq_teardown();