From 55ff8b856b3e8731950e9ba960e581cfbf8bf445 Mon Sep 17 00:00:00 2001 From: Mattias Wadman Date: Sun, 26 Jan 2025 16:19:07 +0100 Subject: [PATCH] Non-matched optional capture group should be null and offset -1 Fixes #3093 --- src/builtin.c | 9 +++++++-- tests/onig.test | 28 +++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/builtin.c b/src/builtin.c index 717a07528a..534c969545 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -1006,8 +1006,13 @@ static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) { jv captures = jv_array(); for (int i = 1; i < region->num_regs; ++i) { jv cap = jv_object(); - cap = jv_object_set(cap, jv_string("offset"), jv_number(idx)); - cap = jv_object_set(cap, jv_string("string"), jv_string("")); + if (region->beg[i] == -1) { + cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(-1)); + cap = jv_object_set(cap, jv_string("string"), jv_null()); + } else { + cap = jv_object_set(cap, jv_string("offset"), jv_number(idx)); + cap = jv_object_set(cap, jv_string("string"), jv_string("")); + } cap = jv_object_set(cap, jv_string("length"), jv_number(0)); cap = jv_object_set(cap, jv_string("name"), jv_null()); captures = jv_array_append(captures, cap); diff --git a/tests/onig.test b/tests/onig.test index 5b6dab6a36..87aae375ea 100644 --- a/tests/onig.test +++ b/tests/onig.test @@ -1,7 +1,7 @@ # match builtin [match("( )*"; "g")] "abc" -[{"offset":0,"length":0,"string":"","captures":[{"offset":0,"string":"","length":0,"name":null}]},{"offset":1,"length":0,"string":"","captures":[{"offset":1,"string":"","length":0,"name":null}]},{"offset":2,"length":0,"string":"","captures":[{"offset":2,"string":"","length":0,"name":null}]},{"offset":3,"length":0,"string":"","captures":[{"offset":3,"string":"","length":0,"name":null}]}] +[{"offset":0,"length":0,"string":"","captures":[{"offset":-1,"string":null,"length":0,"name":null}]},{"offset":1,"length":0,"string":"","captures":[{"offset":-1,"string":null,"length":0,"name":null}]},{"offset":2,"length":0,"string":"","captures":[{"offset":-1,"string":null,"length":0,"name":null}]},{"offset":3,"length":0,"string":"","captures":[{"offset":-1,"string":null,"length":0,"name":null}]}] [match("( )*"; "gn")] "abc" @@ -37,6 +37,32 @@ "foo bar foo foo foo" [{"offset": 0, "length": 11, "string": "foo bar foo", "captures":[{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]},{"offset":12, "length": 8, "string": "foo foo", "captures":[{"offset": -1, "length": 0, "string": null, "name": "bar123"}]}] +# non-matched optional group +"a","b","c" | capture("(?a)?b?") +null +{"x":"a"} +{"x":null} +{"x":null} + +"a","b","c" | match("(?a)?b?") +null +{"offset":0,"length":1,"string":"a","captures":[{"offset":0,"length":1,"string":"a","name":"x"}]} +{"offset":0,"length":1,"string":"b","captures":[{"offset":-1,"string":null,"length":0,"name":"x"}]} +{"offset":0,"length":0,"string":"","captures":[{"offset":-1,"string":null,"length":0,"name":"x"}]} + +# same as above but allow empty match for group +"a","b","c" | capture("(?a?)?b?") +null +{"x":"a"} +{"x":""} +{"x":""} + +"a","b","c" | match("(?a?)?b?") +null +{"offset":0,"length":1,"string":"a","captures":[{"offset":0,"length":1,"string":"a","name":"x"}]} +{"offset":0,"length":1,"string":"b","captures":[{"offset":0,"string":"","length":0,"name":"x"}]} +{"offset":0,"length":0,"string":"","captures":[{"offset":0,"string":"","length":0,"name":"x"}]} + #test builtin [test("( )*"; "gn")] "abc"