diff options
author | itchyny <itchyny@cybozu.co.jp> | 2024-11-13 20:35:50 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-13 20:35:50 +0900 |
commit | a7b22539a479400fe8b3fc40840ca587044ecbc2 (patch) | |
tree | 3261ad597c3a3d74638a2ef8c7bdc2710a4e218a | |
parent | 3c5707f7fbbdcf2cad0bb24ad2ed00d5771aae57 (diff) |
fix: make last(empty) yield no output values (#3179)
-rw-r--r-- | docs/content/manual/dev/manual.yml | 5 | ||||
-rw-r--r-- | jq.1.prebuilt | 6 | ||||
-rw-r--r-- | src/builtin.c | 28 | ||||
-rw-r--r-- | src/builtin.jq | 1 | ||||
-rw-r--r-- | tests/jq.test | 4 | ||||
-rw-r--r-- | tests/man.test | 6 |
6 files changed, 46 insertions, 4 deletions
diff --git a/docs/content/manual/dev/manual.yml b/docs/content/manual/dev/manual.yml index 82061f82..895deab9 100644 --- a/docs/content/manual/dev/manual.yml +++ b/docs/content/manual/dev/manual.yml @@ -3069,9 +3069,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: | diff --git a/jq.1.prebuilt b/jq.1.prebuilt index e391bf3c..1a2c3cbc 100644 --- a/jq.1.prebuilt +++ b/jq.1.prebuilt @@ -3475,9 +3475,13 @@ The \fBnth(n; expr)\fR function extracts the nth value output by \fBexpr\fR\. No . .nf -jq \'[first(range(\.)), last(range(\.)), nth(\./2; range(\.))]\' +jq \'[first(range(\.)), last(range(\.)), nth(5; range(\.))]\' 10 => [0,9,5] + +jq \'[first(empty), last(empty), nth(5; empty)]\' + null +=> [] . .fi . diff --git a/src/builtin.c b/src/builtin.c index da4a770f..dbd72f3d 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -1879,6 +1879,33 @@ BINOPS #undef LIBM_DD #undef LIBM_DA +// This is a hack to make last(g) yield no output values, +// if g yields 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(); @@ -1898,6 +1925,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, diff --git a/src/builtin.jq b/src/builtin.jq index 0d1d6774..0e12bd53 100644 --- a/src/builtin.jq +++ b/src/builtin.jq @@ -170,7 +170,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 first(skip($n; g)) end; diff --git a/tests/jq.test b/tests/jq.test index 404994e2..5c1b81d3 100644 --- a/tests/jq.test +++ b/tests/jq.test @@ -362,6 +362,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"] diff --git a/tests/man.test b/tests/man.test index 5529e011..5489c5e2 100644 --- a/tests/man.test +++ b/tests/man.test @@ -881,10 +881,14 @@ false [0,1,2,3,4,5,6,7,8,9] [3,4,5,6,7,8,9] -[first(range(.)), last(range(.)), nth(./2; range(.))] +[first(range(.)), last(range(.)), nth(5; range(.))] 10 [0,9,5] +[first(empty), last(empty), nth(5; empty)] +null +[] + [range(.)]|[first, last, nth(5)] 10 [0,9,5] |