diff --git a/src/builtin.c b/src/builtin.c index ad14da2555..6ac7e1a72a 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -2006,21 +2006,19 @@ static block bind_bytecoded_builtins(block b) { } } { - // Note that we can now define `range` as a jq-coded function block rangevar = gen_op_var_fresh(STOREV, "rangevar"); - block rangestart = gen_op_var_fresh(STOREV, "rangestart"); - block range = BLOCK(gen_op_simple(DUP), - gen_call("start", gen_noop()), - rangestart, - gen_call("end", gen_noop()), - gen_op_simple(DUP), - gen_op_bound(LOADV, rangestart), - // Reset rangevar for every value generated by "end" - rangevar, - gen_op_bound(RANGE, rangevar)); - builtins = BLOCK(builtins, gen_function("range", - BLOCK(gen_param("start"), gen_param("end")), - range)); + builtins = BLOCK(builtins, + gen_function("range", + BLOCK(gen_param_regular("start"), + gen_param_regular("end"), + gen_param_regular("step")), + BLOCK(gen_op_unbound(LOADV, "step"), + gen_op_simple(DUP), + gen_op_unbound(LOADV, "end"), + gen_op_simple(DUP), + gen_op_unbound(LOADV, "start"), + rangevar, + gen_op_bound(RANGE, rangevar)))); } return BLOCK(builtins, b); } diff --git a/src/builtin.jq b/src/builtin.jq index bb32c515cc..183eceb199 100644 --- a/src/builtin.jq +++ b/src/builtin.jq @@ -42,7 +42,7 @@ def recurse: recurse(.[]?); def to_entries: [keys_unsorted[] as $k | {key: $k, value: .[$k]}]; def from_entries: map({(.key // .Key // .name // .Name): (if has("value") then .value else .Value end)}) | add | .//={}; def with_entries(f): to_entries | map(f) | from_entries; -def reverse: [.[length - 1 - range(0;length)]]; +def reverse: [.[range(length-1;-1;-1)]]; def indices($i): if type == "array" and ($i|type) == "array" then .[$i] elif type == "array" then .[[$i]] elif type == "string" and ($i|type) == "string" then _strindices($i) @@ -70,6 +70,7 @@ def join($x): reduce .[] as $i (null; def _flatten($x): reduce .[] as $i ([]; if $i | type == "array" and $x != 0 then . + ($i | _flatten($x-1)) else . + [$i] end); def flatten($x): if $x < 0 then error("flatten depth must not be negative") else _flatten($x) end; def flatten: _flatten(-1); +def range($x;$y): range($x;$y;1); def range($x): range(0;$x); def fromdateiso8601: strptime("%Y-%m-%dT%H:%M:%SZ")|mktime; def todateiso8601: strftime("%Y-%m-%dT%H:%M:%SZ"); @@ -157,11 +158,6 @@ def skip($n; expr): if $n > 0 then foreach expr as $item ($n; . - 1; if . < 0 then $item else empty end) elif $n == 0 then expr else error("skip doesn't support negative count") end; -# range/3, with a `by` expression argument -def range($init; $upto; $by): - if $by > 0 then $init|while(. < $upto; . + $by) - elif $by < 0 then $init|while(. > $upto; . + $by) - else empty end; def first(g): label $out | g | ., break $out; def isempty(g): first((g|false), true); def all(generator; condition): isempty(generator|condition and empty); diff --git a/src/execute.c b/src/execute.c index 356e6dfd02..b73a95bb78 100644 --- a/src/execute.c +++ b/src/execute.c @@ -10,13 +10,10 @@ #include "bytecode.h" #include "jv_alloc.h" -#include "jq_parser.h" #include "locfile.h" #include "jv.h" #include "jq.h" -#include "parser.h" #include "builtin.h" -#include "util.h" #include "linker.h" struct jq_state { @@ -518,24 +515,29 @@ jv jq_next(jq_state *jq) { uint16_t v = *pc++; jv* var = frame_local_var(jq, v, level); jv max = stack_pop(jq); + jv step = stack_pop(jq); if (raising) { jv_free(max); + jv_free(step); goto do_backtrack; - } - if (jv_get_kind(*var) != JV_KIND_NUMBER || - jv_get_kind(max) != JV_KIND_NUMBER) { - set_error(jq, jv_invalid_with_msg(jv_string_fmt("Range bounds must be numeric"))); + } + + if (jv_get_kind(*var) != JV_KIND_NUMBER || jv_get_kind(max) != JV_KIND_NUMBER || + jv_get_kind(step) != JV_KIND_NUMBER) { + set_error(jq, jv_invalid_with_msg(jv_string("Range bounds and step must be numeric"))); jv_free(max); + jv_free(step); goto do_backtrack; - } else if (jv_number_value(*var) >= jv_number_value(max)) { - /* finished iterating */ + } else if (jv_cmp(jv_copy(*var), jv_copy(max)) * jv_cmp(jv_copy(step), jv_number(0)) >= 0) { jv_free(max); + jv_free(step); goto do_backtrack; } else { jv curr = *var; - *var = jv_number(jv_number_value(*var) + 1); + *var = jv_number(jv_number_value(curr) + jv_number_value(step)); struct stack_pos spos = stack_get_pos(jq); + stack_push(jq, step); stack_push(jq, max); stack_save(jq, pc - 3, spos); diff --git a/tests/jq.test b/tests/jq.test index d5eb8dde53..bb1d0df043 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -282,10 +282,30 @@ null null [0,-1,-2,-3,-4] +[range(0;-10;-3)] +null +[0,-3,-6,-9] + +[range(0;10;0)] +null +[] + [range(0,1;4,5;1,2)] null [0,1,2,3,0,2, 0,1,2,3,4,0,2,4, 1,2,3,1,3, 1,2,3,4,1,3] +try range(.) catch . +null +"Range bounds and step must be numeric" + +try range(0;"") catch . +null +"Range bounds and step must be numeric" + +try range(0;1;[]) catch . +null +"Range bounds and step must be numeric" + [while(.<100; .*2)] 1 [1,2,4,8,16,32,64]