summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoritchyny <itchyny@cybozu.co.jp>2024-11-13 20:35:50 +0900
committerGitHub <noreply@github.com>2024-11-13 20:35:50 +0900
commita7b22539a479400fe8b3fc40840ca587044ecbc2 (patch)
tree3261ad597c3a3d74638a2ef8c7bdc2710a4e218a
parent3c5707f7fbbdcf2cad0bb24ad2ed00d5771aae57 (diff)
fix: make last(empty) yield no output values (#3179)
-rw-r--r--docs/content/manual/dev/manual.yml5
-rw-r--r--jq.1.prebuilt6
-rw-r--r--src/builtin.c28
-rw-r--r--src/builtin.jq1
-rw-r--r--tests/jq.test4
-rw-r--r--tests/man.test6
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]