Skip to content

Commit a7b2253

Browse files
authored
fix: make last(empty) yield no output values (#3179)
1 parent 3c5707f commit a7b2253

File tree

6 files changed

+46
-4
lines changed

6 files changed

+46
-4
lines changed

docs/content/manual/dev/manual.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3069,9 +3069,12 @@ sections:
30693069
Note that `nth(n; expr)` doesn't support negative values of `n`.
30703070
30713071
examples:
3072-
- program: '[first(range(.)), last(range(.)), nth(./2; range(.))]'
3072+
- program: '[first(range(.)), last(range(.)), nth(5; range(.))]'
30733073
input: '10'
30743074
output: ['[0,9,5]']
3075+
- program: '[first(empty), last(empty), nth(5; empty)]'
3076+
input: 'null'
3077+
output: ['[]']
30753078

30763079
- title: "`first`, `last`, `nth(n)`"
30773080
body: |

jq.1.prebuilt

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/builtin.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1879,6 +1879,33 @@ BINOPS
18791879
#undef LIBM_DD
18801880
#undef LIBM_DA
18811881

1882+
// This is a hack to make last(g) yield no output values,
1883+
// if g yields no output values, without using boxing.
1884+
static block gen_last_1() {
1885+
block last_var = gen_op_var_fresh(STOREV, "last");
1886+
block is_empty_var = gen_op_var_fresh(STOREV, "is_empty");
1887+
block init = BLOCK(gen_op_simple(DUP),
1888+
gen_const(jv_null()),
1889+
last_var,
1890+
gen_op_simple(DUP),
1891+
gen_const(jv_true()),
1892+
is_empty_var);
1893+
block call_arg = BLOCK(gen_call("arg", gen_noop()),
1894+
gen_op_simple(DUP),
1895+
gen_op_bound(STOREV, last_var),
1896+
gen_const(jv_false()),
1897+
gen_op_bound(STOREV, is_empty_var),
1898+
gen_op_simple(BACKTRACK));
1899+
block if_empty = gen_op_simple(BACKTRACK);
1900+
return BLOCK(init,
1901+
gen_op_target(FORK, call_arg),
1902+
call_arg,
1903+
BLOCK(gen_op_bound(LOADVN, is_empty_var),
1904+
gen_op_target(JUMP_F, if_empty),
1905+
if_empty,
1906+
gen_op_bound(LOADVN, last_var)));
1907+
}
1908+
18821909
struct bytecoded_builtin { const char* name; block code; };
18831910
static block bind_bytecoded_builtins(block b) {
18841911
block builtins = gen_noop();
@@ -1898,6 +1925,7 @@ static block bind_bytecoded_builtins(block b) {
18981925
{"path", BLOCK(gen_op_simple(PATH_BEGIN),
18991926
gen_call("arg", gen_noop()),
19001927
gen_op_simple(PATH_END))},
1928+
{"last", gen_last_1()},
19011929
};
19021930
for (unsigned i=0; i<sizeof(builtin_def_1arg)/sizeof(builtin_def_1arg[0]); i++) {
19031931
builtins = BLOCK(builtins, gen_function(builtin_def_1arg[i].name,

src/builtin.jq

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ def all(condition): all(.[]; condition);
170170
def any(condition): any(.[]; condition);
171171
def all: all(.[]; .);
172172
def any: any(.[]; .);
173-
def last(g): reduce g as $item (null; $item);
174173
def nth($n; g):
175174
if $n < 0 then error("nth doesn't support negative indices")
176175
else first(skip($n; g)) end;

tests/jq.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,10 @@ null
362362
10
363363
[0,9]
364364

365+
[first(range(.)), last(range(.))]
366+
0
367+
[]
368+
365369
[nth(0,5,9,10,15; range(.)), try nth(-1; range(.)) catch .]
366370
10
367371
[0,5,9,"nth doesn't support negative indices"]

tests/man.test

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)