diff options
author | Stephen Dolan <mu@netsoc.tcd.ie> | 2012-12-28 16:08:29 +0000 |
---|---|---|
committer | Stephen Dolan <mu@netsoc.tcd.ie> | 2012-12-28 16:08:29 +0000 |
commit | 79c9c418c6c6312c550b9253b62701f3f788dfa2 (patch) | |
tree | bbddd669b9c2daa61a8144fead798e1fa45e26f6 | |
parent | f1e23448e789983f8581f291e56a37c3c63e7dbf (diff) |
Path manipulation (path/getpath/setpath/delpath) and docs.
del function should fix #37.
-rw-r--r-- | builtin.c | 42 | ||||
-rw-r--r-- | execute.c | 23 | ||||
-rw-r--r-- | jv.c | 3 | ||||
-rw-r--r-- | jv_aux.c | 102 | ||||
-rw-r--r-- | jv_aux.h | 8 | ||||
-rw-r--r-- | opcode_list.h | 1 | ||||
-rw-r--r-- | testdata | 44 |
7 files changed, 198 insertions, 25 deletions
@@ -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));", }; @@ -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; @@ -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); @@ -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; @@ -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); diff --git a/opcode_list.h b/opcode_list.h index e6dece6e..e460ffe9 100644 --- a/opcode_list.h +++ b/opcode_list.h @@ -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) @@ -336,6 +336,50 @@ fold 0 as $s (.[] | $s + .) 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 # .message = "goodbye" |