Debianized package
Debian Janitor
1 year, 5 months ago
0 | name: CI | |
1 | ||
2 | on: | |
3 | push: | |
4 | branches: | |
5 | - main | |
6 | pull_request: | |
7 | ||
8 | jobs: | |
9 | test: | |
10 | name: Test | |
11 | runs-on: ${{ matrix.os }} | |
12 | strategy: | |
13 | matrix: | |
14 | os: [ubuntu-latest, macos-latest, windows-latest] | |
15 | go: [1.19.x, 1.18.x, 1.17.x] | |
16 | steps: | |
17 | - name: Checkout code | |
18 | uses: actions/checkout@v3 | |
19 | - name: Setup Go | |
20 | uses: actions/setup-go@v3 | |
21 | with: | |
22 | go-version: ${{ matrix.go }} | |
23 | - name: Test | |
24 | run: make test | |
25 | - name: Test with GOARCH=386 | |
26 | run: env GOARCH=386 go test -v ./... | |
27 | if: matrix.os != 'macos-latest' | |
28 | - name: Lint | |
29 | run: make lint | |
30 | - name: Check tools | |
31 | run: make check-tools | |
32 | - name: Check go generate | |
33 | run: go generate && ! git diff | grep ^ | |
34 | shell: bash | |
35 | - name: Check command examples in README.md | |
36 | run: | | |
37 | ./gojq -Rnr 'reduce inputs as $x ( | |
38 | {}; | |
39 | if $x|test("^ [$] .*gojq|^```") | |
40 | then | |
41 | if .command | |
42 | then .results += [{command: .command, output: .output}] | del(.command,.output) | |
43 | end | if $x|test("gojq") then .command = $x[3:] end | |
44 | elif .command then .output += ($x + "\n" | sub(" +#.*"; "")) | |
45 | end | |
46 | ) | .results[] | | |
47 | "if got=$(diff <(printf %s \(.output | @sh)) \\ | |
48 | <(\(.command | gsub("gojq"; "./gojq")) 2>&1)); then | |
49 | echo ok: \(.command | @sh) | |
50 | else | |
51 | echo ng: \(.command | @sh); echo \"$got\"; exit 1 | |
52 | fi" | |
53 | ' README.md | bash | |
54 | shell: bash |
0 | name: Release | |
1 | ||
2 | on: | |
3 | push: | |
4 | tags: | |
5 | - 'v*' | |
6 | ||
7 | jobs: | |
8 | ||
9 | release: | |
10 | name: Release | |
11 | runs-on: ubuntu-latest | |
12 | steps: | |
13 | ||
14 | - name: Checkout code | |
15 | uses: actions/checkout@v3 | |
16 | ||
17 | - name: Setup Go | |
18 | uses: actions/setup-go@v3 | |
19 | with: | |
20 | go-version: 1.x | |
21 | ||
22 | - name: Cross build | |
23 | run: make cross | |
24 | ||
25 | - name: Create Release | |
26 | id: create_release | |
27 | uses: actions/create-release@v1 | |
28 | env: | |
29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
30 | with: | |
31 | tag_name: ${{ github.ref }} | |
32 | release_name: Release ${{ github.ref }} | |
33 | ||
34 | - name: Upload | |
35 | run: make upload | |
36 | env: | |
37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
38 | ||
39 | - name: Docker metadata | |
40 | uses: docker/metadata-action@v4 | |
41 | id: metadata | |
42 | with: | |
43 | images: | | |
44 | ${{ github.repository }} | |
45 | ghcr.io/${{ github.repository }} | |
46 | tags: | | |
47 | type=semver,pattern={{version}} | |
48 | type=semver,pattern={{major}}.{{minor}} | |
49 | type=semver,pattern={{major}} | |
50 | ||
51 | - name: Set up QEMU | |
52 | uses: docker/setup-qemu-action@v2 | |
53 | ||
54 | - name: Set up Docker Buildx | |
55 | uses: docker/setup-buildx-action@v2 | |
56 | ||
57 | - name: Login to Docker Hub | |
58 | uses: docker/login-action@v2 | |
59 | with: | |
60 | username: ${{ secrets.DOCKER_USERNAME }} | |
61 | password: ${{ secrets.DOCKER_PASSWORD }} | |
62 | ||
63 | - name: Login to GitHub Container Registry | |
64 | uses: docker/login-action@v2 | |
65 | with: | |
66 | registry: ghcr.io | |
67 | username: ${{ github.repository_owner }} | |
68 | password: ${{ secrets.GITHUB_TOKEN }} | |
69 | ||
70 | - name: Build and release Docker image | |
71 | uses: docker/build-push-action@v3 | |
72 | with: | |
73 | context: . | |
74 | push: true | |
75 | platforms: linux/amd64, linux/arm64 | |
76 | tags: ${{ steps.metadata.outputs.tags }} | |
77 | labels: ${{ steps.metadata.outputs.labels }} |
32 | 32 | |
33 | 33 | .PHONY: install |
34 | 34 | install: |
35 | go install -ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) | |
35 | go install -ldflags=$(BUILD_LDFLAGS) ./... | |
36 | 36 | |
37 | 37 | .PHONY: install-dev |
38 | 38 | install-dev: parser.go builtin.go |
39 | go install -ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) | |
39 | go install -ldflags=$(BUILD_LDFLAGS) ./... | |
40 | 40 | |
41 | 41 | .PHONY: install-debug |
42 | 42 | install-debug: parser.go builtin.go |
43 | go install -tags gojq_debug -ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) | |
43 | go install -tags gojq_debug -ldflags=$(BUILD_LDFLAGS) ./... | |
44 | 44 | |
45 | 45 | .PHONY: show-version |
46 | 46 | show-version: $(GOBIN)/gobump |
77 | 77 | - gojq does not keep the order of object keys. I understand this might cause problems for some scripts but basically, we should not rely on the order of object keys. Due to this limitation, gojq does not have `keys_unsorted` function and `--sort-keys` (`-S`) option. I would implement when ordered map is implemented in the standard library of Go but I'm less motivated. |
78 | 78 | - gojq supports arbitrary-precision integer calculation while jq does not; jq loses the precision of large integers when calculation is involved. Note that even with gojq, all mathematical functions, including `floor` and `round`, convert integers to floating-point numbers; only addition, subtraction, multiplication, modulo, and division operators (when divisible) keep the integer precision. To calculate floor division of integers without losing the precision, use `def idivide($n): (. - . % $n) / $n;`. To round down floating-point numbers to integers, use `def ifloor: floor | tostring | tonumber;`, but note that this function does not work with large floating-point numbers and also loses the precision of large integers. |
79 | 79 | - gojq fixes various bugs of jq. gojq correctly deletes elements of arrays by `|= empty` ([jq#2051](https://github.com/stedolan/jq/issues/2051)). gojq fixes `try`/`catch` handling ([jq#1859](https://github.com/stedolan/jq/issues/1859), [jq#1885](https://github.com/stedolan/jq/issues/1885), [jq#2140](https://github.com/stedolan/jq/issues/2140)). gojq fixes `nth/2` to output nothing when the count is equal to or larger than the stream size ([jq#1867](https://github.com/stedolan/jq/issues/1867)). gojq consistently counts by characters (not by bytes) in `index`, `rindex`, and `indices` functions; `"12345" | .[index("3"):]` results in `"345"` ([jq#1430](https://github.com/stedolan/jq/issues/1430), [jq#1624](https://github.com/stedolan/jq/issues/1624)). gojq handles overlapping occurrence differently in `rindex` and `indices`; `"ababa" | [rindex("aba"), indices("aba")]` results in `[2,[0,2]]` ([jq#2433](https://github.com/stedolan/jq/issues/2433)). gojq supports string indexing; `"abcde"[2]` ([jq#1520](https://github.com/stedolan/jq/issues/1520)). gojq accepts indexing query `.e0` ([jq#1526](https://github.com/stedolan/jq/issues/1526), [jq#1651](https://github.com/stedolan/jq/issues/1651)), and allows `gsub` to handle patterns including `"^"` ([jq#2148](https://github.com/stedolan/jq/issues/2148)). gojq improves variable lexer to allow using keywords for variable names, especially in binding patterns, also disallows spaces after `$` ([jq#526](https://github.com/stedolan/jq/issues/526)). gojq fixes handling files with no newline characters at the end ([jq#2374](https://github.com/stedolan/jq/issues/2374)). |
80 | - gojq truncates down floating-point numbers on indexing (`[0] | .[0.5]` results in `0` not `null`), and slicing (`[0,1,2] | .[0.5:1.5]` results in `[0]` not `[0,1]`). gojq parses unary operators with higher precedence than variable binding (`[-1 as $x | 1,$x]` results in `[1,-1]` not `[-1,-1]`). gojq implements `@uri` to escape all the reserved characters defined in RFC 3986, Sec. 2.2 ([jq#1506](https://github.com/stedolan/jq/issues/1506)), and fixes `@base64d` to allow binary string as the decoded string ([jq#1931](https://github.com/stedolan/jq/issues/1931)). gojq improves time formatting and parsing; deals with `%f` in `strftime` and `strptime` ([jq#1409](https://github.com/stedolan/jq/issues/1409)), parses timezone offsets with `fromdate` and `fromdateiso8601` ([jq#1053](https://github.com/stedolan/jq/issues/1053)), supports timezone name/offset with `%Z`/`%z` in `strptime` ([jq#929](https://github.com/stedolan/jq/issues/929), [jq#2195](https://github.com/stedolan/jq/issues/2195)), and looks up correct timezone during daylight saving time on formatting with `%Z` ([jq#1912](https://github.com/stedolan/jq/issues/1912)). gojq supports nanoseconds in date and time functions. | |
81 | - gojq does not support some functions intentionally; `get_jq_origin`, `get_prog_origin`, `get_search_list` (unstable, not listed in jq document), `input_line_number`, `$__loc__` (performance issue), `recurse_down` (deprecated in jq). gojq does not support some flags; `--ascii-output, -a` (performance issue), `--seq` (not used commonly), `--sort-keys, -S` (sorts by default because `map[string]interface{}` does not keep the order), `--unbuffered` (unbuffered by default). gojq does not parse JSON extensions supported by jq; `NaN`, `Infinity`, and `[000]`. gojq normalizes floating-point numbers to fit to double-precision floating-point numbers. gojq does not support some regular expression flags (regular expression engine differences). gojq does not support BOM (`encoding/json` does not support this). gojq disallows using keywords for function names (`def true: .; true` is a confusing query), and module name prefixes in function declarations (using module prefixes like `def m::f: .;` is undocumented). | |
80 | - gojq implements `@uri` to escape all the reserved characters defined in RFC 3986, Sec. 2.2 ([jq#1506](https://github.com/stedolan/jq/issues/1506)), and fixes `@base64d` to allow binary string as the decoded string ([jq#1931](https://github.com/stedolan/jq/issues/1931)). gojq improves time formatting and parsing; deals with `%f` in `strftime` and `strptime` ([jq#1409](https://github.com/stedolan/jq/issues/1409)), parses timezone offsets with `fromdate` and `fromdateiso8601` ([jq#1053](https://github.com/stedolan/jq/issues/1053)), supports timezone name/offset with `%Z`/`%z` in `strptime` ([jq#929](https://github.com/stedolan/jq/issues/929), [jq#2195](https://github.com/stedolan/jq/issues/2195)), and looks up correct timezone during daylight saving time on formatting with `%Z` ([jq#1912](https://github.com/stedolan/jq/issues/1912)). gojq supports nanoseconds in date and time functions. | |
81 | - gojq does not support some functions intentionally; `get_jq_origin`, `get_prog_origin`, `get_search_list` (unstable, not listed in jq document), `input_line_number`, `$__loc__` (performance issue), `recurse_down` (deprecated in jq). gojq does not support some flags; `--ascii-output, -a` (performance issue), `--seq` (not used commonly), `--sort-keys, -S` (sorts by default because `map[string]interface{}` does not keep the order), `--unbuffered` (unbuffered by default). gojq does not parse JSON extensions supported by jq; `NaN`, `Infinity`, and `[000]`. gojq normalizes floating-point numbers to fit to double-precision floating-point numbers. gojq does not support some regular expression flags (regular expression engine differences). gojq does not support BOM (`encoding/json` does not support this). gojq disallows using keywords for function names (declaration of `def true: .;` is a confusing query). | |
82 | 82 | - gojq supports reading from YAML input (`--yaml-input`) while jq does not. gojq also supports YAML output (`--yaml-output`). |
83 | 83 | |
84 | 84 | ### Color configuration |
45 | 45 | fd.Minify() |
46 | 46 | fds[fd.Name] = append(fds[fd.Name], fd) |
47 | 47 | } |
48 | fds["_assign"] = nil | |
49 | fds["_modify"] = nil | |
50 | 48 | t, err := astgen.Build(fds) |
51 | 49 | if err != nil { |
52 | 50 | return err |
6 | 6 | "IN": []*FuncDef{&FuncDef{Name: "IN", Args: []string{"s"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{&Query{Left: &Query{Func: "s"}, Op: OpEq, Right: &Query{Func: "."}}, &Query{Func: "."}}}}}}, &FuncDef{Name: "IN", Args: []string{"src", "s"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{&Query{Left: &Query{Func: "src"}, Op: OpEq, Right: &Query{Func: "s"}}, &Query{Func: "."}}}}}}}, |
7 | 7 | "INDEX": []*FuncDef{&FuncDef{Name: "INDEX", Args: []string{"stream", "idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "stream"}}, Pattern: &Pattern{Name: "$row"}, Start: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Left: &Query{Func: "$row"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "idx_expr"}, Op: OpPipe, Right: &Query{Func: "tostring"}}}}}}, Op: OpAssign, Right: &Query{Func: "$row"}}}}}}, &FuncDef{Name: "INDEX", Args: []string{"idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "INDEX", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &Query{Func: "idx_expr"}}}}}}}, |
8 | 8 | "JOIN": []*FuncDef{&FuncDef{Name: "JOIN", Args: []string{"$idx", "idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Func: "idx_expr"}}}}}}}}}}}}}}}, &FuncDef{Name: "JOIN", Args: []string{"$idx", "stream", "idx_expr"}, Body: &Query{Left: &Query{Func: "stream"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Func: "idx_expr"}}}}}}}}}}}}, &FuncDef{Name: "JOIN", Args: []string{"$idx", "stream", "idx_expr", "join_expr"}, Body: &Query{Left: &Query{Func: "stream"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Func: "idx_expr"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "join_expr"}}}}}, |
9 | "_assign": []*FuncDef{}, | |
10 | "_modify": []*FuncDef{}, | |
9 | "_assign": []*FuncDef{&FuncDef{Name: "_assign", Args: []string{"ps", "$v"}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Func: "ps"}}}}, Pattern: &Pattern{Name: "$p"}, Start: &Query{Func: "."}, Update: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Func: "$p"}, &Query{Func: "$v"}}}}}}}}}}, | |
10 | "_modify": []*FuncDef{&FuncDef{Name: "_modify", Args: []string{"ps", "f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Func: "ps"}}}}, Pattern: &Pattern{Name: "$p"}, Start: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}}, Update: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, Op: OpAdd, Right: &Query{Func: "$p"}}, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$q"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Func: "$q"}, &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{&Query{Func: "$q"}}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}}}}}}}}}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}, &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "$p"}}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "delpaths", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}}}}}}}}}, | |
11 | 11 | "all": []*FuncDef{&FuncDef{Name: "all", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "all", Args: []*Query{&Query{Func: "."}}}}}}, &FuncDef{Name: "all", Args: []string{"y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "all", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &Query{Func: "y"}}}}}}, &FuncDef{Name: "all", Args: []string{"g", "y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isempty", Args: []*Query{&Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "y"}, Op: OpPipe, Right: &Query{Func: "not"}}}}}}}}}}}}}, |
12 | 12 | "any": []*FuncDef{&FuncDef{Name: "any", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{&Query{Func: "."}}}}}}, &FuncDef{Name: "any", Args: []string{"y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &Query{Func: "y"}}}}}}, &FuncDef{Name: "any", Args: []string{"g", "y"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isempty", Args: []*Query{&Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Func: "y"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "not"}}}}, |
13 | 13 | "arrays": []*FuncDef{&FuncDef{Name: "arrays", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}}}}}}}, |
14 | "ascii_downcase": []*FuncDef{&FuncDef{Name: "ascii_downcase", Body: &Query{Left: &Query{Func: "explode"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeNumber, Number: "65"}}, Op: OpLe, Right: &Query{Func: "."}}, Op: OpAnd, Right: &Query{Left: &Query{Func: "."}, Op: OpLe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "90"}}}}, Then: &Query{Left: &Query{Func: "."}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "32"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "implode"}}}}}, | |
15 | "ascii_upcase": []*FuncDef{&FuncDef{Name: "ascii_upcase", Body: &Query{Left: &Query{Func: "explode"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeNumber, Number: "97"}}, Op: OpLe, Right: &Query{Func: "."}}, Op: OpAnd, Right: &Query{Left: &Query{Func: "."}, Op: OpLe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "122"}}}}, Then: &Query{Left: &Query{Func: "."}, Op: OpSub, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "32"}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "implode"}}}}}, | |
14 | 16 | "booleans": []*FuncDef{&FuncDef{Name: "booleans", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "boolean"}}}}}}}}}}, |
15 | 17 | "capture": []*FuncDef{&FuncDef{Name: "capture", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "capture", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "capture", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}}}}}, Op: OpPipe, Right: &Query{Func: "_capture"}}}}, |
16 | 18 | "combinations": []*FuncDef{&FuncDef{Name: "combinations", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "$x"}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, IsSlice: true}}}, Op: OpPipe, Right: &Query{Func: "combinations"}}}}}}}}}}}}}}, &FuncDef{Name: "combinations", Args: []string{"n"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "limit", Args: []*Query{&Query{Func: "n"}, &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "repeat", Args: []*Query{&Query{Func: "."}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "combinations"}}}}, |
19 | 21 | "first": []*FuncDef{&FuncDef{Name: "first", Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}, &FuncDef{Name: "first", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}}}}}}}, |
20 | 22 | "fromdate": []*FuncDef{&FuncDef{Name: "fromdate", Body: &Query{Func: "fromdateiso8601"}}}, |
21 | 23 | "fromdateiso8601": []*FuncDef{&FuncDef{Name: "fromdateiso8601", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strptime", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%S%z"}}}}}}}, Op: OpPipe, Right: &Query{Func: "mktime"}}}}, |
22 | "fromstream": []*FuncDef{&FuncDef{Name: "fromstream", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "x", Val: &ObjectVal{Queries: []*Query{&Query{Func: "null"}}}}, &ObjectKeyVal{Key: "e", Val: &ObjectVal{Queries: []*Query{&Query{Func: "false"}}}}}}, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$init"}}, Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}, Pattern: &Pattern{Name: "$i"}, Start: &Query{Func: "$init"}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Func: "$init"}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$i"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "2"}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "x"}}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}, &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}, Extract: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "x"}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}}}, | |
24 | "fromstream": []*FuncDef{&FuncDef{Name: "fromstream", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "x", Val: &ObjectVal{Queries: []*Query{&Query{Func: "null"}}}}, &ObjectKeyVal{Key: "e", Val: &ObjectVal{Queries: []*Query{&Query{Func: "false"}}}}}}, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$init"}}, Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}, Pattern: &Pattern{Name: "$i"}, Start: &Query{Func: "$init"}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Func: "$init"}, Else: &Query{Func: "."}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$i"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "2"}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "x"}}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}, &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$i"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}}}}}, Extract: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "x"}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}}}, | |
23 | 25 | "group_by": []*FuncDef{&FuncDef{Name: "group_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_group_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, |
24 | 26 | "gsub": []*FuncDef{&FuncDef{Name: "gsub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "str"}, &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}, &FuncDef{Name: "gsub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "str"}, &Query{Left: &Query{Func: "$flags"}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}}}, |
25 | 27 | "in": []*FuncDef{&FuncDef{Name: "in", Args: []string{"xs"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Left: &Query{Func: "xs"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "has", Args: []*Query{&Query{Func: "$x"}}}}}}}}}}}}}, |
32 | 34 | "limit": []*FuncDef{&FuncDef{Name: "limit", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$n"}, Op: OpGt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Func: "$n"}, Update: &Query{Left: &Query{Func: "."}, Op: OpSub, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}, Extract: &Query{Left: &Query{Func: "$item"}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "."}, Op: OpLe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}, Else: &Query{Func: "empty"}}}}}}}}}}}, Elif: []*IfElif{&IfElif{Cond: &Query{Left: &Query{Func: "$n"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Func: "empty"}}}, Else: &Query{Func: "g"}}}}}}, |
33 | 35 | "map": []*FuncDef{&FuncDef{Name: "map", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}}}}, |
34 | 36 | "map_values": []*FuncDef{&FuncDef{Name: "map_values", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Op: OpModify, Right: &Query{Func: "f"}}}}, |
35 | "match": []*FuncDef{&FuncDef{Name: "match", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "match", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}, &Query{Func: "false"}}}, SuffixList: []*Suffix{&Suffix{Iter: true}}}}}}, | |
37 | "match": []*FuncDef{&FuncDef{Name: "match", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "match", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}, &Query{Func: "false"}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}}}}, | |
36 | 38 | "max_by": []*FuncDef{&FuncDef{Name: "max_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_max_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, |
37 | 39 | "min_by": []*FuncDef{&FuncDef{Name: "min_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_min_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, |
38 | 40 | "normals": []*FuncDef{&FuncDef{Name: "normals", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Func: "isnormal"}}}}}}}, |
46 | 48 | "recurse": []*FuncDef{&FuncDef{Name: "recurse", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "recurse", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Optional: true}}}}}}}}}, &FuncDef{Name: "recurse", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "r", Body: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Func: "r"}}}}}}}, Func: "r"}}, &FuncDef{Name: "recurse", Args: []string{"f", "cond"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "r", Body: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Func: "cond"}}}}}, Op: OpPipe, Right: &Query{Func: "r"}}}}}}}}, Func: "r"}}}, |
47 | 49 | "repeat": []*FuncDef{&FuncDef{Name: "repeat", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_repeat", Body: &Query{Left: &Query{Func: "f"}, Op: OpComma, Right: &Query{Func: "_repeat"}}}}, Func: "_repeat"}}}, |
48 | 50 | "scalars": []*FuncDef{&FuncDef{Name: "scalars", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpAnd, Right: &Query{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}}}}}}}, |
49 | "scan": []*FuncDef{&FuncDef{Name: "scan", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "scan", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "scan", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Left: &Query{Func: "$flags"}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}}}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}, Else: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Index: &Index{Name: "string"}}}}}}}}}}}}}}, | |
51 | "scan": []*FuncDef{&FuncDef{Name: "scan", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "scan", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "scan", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Left: &Query{Func: "$flags"}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpGt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, Then: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Index: &Index{Name: "string"}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}, | |
50 | 52 | "select": []*FuncDef{&FuncDef{Name: "select", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "f"}, Then: &Query{Func: "."}, Else: &Query{Func: "empty"}}}}}}, |
51 | 53 | "sort_by": []*FuncDef{&FuncDef{Name: "sort_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_sort_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, |
52 | "splits": []*FuncDef{&FuncDef{Name: "splits", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "splits", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "splits", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "split", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}}}, SuffixList: []*Suffix{&Suffix{Iter: true}}}}}}, | |
54 | "splits": []*FuncDef{&FuncDef{Name: "splits", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "splits", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "splits", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "split", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}}}}, | |
53 | 55 | "strings": []*FuncDef{&FuncDef{Name: "strings", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}}}}}}, |
54 | "sub": []*FuncDef{&FuncDef{Name: "sub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "str"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "sub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$str"}}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_sub", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}}}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$str"}, SuffixList: []*Suffix{&Suffix{Index: &Index{End: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}, &Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$r"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "string", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$r"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "_capture"}, Op: OpPipe, Right: &Query{Func: "str"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$str"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "offset"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "length"}}}}}}, End: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}, &ObjectKeyVal{Key: "offset", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "offset"}}}}}}}}, &ObjectKeyVal{Key: "matches", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{&Suffix{Index: &Index{End: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}, IsSlice: true}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "_sub"}}}}}}}}}}}}, Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "string", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{}}}}}}, &ObjectKeyVal{Key: "matches", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "_sub"}}}}}}}}}, | |
56 | "sub": []*FuncDef{&FuncDef{Name: "sub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "str"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "sub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$str"}}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_sub", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "length"}, Op: OpGt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}, &Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$r"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "string", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$r"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "_capture"}, Op: OpPipe, Right: &Query{Func: "str"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$str"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "offset"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "length"}}}}}}, End: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}, &ObjectKeyVal{Key: "offset", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "offset"}}}}}}}}, &ObjectKeyVal{Key: "matches", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{&Suffix{Index: &Index{End: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}, IsSlice: true}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "_sub"}}}}}}}, Else: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$str"}, SuffixList: []*Suffix{&Suffix{Index: &Index{End: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}, Left: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "string", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{}}}}}}, &ObjectKeyVal{Key: "matches", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "_sub"}}}}}}}}}, | |
55 | 57 | "test": []*FuncDef{&FuncDef{Name: "test", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "test", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "null"}}}}}}, &FuncDef{Name: "test", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{&Query{Func: "$re"}, &Query{Func: "$flags"}, &Query{Func: "true"}}}}}}}, |
56 | 58 | "todate": []*FuncDef{&FuncDef{Name: "todate", Body: &Query{Func: "todateiso8601"}}}, |
57 | 59 | "todateiso8601": []*FuncDef{&FuncDef{Name: "todateiso8601", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strftime", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%SZ"}}}}}}}}}, |
60 | 62 | "unique_by": []*FuncDef{&FuncDef{Name: "unique_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_unique_by", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "f"}}}}}}}}}}}}}}, |
61 | 63 | "until": []*FuncDef{&FuncDef{Name: "until", Args: []string{"cond", "next"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_until", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "cond"}, Then: &Query{Func: "."}, Else: &Query{Left: &Query{Func: "next"}, Op: OpPipe, Right: &Query{Func: "_until"}}}}}}}, Func: "_until"}}}, |
62 | 64 | "values": []*FuncDef{&FuncDef{Name: "values", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "."}, Op: OpNe, Right: &Query{Func: "null"}}}}}}}}, |
63 | "walk": []*FuncDef{&FuncDef{Name: "walk", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_walk", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Func: "_walk"}}}}}, Elif: []*IfElif{&IfElif{Cond: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map_values", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "last", Args: []*Query{&Query{Func: "_walk"}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}, Func: "_walk"}}}, | |
65 | "walk": []*FuncDef{&FuncDef{Name: "walk", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_walk", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpOr, Right: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map_values", Args: []*Query{&Query{Func: "_walk"}}}}}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}, Func: "_walk"}}}, | |
64 | 66 | "while": []*FuncDef{&FuncDef{Name: "while", Args: []string{"cond", "update"}, Body: &Query{FuncDefs: []*FuncDef{&FuncDef{Name: "_while", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "cond"}, Then: &Query{Left: &Query{Func: "."}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "update"}, Op: OpPipe, Right: &Query{Func: "_while"}}}}}, Else: &Query{Func: "empty"}}}}}}, Func: "_while"}}}, |
65 | 67 | "with_entries": []*FuncDef{&FuncDef{Name: "with_entries", Args: []string{"f"}, Body: &Query{Left: &Query{Func: "to_entries"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Func: "f"}}}}}, Op: OpPipe, Right: &Query{Func: "from_entries"}}}}}, |
66 | 68 | } |
39 | 39 | def leaf_paths: paths(scalars); |
40 | 40 | |
41 | 41 | def inside(xs): . as $x | xs | contains($x); |
42 | def combinations: | |
43 | if length == 0 then | |
44 | [] | |
45 | else | |
46 | .[0][] as $x | [$x] + (.[1:] | combinations) | |
47 | end; | |
42 | def combinations: if length == 0 then [] else .[0][] as $x | [$x] + (.[1:] | combinations) end; | |
48 | 43 | def combinations(n): [limit(n; repeat(.))] | combinations; |
44 | def ascii_downcase: | |
45 | explode | map(if 65 <= . and . <= 90 then . + 32 end) | implode; | |
46 | def ascii_upcase: | |
47 | explode | map(if 97 <= . and . <= 122 then . - 32 end) | implode; | |
49 | 48 | def walk(f): |
50 | def _walk: | |
51 | if type == "array" then | |
52 | map(_walk) | |
53 | elif type == "object" then | |
54 | map_values(last(_walk)) | |
55 | end | f; | |
49 | def _walk: if type | . == "array" or . == "object" then map_values(_walk) end | f; | |
56 | 50 | _walk; |
57 | 51 | |
58 | 52 | def first: .[0]; |
68 | 62 | def any(g; y): isempty(g | select(y)) | not; |
69 | 63 | def limit($n; g): |
70 | 64 | if $n > 0 then |
71 | label $out | | |
72 | foreach g as $item ( | |
73 | $n; | |
74 | . - 1; | |
75 | $item, if . <= 0 then break $out else empty end | |
76 | ) | |
65 | label $out | |
66 | | foreach g as $item | |
67 | ($n; . - 1; $item, if . <= 0 then break $out else empty end) | |
77 | 68 | elif $n == 0 then |
78 | 69 | empty |
79 | 70 | else |
84 | 75 | if $n < 0 then |
85 | 76 | error("nth doesn't support negative indices") |
86 | 77 | else |
87 | label $out | | |
88 | foreach g as $item ( | |
89 | $n + 1; | |
90 | . - 1; | |
91 | if . <= 0 then $item, break $out else empty end | |
92 | ) | |
78 | label $out | |
79 | | foreach g as $item | |
80 | ($n + 1; . - 1; if . <= 0 then $item, break $out else empty end) | |
93 | 81 | end; |
94 | 82 | |
95 | 83 | def truncate_stream(f): |
96 | . as $n | null | f | | |
97 | if .[0] | length > $n then .[0] |= .[$n:] else empty end; | |
84 | . as $n | null | f | if .[0] | length > $n then .[0] |= .[$n:] else empty end; | |
98 | 85 | def fromstream(f): |
99 | { x: null, e: false } as $init | | |
100 | foreach f as $i ( | |
101 | $init; | |
102 | if .e then $init end | | |
103 | if $i | length == 2 then | |
104 | setpath(["e"]; $i[0] | length == 0) | | |
105 | setpath(["x"] + $i[0]; $i[1]) | |
106 | else | |
107 | setpath(["e"]; $i[0] | length == 1) | |
108 | end; | |
109 | if .e then .x else empty end | |
110 | ); | |
86 | { x: null, e: false } as $init | |
87 | | foreach f as $i | |
88 | ( $init; | |
89 | if .e then $init else . end | |
90 | | if $i | length == 2 | |
91 | then setpath(["e"]; $i[0] | length==0) | setpath(["x"] + $i[0]; $i[1]) | |
92 | else setpath(["e"]; $i[0] | length==1) end; | |
93 | if .e then .x else empty end); | |
111 | 94 | def tostream: |
112 | path(def r: (.[]? | r), .; r) as $p | | |
113 | getpath($p) | | |
114 | reduce path(.[]?) as $q ([$p, .]; [$p + $q]); | |
95 | path(def r: (.[]? | r), .; r) as $p | |
96 | | getpath($p) | |
97 | | reduce path(.[]?) as $q ([$p, .]; [$p + $q]); | |
115 | 98 | |
99 | def _assign(ps; $v): | |
100 | reduce path(ps) as $p (.; setpath($p; $v)); | |
101 | def _modify(ps; f): | |
102 | reduce path(ps) as $p | |
103 | ([., []]; label $out | (([0] + $p) as $q | setpath($q; getpath($q) | f) | ., break $out), setpath([1]; .[1] + [$p])) | |
104 | | . as $x | $x[0] | delpaths($x[1]); | |
116 | 105 | def map_values(f): .[] |= f; |
117 | 106 | def del(f): delpaths([path(f)]); |
118 | 107 | def paths: path(..) | select(. != []); |
124 | 113 | def todate: todateiso8601; |
125 | 114 | |
126 | 115 | def match($re): match($re; null); |
127 | def match($re; $flags): _match($re; $flags; false)[]; | |
116 | def match($re; $flags): _match($re; $flags; false) | .[]; | |
128 | 117 | def test($re): test($re; null); |
129 | 118 | def test($re; $flags): _match($re; $flags; true); |
130 | 119 | def capture($re): capture($re; null); |
131 | 120 | def capture($re; $flags): match($re; $flags) | _capture; |
132 | 121 | def scan($re): scan($re; null); |
133 | 122 | def scan($re; $flags): |
134 | match($re; $flags + "g") | | |
135 | if .captures == [] then | |
136 | .string | |
137 | else | |
138 | [.captures[].string] | |
139 | end; | |
123 | match($re; $flags + "g") | |
124 | | if .captures|length > 0 then [.captures[].string] else .string end; | |
140 | 125 | def splits($re): splits($re; null); |
141 | def splits($re; $flags): split($re; $flags)[]; | |
126 | def splits($re; $flags): split($re; $flags) | .[]; | |
142 | 127 | def sub($re; str): sub($re; str; null); |
143 | 128 | def sub($re; str; $flags): |
144 | . as $str | | |
145 | def _sub: | |
146 | if .matches == [] then | |
147 | $str[:.offset] + .string | |
148 | else | |
149 | .matches[-1] as $r | | |
150 | { | |
151 | string: (($r | _capture | str) + $str[$r.offset+$r.length:.offset] + .string), | |
152 | offset: $r.offset, | |
153 | matches: .matches[:-1], | |
154 | } | | |
155 | _sub | |
156 | end; | |
129 | . as $str | |
130 | | def _sub: | |
131 | if .matches|length > 0 | |
132 | then | |
133 | .matches[-1] as $r | |
134 | | { | |
135 | string: (($r | _capture | str) + $str[$r.offset+$r.length:.offset] + .string), | |
136 | offset: $r.offset, | |
137 | matches: .matches[:-1], | |
138 | } | |
139 | | _sub | |
140 | else | |
141 | $str[:.offset] + .string | |
142 | end; | |
157 | 143 | { string: "", matches: [match($re; $flags)] } | _sub; |
158 | 144 | def gsub($re; str): sub($re; str; "g"); |
159 | 145 | def gsub($re; str; $flags): sub($re; str; $flags + "g"); |
165 | 151 | if . == "break" then empty else error end; |
166 | 152 | |
167 | 153 | def INDEX(stream; idx_expr): |
168 | reduce stream as $row ({}; .[$row | idx_expr | tostring] = $row); | |
169 | def INDEX(idx_expr): | |
170 | INDEX(.[]; idx_expr); | |
154 | reduce stream as $row ({}; .[$row|idx_expr|tostring] = $row); | |
155 | def INDEX(idx_expr): INDEX(.[]; idx_expr); | |
171 | 156 | def JOIN($idx; idx_expr): |
172 | 157 | [.[] | [., $idx[idx_expr]]]; |
173 | 158 | def JOIN($idx; stream; idx_expr): |
24 | 24 | return &encoder{w: new(bytes.Buffer), tab: tab, indent: indent} |
25 | 25 | } |
26 | 26 | |
27 | func (e *encoder) flush() error { | |
28 | _, err := e.out.Write(e.w.Bytes()) | |
27 | func (e *encoder) marshal(v interface{}, w io.Writer) error { | |
28 | e.out = w | |
29 | e.encode(v) | |
30 | _, err := w.Write(e.w.Bytes()) | |
29 | 31 | e.w.Reset() |
30 | 32 | return err |
31 | 33 | } |
32 | 34 | |
33 | func (e *encoder) marshal(v interface{}, w io.Writer) error { | |
34 | e.out = w | |
35 | err := e.encode(v) | |
36 | if ferr := e.flush(); ferr != nil && err == nil { | |
37 | err = ferr | |
38 | } | |
39 | return err | |
40 | } | |
41 | ||
42 | func (e *encoder) encode(v interface{}) error { | |
35 | func (e *encoder) encode(v interface{}) { | |
43 | 36 | switch v := v.(type) { |
44 | 37 | case nil: |
45 | 38 | e.write([]byte("null"), nullColor) |
58 | 51 | case string: |
59 | 52 | e.encodeString(v, stringColor) |
60 | 53 | case []interface{}: |
61 | if err := e.encodeArray(v); err != nil { | |
62 | return err | |
63 | } | |
54 | e.encodeArray(v) | |
64 | 55 | case map[string]interface{}: |
65 | if err := e.encodeMap(v); err != nil { | |
66 | return err | |
67 | } | |
56 | e.encodeMap(v) | |
68 | 57 | default: |
69 | 58 | panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v)) |
70 | 59 | } |
71 | 60 | if e.w.Len() > 8*1024 { |
72 | return e.flush() | |
73 | } | |
74 | return nil | |
61 | e.out.Write(e.w.Bytes()) | |
62 | e.w.Reset() | |
63 | } | |
75 | 64 | } |
76 | 65 | |
77 | 66 | // ref: floatEncoder in encoding/json |
162 | 151 | } |
163 | 152 | } |
164 | 153 | |
165 | func (e *encoder) encodeArray(vs []interface{}) error { | |
154 | func (e *encoder) encodeArray(vs []interface{}) { | |
166 | 155 | e.writeByte('[', arrayColor) |
167 | 156 | e.depth += e.indent |
168 | 157 | for i, v := range vs { |
172 | 161 | if e.indent != 0 { |
173 | 162 | e.writeIndent() |
174 | 163 | } |
175 | if err := e.encode(v); err != nil { | |
176 | return err | |
177 | } | |
164 | e.encode(v) | |
178 | 165 | } |
179 | 166 | e.depth -= e.indent |
180 | 167 | if len(vs) > 0 && e.indent != 0 { |
181 | 168 | e.writeIndent() |
182 | 169 | } |
183 | 170 | e.writeByte(']', arrayColor) |
184 | return nil | |
185 | } | |
186 | ||
187 | func (e *encoder) encodeMap(vs map[string]interface{}) error { | |
171 | } | |
172 | ||
173 | func (e *encoder) encodeMap(vs map[string]interface{}) { | |
188 | 174 | e.writeByte('{', objectColor) |
189 | 175 | e.depth += e.indent |
190 | 176 | type keyVal struct { |
212 | 198 | if e.indent != 0 { |
213 | 199 | e.w.WriteByte(' ') |
214 | 200 | } |
215 | if err := e.encode(kv.val); err != nil { | |
216 | return err | |
217 | } | |
201 | e.encode(kv.val) | |
218 | 202 | } |
219 | 203 | e.depth -= e.indent |
220 | 204 | if len(vs) > 0 && e.indent != 0 { |
221 | 205 | e.writeIndent() |
222 | 206 | } |
223 | 207 | e.writeByte('}', objectColor) |
224 | return nil | |
225 | 208 | } |
226 | 209 | |
227 | 210 | func (e *encoder) writeIndent() { |
228 | 211 | e.w.WriteByte('\n') |
229 | 212 | if n := e.depth; n > 0 { |
230 | 213 | if e.tab { |
231 | e.writeIndentInternal(n, "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t") | |
214 | const tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" | |
215 | for n > len(tabs) { | |
216 | e.w.Write([]byte(tabs)) | |
217 | n -= len(tabs) | |
218 | } | |
219 | e.w.Write([]byte(tabs)[:n]) | |
232 | 220 | } else { |
233 | e.writeIndentInternal(n, " ") | |
234 | } | |
235 | } | |
236 | } | |
237 | ||
238 | func (e *encoder) writeIndentInternal(n int, spaces string) { | |
239 | if l := len(spaces); n <= l { | |
240 | e.w.WriteString(spaces[:n]) | |
241 | } else { | |
242 | e.w.WriteString(spaces) | |
243 | for n -= l; n > 0; n, l = n-l, l*2 { | |
244 | if n < l { | |
245 | l = n | |
246 | } | |
247 | e.w.Write(e.w.Bytes()[e.w.Len()-l:]) | |
221 | const spaces = " " | |
222 | for n > len(spaces) { | |
223 | e.w.Write([]byte(spaces)) | |
224 | n -= len(spaces) | |
225 | } | |
226 | e.w.Write([]byte(spaces)[:n]) | |
248 | 227 | } |
249 | 228 | } |
250 | 229 | } |
836 | 836 | [-1,-2,-3,-4722366482869645213696] |
837 | 837 | [1,2,3,4722366482869645213696] |
838 | 838 | |
839 | - name: unary operator against string | |
839 | - name: unary operators against string | |
840 | 840 | args: |
841 | 841 | - '(-.)' |
842 | 842 | input: '"abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde"' |
843 | 843 | error: | |
844 | 844 | cannot negate: string ("abcdeabcdeabcdeabcdeabcd ...") |
845 | ||
846 | - name: unary operator with variable binding | |
847 | args: | |
848 | - '-1 as $x | 1, $x' | |
849 | input: 'null' | |
850 | expected: | | |
851 | 1 | |
852 | -1 | |
853 | 845 | |
854 | 846 | - name: object construction |
855 | 847 | args: |
1927 | 1919 | "endswith cannot be applied to: object ({})" |
1928 | 1920 | "endswith cannot be applied to: number (10)" |
1929 | 1921 | |
1930 | - name: combinations/0 function | |
1931 | args: | |
1932 | - -c | |
1933 | - '[combinations]' | |
1934 | input: '[] [[0]] [[1, 2, 3], [4, 5, 6]]' | |
1935 | expected: | | |
1936 | [[]] | |
1937 | [[0]] | |
1938 | [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] | |
1939 | ||
1940 | - name: combinations/1 function | |
1941 | args: | |
1942 | - -c | |
1943 | - '[combinations(3)]' | |
1944 | input: '[] [0] [1, 2]' | |
1945 | expected: | | |
1946 | [] | |
1947 | [[0,0,0]] | |
1948 | [[1,1,1],[1,1,2],[1,2,1],[1,2,2],[2,1,1],[2,1,2],[2,2,1],[2,2,2]] | |
1949 | ||
1950 | 1922 | - name: ltrimstr function |
1951 | 1923 | args: |
1952 | 1924 | - 'map(ltrimstr("foo")), ("x"|ltrimstr(0, null, {}))' |
1989 | 1961 | "x" |
1990 | 1962 | "x" |
1991 | 1963 | |
1992 | - name: explode function | |
1993 | args: | |
1994 | - -c | |
1995 | - 'explode' | |
1996 | input: | | |
1997 | "" | |
1998 | "foo bar" | |
1999 | "12345" | |
2000 | "\n\t" | |
1964 | - name: combinations/0 function | |
1965 | args: | |
1966 | - -c | |
1967 | - '[combinations]' | |
1968 | input: '[] [[0]] [[1, 2, 3], [4, 5, 6]]' | |
1969 | expected: | | |
1970 | [[]] | |
1971 | [[0]] | |
1972 | [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] | |
1973 | ||
1974 | - name: combinations/1 function | |
1975 | args: | |
1976 | - -c | |
1977 | - '[combinations(3)]' | |
1978 | input: '[] [0] [1, 2]' | |
2001 | 1979 | expected: | |
2002 | 1980 | [] |
2003 | [102,111,111,32,98,97,114] | |
2004 | [65297,65298,65299,65300,65301] | |
2005 | [10,9] | |
2006 | ||
2007 | - name: implode function | |
2008 | args: | |
2009 | - 'implode' | |
2010 | input: | | |
2011 | [] | |
2012 | [102,111,111,32,98,97,114] | |
2013 | [65297,65298,65299,65300,65301] | |
2014 | [10,9] | |
2015 | expected: | | |
2016 | "" | |
2017 | "foo bar" | |
2018 | "12345" | |
2019 | "\n\t" | |
2020 | ||
2021 | - name: implode function error | |
2022 | args: | |
2023 | - 'try implode catch .' | |
2024 | input: | | |
2025 | [0] | |
2026 | [-1] | |
2027 | [1114112] | |
2028 | [[]] | |
2029 | [{}] | |
2030 | expected: | | |
2031 | "\u0000" | |
2032 | "implode cannot be applied to: array ([-1])" | |
2033 | "implode cannot be applied to: array ([1114112])" | |
2034 | "implode cannot be applied to: array ([[]])" | |
2035 | "implode cannot be applied to: array ([{}])" | |
2036 | ||
2037 | - name: split/1 function | |
2038 | args: | |
2039 | - 'split(", ","") | join("/")' | |
2040 | input: | | |
2041 | "a,b, c, d, e,f" | |
2042 | ", a,b, c, d, e,f, " | |
2043 | expected: | | |
2044 | "a,b/c/d/e,f" | |
2045 | "a/,/b/,/ /c/,/ /d/,/ /e/,/f" | |
2046 | "/a,b/c/d/e,f/" | |
2047 | ",/ /a/,/b/,/ /c/,/ /d/,/ /e/,/f/,/ " | |
1981 | [[0,0,0]] | |
1982 | [[1,1,1],[1,1,2],[1,2,1],[1,2,2],[2,1,1],[2,1,2],[2,2,1],[2,2,2]] | |
2048 | 1983 | |
2049 | 1984 | - name: join function |
2050 | 1985 | args: |
2108 | 2043 | - name: ascii_downcase function |
2109 | 2044 | args: |
2110 | 2045 | - 'ascii_downcase' |
2111 | input: '"@ABC XYZ[] `abc xyz{} Αα"' | |
2112 | expected: | | |
2113 | "@abc xyz[] `abc xyz{} Αα" | |
2046 | input: '"@ABC XYZ[] `abc xyz{} ☆"' | |
2047 | expected: | | |
2048 | "@abc xyz[] `abc xyz{} ☆" | |
2114 | 2049 | |
2115 | 2050 | - name: ascii_upcase function |
2116 | 2051 | args: |
2117 | 2052 | - 'ascii_upcase' |
2118 | input: '"@ABC XYZ[] `abc xyz{} Αα"' | |
2119 | expected: | | |
2120 | "@ABC XYZ[] `ABC XYZ{} Αα" | |
2053 | input: '"@ABC XYZ[] `abc xyz{} ☆"' | |
2054 | expected: | | |
2055 | "@ABC XYZ[] `ABC XYZ{} ☆" | |
2121 | 2056 | |
2122 | 2057 | - name: walk function |
2123 | 2058 | args: |
2131 | 2066 | 2 |
2132 | 2067 | ["aa",{"b":[]},"aa",{"b":[]}] |
2133 | 2068 | {"c":[null,true,false,null,true,false]} |
2134 | ||
2135 | - name: walk function with multiple values | |
2136 | args: | |
2137 | - -c | |
2138 | - 'walk(.,0)' | |
2139 | input: '[1,2,3] {"x":1,"y":2,"z":3}' | |
2140 | expected: | | |
2141 | [1,0,2,0,3,0] | |
2142 | 0 | |
2143 | {"x":0,"y":0,"z":0} | |
2144 | 0 | |
2145 | 2069 | |
2146 | 2070 | - name: transpose function |
2147 | 2071 | args: |
2882 | 2806 | expected: | |
2883 | 2807 | [5,5,false,3,3,[null,false],null,false] |
2884 | 2808 | |
2809 | - name: explode function | |
2810 | args: | |
2811 | - -c | |
2812 | - 'explode' | |
2813 | input: | | |
2814 | "" | |
2815 | "foo bar" | |
2816 | "12345" | |
2817 | "\n\t" | |
2818 | expected: | | |
2819 | [] | |
2820 | [102,111,111,32,98,97,114] | |
2821 | [65297,65298,65299,65300,65301] | |
2822 | [10,9] | |
2823 | ||
2824 | - name: implode function | |
2825 | args: | |
2826 | - 'implode' | |
2827 | input: | | |
2828 | [] | |
2829 | [102,111,111,32,98,97,114] | |
2830 | [65297,65298,65299,65300,65301] | |
2831 | [10,9] | |
2832 | expected: | | |
2833 | "" | |
2834 | "foo bar" | |
2835 | "12345" | |
2836 | "\n\t" | |
2837 | ||
2838 | - name: implode function error | |
2839 | args: | |
2840 | - 'try implode catch .' | |
2841 | input: | | |
2842 | [0] | |
2843 | [-1] | |
2844 | [1114112] | |
2845 | [[]] | |
2846 | [{}] | |
2847 | expected: | | |
2848 | "\u0000" | |
2849 | "implode cannot be applied to: array ([-1])" | |
2850 | "implode cannot be applied to: array ([1114112])" | |
2851 | "implode cannot be applied to: array ([[]])" | |
2852 | "implode cannot be applied to: array ([{}])" | |
2853 | ||
2854 | - name: split/1 function | |
2855 | args: | |
2856 | - 'split(", ","") | join("/")' | |
2857 | input: | | |
2858 | "a,b, c, d, e,f" | |
2859 | ", a,b, c, d, e,f, " | |
2860 | expected: | | |
2861 | "a,b/c/d/e,f" | |
2862 | "a/,/b/,/ /c/,/ /d/,/ /e/,/f" | |
2863 | "/a,b/c/d/e,f/" | |
2864 | ",/ /a/,/b/,/ /c/,/ /d/,/ /e/,/f/,/ " | |
2865 | ||
2885 | 2866 | - name: condition |
2886 | 2867 | args: |
2887 | 2868 | - 'if . then . else [.] end' |
3183 | 3164 | 1 |
3184 | 3165 | 3 |
3185 | 3166 | 6 |
3186 | ||
3187 | - name: label and break in try catch syntax | |
3188 | args: | |
3189 | - 'label $x | (label $y | try break $y catch .), .' | |
3190 | input: '10' | |
3191 | expected: | | |
3192 | 10 | |
3193 | 3167 | |
3194 | 3168 | - name: label and break in function argument |
3195 | 3169 | args: |
4260 | 4234 | expected: | |
4261 | 4235 | [1,2,4,5] |
4262 | 4236 | |
4263 | - name: delpaths function with overlapping paths | |
4237 | - name: delpaths function with overlapped paths | |
4264 | 4238 | args: |
4265 | 4239 | - -c |
4266 | 4240 | - 'delpaths([["x"],["x",0,"y"]], [["x",0],["x",0,"y"]], [["x"],["x",{"start":0}]])' |
4361 | 4335 | expected: | |
4362 | 4336 | [0,3,5,6,9] |
4363 | 4337 | |
4364 | - name: del function against large array with select | |
4365 | args: | |
4366 | - '[range(.)] | (5,7) as $x | del(.[] | select(. % $x == 0)) | add' | |
4367 | input: '50000' | |
4368 | expected: | | |
4369 | 1000000000 | |
4370 | 1071421429 | |
4371 | ||
4372 | - name: del function against large array with slicing | |
4373 | args: | |
4374 | - '[range(.)] | del(.[range(length) | {start:.,end:(.+1)}])' | |
4375 | input: '50000' | |
4376 | expected: | | |
4377 | [] | |
4378 | ||
4379 | 4338 | - name: assignment operator against object |
4380 | 4339 | args: |
4381 | 4340 | - -c |
4391 | 4350 | input: '[0,1,2]' |
4392 | 4351 | expected: | |
4393 | 4352 | [3,1,3] |
4394 | ||
4395 | - name: assignment operator against number | |
4396 | args: | |
4397 | - '. = 1' | |
4398 | input: '0' | |
4399 | expected: | | |
4400 | 1 | |
4401 | ||
4402 | - name: assignment operator against number error | |
4403 | args: | |
4404 | - '.foo = 1' | |
4405 | input: '0' | |
4406 | error: | | |
4407 | expected an object but got: number (0) | |
4408 | ||
4409 | - name: assignment operator against object with multiple paths | |
4410 | args: | |
4411 | - -c | |
4412 | - '(.foo,.bar,.baz) = 1' | |
4413 | input: 'null' | |
4414 | expected: | | |
4415 | {"bar":1,"baz":1,"foo":1} | |
4416 | 4353 | |
4417 | 4354 | - name: assignment operator against array with slicing |
4418 | 4355 | args: |
4485 | 4422 | expected: | |
4486 | 4423 | {"foo":[{"bar":1},{"bar":1}]} |
4487 | 4424 | |
4488 | - name: assignment operator with overlapping paths | |
4489 | args: | |
4490 | - -c | |
4491 | - '[(.x,.x.y,.x.y.z) = {}, (.x.y.z,.x.y,.x) = {}]' | |
4492 | input: 'null' | |
4493 | expected: | | |
4494 | [{"x":{"y":{"z":{}}}},{"x":{}}] | |
4495 | ||
4496 | 4425 | - name: assignment operator query |
4497 | 4426 | args: |
4498 | 4427 | - -c |
4501 | 4430 | expected: | |
4502 | 4431 | {"bar":42,"foo":42} |
4503 | 4432 | |
4504 | - name: assignment operator query with multiple paths | |
4505 | args: | |
4506 | - -c | |
4507 | - '(.foo,.bar,.baz) = .bar + 1' | |
4508 | input: '{"bar":42}' | |
4509 | expected: | | |
4510 | {"bar":43,"baz":43,"foo":43} | |
4511 | ||
4512 | - name: assignment operator array index negative error | |
4513 | args: | |
4514 | - '.[-1,0] = 1' | |
4515 | input: '[]' | |
4516 | error: | | |
4517 | setpath cannot be applied to: number (-1) | |
4518 | ||
4519 | 4433 | - name: assignment operator array index limit error |
4520 | 4434 | args: |
4521 | 4435 | - '.[200000000] = 1' |
4547 | 4461 | expected: | |
4548 | 4462 | [{"k":"a","v":1},{"k":"b","v":0},{"k":"c","v":3}] |
4549 | 4463 | |
4550 | - name: assignment operator with fork | |
4551 | args: | |
4552 | - -c | |
4553 | - '[range(3)] | .[] = 0 | [.[] = 1, .[] = 2]' | |
4554 | input: 'null' | |
4555 | expected: | | |
4556 | [[1,1,1],[2,2,2]] | |
4557 | ||
4558 | - name: assignment operator against large array | |
4559 | args: | |
4560 | - '[range(.)] | .[] = 1 | add' | |
4561 | input: '50000' | |
4562 | expected: | | |
4563 | 50000 | |
4564 | ||
4565 | - name: assignment operator against large array with select | |
4566 | args: | |
4567 | - '[range(.)] | (.[] | select(. % 2 > 0)) = 1 | add' | |
4568 | input: '50000' | |
4569 | expected: | | |
4570 | 625000000 | |
4571 | ||
4572 | - name: assignment operator against large array with range | |
4573 | args: | |
4574 | - '[range(.)] | .[range(length)] = 1 | add' | |
4575 | input: '50000' | |
4576 | expected: | | |
4577 | 50000 | |
4578 | ||
4579 | - name: assignment operator against large array with slicing | |
4580 | args: | |
4581 | - '[range(.)] | .[range(length) | {start:.,end:(.+1)}] = [1] | add' | |
4582 | input: '50000' | |
4583 | expected: | | |
4584 | 50000 | |
4585 | ||
4586 | - name: assignment operator against large object | |
4587 | args: | |
4588 | - '[{key:range(.)|tostring}] | from_entries | .[] = 1 | add' | |
4589 | input: '50000' | |
4590 | expected: | | |
4591 | 50000 | |
4592 | ||
4593 | - name: assignment operator constructing large array | |
4594 | args: | |
4595 | - '.[range(50000)] = 1 | add' | |
4596 | input: 'null' | |
4597 | expected: | | |
4598 | 50000 | |
4599 | ||
4600 | - name: update-assignment operator | |
4464 | - name: modify operator | |
4601 | 4465 | args: |
4602 | 4466 | - -c |
4603 | 4467 | - '.foo |= .+1' |
4605 | 4469 | expected: | |
4606 | 4470 | {"foo":2} |
4607 | 4471 | |
4608 | - name: update-assignment operator deeply | |
4472 | - name: modify operator deeply | |
4609 | 4473 | args: |
4610 | 4474 | - -c |
4611 | 4475 | - '.[0].a |= {"old":., "new":(.+1)}' |
4613 | 4477 | expected: | |
4614 | 4478 | [{"a":{"new":2,"old":1},"b":2}] |
4615 | 4479 | |
4616 | - name: update-assignment operator in function | |
4480 | - name: modify operator in function | |
4617 | 4481 | args: |
4618 | 4482 | - -c |
4619 | 4483 | - 'def inc(x): x |= .+1; inc(.[].a)' |
4621 | 4485 | expected: | |
4622 | 4486 | [{"a":2,"b":2},{"a":3,"b":4},{"a":8,"b":8}] |
4623 | 4487 | |
4624 | - name: update-assignment operator with empty | |
4488 | - name: modify operator with empty | |
4625 | 4489 | args: |
4626 | 4490 | - -c |
4627 | 4491 | - '.foo |= empty' |
4629 | 4493 | expected: | |
4630 | 4494 | {"bar":48} |
4631 | 4495 | |
4632 | - name: update-assignment operator with repeat | |
4633 | args: | |
4634 | - -c | |
4635 | - '.foo |= repeat(.)' | |
4636 | input: '{"foo": 42, "bar": 48}' | |
4637 | expected: | | |
4638 | {"bar":48,"foo":42} | |
4639 | ||
4640 | - name: update-assignment operator with same paths | |
4641 | args: | |
4642 | - -c | |
4643 | - '(.foo,.foo,.foo) |= .+1' | |
4644 | input: '{"foo": 0}' | |
4645 | expected: | | |
4646 | {"foo":3} | |
4647 | ||
4648 | - name: update-assignment operator against array with empty function | |
4496 | - name: modify operator on array with empty function | |
4649 | 4497 | args: |
4650 | 4498 | - -c |
4651 | 4499 | - '(.[] | select(. % 2 > 0)) |= empty' |
4653 | 4501 | expected: | |
4654 | 4502 | [2,4] |
4655 | 4503 | |
4656 | - name: update-assignment operator against array with conditional empty function | |
4504 | - name: modify operator on array with conditional empty function | |
4657 | 4505 | args: |
4658 | 4506 | - -c |
4659 | 4507 | - '[range(.)] | .[] |= if . % 2 == 0 then empty else [., -.] end' |
4661 | 4509 | expected: | |
4662 | 4510 | [[1,-1],[3,-3],[5,-5],[7,-7],[9,-9]] |
4663 | 4511 | |
4664 | - name: update-assignment operator with fork | |
4665 | args: | |
4666 | - -c | |
4667 | - '[range(3)] | .[] |= 0 | [.[] |= 1, .[] |= 2]' | |
4668 | input: 'null' | |
4669 | expected: | | |
4670 | [[1,1,1],[2,2,2]] | |
4671 | ||
4672 | - name: update-assignment operator against large array | |
4673 | args: | |
4674 | - '[range(.)] | .[] |= . * 2 | add' | |
4675 | input: '50000' | |
4676 | expected: | | |
4677 | 2499950000 | |
4678 | ||
4679 | - name: update-assignment operator against large array with select | |
4680 | args: | |
4681 | - '[range(.)] | (.[] | select(. % 2 > 0)) |= (. * 2) | add' | |
4682 | input: '50000' | |
4683 | expected: | | |
4684 | 1874975000 | |
4685 | ||
4686 | - name: update-assignment operator against large object | |
4687 | args: | |
4688 | - '[range(.) | {key:tostring,value:.}] | from_entries | .[] |= . * 2 | add' | |
4689 | input: '50000' | |
4690 | expected: | | |
4691 | 2499950000 | |
4692 | ||
4693 | - name: update-assignment operator against array with overlapping paths | |
4694 | args: | |
4695 | - -c | |
4696 | - '(.[1:3][],.[2:4][],.[2]) |= .+1' | |
4697 | input: '[1,2,3,4,5]' | |
4698 | expected: | | |
4699 | [1,3,6,5,5] | |
4700 | ||
4701 | - name: update-assignment operator with optional operator | |
4512 | - name: modify operator with optional operator | |
4702 | 4513 | args: |
4703 | 4514 | - -c |
4704 | 4515 | - '.foo |= .?' |
4706 | 4517 | expected: | |
4707 | 4518 | {"foo":42} |
4708 | 4519 | |
4709 | - name: update-assignment operator with infinite range | |
4710 | args: | |
4711 | - -c | |
4712 | - '[range(.)] | .[] |= range(infinite)' | |
4713 | input: '5' | |
4714 | expected: | | |
4715 | [0,0,0,0,0] | |
4716 | ||
4717 | - name: update-assignment operator with try catch | |
4520 | - name: modify operator with try catch | |
4718 | 4521 | args: |
4719 | 4522 | - -c |
4720 | 4523 | - '. |= try . catch .' |
4722 | 4525 | expected: | |
4723 | 4526 | 1 |
4724 | 4527 | |
4725 | - name: update-assignment operator with alternative operator | |
4528 | - name: modify operator with alternative operator | |
4726 | 4529 | args: |
4727 | 4530 | - -c |
4728 | 4531 | - '.foo |= (fromjson? // "x")' |
4891 | 4694 | expected: | |
4892 | 4695 | [0,"id"] |
4893 | 4696 | |
4894 | - name: path function with getpath | |
4895 | args: | |
4896 | - -c | |
4897 | - '[path(getpath([[0]] | .[0],.[]))]' | |
4898 | input: 'null' | |
4899 | expected: | | |
4900 | [[0],[0]] | |
4901 | ||
4902 | 4697 | - name: path function against nan |
4903 | 4698 | args: |
4904 | 4699 | - -c |
4974 | 4769 | [ |
4975 | 4770 | "y" |
4976 | 4771 | ] |
4977 | ||
4978 | - name: path function with assignment operator | |
4979 | args: | |
4980 | - 'path(. = .x)' | |
4981 | input: 'null' | |
4982 | expected: | | |
4983 | [] | |
4984 | 4772 | |
4985 | 4773 | - name: path function with error function |
4986 | 4774 | args: |
5862 | 5650 | args: |
5863 | 5651 | - '@csv' |
5864 | 5652 | input: | |
5865 | [1, "foo", null, "foo,\n\"bar\"\tbaz\u0000\\0"] | |
5866 | expected: | | |
5867 | "1,\"foo\",,\"foo,\n\"\"bar\"\"\tbaz\\0\\0\"" | |
5653 | [1, "foo", null, "foo,\n\"bar\"\tbaz"] | |
5654 | expected: | | |
5655 | "1,\"foo\",,\"foo,\n\"\"bar\"\"\tbaz\"" | |
5868 | 5656 | |
5869 | 5657 | - name: format strings @csv with string interpolation |
5870 | 5658 | args: |
5893 | 5681 | args: |
5894 | 5682 | - '@tsv' |
5895 | 5683 | input: | |
5896 | [1, "foo", null, "foo,\n\"bar\"\tbaz\u0000\\0"] | |
5897 | expected: | | |
5898 | "1\tfoo\t\tfoo,\\n\"bar\"\\tbaz\\0\\\\0" | |
5684 | [1, "foo", null, "foo,\n\"bar\"\tba\\z"] | |
5685 | expected: | | |
5686 | "1\tfoo\t\tfoo,\\n\"bar\"\\tba\\\\z" | |
5899 | 5687 | |
5900 | 5688 | - name: format strings @tsv with string interpolation |
5901 | 5689 | args: |
5926 | 5714 | input: | |
5927 | 5715 | null |
5928 | 5716 | true |
5929 | [1, "f'o'o\u0000\\0", null, false] | |
5717 | [1, "f'o'o", null, false] | |
5930 | 5718 | expected: | |
5931 | 5719 | "null" |
5932 | 5720 | "true" |
5933 | "1 'f'\\''o'\\''o\\0\\0' null false" | |
5721 | "1 'f'\\''o'\\''o' null false" | |
5934 | 5722 | |
5935 | 5723 | - name: format strings @sh error |
5936 | 5724 | args: |
276 | 276 | } |
277 | 277 | } |
278 | 278 | return nil |
279 | } | |
280 | ||
281 | func (c *compiler) appendBuiltin(name string, argcnt int) func() { | |
282 | setjump := c.lazy(func() *code { | |
283 | return &code{op: opjump, v: len(c.codes)} | |
284 | }) | |
285 | c.appendCodeInfo(name) | |
286 | c.builtinScope.funcs = append( | |
287 | c.builtinScope.funcs, | |
288 | &funcinfo{name, len(c.codes), argcnt}, | |
289 | ) | |
290 | return func() { | |
291 | setjump() | |
292 | c.appendCodeInfo("end of " + name) | |
293 | } | |
294 | 279 | } |
295 | 280 | |
296 | 281 | func (c *compiler) newScope() *scopeinfo { |
788 | 773 | func (c *compiler) compileLabel(e *Label) error { |
789 | 774 | c.appendCodeInfo(e) |
790 | 775 | v := c.pushVariable("$%" + e.Ident[1:]) |
791 | c.append(&code{op: opforklabel, v: v}) | |
776 | defer c.lazy(func() *code { | |
777 | return &code{op: opforklabel, v: v} | |
778 | })() | |
792 | 779 | return c.compileQuery(e.Body) |
793 | 780 | } |
794 | 781 | |
799 | 786 | } |
800 | 787 | c.append(&code{op: oppop}) |
801 | 788 | c.append(&code{op: opload, v: v}) |
802 | c.append(&code{op: opcall, v: [3]interface{}{funcBreak(label), 0, "_break"}}) | |
789 | c.append(&code{op: opcall, v: [3]interface{}{ | |
790 | func(v interface{}, _ []interface{}) interface{} { | |
791 | return &breakError{label, v} | |
792 | }, | |
793 | 0, | |
794 | "_break", | |
795 | }}) | |
803 | 796 | return nil |
804 | } | |
805 | ||
806 | func funcBreak(label string) func(interface{}, []interface{}) interface{} { | |
807 | return func(v interface{}, _ []interface{}) interface{} { | |
808 | return &breakError{label, v} | |
809 | } | |
810 | 797 | } |
811 | 798 | |
812 | 799 | func (c *compiler) compileTerm(e *Term) error { |
941 | 928 | break |
942 | 929 | } |
943 | 930 | } |
944 | if len(fds) == 0 { | |
945 | switch e.Name { | |
946 | case "_assign": | |
947 | c.compileAssign() | |
948 | case "_modify": | |
949 | c.compileModify() | |
950 | } | |
951 | } | |
952 | 931 | if f := c.lookupBuiltin(e.Name, len(e.Args)); f != nil { |
953 | 932 | return c.compileCallPc(f, e.Args) |
954 | 933 | } |
970 | 949 | [3]interface{}{c.funcBuiltins, 0, e.Name}, |
971 | 950 | e.Args, |
972 | 951 | true, |
973 | -1, | |
952 | false, | |
974 | 953 | ) |
975 | 954 | case "input": |
976 | 955 | if c.inputIter == nil { |
980 | 959 | [3]interface{}{c.funcInput, 0, e.Name}, |
981 | 960 | e.Args, |
982 | 961 | true, |
983 | -1, | |
962 | false, | |
984 | 963 | ) |
985 | 964 | case "modulemeta": |
986 | 965 | return c.compileCallInternal( |
987 | 966 | [3]interface{}{c.funcModulemeta, 0, e.Name}, |
988 | 967 | e.Args, |
989 | 968 | true, |
990 | -1, | |
969 | false, | |
991 | 970 | ) |
992 | 971 | default: |
993 | 972 | return c.compileCall(e.Name, e.Args) |
998 | 977 | [3]interface{}{fn.callback, len(e.Args), e.Name}, |
999 | 978 | e.Args, |
1000 | 979 | true, |
1001 | -1, | |
980 | false, | |
1002 | 981 | ); err != nil { |
1003 | 982 | return err |
1004 | 983 | } |
1008 | 987 | return nil |
1009 | 988 | } |
1010 | 989 | return &funcNotFoundError{e} |
1011 | } | |
1012 | ||
1013 | // Appends the compiled code for the assignment operator (`=`) to maximize | |
1014 | // performance. Originally the operator was implemented as follows. | |
1015 | // | |
1016 | // def _assign(p; $x): reduce path(p) as $p (.; setpath($p; $x)); | |
1017 | // | |
1018 | // To overcome the difficulty of reducing allocations on `setpath`, we use the | |
1019 | // `allocator` type and track the allocated addresses during the reduction. | |
1020 | func (c *compiler) compileAssign() { | |
1021 | defer c.appendBuiltin("_assign", 2)() | |
1022 | scope := c.newScope() | |
1023 | v, p := [2]int{scope.id, 0}, [2]int{scope.id, 1} | |
1024 | x, a := [2]int{scope.id, 2}, [2]int{scope.id, 3} | |
1025 | c.appends( | |
1026 | &code{op: opscope, v: [3]int{scope.id, 4, 2}}, | |
1027 | &code{op: opstore, v: v}, // def _assign(p; $x): | |
1028 | &code{op: opstore, v: p}, | |
1029 | &code{op: opstore, v: x}, | |
1030 | &code{op: opload, v: v}, | |
1031 | &code{op: opexpbegin}, | |
1032 | &code{op: opload, v: x}, | |
1033 | &code{op: opcallpc}, | |
1034 | &code{op: opstore, v: x}, | |
1035 | &code{op: opexpend}, | |
1036 | &code{op: oppush, v: nil}, | |
1037 | &code{op: opcall, v: [3]interface{}{funcAllocator, 0, "_allocator"}}, | |
1038 | &code{op: opstore, v: a}, | |
1039 | &code{op: opload, v: v}, | |
1040 | &code{op: opfork, v: len(c.codes) + 28}, // reduce [L1] | |
1041 | &code{op: oppathbegin}, // path(p) | |
1042 | &code{op: opload, v: p}, | |
1043 | &code{op: opcallpc}, | |
1044 | &code{op: opload, v: v}, | |
1045 | &code{op: oppathend}, | |
1046 | &code{op: opstore, v: p}, // as $p (.; | |
1047 | &code{op: opload, v: a}, // setpath($p; $x) | |
1048 | &code{op: opload, v: x}, | |
1049 | &code{op: opload, v: p}, | |
1050 | &code{op: opload, v: v}, | |
1051 | &code{op: opcall, v: [3]interface{}{funcSetpathWithAllocator, 3, "_setpath"}}, | |
1052 | &code{op: opstore, v: v}, | |
1053 | &code{op: opbacktrack}, // ); | |
1054 | &code{op: oppop}, // [L1] | |
1055 | &code{op: opload, v: v}, | |
1056 | &code{op: opret}, | |
1057 | ) | |
1058 | } | |
1059 | ||
1060 | // Appends the compiled code for the update-assignment operator (`|=`) to | |
1061 | // maximize performance. We use the `allocator` type, just like `_assign/2`. | |
1062 | func (c *compiler) compileModify() { | |
1063 | defer c.appendBuiltin("_modify", 2)() | |
1064 | scope := c.newScope() | |
1065 | v, p := [2]int{scope.id, 0}, [2]int{scope.id, 1} | |
1066 | f, d := [2]int{scope.id, 2}, [2]int{scope.id, 3} | |
1067 | a, l := [2]int{scope.id, 4}, [2]int{scope.id, 5} | |
1068 | c.appends( | |
1069 | &code{op: opscope, v: [3]int{scope.id, 6, 2}}, | |
1070 | &code{op: opstore, v: v}, // def _modify(p; f): | |
1071 | &code{op: opstore, v: p}, | |
1072 | &code{op: opstore, v: f}, | |
1073 | &code{op: oppush, v: []interface{}{}}, | |
1074 | &code{op: opstore, v: d}, | |
1075 | &code{op: oppush, v: nil}, | |
1076 | &code{op: opcall, v: [3]interface{}{funcAllocator, 0, "_allocator"}}, | |
1077 | &code{op: opstore, v: a}, | |
1078 | &code{op: opload, v: v}, | |
1079 | &code{op: opfork, v: len(c.codes) + 39}, // reduce [L1] | |
1080 | &code{op: oppathbegin}, // path(p) | |
1081 | &code{op: opload, v: p}, | |
1082 | &code{op: opcallpc}, | |
1083 | &code{op: opload, v: v}, | |
1084 | &code{op: oppathend}, | |
1085 | &code{op: opstore, v: p}, // as $p (.; | |
1086 | &code{op: opforklabel, v: l}, // label $l | | |
1087 | &code{op: opload, v: v}, // | |
1088 | &code{op: opfork, v: len(c.codes) + 36}, // [L2] | |
1089 | &code{op: oppop}, // (getpath($p) | | |
1090 | &code{op: opload, v: a}, | |
1091 | &code{op: opload, v: p}, | |
1092 | &code{op: opload, v: v}, | |
1093 | &code{op: opcall, v: [3]interface{}{internalFuncs["getpath"].callback, 1, "getpath"}}, | |
1094 | &code{op: opload, v: f}, // f) | |
1095 | &code{op: opcallpc}, | |
1096 | &code{op: opload, v: p}, // setpath($p; ...) | |
1097 | &code{op: opload, v: v}, | |
1098 | &code{op: opcall, v: [3]interface{}{funcSetpathWithAllocator, 3, "_setpath"}}, | |
1099 | &code{op: opstore, v: v}, | |
1100 | &code{op: opload, v: v}, // ., break $l | |
1101 | &code{op: opfork, v: len(c.codes) + 34}, // [L4] | |
1102 | &code{op: opjump, v: len(c.codes) + 38}, // [L3] | |
1103 | &code{op: opload, v: l}, // [L4] | |
1104 | &code{op: opcall, v: [3]interface{}{funcBreak(""), 0, "_break"}}, | |
1105 | &code{op: opload, v: p}, // append $p to $d [L2] | |
1106 | &code{op: opappend, v: d}, // | |
1107 | &code{op: opbacktrack}, // ) | [L3] | |
1108 | &code{op: oppop}, // delpaths($d); [L1] | |
1109 | &code{op: opload, v: a}, | |
1110 | &code{op: opload, v: d}, | |
1111 | &code{op: opload, v: v}, | |
1112 | &code{op: opcall, v: [3]interface{}{funcDelpathsWithAllocator, 2, "_delpaths"}}, | |
1113 | &code{op: opret}, | |
1114 | ) | |
1115 | 990 | } |
1116 | 991 | |
1117 | 992 | func (c *compiler) funcBuiltins(interface{}, []interface{}) interface{} { |
1471 | 1346 | |
1472 | 1347 | func (c *compiler) compileCall(name string, args []*Query) error { |
1473 | 1348 | fn := internalFuncs[name] |
1474 | var indexing int | |
1475 | switch name { | |
1476 | case "_index", "_slice": | |
1477 | indexing = 1 | |
1478 | case "getpath": | |
1479 | indexing = 0 | |
1480 | default: | |
1481 | indexing = -1 | |
1482 | } | |
1483 | 1349 | if err := c.compileCallInternal( |
1484 | 1350 | [3]interface{}{fn.callback, len(args), name}, |
1485 | 1351 | args, |
1486 | 1352 | true, |
1487 | indexing, | |
1353 | name == "_index" || name == "_slice", | |
1488 | 1354 | ); err != nil { |
1489 | 1355 | return err |
1490 | 1356 | } |
1495 | 1361 | } |
1496 | 1362 | |
1497 | 1363 | func (c *compiler) compileCallPc(fn *funcinfo, args []*Query) error { |
1498 | return c.compileCallInternal(fn.pc, args, false, -1) | |
1364 | return c.compileCallInternal(fn.pc, args, false, false) | |
1499 | 1365 | } |
1500 | 1366 | |
1501 | 1367 | func (c *compiler) compileCallInternal( |
1502 | fn interface{}, args []*Query, internal bool, indexing int, | |
1503 | ) error { | |
1368 | fn interface{}, args []*Query, internal, indexing bool) error { | |
1504 | 1369 | if len(args) == 0 { |
1505 | 1370 | c.append(&code{op: opcall, v: fn}) |
1506 | 1371 | return nil |
1507 | 1372 | } |
1508 | 1373 | v := c.newVariable() |
1509 | 1374 | c.append(&code{op: opstore, v: v}) |
1510 | if indexing >= 0 { | |
1375 | if indexing { | |
1511 | 1376 | c.append(&code{op: opexpbegin}) |
1512 | 1377 | } |
1513 | 1378 | for i := len(args) - 1; i >= 0; i-- { |
1546 | 1411 | } else { |
1547 | 1412 | c.append(&code{op: oppushpc, v: pc}) |
1548 | 1413 | } |
1549 | if i == indexing { | |
1414 | if indexing && i == 1 { | |
1550 | 1415 | if c.codes[len(c.codes)-2].op == opexpbegin { |
1551 | 1416 | c.codes[len(c.codes)-2] = c.codes[len(c.codes)-1] |
1552 | 1417 | c.codes = c.codes[:len(c.codes)-1] |
1555 | 1420 | } |
1556 | 1421 | } |
1557 | 1422 | } |
1558 | if indexing > 0 { | |
1423 | if indexing { | |
1559 | 1424 | c.append(&code{op: oppush, v: nil}) |
1560 | 1425 | } else { |
1561 | 1426 | c.append(&code{op: opload, v: v}) |
1566 | 1431 | |
1567 | 1432 | func (c *compiler) append(code *code) { |
1568 | 1433 | c.codes = append(c.codes, code) |
1569 | } | |
1570 | ||
1571 | func (c *compiler) appends(codes ...*code) { | |
1572 | c.codes = append(c.codes, codes...) | |
1573 | 1434 | } |
1574 | 1435 | |
1575 | 1436 | func (c *compiler) lazy(f func() *code) func() { |
0 | golang-githubitchyny-gojq (0.12.9-1) UNRELEASED; urgency=low | |
1 | ||
2 | * Initial release. | |
3 | * Debianized package | |
4 | ||
5 | -- Debian Janitor <janitor@jelmer.uk> Thu, 01 Dec 2022 11:36:30 +0000 |
0 | Rules-Requires-Root: no | |
1 | Standards-Version: 4.6.1.0 | |
2 | XS-Go-Import-Path: github.com/itchyny/gojq | |
3 | Homepage: https://github.com/itchyny/gojq | |
4 | Section: devel | |
5 | Source: golang-githubitchyny-gojq | |
6 | Build-Depends: debhelper-compat (= 13), | |
7 | dh-sequence-golang, | |
8 | golang-github-google-go-cmp-dev, | |
9 | golang-github-itchyny-timefmt-go-dev, | |
10 | golang-github-mattn-go-isatty-dev, | |
11 | golang-github-mattn-go-runewidth-dev, | |
12 | golang-github-rivo-uniseg-dev, | |
13 | golang-go (>= 2:1.17~), | |
14 | golang-golang-x-sys-dev, | |
15 | golang-gopkg-yaml.v3-dev | |
16 | Testsuite: autopkgtest-pkg-go | |
17 | Priority: optional | |
18 | Maintainer: Debian Janitor <janitor@jelmer.uk> | |
19 | ||
20 | Package: golang-githubitchyny-gojq-dev | |
21 | Architecture: all | |
22 | Multi-Arch: foreign | |
23 | Depends: ${misc:Depends} | |
24 | Section: golang |
0 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | |
1 | Upstream-Name: github.com/itchyny/gojq | |
2 | ||
3 | Files: * | |
4 | Copyright: Unknown | |
5 | License: Expat | |
6 | Comment: No explicit license found, using license(s) from: | |
7 | LICENSE | |
8 | ||
9 | Files: debian/* | |
10 | Copyright: Unknown | |
11 | License: Expat | |
12 | Comment: No explicit license found, using license(s) from: | |
13 | LICENSE | |
14 | ||
15 | License: Expat | |
16 | Comment: Add the corresponding license text here |
0 | 3.0 (quilt) |
0 | --- | |
1 | Bug-Database: https://github.com/itchyny/gojq/issues | |
2 | Bug-Submit: https://github.com/itchyny/gojq/issues/new | |
3 | Repository: https://github.com/itchyny/gojq.git | |
4 | Repository-Browse: https://github.com/itchyny/gojq |
0 | version=4 | |
1 | opts=filenamemangle=s/.*\/v(\d\S+)\.tar\.gz/gojq-$1\.tar\.gz/ https://github.com/itchyny/gojq/tags .*\/v(\d\S+)\.tar\.gz |
203 | 203 | return fmt.Sprintf("[%d,%d,%d]", v[0], v[1], v[2]) |
204 | 204 | case [3]interface{}: |
205 | 205 | return fmt.Sprintf("[%v,%v,%v]", v[0], v[1], v[2]) |
206 | case allocator: | |
207 | return fmt.Sprintf("%v", v) | |
208 | 206 | default: |
209 | 207 | return Preview(v) |
210 | 208 | } |
88 | 88 | switch er := err.(type) { |
89 | 89 | case *tryEndError: |
90 | 90 | err = er.err |
91 | break loop | |
92 | case *breakError: | |
93 | 91 | break loop |
94 | 92 | case ValueError: |
95 | 93 | if er, ok := er.(*exitCodeError); ok && er.halt { |
307 | 305 | return xs[i].path.(string) < xs[j].path.(string) |
308 | 306 | }) |
309 | 307 | case Iter: |
308 | if !env.paths.empty() && env.expdepth == 0 { | |
309 | err = &invalidPathIterError{v} | |
310 | break loop | |
311 | } | |
310 | 312 | if w, ok := v.Next(); ok { |
311 | 313 | env.push(v) |
312 | 314 | env.pushfork(pc) |
7 | 7 | "math" |
8 | 8 | "math/big" |
9 | 9 | "net/url" |
10 | "reflect" | |
11 | 10 | "regexp" |
12 | 11 | "sort" |
13 | 12 | "strconv" |
70 | 69 | "explode": argFunc0(funcExplode), |
71 | 70 | "implode": argFunc0(funcImplode), |
72 | 71 | "split": {argcount1 | argcount2, false, funcSplit}, |
73 | "ascii_downcase": argFunc0(funcASCIIDowncase), | |
74 | "ascii_upcase": argFunc0(funcASCIIUpcase), | |
75 | 72 | "tojson": argFunc0(funcToJSON), |
76 | 73 | "fromjson": argFunc0(funcFromJSON), |
77 | 74 | "format": argFunc1(funcFormat), |
749 | 746 | return xs |
750 | 747 | } |
751 | 748 | |
752 | func funcASCIIDowncase(v interface{}) interface{} { | |
753 | s, ok := v.(string) | |
754 | if !ok { | |
755 | return &funcTypeError{"ascii_downcase", v} | |
756 | } | |
757 | return strings.Map(func(r rune) rune { | |
758 | if 'A' <= r && r <= 'Z' { | |
759 | return r + ('a' - 'A') | |
760 | } | |
761 | return r | |
762 | }, s) | |
763 | } | |
764 | ||
765 | func funcASCIIUpcase(v interface{}) interface{} { | |
766 | s, ok := v.(string) | |
767 | if !ok { | |
768 | return &funcTypeError{"ascii_upcase", v} | |
769 | } | |
770 | return strings.Map(func(r rune) rune { | |
771 | if 'a' <= r && r <= 'z' { | |
772 | return r - ('a' - 'A') | |
773 | } | |
774 | return r | |
775 | }, s) | |
776 | } | |
777 | ||
778 | 749 | func funcToJSON(v interface{}) interface{} { |
779 | 750 | return jsonMarshal(v) |
780 | 751 | } |
835 | 806 | } |
836 | 807 | } |
837 | 808 | |
838 | var csvEscaper = strings.NewReplacer( | |
839 | `"`, `""`, | |
840 | "\x00", `\0`, | |
841 | ) | |
842 | ||
843 | 809 | func funcToCSV(v interface{}) interface{} { |
844 | 810 | return formatJoin("csv", v, ",", func(s string) string { |
845 | return `"` + csvEscaper.Replace(s) + `"` | |
811 | return `"` + strings.ReplaceAll(s, `"`, `""`) + `"` | |
846 | 812 | }) |
847 | 813 | } |
848 | 814 | |
851 | 817 | "\r", `\r`, |
852 | 818 | "\n", `\n`, |
853 | 819 | "\\", `\\`, |
854 | "\x00", `\0`, | |
855 | 820 | ) |
856 | 821 | |
857 | 822 | func funcToTSV(v interface{}) interface{} { |
858 | 823 | return formatJoin("tsv", v, "\t", tsvEscaper.Replace) |
859 | 824 | } |
860 | ||
861 | var shEscaper = strings.NewReplacer( | |
862 | "'", `'\''`, | |
863 | "\x00", `\0`, | |
864 | ) | |
865 | 825 | |
866 | 826 | func funcToSh(v interface{}) interface{} { |
867 | 827 | if _, ok := v.([]interface{}); !ok { |
868 | 828 | v = []interface{}{v} |
869 | 829 | } |
870 | 830 | return formatJoin("sh", v, " ", func(s string) string { |
871 | return "'" + shEscaper.Replace(s) + "'" | |
831 | return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'" | |
872 | 832 | }) |
873 | 833 | } |
874 | 834 | |
1414 | 1374 | return ok && !math.IsNaN(x) && !math.IsInf(x, 0) && x != 0.0 |
1415 | 1375 | } |
1416 | 1376 | |
1417 | // An `allocator` creates new maps and slices, stores the allocated addresses. | |
1418 | // This allocator is used to reduce allocations on assignment operator (`=`), | |
1419 | // update-assignment operator (`|=`), and the `map_values`, `del`, `delpaths` | |
1420 | // functions. | |
1421 | type allocator map[uintptr]struct{} | |
1422 | ||
1423 | func funcAllocator(interface{}, []interface{}) interface{} { | |
1424 | return allocator{} | |
1425 | } | |
1426 | ||
1427 | func (a allocator) allocated(v interface{}) bool { | |
1428 | _, ok := a[reflect.ValueOf(v).Pointer()] | |
1429 | return ok | |
1430 | } | |
1431 | ||
1432 | func (a allocator) makeObject(l int) map[string]interface{} { | |
1433 | v := make(map[string]interface{}, l) | |
1434 | if a != nil { | |
1435 | a[reflect.ValueOf(v).Pointer()] = struct{}{} | |
1436 | } | |
1437 | return v | |
1438 | } | |
1439 | ||
1440 | func (a allocator) makeArray(l, c int) []interface{} { | |
1441 | if c < l { | |
1442 | c = l | |
1443 | } | |
1444 | v := make([]interface{}, l, c) | |
1445 | if a != nil { | |
1446 | a[reflect.ValueOf(v).Pointer()] = struct{}{} | |
1447 | } | |
1448 | return v | |
1449 | } | |
1450 | ||
1451 | 1377 | func funcSetpath(v, p, n interface{}) interface{} { |
1452 | // There is no need to use an allocator on a single update. | |
1453 | return setpath(v, p, n, nil) | |
1454 | } | |
1455 | ||
1456 | // Used in compiler#compileAssign and compiler#compileModify. | |
1457 | func funcSetpathWithAllocator(v interface{}, args []interface{}) interface{} { | |
1458 | return setpath(v, args[0], args[1], args[2].(allocator)) | |
1459 | } | |
1460 | ||
1461 | func setpath(v, p, n interface{}, a allocator) interface{} { | |
1462 | 1378 | path, ok := p.([]interface{}) |
1463 | 1379 | if !ok { |
1464 | 1380 | return &funcTypeError{"setpath", p} |
1465 | 1381 | } |
1466 | 1382 | var err error |
1467 | if v, err = update(v, path, n, a); err != nil { | |
1383 | if v, err = update(v, path, n); err != nil { | |
1468 | 1384 | if err, ok := err.(*funcTypeError); ok { |
1469 | 1385 | err.name = "setpath" |
1470 | 1386 | } |
1474 | 1390 | } |
1475 | 1391 | |
1476 | 1392 | func funcDelpaths(v, p interface{}) interface{} { |
1477 | return delpaths(v, p, allocator{}) | |
1478 | } | |
1479 | ||
1480 | // Used in compiler#compileAssign and compiler#compileModify. | |
1481 | func funcDelpathsWithAllocator(v interface{}, args []interface{}) interface{} { | |
1482 | return delpaths(v, args[0], args[1].(allocator)) | |
1483 | } | |
1484 | ||
1485 | func delpaths(v, p interface{}, a allocator) interface{} { | |
1486 | 1393 | paths, ok := p.([]interface{}) |
1487 | 1394 | if !ok { |
1488 | 1395 | return &funcTypeError{"delpaths", p} |
1489 | } | |
1490 | if len(paths) == 0 { | |
1491 | return v | |
1492 | 1396 | } |
1493 | 1397 | // Fills the paths with an empty value and then delete them. We cannot delete |
1494 | 1398 | // in each loop because array indices should not change. For example, |
1500 | 1404 | if !ok { |
1501 | 1405 | return &funcTypeError{"delpaths", p} |
1502 | 1406 | } |
1503 | if v, err = update(v, path, empty, a); err != nil { | |
1407 | if v, err = update(v, path, empty); err != nil { | |
1504 | 1408 | return err |
1505 | 1409 | } |
1506 | 1410 | } |
1507 | 1411 | return deleteEmpty(v) |
1508 | 1412 | } |
1509 | 1413 | |
1510 | func update(v interface{}, path []interface{}, n interface{}, a allocator) (interface{}, error) { | |
1414 | func update(v interface{}, path []interface{}, n interface{}) (interface{}, error) { | |
1511 | 1415 | if len(path) == 0 { |
1512 | 1416 | return n, nil |
1513 | 1417 | } |
1515 | 1419 | case string: |
1516 | 1420 | switch v := v.(type) { |
1517 | 1421 | case nil: |
1518 | return updateObject(nil, p, path[1:], n, a) | |
1422 | return updateObject(nil, p, path[1:], n) | |
1519 | 1423 | case map[string]interface{}: |
1520 | return updateObject(v, p, path[1:], n, a) | |
1424 | return updateObject(v, p, path[1:], n) | |
1521 | 1425 | case struct{}: |
1522 | 1426 | return v, nil |
1523 | 1427 | default: |
1527 | 1431 | i, _ := toInt(p) |
1528 | 1432 | switch v := v.(type) { |
1529 | 1433 | case nil: |
1530 | return updateArrayIndex(nil, i, path[1:], n, a) | |
1434 | return updateArrayIndex(nil, i, path[1:], n) | |
1531 | 1435 | case []interface{}: |
1532 | return updateArrayIndex(v, i, path[1:], n, a) | |
1436 | return updateArrayIndex(v, i, path[1:], n) | |
1533 | 1437 | case struct{}: |
1534 | 1438 | return v, nil |
1535 | 1439 | default: |
1538 | 1442 | case map[string]interface{}: |
1539 | 1443 | switch v := v.(type) { |
1540 | 1444 | case nil: |
1541 | return updateArraySlice(nil, p, path[1:], n, a) | |
1445 | return updateArraySlice(nil, p, path[1:], n) | |
1542 | 1446 | case []interface{}: |
1543 | return updateArraySlice(v, p, path[1:], n, a) | |
1447 | return updateArraySlice(v, p, path[1:], n) | |
1544 | 1448 | case struct{}: |
1545 | 1449 | return v, nil |
1546 | 1450 | default: |
1556 | 1460 | } |
1557 | 1461 | } |
1558 | 1462 | |
1559 | func updateObject(v map[string]interface{}, k string, path []interface{}, n interface{}, a allocator) (interface{}, error) { | |
1463 | func updateObject(v map[string]interface{}, k string, path []interface{}, n interface{}) (interface{}, error) { | |
1560 | 1464 | x, ok := v[k] |
1561 | 1465 | if !ok && n == struct{}{} { |
1562 | 1466 | return v, nil |
1563 | 1467 | } |
1564 | u, err := update(x, path, n, a) | |
1468 | u, err := update(x, path, n) | |
1565 | 1469 | if err != nil { |
1566 | 1470 | return nil, err |
1567 | 1471 | } |
1568 | if a.allocated(v) { | |
1569 | v[k] = u | |
1570 | return v, nil | |
1571 | } | |
1572 | w := a.makeObject(len(v) + 1) | |
1472 | w := make(map[string]interface{}, len(v)+1) | |
1573 | 1473 | for k, v := range v { |
1574 | 1474 | w[k] = v |
1575 | 1475 | } |
1577 | 1477 | return w, nil |
1578 | 1478 | } |
1579 | 1479 | |
1580 | func updateArrayIndex(v []interface{}, i int, path []interface{}, n interface{}, a allocator) (interface{}, error) { | |
1480 | func updateArrayIndex(v []interface{}, i int, path []interface{}, n interface{}) (interface{}, error) { | |
1581 | 1481 | var x interface{} |
1582 | 1482 | if j := clampIndex(i, -1, len(v)); j < 0 { |
1583 | 1483 | if n == struct{}{} { |
1595 | 1495 | return nil, &arrayIndexTooLargeError{i} |
1596 | 1496 | } |
1597 | 1497 | } |
1598 | u, err := update(x, path, n, a) | |
1498 | u, err := update(x, path, n) | |
1599 | 1499 | if err != nil { |
1600 | 1500 | return nil, err |
1601 | 1501 | } |
1602 | l, c := len(v), cap(v) | |
1603 | if a.allocated(v) { | |
1604 | if i < c { | |
1605 | if i >= l { | |
1606 | v = v[:i+1] | |
1607 | } | |
1608 | v[i] = u | |
1609 | return v, nil | |
1610 | } | |
1611 | c *= 2 | |
1612 | } | |
1502 | l := len(v) | |
1613 | 1503 | if i >= l { |
1614 | 1504 | l = i + 1 |
1615 | 1505 | } |
1616 | w := a.makeArray(l, c) | |
1506 | w := make([]interface{}, l) | |
1617 | 1507 | copy(w, v) |
1618 | 1508 | w[i] = u |
1619 | 1509 | return w, nil |
1620 | 1510 | } |
1621 | 1511 | |
1622 | func updateArraySlice(v []interface{}, m map[string]interface{}, path []interface{}, n interface{}, a allocator) (interface{}, error) { | |
1512 | func updateArraySlice(v []interface{}, m map[string]interface{}, path []interface{}, n interface{}) (interface{}, error) { | |
1623 | 1513 | s, ok := m["start"] |
1624 | 1514 | if !ok { |
1625 | 1515 | return nil, &expectedStartEndError{m} |
1640 | 1530 | if start == end && n == struct{}{} { |
1641 | 1531 | return v, nil |
1642 | 1532 | } |
1643 | u, err := update(v[start:end], path, n, a) | |
1533 | u, err := update(v[start:end], path, n) | |
1644 | 1534 | if err != nil { |
1645 | 1535 | return nil, err |
1646 | 1536 | } |
1647 | 1537 | switch u := u.(type) { |
1648 | 1538 | case []interface{}: |
1649 | var w []interface{} | |
1650 | if len(u) == end-start && a.allocated(v) { | |
1651 | w = v | |
1652 | } else { | |
1653 | w = a.makeArray(len(v)-(end-start)+len(u), 0) | |
1654 | copy(w, v[:start]) | |
1655 | copy(w[start+len(u):], v[end:]) | |
1656 | } | |
1539 | w := make([]interface{}, len(v)-(end-start)+len(u)) | |
1540 | copy(w, v[:start]) | |
1657 | 1541 | copy(w[start:], u) |
1542 | copy(w[start+len(u):], v[end:]) | |
1658 | 1543 | return w, nil |
1659 | 1544 | case struct{}: |
1660 | var w []interface{} | |
1661 | if a.allocated(v) { | |
1662 | w = v | |
1663 | } else { | |
1664 | w = a.makeArray(len(v), 0) | |
1665 | copy(w, v) | |
1666 | } | |
1545 | w := make([]interface{}, len(v)) | |
1546 | copy(w, v) | |
1667 | 1547 | for i := start; i < end; i++ { |
1668 | 1548 | w[i] = u |
1669 | 1549 | } |
87 | 87 | type moduleLoaderJSON struct{} |
88 | 88 | |
89 | 89 | func (*moduleLoaderJSON) LoadJSON(name string) (interface{}, error) { |
90 | if name == "module1" { | |
90 | switch name { | |
91 | case "module1": | |
91 | 92 | return []interface{}{1.0, 42, json.Number("123")}, nil |
92 | 93 | } |
93 | 94 | return nil, fmt.Errorf("module not found: %q", name) |
670 | 671 | } |
671 | 672 | } |
672 | 673 | |
673 | func TestWithIterFunctionPathIndexing(t *testing.T) { | |
674 | func TestWithIterFunctionPath(t *testing.T) { | |
674 | 675 | query, err := gojq.Parse(".[f] = 1") |
675 | 676 | if err != nil { |
676 | 677 | t.Fatal(err) |
695 | 696 | } |
696 | 697 | } |
697 | 698 | |
698 | func TestWithIterFunctionPathInputValue(t *testing.T) { | |
699 | query, err := gojq.Parse("{x: 0} | (f|.x) = 1") | |
700 | if err != nil { | |
701 | t.Fatal(err) | |
702 | } | |
703 | code, err := gojq.Compile(query, | |
704 | gojq.WithIterFunction("f", 0, 0, func(v interface{}, _ []interface{}) gojq.Iter { | |
705 | return gojq.NewIter(v, v, v) | |
706 | }), | |
707 | ) | |
708 | if err != nil { | |
709 | t.Fatal(err) | |
710 | } | |
711 | iter := code.Run(nil) | |
712 | for { | |
713 | v, ok := iter.Next() | |
714 | if !ok { | |
715 | break | |
716 | } | |
717 | if expected := map[string]interface{}{"x": 1}; !reflect.DeepEqual(v, expected) { | |
718 | t.Errorf("expected: %v, got: %v", expected, v) | |
719 | } | |
720 | } | |
721 | } | |
722 | ||
723 | func TestWithIterFunctionPathEmpty(t *testing.T) { | |
699 | func TestWithIterFunctionPathError(t *testing.T) { | |
724 | 700 | query, err := gojq.Parse("{x: 0} | (f|.x) = 1") |
725 | 701 | if err != nil { |
726 | 702 | t.Fatal(err) |
727 | 703 | } |
728 | 704 | code, err := gojq.Compile(query, |
729 | 705 | gojq.WithIterFunction("f", 0, 0, func(interface{}, []interface{}) gojq.Iter { |
730 | return gojq.NewIter() | |
731 | }), | |
732 | ) | |
733 | if err != nil { | |
734 | t.Fatal(err) | |
735 | } | |
736 | iter := code.Run(nil) | |
737 | for { | |
738 | v, ok := iter.Next() | |
739 | if !ok { | |
740 | break | |
741 | } | |
742 | if expected := map[string]interface{}{"x": 0}; !reflect.DeepEqual(v, expected) { | |
743 | t.Errorf("expected: %v, got: %v", expected, v) | |
744 | } | |
745 | } | |
746 | } | |
747 | ||
748 | func TestWithIterFunctionInvalidPathError(t *testing.T) { | |
749 | query, err := gojq.Parse("{x: 0} | (f|.x) = 1") | |
750 | if err != nil { | |
751 | t.Fatal(err) | |
752 | } | |
753 | code, err := gojq.Compile(query, | |
754 | gojq.WithIterFunction("f", 0, 0, func(interface{}, []interface{}) gojq.Iter { | |
755 | return gojq.NewIter(map[string]interface{}{"x": 1}) | |
706 | return gojq.NewIter(map[string]interface{}{"x": 0}) | |
756 | 707 | }), |
757 | 708 | ) |
758 | 709 | if err != nil { |
764 | 715 | t.Fatal("should emit an error but got no output") |
765 | 716 | } |
766 | 717 | if err, ok := v.(error); ok { |
767 | if expected := `invalid path against: object ({"x":1})`; err.Error() != expected { | |
768 | t.Errorf("expected: %v, got: %v", expected, err) | |
769 | } | |
770 | } else { | |
771 | t.Errorf("should emit an error but got: %v", v) | |
772 | } | |
773 | } | |
774 | ||
775 | func TestWithIterFunctionPathError(t *testing.T) { | |
776 | query, err := gojq.Parse("{x: 0} | (f|.x) = 1") | |
777 | if err != nil { | |
778 | t.Fatal(err) | |
779 | } | |
780 | code, err := gojq.Compile(query, | |
781 | gojq.WithIterFunction("f", 0, 0, func(interface{}, []interface{}) gojq.Iter { | |
782 | return gojq.NewIter(errors.New("error")) | |
783 | }), | |
784 | ) | |
785 | if err != nil { | |
786 | t.Fatal(err) | |
787 | } | |
788 | iter := code.Run(nil) | |
789 | v, ok := iter.Next() | |
790 | if !ok { | |
791 | t.Fatal("should emit an error but got no output") | |
792 | } | |
793 | if err, ok := v.(error); ok { | |
794 | if expected := "error"; err.Error() != expected { | |
718 | if expected := "invalid path on iterating against: gojq.Iter"; err.Error() != expected { | |
795 | 719 | t.Errorf("expected: %v, got: %v", expected, err) |
796 | 720 | } |
797 | 721 | } else { |
823 | 747 | type moduleLoader2 struct{} |
824 | 748 | |
825 | 749 | func (*moduleLoader2) LoadModule(name string) (*gojq.Query, error) { |
826 | if name == "module1" { | |
750 | switch name { | |
751 | case "module1": | |
827 | 752 | return gojq.Parse(` |
828 | 753 | def g: def h: f * 3; h * 4; |
829 | 754 | `) |
3 | 3 | package gojq |
4 | 4 | |
5 | 5 | import __yyfmt__ "fmt" |
6 | ||
7 | //line parser.go.y:2 | |
6 | 8 | |
7 | 9 | // Parse a query string, and returns the query struct. |
8 | 10 | // |
10 | 12 | // which reports the invalid token and the byte offset in the query string. The |
11 | 13 | // token is empty if the error occurred after scanning the entire query string. |
12 | 14 | // The byte offset is the scanned bytes when the error occurred. |
13 | // | |
14 | //line parser.go.y:2 | |
15 | 15 | func Parse(src string) (*Query, error) { |
16 | 16 | l := newLexer(src) |
17 | 17 | if yyParse(l) > 0 { |