Upstream itchyny/gojq state of the touched files before 3662ece89c
This commit is contained in:
1 parent
1b0e1db888
commit
d1b69dc316
3 files changed
+906
-34
No files matched your search
+418
-5
@@ -710,6 +710,27 @@
|
||||
error: |
|
||||
expected a number for indexing an array but got: boolean (true)
|
||||
|
||||
- name: indexing number literal error
|
||||
args:
|
||||
- '-1[0]'
|
||||
input: 'null'
|
||||
error: |
|
||||
expected an array but got: number (1)
|
||||
|
||||
- name: slicing number literal error
|
||||
args:
|
||||
- '1 - -1[1:]'
|
||||
input: 'null'
|
||||
error: |
|
||||
expected an array but got: number (1)
|
||||
|
||||
- name: indexing by indexed number literal error
|
||||
args:
|
||||
- '.[1[0]]'
|
||||
input: '[0, 1]'
|
||||
error: |
|
||||
expected an array but got: number (1)
|
||||
|
||||
- name: array slicing start error
|
||||
args:
|
||||
- '.[[]:0]'
|
||||
@@ -1325,6 +1346,33 @@
|
||||
expected: |
|
||||
9223372036854775808
|
||||
|
||||
- name: abs function against non-numbers
|
||||
args:
|
||||
- -c
|
||||
- 'abs'
|
||||
input: |
|
||||
""
|
||||
"-42"
|
||||
[]
|
||||
[1,-2]
|
||||
{}
|
||||
{"a":-1}
|
||||
expected: |
|
||||
""
|
||||
"-42"
|
||||
[]
|
||||
[1,-2]
|
||||
{}
|
||||
{"a":-1}
|
||||
|
||||
- name: abs function error
|
||||
args:
|
||||
- 'abs'
|
||||
input: 'null true'
|
||||
error: |
|
||||
cannot negate: null
|
||||
cannot negate: boolean (true)
|
||||
|
||||
- name: length function
|
||||
args:
|
||||
- 'length'
|
||||
@@ -2407,6 +2455,40 @@
|
||||
"/a,b/c/d/e,f/"
|
||||
",/ /a/,/b/,/ /c/,/ /d/,/ /e/,/f/,/ "
|
||||
|
||||
- name: split/1 function against empty string
|
||||
args:
|
||||
- -c
|
||||
- '.[] as $t | .[] as $s | $s | split($t)'
|
||||
input: '["abc", "a", "b", ""]'
|
||||
expected: |
|
||||
["",""]
|
||||
["a"]
|
||||
["b"]
|
||||
[]
|
||||
["","bc"]
|
||||
["",""]
|
||||
["b"]
|
||||
[]
|
||||
["a","c"]
|
||||
["a"]
|
||||
["",""]
|
||||
[]
|
||||
["a","b","c"]
|
||||
["a"]
|
||||
["b"]
|
||||
[]
|
||||
|
||||
- name: split/1 function error
|
||||
args:
|
||||
- -c
|
||||
- 'try split(",") catch ., try split(1) catch .'
|
||||
input: '1 "a"'
|
||||
expected: |
|
||||
"split(\",\") cannot be applied to: number (1)"
|
||||
"split(1) cannot be applied to: number (1)"
|
||||
["a"]
|
||||
"split(1) cannot be applied to: string (\"a\")"
|
||||
|
||||
- name: join function
|
||||
args:
|
||||
- 'join(",")'
|
||||
@@ -3326,6 +3408,13 @@
|
||||
expected: |
|
||||
[5,5,false,3,3,[null,false],null,false]
|
||||
|
||||
- name: optional operator with alternative operator
|
||||
args:
|
||||
- '.a ?// 1'
|
||||
input: 'null'
|
||||
expected: |
|
||||
1
|
||||
|
||||
- name: condition
|
||||
args:
|
||||
- 'if . then . else [.] end'
|
||||
@@ -3541,6 +3630,29 @@
|
||||
expected: |
|
||||
5
|
||||
|
||||
- name: reduce with destructuring alternative operator
|
||||
args:
|
||||
- 'reduce .[] as [$i,$j] ?// {$i,$j} (0; . + $i * $j)'
|
||||
input: '[[1,2], {"i":3,"j":4}, [5,6]]'
|
||||
expected: |
|
||||
44
|
||||
|
||||
- name: reduce with destructuring alternative operator resetting variables
|
||||
args:
|
||||
- -c
|
||||
- 'reduce .[] as [$a] ?// {$b} ?// $c ([]; . + [[$a, $b, $c]])'
|
||||
input: '[{"b":1}, [2], 3, [4]]'
|
||||
expected: |
|
||||
[[null,1,null],[2,null,null],[null,null,3],[4,null,null]]
|
||||
|
||||
- name: reduce with destructuring alternative operator and error backtrack
|
||||
args:
|
||||
- -c
|
||||
- 'reduce .[] as [$a] ?// $a ([]; . + [if $a == 2 then error else $a end])'
|
||||
input: '[[1], [2], [3]]'
|
||||
expected: |
|
||||
[1,[2],3]
|
||||
|
||||
- name: reduce null
|
||||
args:
|
||||
- 'reduce . as $n (.; .)'
|
||||
@@ -3585,6 +3697,16 @@
|
||||
expected: |
|
||||
8
|
||||
|
||||
- name: reduce with assignment of multiple paths in update
|
||||
args:
|
||||
- -c
|
||||
- 'reduce (1,2) as $i (.; (.[0], .[.[0]]) = $i),
|
||||
reduce (1,2) as $i (.; (.[0], .[.[0]]) |= $i)'
|
||||
input: '[0,0,0]'
|
||||
expected: |
|
||||
[2,2,0]
|
||||
[2,2,0]
|
||||
|
||||
- name: reduce with query in start
|
||||
args:
|
||||
- 'reduce range(5) as $x (range(3); . + $x)'
|
||||
@@ -3638,6 +3760,37 @@
|
||||
-1
|
||||
-4
|
||||
|
||||
- name: foreach with destructuring alternative operator
|
||||
args:
|
||||
- 'foreach .[] as [$i,$j] ?// {$i,$j} (0; . + $i * $j)'
|
||||
input: '[[1,2], {"i":3,"j":4}, [5,6]]'
|
||||
expected: |
|
||||
2
|
||||
14
|
||||
44
|
||||
|
||||
- name: foreach with destructuring alternative operator resetting variables
|
||||
args:
|
||||
- -c
|
||||
- 'foreach .[] as [$a] ?// {$b} ?// $c (null; [$a, $b, $c])'
|
||||
input: '[{"b":1}, [2], 3, [4]]'
|
||||
expected: |
|
||||
[null,1,null]
|
||||
[2,null,null]
|
||||
[null,null,3]
|
||||
[4,null,null]
|
||||
|
||||
- name: foreach with destructuring alternative operator and error backtrack in extract
|
||||
args:
|
||||
- -c
|
||||
- 'foreach .[] as {$a} ?// [$a] ?// $a (null; $a; if 1 < . and . < 4 then error else . end)'
|
||||
input: '[[1], [2], {"a":3}, 4]'
|
||||
expected: |
|
||||
1
|
||||
[2]
|
||||
{"a":3}
|
||||
4
|
||||
|
||||
- name: foreach with iterator in update
|
||||
args:
|
||||
- 'foreach .[] as $i (1; ., . + $i, range(.))'
|
||||
@@ -4462,6 +4615,17 @@
|
||||
[-3,-3,-2,-1,-1,-0,-0,0,0,0,0,1,1,2,3,3]
|
||||
[3.7,3.3,2.5,1.7,1,0.7,0.3,0,0.3,0.5,0.7,1,1.7,2.5,3.3,3.7]
|
||||
|
||||
- name: math functions against large number literals
|
||||
args:
|
||||
- -c
|
||||
- '[floor, ceil, sqrt, exp10]'
|
||||
input: |
|
||||
1e1000
|
||||
-1e1000
|
||||
expected: |
|
||||
[1.7976931348623157e+308,1.7976931348623157e+308,1.7976931348623157e+308,1.7976931348623157e+308]
|
||||
[-1.7976931348623157e+308,-1.7976931348623157e+308,null,0]
|
||||
|
||||
- name: round, nearbyint, rint functions
|
||||
args:
|
||||
- -c
|
||||
@@ -4989,6 +5153,46 @@
|
||||
expected: |
|
||||
{"a":[{},{"b":[1,2,3]}]}
|
||||
|
||||
- name: getpath function against string
|
||||
args:
|
||||
- -c
|
||||
- '[getpath([{"start":1,"end":3}]), getpath([{"start":-2,"end":null}]), getpath([1]), getpath([])]'
|
||||
input: '"abcde"'
|
||||
expected: |
|
||||
["bc","de","b","abcde"]
|
||||
|
||||
- name: getpath function against string in a container
|
||||
args:
|
||||
- -c
|
||||
- 'getpath(["a",{"start":0,"end":2}]), getpath(["a",1])'
|
||||
input: '{"a":"abc"}'
|
||||
expected: |
|
||||
"ab"
|
||||
"b"
|
||||
|
||||
- name: getpath function against string round trip with path function
|
||||
args:
|
||||
- -c
|
||||
- '[getpath(path(.[1:3])), getpath(path(.[1]))]'
|
||||
input: '"abcde"'
|
||||
expected: |
|
||||
["bc","b"]
|
||||
|
||||
- name: getpath function against string round trip in a container
|
||||
args:
|
||||
- -c
|
||||
- '. as $d | [path(.a[0:2]), path(.b[1])] | map(. as $p | $d | getpath($p))'
|
||||
input: '{"a":"abc","b":"xyz"}'
|
||||
expected: |
|
||||
["ab","y"]
|
||||
|
||||
- name: getpath function against string with object key
|
||||
args:
|
||||
- 'getpath(["k"])'
|
||||
input: '"abc"'
|
||||
error: |
|
||||
getpath(["k"]) cannot be applied to "abc": expected an object but got: string ("abc")
|
||||
|
||||
- name: getpath function error
|
||||
args:
|
||||
- 'getpath(["a",1,"b","c"])'
|
||||
@@ -5021,9 +5225,9 @@
|
||||
expected: |
|
||||
{"a":[{"b":5}]}
|
||||
{"a":[{"b":5}],"b":0}
|
||||
"getpath([\"a\",0,\"b\"]) cannot be applied to: object ({\"a\":0})"
|
||||
"getpath([\"a\",0,\"b\"]) cannot be applied to {\"a\":0}: expected an array but got: number (0)"
|
||||
{"a":[{"b":5}]}
|
||||
"getpath([\"a\",0,\"b\"]) cannot be applied to: object ({\"a\":[0,1]})"
|
||||
"getpath([\"a\",0,\"b\"]) cannot be applied to {\"a\":[0,1]}: expected an object but got: number (0)"
|
||||
"getpath([\"a\",0,\"b\"]) cannot be applied to {\"a\":{\"b\":1}}: expected an array but got: object ({\"b\":1})"
|
||||
{"a":[{"b":5}]}
|
||||
{"a":[{"b":5,"c":3}]}
|
||||
@@ -5437,6 +5641,14 @@
|
||||
expected: |
|
||||
{"a":{"p":{"b":{"p":1,"q":1},"z":{"p":null,"q":null}},"q":{"b":{"p":1,"q":1}}}}
|
||||
|
||||
- name: update-assignment operator against nested object with overlapping paths
|
||||
args:
|
||||
- -c
|
||||
- '(.a.b.c,.a,.a.p.b.z) |= {p: ., q: .}'
|
||||
input: '{"a":{"b":{"c":1}}}'
|
||||
expected: |
|
||||
{"a":{"p":{"b":{"c":{"p":1,"q":1},"z":{"p":null,"q":null}}},"q":{"b":{"c":{"p":1,"q":1}}}}}
|
||||
|
||||
- name: update-assignment operator with optional operator
|
||||
args:
|
||||
- -c
|
||||
@@ -5794,7 +6006,7 @@
|
||||
args:
|
||||
- -c
|
||||
- 'gmtime, localtime | map(. * 1000000 | floor / 1000000)'
|
||||
input: '1500000000 1567890123.456 1600000000.111'
|
||||
input: '1500000000 1567890123.456 1600000000.111 -1 -0.5 0.5'
|
||||
expected: | # UTC-7 (TZ=Etc/GMT+7)
|
||||
[2017,6,14,2,40,0,5,194]
|
||||
[2017,6,13,19,40,0,4,193]
|
||||
@@ -5802,6 +6014,12 @@
|
||||
[2019,8,7,14,2,3.456,6,249]
|
||||
[2020,8,13,12,26,40.111,0,256]
|
||||
[2020,8,13,5,26,40.111,0,256]
|
||||
[1969,11,31,23,59,59,3,364]
|
||||
[1969,11,31,16,59,59,3,364]
|
||||
[1969,11,31,23,59,59.5,3,364]
|
||||
[1969,11,31,16,59,59.5,3,364]
|
||||
[1970,0,1,0,0,0.5,4,0]
|
||||
[1969,11,31,17,0,0.5,3,364]
|
||||
|
||||
- name: mktime, strftime, strflocaltime, todate functions
|
||||
args:
|
||||
@@ -5813,6 +6031,7 @@
|
||||
[2020,8,13,12,26,40.111,0,256]
|
||||
[2024,7,20]
|
||||
[2025]
|
||||
[1969,11,31,23,59,59.5,3,364]
|
||||
expected: |
|
||||
[2017,6,14,2,40,0,5,194]
|
||||
2017-07-14T02:40:00Z
|
||||
@@ -5849,15 +6068,105 @@
|
||||
2024-12-31T00:00:00Z
|
||||
2024-12-30T17:00:00-0700
|
||||
2024-12-31T00:00:00Z
|
||||
[1969,11,31,23,59,59.5,3,364]
|
||||
1969-12-31T23:59:59Z
|
||||
1969-12-31T23:59:59-0700
|
||||
-0.5
|
||||
1969-12-31T23:59:59Z
|
||||
1969-12-31T16:59:59-0700
|
||||
1969-12-31T23:59:59Z
|
||||
|
||||
- name: date functions with fractional seconds
|
||||
args:
|
||||
- -r
|
||||
- 'mktime, strftime("%Y-%m-%dT%H:%M:%S.%fZ"), strflocaltime("%Y-%m-%dT%H:%M:%S.%f%z")'
|
||||
input: |
|
||||
[1970,0,1,0,0,-0.5]
|
||||
[1970,0,1,0,0,-1.25]
|
||||
[1970,0,1,0,0,-60.25]
|
||||
[1970,0,1,0,0,-1]
|
||||
[1970,0,1,0,0,0]
|
||||
[1970,0,1,0,0,0.5]
|
||||
[1970,0,1,0,0,60.25]
|
||||
expected: | # UTC-7 (TZ=Etc/GMT+7)
|
||||
-0.5
|
||||
1969-12-31T23:59:59.500000Z
|
||||
1969-12-31T23:59:59.500000-0700
|
||||
-1.25
|
||||
1969-12-31T23:59:58.750000Z
|
||||
1969-12-31T23:59:58.750000-0700
|
||||
-60.25
|
||||
1969-12-31T23:58:59.750000Z
|
||||
1969-12-31T23:58:59.750000-0700
|
||||
-1
|
||||
1969-12-31T23:59:59.000000Z
|
||||
1969-12-31T23:59:59.000000-0700
|
||||
0
|
||||
1970-01-01T00:00:00.000000Z
|
||||
1970-01-01T00:00:00.000000-0700
|
||||
0.5
|
||||
1970-01-01T00:00:00.500000Z
|
||||
1970-01-01T00:00:00.500000-0700
|
||||
60.25
|
||||
1970-01-01T00:01:00.250000Z
|
||||
1970-01-01T00:01:00.250000-0700
|
||||
|
||||
- name: mktime after subtracting fractional seconds
|
||||
args:
|
||||
- 'gmtime | .[5] -= 0.5 | mktime'
|
||||
input: '0 60 86400 -60'
|
||||
expected: |
|
||||
-0.5
|
||||
59.5
|
||||
86399.5
|
||||
-60.5
|
||||
|
||||
- name: date functions with non-finite or out of range epoch timestamps
|
||||
args:
|
||||
- -n
|
||||
- '[infinite, -infinite, nan, 1e19, -1e19, 9223372036854775807, -9223372036854775808] | .[] | try gmtime catch .'
|
||||
expected: |
|
||||
"gmtime cannot be applied to 1.7976931348623157e+308: number of seconds is out of range"
|
||||
"gmtime cannot be applied to -1.7976931348623157e+308: number of seconds is out of range"
|
||||
"gmtime cannot be applied to null: number of seconds is out of range"
|
||||
"gmtime cannot be applied to 10000000000000000000: number of seconds is out of range"
|
||||
"gmtime cannot be applied to -10000000000000000000: number of seconds is out of range"
|
||||
"gmtime cannot be applied to 9223372036854775807: number of seconds is out of range"
|
||||
"gmtime cannot be applied to -9223372036854775808: number of seconds is out of range"
|
||||
|
||||
- name: date functions with out of range numbers in the array
|
||||
args:
|
||||
- -n
|
||||
- '[[infinite,0,1], [nan,0,1], [1e19,0,1], [0,1e19,1], [2001,0,1,0,0,infinite], [2001,0,1,0,0,nan], [2001,0,1,0,0,1e19], [9223372036854775807,0,1], ["x",0,1]] | .[] | try mktime catch .'
|
||||
expected: |
|
||||
"mktime cannot be applied to [1.7976931348623157e+308,0,1]: number is out of range"
|
||||
"mktime cannot be applied to [null,0,1]: number is out of range"
|
||||
"mktime cannot be applied to [10000000000000000000,0,1]: number is out of range"
|
||||
"mktime cannot be applied to [0,10000000000000000000,1]: number is out of range"
|
||||
"mktime cannot be applied to [2001,0,1,0,0,1.797693134 ...]: number is out of range"
|
||||
"mktime cannot be applied to [2001,0,1,0,0,null]: number is out of range"
|
||||
"mktime cannot be applied to [2001,0,1,0,0,10000000000 ...]: number is out of range"
|
||||
"mktime cannot be applied to [9223372036854775807,0,1]: number is out of range"
|
||||
"mktime cannot be applied to [\"x\",0,1]: expected an array of 8 numbers"
|
||||
|
||||
- name: strftime function with out of range datetime
|
||||
args:
|
||||
- -n
|
||||
- 'try (infinite | todate) catch ., try ([1e19,0,1] | strftime("%Y")) catch .'
|
||||
expected: |
|
||||
"strftime(\"%Y-%m-%dT%H:%M:%SZ\") cannot be applied to 1.7976931348623157e+308: number of seconds is out of range"
|
||||
"strftime(\"%Y\") cannot be applied to [10000000000000000000,0,1]: number is out of range"
|
||||
|
||||
- name: strptime, fromdate functions
|
||||
args:
|
||||
- -c
|
||||
- 'strptime("%Y-%m-%dT%H:%M:%S%z"), fromdate'
|
||||
- 'strptime("%Y-%m-%dT%H:%M:%S%z")? // strptime("%Y-%m-%dT%H:%M:%S.%f%z"), fromdate?'
|
||||
input: |
|
||||
"2017-07-14T02:40:00Z"
|
||||
"2019-09-08T06:02:03+0900"
|
||||
"2020-09-13T07:26:40-05:00"
|
||||
"1969-12-31T23:59:59.5+0000"
|
||||
"2345-01-02T03:04:05.678901+0000"
|
||||
expected: |
|
||||
[2017,6,14,2,40,0,5,194]
|
||||
1500000000
|
||||
@@ -5865,6 +6174,8 @@
|
||||
1567890123
|
||||
[2020,8,13,12,26,40,0,256]
|
||||
1600000000
|
||||
[1969,11,31,23,59,59.5,3,364]
|
||||
[2345,0,2,3,4,5.678901,2,1]
|
||||
|
||||
- name: strftime, strptime functions
|
||||
args:
|
||||
@@ -6580,6 +6891,49 @@
|
||||
"aabcABC☆★☆ABCabc"
|
||||
"abcABC☆★☆ABCabc"
|
||||
|
||||
- name: match function with empty matches
|
||||
args:
|
||||
- -c
|
||||
- '[match("a*"; "g").offset], [scan("[a-z]*")], gsub("[0-9]*"; "#"), split("-*"; ""), [splits("\\s*")]'
|
||||
input: '"a1b-c d"'
|
||||
expected: |
|
||||
[0,1,2,3,4,5,6,7]
|
||||
["a","","b","","c","","d",""]
|
||||
"#a##b#-#c# #d#"
|
||||
["","a","1","b","","c"," ","d",""]
|
||||
["","a","1","b","-","c","","d",""]
|
||||
|
||||
- name: match function with empty matches and assertions
|
||||
args:
|
||||
- -c
|
||||
- 'gsub("(?m)^a*"; "#"), gsub("\\ba*"; "#"), gsub("(^|-)a*"; "#"), gsub("[a-z]*\\b"; "#")'
|
||||
input: '"a-a b"'
|
||||
expected: |
|
||||
"#-a b"
|
||||
"##-## #b#"
|
||||
"## b"
|
||||
"##-## ##"
|
||||
|
||||
- name: match function with empty matches and captures
|
||||
args:
|
||||
- -c
|
||||
- '[scan("(x)?a*")], [match("(a|)"; "g") | [.offset, .captures[0].offset]], [scan("[^a]*"; "i")]'
|
||||
input: '"1a-A"'
|
||||
expected: |
|
||||
[[null],[null],[null],[null],[null]]
|
||||
[[0,0],[1,1],[2,2],[3,3],[4,4]]
|
||||
["1","","-","",""]
|
||||
|
||||
- name: match function with empty matches against multibyte characters
|
||||
args:
|
||||
- -c
|
||||
- '[scan("[☆★]*")], [match("[☆★]*"; "g").offset], gsub("[☆★]*"; "#")'
|
||||
input: '"☆★1☆★"'
|
||||
expected: |
|
||||
["☆★","","☆★",""]
|
||||
[0,2,3,5]
|
||||
"##1##"
|
||||
|
||||
- name: INDEX function
|
||||
args:
|
||||
- -c
|
||||
@@ -6877,6 +7231,17 @@
|
||||
{"a":3}
|
||||
{"a":4}
|
||||
|
||||
- name: destructuring alternative operator with break in try
|
||||
args:
|
||||
- -c
|
||||
- 'first((. as {$a} ?// [$a] ?// $a | {$a})?)'
|
||||
input: '[1] [2] {"a":3} 4'
|
||||
expected: |
|
||||
{"a":1}
|
||||
{"a":2}
|
||||
{"a":3}
|
||||
{"a":4}
|
||||
|
||||
- name: destructuring alternative operator with halt
|
||||
args:
|
||||
- -c
|
||||
@@ -6896,6 +7261,28 @@
|
||||
[1]
|
||||
exit_code: 5
|
||||
|
||||
- name: destructuring alternative operator with halt_error in try
|
||||
args:
|
||||
- -c
|
||||
- 'try (. as {$a} ?// [$a] ?// $a | {$a}, halt_error)'
|
||||
input: '[1]'
|
||||
expected: |
|
||||
{"a":1}
|
||||
error: |
|
||||
[1]
|
||||
exit_code: 5
|
||||
|
||||
- name: destructuring alternative operator resets variables of later alternatives
|
||||
args:
|
||||
- -c
|
||||
- '.[] as [$a] ?// {$b} ?// $c | [$a, $b, $c]'
|
||||
input: '[{"b":1}, [2], 3, [4]]'
|
||||
expected: |
|
||||
[null,1,null]
|
||||
[2,null,null]
|
||||
[null,null,3]
|
||||
[4,null,null]
|
||||
|
||||
- name: destructuring alternative operator with multiple variables
|
||||
args:
|
||||
- -c
|
||||
@@ -7559,7 +7946,7 @@
|
||||
[["c"],3]
|
||||
[["c"]]
|
||||
|
||||
- name: stream option with unterminated input
|
||||
- name: stream option with unterminated input after a comma
|
||||
args:
|
||||
- -c
|
||||
- --stream
|
||||
@@ -7572,6 +7959,21 @@
|
||||
{"a":1,
|
||||
^ unexpected EOF
|
||||
|
||||
- name: stream option with unterminated input after a value
|
||||
args:
|
||||
- -c
|
||||
- --stream
|
||||
- '.'
|
||||
input: '[1,[2,3'
|
||||
expected: |
|
||||
[[0],1]
|
||||
[[1,0],2]
|
||||
[[1,1],3]
|
||||
error: |
|
||||
invalid json: <stdin>
|
||||
[1,[2,3
|
||||
^ unexpected EOF
|
||||
|
||||
- name: yaml input option
|
||||
args:
|
||||
- --yaml-input
|
||||
@@ -7741,6 +8143,17 @@
|
||||
2 | bar: ]
|
||||
^ did not find expected node content
|
||||
|
||||
- name: yaml input option error with multibyte characters
|
||||
args:
|
||||
- --yaml-input
|
||||
- '.'
|
||||
input: |
|
||||
{123: 4, 123: 5}
|
||||
error: |
|
||||
invalid yaml: <stdin>:1
|
||||
1 | {123: 4, 123: 5}
|
||||
^ mapping key "123" already defined at line 1
|
||||
|
||||
- name: yaml input option multiple errors
|
||||
args:
|
||||
- --yaml-input
|
||||
|
||||
+55
-29
@@ -17,6 +17,7 @@ type compiler struct {
|
||||
variables []string
|
||||
customFuncs map[string]function
|
||||
inputIter Iter
|
||||
inputMu sync.Mutex
|
||||
codes []*code
|
||||
codeinfos []codeinfo
|
||||
builtinScope *scopeinfo
|
||||
@@ -132,18 +133,13 @@ func (c *compiler) compile(q *Query) error {
|
||||
}
|
||||
|
||||
func (c *compiler) compileImport(i *Import) error {
|
||||
var path, alias string
|
||||
var err error
|
||||
if i.ImportPath != "" {
|
||||
path, alias = i.ImportPath, i.ImportAlias
|
||||
} else {
|
||||
path = i.IncludePath
|
||||
}
|
||||
path, alias := cmp.Or(i.ImportPath, i.IncludePath), i.ImportAlias
|
||||
if c.moduleLoader == nil {
|
||||
return fmt.Errorf("cannot load module: %q", path)
|
||||
}
|
||||
if strings.HasPrefix(alias, "$") {
|
||||
var vals any
|
||||
var err error
|
||||
if moduleLoader, ok := c.moduleLoader.(interface {
|
||||
LoadJSONWithMeta(string, map[string]any) (any, error)
|
||||
}); ok {
|
||||
@@ -166,6 +162,7 @@ func (c *compiler) compileImport(i *Import) error {
|
||||
return nil
|
||||
}
|
||||
var q *Query
|
||||
var err error
|
||||
if moduleLoader, ok := c.moduleLoader.(interface {
|
||||
LoadModuleWithMeta(string, map[string]any) (*Query, error)
|
||||
}); ok {
|
||||
@@ -532,23 +529,36 @@ func (c *compiler) compileBind(l, r *Query, patterns []*Pattern) error {
|
||||
if err := c.compileQuery(l); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.compilePatterns(patterns, func() error {
|
||||
if len(patterns) == 1 && c.codes[len(c.codes)-2].op == opexpbegin {
|
||||
c.codes[len(c.codes)-2].op = opnop
|
||||
} else {
|
||||
c.append(&code{op: opexpend})
|
||||
}
|
||||
return c.compileQuery(r)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *compiler) compilePatterns(patterns []*Pattern, body func() error) error {
|
||||
var pc int
|
||||
var vs [][2]int
|
||||
if len(patterns) > 1 {
|
||||
for _, p := range patterns {
|
||||
vs = c.pushPatternVariables(vs, p)
|
||||
}
|
||||
}
|
||||
for i, p := range patterns {
|
||||
var pcc int
|
||||
var err error
|
||||
if i < len(patterns)-1 {
|
||||
defer c.lazy(func() *code {
|
||||
return &code{op: opforkalt, v: pcc}
|
||||
})()
|
||||
}
|
||||
if 0 < i {
|
||||
for _, v := range vs {
|
||||
c.append(&code{op: oppush, v: nil})
|
||||
c.append(&code{op: opstore, v: v})
|
||||
}
|
||||
}
|
||||
if vs, err = c.compilePattern(vs[:0], p); err != nil {
|
||||
if _, err := c.compilePattern(nil, p); err != nil {
|
||||
return err
|
||||
}
|
||||
if i < len(patterns)-1 {
|
||||
@@ -561,12 +571,29 @@ func (c *compiler) compileBind(l, r *Query, patterns []*Pattern) error {
|
||||
if len(patterns) > 1 {
|
||||
pc = len(c.codes)
|
||||
}
|
||||
if len(patterns) == 1 && c.codes[len(c.codes)-2].op == opexpbegin {
|
||||
c.codes[len(c.codes)-2].op = opnop
|
||||
} else {
|
||||
c.append(&code{op: opexpend})
|
||||
return body()
|
||||
}
|
||||
|
||||
func (c *compiler) pushPatternVariables(vs [][2]int, p *Pattern) [][2]int {
|
||||
if p.Name != "" {
|
||||
if v := c.pushVariable(p.Name); !slices.Contains(vs, v) {
|
||||
vs = append(vs, v)
|
||||
}
|
||||
return c.compileQuery(r)
|
||||
} else if len(p.Array) > 0 {
|
||||
for _, p := range p.Array {
|
||||
vs = c.pushPatternVariables(vs, p)
|
||||
}
|
||||
} else if len(p.Object) > 0 {
|
||||
for _, kv := range p.Object {
|
||||
if kv.Key != "" && kv.Key[0] == '$' {
|
||||
vs = c.pushPatternVariables(vs, &Pattern{Name: kv.Key})
|
||||
}
|
||||
if kv.Val != nil {
|
||||
vs = c.pushPatternVariables(vs, kv.Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
func (c *compiler) compilePattern(vs [][2]int, p *Pattern) ([][2]int, error) {
|
||||
@@ -726,17 +753,19 @@ func (c *compiler) compileReduce(e *Reduce) error {
|
||||
if err := c.compileQuery(e.Query); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.compilePattern(nil, e.Pattern); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.compilePatterns(e.Patterns, func() error {
|
||||
c.append(&code{op: opload, v: v})
|
||||
f = c.newScopeDepth()
|
||||
f := c.newScopeDepth()
|
||||
if err := c.compileQuery(e.Update); err != nil {
|
||||
return err
|
||||
}
|
||||
f()
|
||||
c.append(&code{op: opstore, v: v})
|
||||
c.append(&code{op: opbacktrack})
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
setfork()
|
||||
c.append(&code{op: oppop})
|
||||
c.append(&code{op: opload, v: v})
|
||||
@@ -757,11 +786,9 @@ func (c *compiler) compileForeach(e *Foreach) error {
|
||||
if err := c.compileQuery(e.Query); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.compilePattern(nil, e.Pattern); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.compilePatterns(e.Patterns, func() error {
|
||||
c.append(&code{op: opload, v: v})
|
||||
f = c.newScopeDepth()
|
||||
f := c.newScopeDepth()
|
||||
if err := c.compileQuery(e.Update); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -773,6 +800,7 @@ func (c *compiler) compileForeach(e *Foreach) error {
|
||||
return c.compileQuery(e.Extract)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (c *compiler) compileLabel(e *Label) error {
|
||||
@@ -1207,6 +1235,8 @@ func (c *compiler) funcBuiltins(any, []any) any {
|
||||
}
|
||||
|
||||
func (c *compiler) funcInput(any, []any) any {
|
||||
c.inputMu.Lock()
|
||||
defer c.inputMu.Unlock()
|
||||
v, ok := c.inputIter.Next()
|
||||
if !ok {
|
||||
return errors.New("break")
|
||||
@@ -1274,11 +1304,7 @@ func listModuleDeps(q *Query) []any {
|
||||
if v == nil {
|
||||
v = make(map[string]any)
|
||||
}
|
||||
relpath := i.ImportPath
|
||||
if relpath == "" {
|
||||
relpath = i.IncludePath
|
||||
}
|
||||
v["relpath"] = relpath
|
||||
v["relpath"] = cmp.Or(i.ImportPath, i.IncludePath)
|
||||
if i.ImportAlias != "" {
|
||||
v["as"] = strings.TrimPrefix(i.ImportAlias, "$")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
package gojq_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/itchyny/gojq"
|
||||
)
|
||||
|
||||
func ExampleCompile() {
|
||||
query, err := gojq.Parse(".[] | .foo")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
iter := code.Run([]any{
|
||||
nil,
|
||||
"string",
|
||||
42,
|
||||
[]any{"foo"},
|
||||
map[string]any{"foo": 42},
|
||||
})
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%#v\n", v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// <nil>
|
||||
// expected an object but got: string ("string")
|
||||
// expected an object but got: number (42)
|
||||
// expected an object but got: array (["foo"])
|
||||
// 42
|
||||
}
|
||||
|
||||
func ExampleCode_Run() {
|
||||
query, err := gojq.Parse(".foo")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
input := map[string]any{"foo": 42}
|
||||
iter := code.Run(input)
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Printf("%#v\n", v)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// 42
|
||||
}
|
||||
|
||||
func ExampleCode_RunWithContext() {
|
||||
query, err := gojq.Parse("def f: f; f, f")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
||||
defer cancel()
|
||||
iter := code.RunWithContext(ctx, nil)
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
_ = v
|
||||
}
|
||||
|
||||
// Output:
|
||||
// context deadline exceeded
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeConstants(t *testing.T) {
|
||||
query, err := gojq.Parse(`[1,{foo:2,"bar":+3},[-4]]`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 3; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if expected := []any{
|
||||
1, map[string]any{"foo": 2, "bar": 3}, []any{-4},
|
||||
}; !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeIndexSlice(t *testing.T) {
|
||||
query, err := gojq.Parse(`.foo."bar".["baz"].[-1]."".[0:1]`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 8; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if got != nil {
|
||||
t.Errorf("expected: %v, got: %v", nil, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeIndexSliceAssign(t *testing.T) {
|
||||
query, err := gojq.Parse(`.foo."bar".["baz"].[0]."".[0:1] = [0]`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 8; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if expected := map[string]any{
|
||||
"foo": map[string]any{
|
||||
"bar": map[string]any{
|
||||
"baz": []any{map[string]any{"": []any{0}}},
|
||||
},
|
||||
},
|
||||
}; !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeTailRec_While(t *testing.T) {
|
||||
query, err := gojq.Parse("0 | while(. < 10; . + 1)")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 48; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
op1 := codes.Index(2).Elem().FieldByName("op")
|
||||
op2 := codes.Index(21).Elem().FieldByName("op") // test jump of call _while
|
||||
if got, expected := *(*int)(unsafe.Pointer(op2.UnsafeAddr())),
|
||||
*(*int)(unsafe.Pointer(op1.UnsafeAddr())); expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
n := 0
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if !reflect.DeepEqual(got, n) {
|
||||
t.Errorf("expected: %v, got: %v", n, got)
|
||||
}
|
||||
n++
|
||||
}
|
||||
if expected := 10; n != expected {
|
||||
t.Errorf("expected: %v, got: %v", expected, n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeTailRec_CallRec(t *testing.T) {
|
||||
query, err := gojq.Parse(`
|
||||
def f: . as $x | $x, (if $x < 3 then $x + 1 | f else empty end), $x; f
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 48; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
op1 := codes.Index(39).Elem().FieldByName("op") // callrec f
|
||||
op2 := codes.Index(38).Elem().FieldByName("op") // call _add/2
|
||||
if got, expected := *(*int)(unsafe.Pointer(op2.UnsafeAddr()))+1,
|
||||
*(*int)(unsafe.Pointer(op1.UnsafeAddr())); expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeCompile_OptimizeJumps(t *testing.T) {
|
||||
query, err := gojq.Parse("def f: 1; def g: 2; def h: 3; f")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes := reflect.ValueOf(code).Elem().FieldByName("codes")
|
||||
if got, expected := codes.Len(), 15; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
v := codes.Index(1).Elem().FieldByName("v")
|
||||
if got, expected := *(*any)(unsafe.Pointer(v.UnsafeAddr())), 13; expected != got {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if expected := 1; !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("expected: %v, got: %v", expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseErrorTokenOffset(t *testing.T) {
|
||||
testCases := []struct {
|
||||
src string
|
||||
offset int
|
||||
}{
|
||||
{src: "^", offset: 1},
|
||||
{src: " ^", offset: 2},
|
||||
{src: " ^ ", offset: 2},
|
||||
{src: "👍", offset: 4},
|
||||
{src: " 👍", offset: 5},
|
||||
{src: " 👍 ", offset: 5},
|
||||
{src: "test👍", offset: 8},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.src, func(t *testing.T) {
|
||||
_, err := gojq.Parse(tc.src)
|
||||
if err == nil {
|
||||
t.Fatal("expected: error")
|
||||
}
|
||||
var pe *gojq.ParseError
|
||||
if !errors.As(err, &pe) {
|
||||
t.Fatalf("expected: *gojq.ParseError, got %v", err)
|
||||
}
|
||||
if pe.Offset != tc.offset {
|
||||
t.Fatalf("expected: %v, got %v", tc.offset, pe.Offset)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodeRun_Race(t *testing.T) {
|
||||
query, err := gojq.Parse("range(10)")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
for range 10 {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
iter := code.Run(nil)
|
||||
n := 0
|
||||
for {
|
||||
got, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if got != n {
|
||||
t.Errorf("expected: %v, got: %v", n, got)
|
||||
}
|
||||
n++
|
||||
}
|
||||
if expected := 10; n != expected {
|
||||
t.Errorf("expected: %v, got: %v", expected, n)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestCodeRun_RaceRegexp(t *testing.T) {
|
||||
query, err := gojq.Parse(`
|
||||
"abcdefg" | test("cd"), split("e") | . == true or length > 0
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
for range 10 {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
iter := code.Run(nil)
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if v != true {
|
||||
t.Errorf("expected: true, got: %v", v)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestCodeRun_RaceInputIter(t *testing.T) {
|
||||
const n = 10
|
||||
values := make([]int, n)
|
||||
for i := range values {
|
||||
values[i] = i
|
||||
}
|
||||
query, err := gojq.Parse("input")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, err := gojq.Compile(query, gojq.WithInputIter(gojq.NewIter(values...)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var seen sync.Map
|
||||
var wg sync.WaitGroup
|
||||
for range n {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
v, ok := code.Run(nil).Next()
|
||||
if !ok {
|
||||
t.Error("expected a value")
|
||||
return
|
||||
}
|
||||
if _, loaded := seen.LoadOrStore(v, struct{}{}); loaded {
|
||||
t.Errorf("input %v was consumed more than once", v)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
for i := range n {
|
||||
if _, ok := seen.Load(i); !ok {
|
||||
t.Errorf("expected input %d to be consumed", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCompile(b *testing.B) {
|
||||
cnt, err := os.ReadFile("builtin.jq")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
query, err := gojq.Parse(string(cnt) + ".")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
for b.Loop() {
|
||||
_, err := gojq.Compile(
|
||||
query,
|
||||
gojq.WithInputIter(gojq.NewIter[any]()),
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user