Skip to content

Commit

Permalink
fix: make last(empty) yield no output values
Browse files Browse the repository at this point in the history
  • Loading branch information
itchyny committed Sep 18, 2024
1 parent 860af44 commit 66249a3
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 5 deletions.
5 changes: 4 additions & 1 deletion docs/content/manual/dev/manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3057,9 +3057,12 @@ sections:
Note that `nth(n; expr)` doesn't support negative values of `n`.
examples:
- program: '[first(range(.)), last(range(.)), nth(./2; range(.))]'
- program: '[first(range(.)), last(range(.)), nth(5; range(.))]'
input: '10'
output: ['[0,9,5]']
- program: '[first(empty), last(empty), nth(5; empty)]'
input: 'null'
output: ['[]']

- title: "`first`, `last`, `nth(n)`"
body: |
Expand Down
8 changes: 6 additions & 2 deletions jq.1.prebuilt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions src/builtin.c
Original file line number Diff line number Diff line change
Expand Up @@ -1879,6 +1879,32 @@ BINOPS
#undef LIBM_DD
#undef LIBM_DA

// This is a hack to make last(empty) yield no output values without using boxing.
static block gen_last_1() {
block last_var = gen_op_var_fresh(STOREV, "last");
block is_empty_var = gen_op_var_fresh(STOREV, "is_empty");
block init = BLOCK(gen_op_simple(DUP),
gen_const(jv_null()),
last_var,
gen_op_simple(DUP),
gen_const(jv_true()),
is_empty_var);
block call_arg = BLOCK(gen_call("arg", gen_noop()),
gen_op_simple(DUP),
gen_op_bound(STOREV, last_var),
gen_const(jv_false()),
gen_op_bound(STOREV, is_empty_var),
gen_op_simple(BACKTRACK));
block if_empty = gen_op_simple(BACKTRACK);
return BLOCK(init,
gen_op_target(FORK, call_arg),
call_arg,
BLOCK(gen_op_bound(LOADVN, is_empty_var),
gen_op_target(JUMP_F, if_empty),
if_empty,
gen_op_bound(LOADVN, last_var)));
}

struct bytecoded_builtin { const char* name; block code; };
static block bind_bytecoded_builtins(block b) {
block builtins = gen_noop();
Expand All @@ -1898,6 +1924,7 @@ static block bind_bytecoded_builtins(block b) {
{"path", BLOCK(gen_op_simple(PATH_BEGIN),
gen_call("arg", gen_noop()),
gen_op_simple(PATH_END))},
{"last", gen_last_1()},
};
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,
Expand Down
1 change: 0 additions & 1 deletion src/builtin.jq
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ def all(condition): all(.[]; condition);
def any(condition): any(.[]; condition);
def all: all(.[]; .);
def any: any(.[]; .);
def last(g): reduce g as $item (null; $item);
def nth($n; g):
if $n < 0 then error("nth doesn't support negative indices")
else label $out | foreach g as $item ($n + 1; . - 1; if . <= 0 then $item, break $out else empty end) end;
Expand Down
4 changes: 4 additions & 0 deletions tests/jq.test
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@ null
10
[0,9]

[first(range(.)), last(range(.))]
0
[]

[nth(0,5,9,10,15; range(.)), try nth(-1; range(.)) catch .]
10
[0,5,9,"nth doesn't support negative indices"]
Expand Down
6 changes: 5 additions & 1 deletion tests/man.test

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 66249a3

Please sign in to comment.