]> granicus.if.org Git - jq/commitdiff
Path manipulation (path/getpath/setpath/delpath) and docs.
authorStephen Dolan <mu@netsoc.tcd.ie>
Fri, 28 Dec 2012 16:08:29 +0000 (16:08 +0000)
committerStephen Dolan <mu@netsoc.tcd.ie>
Fri, 28 Dec 2012 16:08:29 +0000 (16:08 +0000)
del function should fix #37.

builtin.c
execute.c
jv.c
jv_aux.c
jv_aux.h
opcode_list.h
testdata

index c431be070d1e268d643b9dbff0b6bed264fa74a0..de40be7e79b8b10d1472af5feafe6f1c273ab529 100644 (file)
--- a/builtin.c
+++ b/builtin.c
@@ -484,6 +484,9 @@ static struct cfunction function_list[] = {
   {(cfunction_ptr)f_tonumber, "tonumber", 1},
   {(cfunction_ptr)f_tostring, "tostring", 1},
   {(cfunction_ptr)f_keys, "keys", 1},
+  {(cfunction_ptr)jv_setpath, "setpath", 3}, // FIXME typechecking
+  {(cfunction_ptr)jv_getpath, "getpath", 2},
+  {(cfunction_ptr)jv_delpath, "delpath", 2},
   {(cfunction_ptr)f_equal, "_equal", 3},
   {(cfunction_ptr)f_notequal, "_notequal", 3},
   {(cfunction_ptr)f_less, "_less", 3},
@@ -508,22 +511,36 @@ static struct cfunction function_list[] = {
 static struct symbol_table cbuiltins = 
   {function_list, sizeof(function_list)/sizeof(function_list[0])};
 
-typedef block (*bytecoded_builtin)();
 struct bytecoded_builtin { const char* name; block code; };
 static block bind_bytecoded_builtins(block b) {
-  struct bytecoded_builtin builtin_defs[] = {
-    {"empty", gen_op_simple(BACKTRACK)},
-    {"false", gen_const(jv_false())},
-    {"true", gen_const(jv_true())},
-    {"null", gen_const(jv_null())},
-    {"not", gen_condbranch(gen_const(jv_false()),
-                           gen_const(jv_true()))}
-  };
   block builtins = gen_noop();
-  for (unsigned i=0; i<sizeof(builtin_defs)/sizeof(builtin_defs[0]); i++) {
-    builtins = BLOCK(builtins, gen_function(builtin_defs[i].name, gen_noop(),
-                                            builtin_defs[i].code));
+  {
+    struct bytecoded_builtin builtin_defs[] = {
+      {"empty", gen_op_simple(BACKTRACK)},
+      {"false", gen_const(jv_false())},
+      {"true", gen_const(jv_true())},
+      {"null", gen_const(jv_null())},
+      {"not", gen_condbranch(gen_const(jv_false()),
+                             gen_const(jv_true()))}
+    };
+    for (unsigned i=0; i<sizeof(builtin_defs)/sizeof(builtin_defs[0]); i++) {
+      builtins = BLOCK(builtins, gen_function(builtin_defs[i].name, gen_noop(),
+                                              builtin_defs[i].code));
+    }
+  }
+  {
+    struct bytecoded_builtin builtin_def_1arg[] = {
+      {"path", BLOCK(gen_op_simple(DUP), 
+                     gen_call("arg", gen_noop()),
+                     gen_op_simple(GETPATH))},
+    };
+    for (unsigned i=0; i<sizeof(builtin_def_1arg)/sizeof(builtin_def_1arg[0]); i++) {
+      builtins = BLOCK(builtins, gen_function(builtin_def_1arg[i].name,
+                                              gen_op_block_unbound(CLOSURE_PARAM, "arg"),
+                                              builtin_def_1arg[i].code));
+    }
   }
+  
   return block_bind(builtins, b, OP_IS_CALL_PSEUDO);
 }
 
@@ -535,6 +552,7 @@ static const char* jq_builtins[] = {
   "def unique: group_by(.) | map(.[0]);",
   "def max_by(f): _max_by_impl(map([f]));",
   "def min_by(f): _min_by_impl(map([f]));",
+  "def del(f): delpath(path(f));",
 };
 
 
index 6d0bf72703e90b868634f6195c73ea7f721efefe..ed251d18f4939589f6446cd22681365b0e0c77de 100644 (file)
--- a/execute.c
+++ b/execute.c
@@ -286,6 +286,18 @@ jv jq_next() {
       break;
     }
 
+    case GETPATH: {
+      stackval path_end = stack_pop();
+      stackval path_start = stack_pop();
+      jv_free(path_end.value);
+      jv path = jv_array();
+      for (int i=path_start.pathidx; i<path_end.pathidx; i++) {
+        path = jv_array_set(path, i, jv_copy(pathbuf[i]));
+      }
+      stack_push(stackval_replace(path_start, path));
+      break;
+    }
+
     case ASSIGN: {
       stackval replacement = stack_pop();
       stackval path_end = stack_pop();
@@ -293,11 +305,18 @@ jv jq_next() {
       jv_free(path_end.value);
       jv_free(path_start.value);
 
+      jv path = jv_array();
+      for (int i=path_start.pathidx; i<path_end.pathidx; i++) {
+        path = jv_array_set(path, i, jv_copy(pathbuf[i]));
+      }
+
+
+
       uint16_t level = *pc++;
       uint16_t v = *pc++;
       frame_ptr fp = frame_get_level(&frame_stk, frame_current(&frame_stk), level);
       jv* var = frame_local_var(fp, v);
-      jv result = jv_insert(*var, replacement.value, pathbuf + path_start.pathidx, path_end.pathidx - path_start.pathidx);
+      jv result = jv_setpath(*var, path, replacement.value);
       if (jv_is_valid(result)) {
         *var = result;
       } else {
@@ -311,7 +330,7 @@ jv jq_next() {
       stackval t = stack_pop();
       jv k = stack_pop().value;
       int pathidx = path_push(t, jv_copy(k));
-      jv v = jv_lookup(t.value, k);
+      jv v = jv_get(t.value, k);
       if (jv_is_valid(v)) {
         stackval sv;
         sv.value = v;
diff --git a/jv.c b/jv.c
index abbeb6d43890f81c8e7e9bafbbcd4b5efe1bf583..9a362a6cffbb573cadf2fc24cb42d551ceecf1fd 100644 (file)
--- a/jv.c
+++ b/jv.c
@@ -254,8 +254,7 @@ static int jvp_array_equal(jv_complex* a, jv_complex* b) {
 static jv_complex jvp_array_slice(jv_complex* a, int start, int end) {
   // FIXME: maybe slice should reallocate if the slice is small enough
   assert(start <= end);
-  jvp_array* array = jvp_array_ptr(a);
-  assert(a->i[1] + end < array->length);
+  assert(a->i[0] + end <= a->i[1]);
   jv_complex slice = *a;
   slice.i[0] += start;
   slice.i[1] = slice.i[0] + (end - start);
index a4bfddd28c76e6cf7b850b11c8bae78e32641000..83695f963a095544117a4b4455aa796b289bcd32 100644 (file)
--- a/jv_aux.c
+++ b/jv_aux.c
@@ -3,7 +3,7 @@
 #include <stdlib.h>
 #include "jv_alloc.h"
 
-jv jv_lookup(jv t, jv k) {
+jv jv_get(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);
@@ -33,7 +33,12 @@ jv jv_lookup(jv t, jv k) {
   return v;
 }
 
-jv jv_modify(jv t, jv k, jv v) {
+jv jv_set(jv t, jv k, jv v) {
+  if (!jv_is_valid(v)) {
+    jv_free(t);
+    jv_free(k);
+    return v;
+  }
   int isnull = jv_get_kind(t) == JV_KIND_NULL;
   if (jv_get_kind(k) == JV_KIND_STRING && 
       (jv_get_kind(t) == JV_KIND_OBJECT || isnull)) {
@@ -55,15 +60,100 @@ jv jv_modify(jv t, jv k, jv v) {
   return t;
 }
 
-jv jv_insert(jv root, jv value, jv* path, int pathlen) {
-  if (pathlen == 0) {
+jv jv_del(jv t, jv k) {
+  if (jv_get_kind(t) == JV_KIND_NULL) {
+    jv_free(k);
+  } else if (jv_get_kind(t) == JV_KIND_ARRAY && 
+             jv_get_kind(k) == JV_KIND_NUMBER) {
+    int idx = (int)jv_number_value(k);
+    jv_free(k);
+    int len = jv_array_length(jv_copy(t));
+    if (idx >= 0 && idx < len) {
+      for (int i = idx+1; i < len; i++) {
+        t = jv_array_set(t, i-1, jv_array_get(jv_copy(t), i));
+      }
+      t = jv_array_slice(t, 0, len-1);
+    }
+  } else if (jv_get_kind(t) == JV_KIND_OBJECT &&
+             jv_get_kind(k) == JV_KIND_STRING) {
+    t = jv_object_delete(t, k);
+  } else {
+    jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete field at %s index of %s",
+                                               jv_kind_name(jv_get_kind(k)),
+                                               jv_kind_name(jv_get_kind(t))));
+    jv_free(t);
+    jv_free(k);
+    t = err;
+  }
+  return t;
+}
+
+jv jv_setpath(jv root, jv path, jv value) {
+  if (jv_get_kind(path) != JV_KIND_ARRAY) {
+    jv_free(value);
+    jv_free(root);
+    jv_free(path);
+    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
+  }
+  if (!jv_is_valid(root)){
+    jv_free(value);
+    jv_free(path);
+    return root;
+  }
+  if (jv_array_length(jv_copy(path)) == 0) {
+    jv_free(path);
     jv_free(root);
     return value;
   }
-  return jv_modify(root, jv_copy(*path), 
-                   jv_insert(jv_lookup(jv_copy(root), jv_copy(*path)), value, path+1, pathlen-1));
+  jv pathcurr = jv_array_get(jv_copy(path), 0);
+  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
+  return jv_set(root, pathcurr, 
+                jv_setpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest, value));
+}
+
+jv jv_getpath(jv root, jv path) {
+  if (jv_get_kind(path) != JV_KIND_ARRAY) {
+    jv_free(root);
+    jv_free(path);
+    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
+  }
+  if (!jv_is_valid(root)) {
+    jv_free(path);
+    return root;
+  }
+  if (jv_array_length(jv_copy(path)) == 0) {
+    jv_free(path);
+    return root;
+  }
+  jv pathcurr = jv_array_get(jv_copy(path), 0);
+  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
+  return jv_getpath(jv_get(root, pathcurr), pathrest);
 }
 
+jv jv_delpath(jv root, jv path) {
+  if (jv_get_kind(path) != JV_KIND_ARRAY) {
+    jv_free(root);
+    jv_free(path);
+    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
+  }
+  if (!jv_is_valid(root)) {
+    jv_free(path);
+    return root;
+  }
+  if (jv_array_length(jv_copy(path)) == 1) {
+    return jv_del(root, jv_array_get(path, 0));
+  }
+  jv pathcurr = jv_array_get(jv_copy(path), 0);
+  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
+  jv new_obj = jv_delpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest);
+  if (jv_get_kind(new_obj) == JV_KIND_NULL) {
+    jv_free(pathcurr);
+    jv_free(new_obj);
+    return root;
+  } else {
+    return jv_set(root, pathcurr, new_obj);
+  }
+}
 
 static int string_cmp(const void* pa, const void* pb){
   const jv* a = pa;
index 36b0e5e4ae55e03f81a73f1aac0e09a4bad4fa4a..0b827d2e0827871d5efb9b1766f4bbed8ca2b20b 100644 (file)
--- a/jv_aux.h
+++ b/jv_aux.h
@@ -3,9 +3,11 @@
 
 #include "jv.h"
 
-jv jv_lookup(jv t, jv k);
-jv jv_modify(jv t, jv k, jv v);
-jv jv_insert(jv root, jv value, jv* path, int pathlen);
+jv jv_get(jv t, jv k);
+jv jv_set(jv t, jv k, jv v);
+jv jv_setpath(jv root, jv path, jv value);
+jv jv_getpath(jv root, jv path);
+jv jv_delpath(jv root, jv path);
 
 jv jv_keys(jv /*object or array*/);
 int jv_cmp(jv, jv);
index e6dece6e238144d406924ac86ad3298e91c350f1..e460ffe9d5b9818744c04722a3cfd2a542dd6dac 100644 (file)
@@ -15,6 +15,7 @@ OP(BACKTRACK, NONE, 0, 0)
 OP(APPEND, NONE,    2, 1)
 OP(INSERT, NONE,    4, 2)
 
+OP(GETPATH, NONE,   2, 1)
 OP(ASSIGN, VARIABLE, 3, 0)
 
 OP(CALL_BUILTIN, CFUNC, -1, 1)
index bb8e677a9553f8d53af81b9664e00137c51a57bf..36429f59ed158c478e80dc0b07edadc923a0acf1 100644 (file)
--- a/testdata
+++ b/testdata
@@ -335,6 +335,50 @@ fold 0 as $s (.[] | $s + .)
 [1,2,4]
 7
 
+#
+# Paths
+#
+
+path(.foo[0,1])
+null
+["foo", 0]
+["foo", 1]
+
+path(.[] | select(.>3))
+[1,5,3]
+[1]
+
+path(.)
+42
+[]
+
+["foo",1] as $p | getpath($p), setpath($p; 20), delpath($p)
+{"bar": 42, "foo": ["a", "b", "c", "d"]}
+"b"
+{"bar": 42, "foo": ["a", 20, "c", "d"]}
+{"bar": 42, "foo": ["a", "c", "d"]}
+
+map(getpath([2])), map(setpath([2]; 42)), map(delpath([2]))
+[[0], [0,1], [0,1,2]]
+[null, null, 2]
+[[0,null,42], [0,1,42], [0,1,42]]
+[[0], [0,1], [0,1]]
+
+map(delpath([0,"foo"]))
+[[{"foo":2, "x":1}], [{"bar":2}]]
+[[{"x":1}], [{"bar":2}]]
+
+["foo",1] as $p | getpath($p), setpath($p; 20), delpath($p)
+{"bar":false}
+null
+{"bar":false, "foo": [null, 20]}
+{"bar":false}
+
+delpath([-200])
+[1,2,3]
+[1,2,3]
+
+
 #
 # Assignment
 #