Path manipulation (path/getpath/setpath/delpath) and docs.
del function should fix #37.
This commit is contained in:
1 parent
f1e23448e7
commit
79c9c418c6
7 files changed
+188
-15
No files matched your search
@@ -484,6 +484,9 @@ static struct cfunction function_list[] = {
|
|||||||
{(cfunction_ptr)f_tonumber, "tonumber", 1},
|
{(cfunction_ptr)f_tonumber, "tonumber", 1},
|
||||||
{(cfunction_ptr)f_tostring, "tostring", 1},
|
{(cfunction_ptr)f_tostring, "tostring", 1},
|
||||||
{(cfunction_ptr)f_keys, "keys", 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_equal, "_equal", 3},
|
||||||
{(cfunction_ptr)f_notequal, "_notequal", 3},
|
{(cfunction_ptr)f_notequal, "_notequal", 3},
|
||||||
{(cfunction_ptr)f_less, "_less", 3},
|
{(cfunction_ptr)f_less, "_less", 3},
|
||||||
@@ -508,9 +511,10 @@ static struct cfunction function_list[] = {
|
|||||||
static struct symbol_table cbuiltins =
|
static struct symbol_table cbuiltins =
|
||||||
{function_list, sizeof(function_list)/sizeof(function_list[0])};
|
{function_list, sizeof(function_list)/sizeof(function_list[0])};
|
||||||
|
|
||||||
typedef block (*bytecoded_builtin)();
|
|
||||||
struct bytecoded_builtin { const char* name; block code; };
|
struct bytecoded_builtin { const char* name; block code; };
|
||||||
static block bind_bytecoded_builtins(block b) {
|
static block bind_bytecoded_builtins(block b) {
|
||||||
|
block builtins = gen_noop();
|
||||||
|
{
|
||||||
struct bytecoded_builtin builtin_defs[] = {
|
struct bytecoded_builtin builtin_defs[] = {
|
||||||
{"empty", gen_op_simple(BACKTRACK)},
|
{"empty", gen_op_simple(BACKTRACK)},
|
||||||
{"false", gen_const(jv_false())},
|
{"false", gen_const(jv_false())},
|
||||||
@@ -519,11 +523,24 @@ static block bind_bytecoded_builtins(block b) {
|
|||||||
{"not", gen_condbranch(gen_const(jv_false()),
|
{"not", gen_condbranch(gen_const(jv_false()),
|
||||||
gen_const(jv_true()))}
|
gen_const(jv_true()))}
|
||||||
};
|
};
|
||||||
block builtins = gen_noop();
|
|
||||||
for (unsigned i=0; i<sizeof(builtin_defs)/sizeof(builtin_defs[0]); i++) {
|
for (unsigned i=0; i<sizeof(builtin_defs)/sizeof(builtin_defs[0]); i++) {
|
||||||
builtins = BLOCK(builtins, gen_function(builtin_defs[i].name, gen_noop(),
|
builtins = BLOCK(builtins, gen_function(builtin_defs[i].name, gen_noop(),
|
||||||
builtin_defs[i].code));
|
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);
|
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 unique: group_by(.) | map(.[0]);",
|
||||||
"def max_by(f): _max_by_impl(map([f]));",
|
"def max_by(f): _max_by_impl(map([f]));",
|
||||||
"def min_by(f): _min_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;
|
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: {
|
case ASSIGN: {
|
||||||
stackval replacement = stack_pop();
|
stackval replacement = stack_pop();
|
||||||
stackval path_end = stack_pop();
|
stackval path_end = stack_pop();
|
||||||
@@ -293,11 +305,18 @@ jv jq_next() {
|
|||||||
jv_free(path_end.value);
|
jv_free(path_end.value);
|
||||||
jv_free(path_start.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 level = *pc++;
|
||||||
uint16_t v = *pc++;
|
uint16_t v = *pc++;
|
||||||
frame_ptr fp = frame_get_level(&frame_stk, frame_current(&frame_stk), level);
|
frame_ptr fp = frame_get_level(&frame_stk, frame_current(&frame_stk), level);
|
||||||
jv* var = frame_local_var(fp, v);
|
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)) {
|
if (jv_is_valid(result)) {
|
||||||
*var = result;
|
*var = result;
|
||||||
} else {
|
} else {
|
||||||
@@ -311,7 +330,7 @@ jv jq_next() {
|
|||||||
stackval t = stack_pop();
|
stackval t = stack_pop();
|
||||||
jv k = stack_pop().value;
|
jv k = stack_pop().value;
|
||||||
int pathidx = path_push(t, jv_copy(k));
|
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)) {
|
if (jv_is_valid(v)) {
|
||||||
stackval sv;
|
stackval sv;
|
||||||
sv.value = v;
|
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) {
|
static jv_complex jvp_array_slice(jv_complex* a, int start, int end) {
|
||||||
// FIXME: maybe slice should reallocate if the slice is small enough
|
// FIXME: maybe slice should reallocate if the slice is small enough
|
||||||
assert(start <= end);
|
assert(start <= end);
|
||||||
jvp_array* array = jvp_array_ptr(a);
|
assert(a->i[0] + end <= a->i[1]);
|
||||||
assert(a->i[1] + end < array->length);
|
|
||||||
jv_complex slice = *a;
|
jv_complex slice = *a;
|
||||||
slice.i[0] += start;
|
slice.i[0] += start;
|
||||||
slice.i[1] = slice.i[0] + (end - start);
|
slice.i[1] = slice.i[0] + (end - start);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "jv_alloc.h"
|
#include "jv_alloc.h"
|
||||||
|
|
||||||
jv jv_lookup(jv t, jv k) {
|
jv jv_get(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);
|
||||||
@@ -33,7 +33,12 @@ jv jv_lookup(jv t, jv k) {
|
|||||||
return v;
|
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;
|
int isnull = jv_get_kind(t) == JV_KIND_NULL;
|
||||||
if (jv_get_kind(k) == JV_KIND_STRING &&
|
if (jv_get_kind(k) == JV_KIND_STRING &&
|
||||||
(jv_get_kind(t) == JV_KIND_OBJECT || isnull)) {
|
(jv_get_kind(t) == JV_KIND_OBJECT || isnull)) {
|
||||||
@@ -55,15 +60,100 @@ jv jv_modify(jv t, jv k, jv v) {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
jv jv_insert(jv root, jv value, jv* path, int pathlen) {
|
jv jv_del(jv t, jv k) {
|
||||||
if (pathlen == 0) {
|
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);
|
jv_free(root);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
return jv_modify(root, jv_copy(*path),
|
jv pathcurr = jv_array_get(jv_copy(path), 0);
|
||||||
jv_insert(jv_lookup(jv_copy(root), jv_copy(*path)), value, path+1, pathlen-1));
|
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){
|
static int string_cmp(const void* pa, const void* pb){
|
||||||
const jv* a = pa;
|
const jv* a = pa;
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
#include "jv.h"
|
#include "jv.h"
|
||||||
|
|
||||||
jv jv_lookup(jv t, jv k);
|
jv jv_get(jv t, jv k);
|
||||||
jv jv_modify(jv t, jv k, jv v);
|
jv jv_set(jv t, jv k, jv v);
|
||||||
jv jv_insert(jv root, jv value, jv* path, int pathlen);
|
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*/);
|
jv jv_keys(jv /*object or array*/);
|
||||||
int jv_cmp(jv, jv);
|
int jv_cmp(jv, jv);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ OP(BACKTRACK, NONE, 0, 0)
|
|||||||
OP(APPEND, NONE, 2, 1)
|
OP(APPEND, NONE, 2, 1)
|
||||||
OP(INSERT, NONE, 4, 2)
|
OP(INSERT, NONE, 4, 2)
|
||||||
|
|
||||||
|
OP(GETPATH, NONE, 2, 1)
|
||||||
OP(ASSIGN, VARIABLE, 3, 0)
|
OP(ASSIGN, VARIABLE, 3, 0)
|
||||||
|
|
||||||
OP(CALL_BUILTIN, CFUNC, -1, 1)
|
OP(CALL_BUILTIN, CFUNC, -1, 1)
|
||||||
|
|||||||
@@ -335,6 +335,50 @@ fold 0 as $s (.[] | $s + .)
|
|||||||
[1,2,4]
|
[1,2,4]
|
||||||
7
|
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
|
# Assignment
|
||||||
#
|
#
|
||||||
|
|||||||
Reference in new issue
Block a user