diff options
author | Stephen Dolan <mu@netsoc.tcd.ie> | 2013-05-13 20:16:19 +0100 |
---|---|---|
committer | Stephen Dolan <mu@netsoc.tcd.ie> | 2013-05-13 20:16:19 +0100 |
commit | 33901b74b16ce037c732d7a33444862d93c91065 (patch) | |
tree | 3bdbf1472b31187a665dd7b601f04ad869473b49 | |
parent | a47cfa475700469d0ea0e7a7c80c5fef8690ac3f (diff) | |
download | jq-33901b74b16ce037c732d7a33444862d93c91065.tar.gz |
Array slicing. Closes #2.
-rw-r--r-- | docs/content/3.manual/manual.yml | 21 | ||||
-rw-r--r-- | jv_aux.c | 136 | ||||
-rw-r--r-- | parser.y | 20 | ||||
-rw-r--r-- | tests/all.test | 19 |
4 files changed, 180 insertions, 16 deletions
diff --git a/docs/content/3.manual/manual.yml b/docs/content/3.manual/manual.yml index 766fef7..b88c4c2 100644 --- a/docs/content/3.manual/manual.yml +++ b/docs/content/3.manual/manual.yml @@ -160,7 +160,7 @@ sections: input: '{"notfoo": true, "alsonotfoo": false}' output: ['null'] - - title: "`.[foo]`" + - title: "`.[foo]`, `.[2]`, `.[10:15]`" body: | You can also look up fields of an object using syntax like @@ -169,6 +169,13 @@ sections: integer. Arrays are zero-based (like javascript), so `.[2]` returns the third element of the array. + The `.[10:15]` syntax can be used to return a subarray of an + array. The array returned by `.[10:15]` will be of length 5, + containing the elements from index 10 (inclusive) to index + 15 (exclusive). Either index may be negative (in which case + it counts backwards from the end of the array), or omitted + (in which case it refers to the start or end of the array). + examples: - program: '.[0]' input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' @@ -178,6 +185,18 @@ sections: input: '[{"name":"JSON", "good":true}, {"name":"XML", "good":false}]' output: ['null'] + - program: '.[2:4]' + input: '["a","b","c","d","e"]' + output: ['["c", "d"]'] + + - program: '.[:3]' + input: '["a","b","c","d","e"]' + output: ['["a", "b", "c"]'] + + - program: '.[-2:]' + input: '["a","b","c","d","e"]' + output: ['["d", "e"]'] + - title: "`.[]`" body: | @@ -3,6 +3,38 @@ #include <stdlib.h> #include "jv_alloc.h" +static int parse_slice(jv array, jv slice, int* pstart, int* pend) { + // Array slices + int len = jv_array_length(jv_copy(array)); + jv start_jv = jv_object_get(jv_copy(slice), jv_string("start")); + jv end_jv = jv_object_get(slice, jv_string("end")); + if (jv_get_kind(start_jv) == JV_KIND_NULL) { + jv_free(start_jv); + start_jv = jv_number(0); + } + if (jv_get_kind(end_jv) == JV_KIND_NULL) { + jv_free(end_jv); + end_jv = jv_number(len); + } + if (jv_get_kind(start_jv) != JV_KIND_NUMBER || + jv_get_kind(end_jv) != JV_KIND_NUMBER) { + jv_free(start_jv); + jv_free(end_jv); + return 0; + } else { + int start = (int)jv_number_value(start_jv); + int end = (int)jv_number_value(end_jv); + if (start < 0) start = len + start; + if (end < 0) end = len + end; + if (start < 0) start = 0; + if (end > len) end = len; + if (end < start) end = start; + *pstart = start; + *pend = end; + return 1; + } +} + jv jv_get(jv t, jv k) { jv v; if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { @@ -18,8 +50,18 @@ jv jv_get(jv t, jv k) { jv_free(v); v = jv_null(); } + } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) { + int start, end; + if (parse_slice(t, k, &start, &end)) { + v = jv_array_slice(t, start, end); + } else { + v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); + jv_free(t); + } } else if (jv_get_kind(t) == JV_KIND_NULL && - (jv_get_kind(k) == JV_KIND_STRING || jv_get_kind(k) == JV_KIND_NUMBER)) { + (jv_get_kind(k) == JV_KIND_STRING || + jv_get_kind(k) == JV_KIND_NUMBER || + jv_get_kind(k) == JV_KIND_OBJECT)) { jv_free(t); jv_free(k); v = jv_null(); @@ -48,10 +90,49 @@ jv jv_set(jv t, jv k, jv v) { (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) { if (isnull) t = jv_array(); t = jv_array_set(t, (int)jv_number_value(k), v); + } else if (jv_get_kind(k) == JV_KIND_OBJECT && + (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) { + if (isnull) t = jv_array(); + int start, end; + if (parse_slice(t, k, &start, &end)) { + if (jv_get_kind(v) == JV_KIND_ARRAY) { + int array_len = jv_array_length(jv_copy(t)); + assert(0 <= start && start <= end && end <= array_len); + int slice_len = end - start; + int insert_len = jv_array_length(jv_copy(v)); + if (slice_len < insert_len) { + // array is growing + int shift = insert_len - slice_len; + for (int i = array_len - 1; i >= end; i--) { + t = jv_array_set(t, i + shift, jv_array_get(jv_copy(t), i)); + } + } else if (slice_len > insert_len) { + // array is shrinking + int shift = slice_len - insert_len; + for (int i = end; i < array_len; i++) { + t = jv_array_set(t, i - shift, jv_array_get(jv_copy(t), i)); + } + t = jv_array_slice(t, 0, array_len - shift); + } + for (int i=0; i < insert_len; i++) { + t = jv_array_set(t, start + i, jv_array_get(jv_copy(v), i)); + } + jv_free(v); + } else { + jv_free(t); + jv_free(v); + t = jv_invalid_with_msg(jv_string_fmt("A slice of an array can only be assigned another array")); + } + } else { + jv_free(t); + jv_free(k); + jv_free(v); + t = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); + } } else { jv err = jv_invalid_with_msg(jv_string_fmt("Cannot update field at %s index of %s", - jv_kind_name(jv_get_kind(t)), - jv_kind_name(jv_get_kind(v)))); + jv_kind_name(jv_get_kind(k)), + jv_kind_name(jv_get_kind(t)))); jv_free(t); jv_free(k); jv_free(v); @@ -96,23 +177,39 @@ jv jv_dels(jv t, jv keys) { if (jv_get_kind(t) == JV_KIND_NULL || jv_array_length(jv_copy(keys)) == 0) { // no change } else if (jv_get_kind(t) == JV_KIND_ARRAY) { + // extract slices, they must be handled differently + jv orig_keys = keys; + keys = jv_array(); jv new_array = jv_array(); + jv starts = jv_array(), ends = jv_array(); + jv_array_foreach(orig_keys, i, key) { + if (jv_get_kind(key) == JV_KIND_NUMBER) { + keys = jv_array_append(keys, key); + } else if (jv_get_kind(key) == JV_KIND_OBJECT) { + int start, end; + if (parse_slice(t, key, &start, &end)) { + starts = jv_array_append(starts, jv_number(start)); + ends = jv_array_append(ends, jv_number(end)); + } else { + jv_free(new_array); + jv_free(key); + new_array = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); + goto arr_out; + } + } else { + jv_free(new_array); + new_array = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s element of array", + jv_kind_name(jv_get_kind(key)))); + jv_free(key); + goto arr_out; + } + } + int kidx = 0; jv_array_foreach(t, i, elem) { int del = 0; while (kidx < jv_array_length(jv_copy(keys))) { - jv nextdel = jv_array_get(jv_copy(keys), kidx); - if (jv_get_kind(nextdel) != JV_KIND_NUMBER) { - jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s element of array", - jv_kind_name(jv_get_kind(nextdel)))); - jv_free(nextdel); - jv_free(new_array); - jv_free(elem); - new_array = err; - goto arr_out; // break twice - } - int delidx = (int)jv_number_value(nextdel); - jv_free(nextdel); + int delidx = (int)jv_number_value(jv_array_get(jv_copy(keys), kidx)); if (i == delidx) { del = 1; } @@ -121,12 +218,21 @@ jv jv_dels(jv t, jv keys) { } kidx++; } + for (int sidx=0; !del && sidx<jv_array_length(jv_copy(starts)); sidx++) { + if ((int)jv_number_value(jv_array_get(jv_copy(starts), sidx)) <= i && + i < (int)jv_number_value(jv_array_get(jv_copy(ends), sidx))) { + del = 1; + } + } if (!del) new_array = jv_array_append(new_array, elem); else jv_free(elem); } arr_out: + jv_free(starts); + jv_free(ends); + jv_free(orig_keys); jv_free(t); t = new_array; } else if (jv_get_kind(t) == JV_KIND_OBJECT) { @@ -141,6 +141,17 @@ static block gen_index(block obj, block key) { return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX)); } +static block gen_slice_index(block obj, block start, block end) { + block key = BLOCK(gen_subexp(gen_const(jv_object())), + gen_subexp(gen_const(jv_string("start"))), + gen_subexp(start), + gen_op_simple(INSERT), + gen_subexp(gen_const(jv_string("end"))), + gen_subexp(end), + gen_op_simple(INSERT)); + return BLOCK(key, obj, gen_op_simple(INDEX)); +} + static block gen_binop(block a, block b, int op) { const char* funcname = 0; switch (op) { @@ -408,6 +419,15 @@ Term '[' Exp ']' { Term '[' ']' { $$ = block_join($1, gen_op_simple(EACH)); } | +Term '[' Exp ':' Exp ']' { + $$ = gen_slice_index($1, $3, $5); +} | +Term '[' Exp ':' ']' { + $$ = gen_slice_index($1, $3, gen_const(jv_null())); +} | +Term '[' ':' Exp ']' { + $$ = gen_slice_index($1, gen_const(jv_null()), $4); +} | LITERAL { $$ = gen_const($1); } | diff --git a/tests/all.test b/tests/all.test index 5b38709..4eaa9b6 100644 --- a/tests/all.test +++ b/tests/all.test @@ -167,6 +167,25 @@ null 3 # +# Slices +# + +[.[3:2], .[-5:4], .[:-2], .[-2:]] +[0,1,2,3,4,5,6] +[[], [2,3], [0,1,2,3,4], [5,6]] + +del(.[2:4],.[0],.[-2:]) +[0,1,2,3,4,5,6,7] +[1,4,5] + +.[2:4] = ([], ["a","b"], ["a","b","c"]) +[0,1,2,3,4,5,6,7] +[0,1,4,5,6,7] +[0,1,"a","b",4,5,6,7] +[0,1,"a","b","c",4,5,6,7] + + +# # Variables # |