pax_global_header00006660000000000000000000000064141664215630014522gustar00rootroot0000000000000052 comment=6a05b6c59acecb7c7ca9701f3a71e927f49a0b58 golang-github-wader-gojq-0.0~git20220108.6a05b6c/000077500000000000000000000000001416642156300207345ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/.dockerignore000066400000000000000000000001011416642156300234000ustar00rootroot00000000000000/gojq /goxz /CREDITS /._* /y.output *.exe *.test *.out /.github/ golang-github-wader-gojq-0.0~git20220108.6a05b6c/.gitattributes000066400000000000000000000000541416642156300236260ustar00rootroot00000000000000**/testdata/** binary /builtin.go eol=lf golang-github-wader-gojq-0.0~git20220108.6a05b6c/.github/000077500000000000000000000000001416642156300222745ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/.github/workflows/000077500000000000000000000000001416642156300243315ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/.github/workflows/ci.yaml000066400000000000000000000027441416642156300256170ustar00rootroot00000000000000name: CI on: push: branches: - main pull_request: jobs: test: name: Test runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] go: [1.17.x, 1.16.x] steps: - name: Checkout code uses: actions/checkout@v2 - name: Setup Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Test run: make test - name: Test with GOARCH=386 run: env GOARCH=386 go test -v ./... if: matrix.os != 'macos-latest' - name: Lint run: make lint - name: Check tools run: make check-tools - name: Check go generate run: go generate && ! git diff | grep ^ shell: bash - name: Check command examples in README.md run: | ./gojq -Rnr 'reduce inputs as $x ( {}; if $x|test("^ [$] .*gojq|^```$") then if .command then .results += [{command: .command, output: .output}] | del(.command,.output) end | if $x|test("gojq") then .command = $x[3:] end elif .command then .output += ($x + "\n" | sub(" +#.*"; "")) end ) | .results[] | "if got=$(diff <(printf %s \(.output | @sh)) \\ <(\(.command | gsub("gojq"; "./gojq")) 2>&1)); then echo ok: \(.command | @sh) else echo ng: \(.command | @sh); echo \"$got\"; exit 1 fi" ' README.md | bash shell: bash golang-github-wader-gojq-0.0~git20220108.6a05b6c/.github/workflows/release.yaml000066400000000000000000000034441416642156300266420ustar00rootroot00000000000000name: Release on: push: tags: - 'v*' jobs: release: name: Release runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Setup Go uses: actions/setup-go@v2 with: go-version: 1.x - name: Cross build run: make cross - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} - name: Upload run: make upload env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Docker metadata uses: docker/metadata-action@v3 id: metadata with: images: | ${{ github.repository }} ghcr.io/${{ github.repository }} tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - name: Set up QEMU uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Login to Docker Hub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to GitHub Container Registry uses: docker/login-action@v1 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and release Docker image uses: docker/build-push-action@v2 with: context: . push: true platforms: linux/amd64, linux/arm64 tags: ${{ steps.metadata.outputs.tags }} labels: ${{ steps.metadata.outputs.labels }} golang-github-wader-gojq-0.0~git20220108.6a05b6c/.gitignore000066400000000000000000000000671416642156300227270ustar00rootroot00000000000000/gojq /goxz /CREDITS /._* /y.output *.exe *.test *.out golang-github-wader-gojq-0.0~git20220108.6a05b6c/CHANGELOG.md000066400000000000000000000364231416642156300225550ustar00rootroot00000000000000# Changelog ## [v0.12.6](https://github.com/itchyny/gojq/compare/v0.12.5..v0.12.6) (2021-12-01) * implement options for consuming remaining arguments (`--args`, `--jsonargs`, `$ARGS.positional`) * fix `delpaths` function with overlapped paths * fix `--exit-status` flag with `halt`, `halt_error` functions * fix `input_filename` function with null input option * fix path value validation for `nan` * fix crash on branch optimization (`if 0 then . else 0|0 end`) * add validation on regular expression flags to reject unsupported ones * improve performance of `range`, `join`, `flatten` functions * improve constant value optimization for object with quoted keys * remove dependency on forked `go-flags` package ## [v0.12.5](https://github.com/itchyny/gojq/compare/v0.12.4..v0.12.5) (2021-09-01) * implement `input_filename` function for the command * fix priority bug of declared functions and arguments (`def g: 1; def f(g): g; f(2)`) * fix label handling to catch the correct break error (`first((0, 0) | first(0))`) * fix `null|error` and `error(null)` to behave like `empty` (`null | [0, error, error(null), 1]`) * fix integer division to keep precision when divisible (`1 / 1 * 1000000000000000000000`) * fix modulo operator on negative number and large number (`(-1) % 10000000000`) * fix combination of slurp (`--slurp`) and raw input option (`--raw-input`) to keep newlines * change the default module paths to `~/.jq`, `$ORIGIN/../lib/gojq`, `$ORIGIN/lib` where `$ORIGIN` is the directory where the executable is located in * improve command argument parser to recognize query with leading hyphen, allow hyphen for standard input, and force posix style on Windows * improve `@base64d` to allow input without padding characters * improve `fromdate`, `fromdateiso8601` to parse date time strings with timezone offset * improve `halt_error` to print error values without prefix * improve `sub`, `gsub` to allow the replacement string emitting multiple values * improve encoding `\b` and `\f` in strings * improve module loader for search path in query, and absolute path * improve query lexer to support string literal including newlines * improve performance of `index`, `rindex`, `indices`, `transpose`, and `walk` functions * improve performance of value preview in errors and debug mode * improve runtime performance including tail call optimization * switch Docker base image to `distroless/static:debug` ## [v0.12.4](https://github.com/itchyny/gojq/compare/v0.12.3..v0.12.4) (2021-06-01) * fix numeric conversion of large floating-point numbers in modulo operator * implement a compiler option for adding custom iterator functions * implement `gojq.NewIter` function for creating a new iterator from values * implement `$ARGS.named` for listing command line variables * remove `debug` and `stderr` functions from the library * stop printing newlines on `stderr` function for jq compatibility ## [v0.12.3](https://github.com/itchyny/gojq/compare/v0.12.2..v0.12.3) (2021-04-01) * fix array slicing with infinities and large numbers (`[0][-infinite:infinite], [0][:1e20]`) * fix multiplying strings and modulo by infinities on MIPS 64 architecture * fix git revision information in Docker images * release multi-platform Docker images for ARM 64 * switch to `distroless` image for Docker base image ## [v0.12.2](https://github.com/itchyny/gojq/compare/v0.12.1..v0.12.2) (2021-03-01) * implement `GOJQ_COLORS` environment variable to configure individual colors * respect `--color-output` (`-C`) option even if `NO_COLOR` is set * implement `gojq.ValueError` interface for custom internal functions * fix crash on timestamps in YAML input * fix calculation on `infinite` (`infinite-infinite | isnan`) * fix comparison on `nan` (`nan < nan`) * fix validation of `implode` (`[-1] | implode`) * fix number normalization for custom JSON module loader * print error line numbers on invalid JSON and YAML * improve `strftime`, `strptime` for time zone offsets * improve performance on reading a large JSON file given by command line argument * improve performance and reduce memory allocation of the lexer, compiler and executor ## [v0.12.1](https://github.com/itchyny/gojq/compare/v0.12.0..v0.12.1) (2021-01-17) * skip adding `$HOME/.jq` to module paths when `$HOME` is unset * fix optional operator followed by division operator (`1?/1`) * fix undefined format followed by optional operator (`@foo?`) * fix parsing invalid consecutive dots while scanning a number (`0..[empty]`) * fix panic on printing a query with `%#v` * improve performance and reduce memory allocation of `query.String()` * change all methods of `ModuleLoader` optional ## [v0.12.0](https://github.com/itchyny/gojq/compare/v0.11.2..v0.12.0) (2020-12-24) * implement tab indentation option (`--tab`) * implement a compiler option for adding custom internal functions * implement `gojq.Marshal` function for jq-flavored encoding * fix slurp option with JSON file arguments * fix escaping characters in object keys * fix normalizing negative `int64` to `int` on 32-bit architecture * fix crash on continuing iteration after emitting an error * `iter.Next()` does not normalize `NaN` and infinities anymore. Library users should take care of them. To handle them for encoding as JSON bytes, use `gojq.Marshal`. Also, `iter.Next()` does not clone values deeply anymore for performance reason. Users must not update the elements of the returned arrays and objects * improve performance of outputting JSON values by about 3.5 times ## [v0.11.2](https://github.com/itchyny/gojq/compare/v0.11.1..v0.11.2) (2020-10-01) * fix build for 32bit architecture * release to [GitHub Container Registry](https://github.com/users/itchyny/packages/container/package/gojq) ## [v0.11.1](https://github.com/itchyny/gojq/compare/v0.11.0..v0.11.1) (2020-08-22) * improve compatibility of `strftime`, `strptime` functions with jq * fix YAML input with numbers in keys * fix crash on multiplying a large number or `infinite` to a string * fix crash on error while slicing a string (`""[:{}]`) * fix crash on modulo by a number near 0.0 (`1 % 0.1`) * include `CREDITS` file in artifacts ## [v0.11.0](https://github.com/itchyny/gojq/compare/v0.10.4..v0.11.0) (2020-07-08) * improve parsing performance significantly * rewrite the parser from `participle` library to `goyacc` generated parser * release to [itchyny/gojq - Docker Hub](https://hub.docker.com/r/itchyny/gojq) * support string interpolation for object pattern key ## [v0.10.4](https://github.com/itchyny/gojq/compare/v0.10.3..v0.10.4) (2020-06-30) * implement variable in object key (`. as $x | { $x: 1 }`) * fix modify operator (`|=`) with `try` `catch` expression * fix optional operator (`?`) with alternative operator (`//`) in `map_values` function * fix normalizing numeric types for library users * export `gojq.NewModuleLoader` function for library users ## [v0.10.3](https://github.com/itchyny/gojq/compare/v0.10.2..v0.10.3) (2020-06-06) * implement `add`, `unique_by`, `max_by`, `min_by`, `reverse` by internal functions for performance and reducing the binary size * improve performance of `setpath`, `delpaths` functions * fix assignment against nested slicing (`[1,2,3] | .[1:][:1] = [5]`) * limit the array index of assignment operator * optimize constant arrays and objects ## [v0.10.2](https://github.com/itchyny/gojq/compare/v0.10.1..v0.10.2) (2020-05-24) * implement `sort_by`, `group_by`, `bsearch` by internal functions for performance and reducing the binary size * fix object construction and constant object to allow trailing commas * fix `tonumber` function to allow leading zeros * minify the builtin functions to reduce the binary size ## [v0.10.1](https://github.com/itchyny/gojq/compare/v0.10.0..v0.10.1) (2020-04-24) * fix array addition not to modify the left hand side ## [v0.10.0](https://github.com/itchyny/gojq/compare/v0.9.0..v0.10.0) (2020-04-02) * implement various functions (`format`, `significand`, `modulemeta`, `halt_error`) * implement `input`, `inputs` functions * implement stream option (`--stream`) * implement slicing with object (`.[{"start": 1, "end": 2}]`) * implement `NO_COLOR` environment variable support * implement `nul` output option (`-0`, `--nul-output`) * implement exit status option (`-e`, `--exit-status`) * implement `search` field of module meta object * implement combination of `--yaml-input` and `--slurp` * improve string token lexer and support nested string interpolation * improve the exit code for jq compatibility * improve default module search paths for jq compatibility * improve documentation for the usage as a library * change methods of `ModuleLoader` optional, implement `LoadModuleWithMeta` and `LoadJSONWithMeta` * fix number normalization for JSON arguments (`--argjson`, `--slurpfile`) * fix `0/0` and `infinite/infinite` * fix `error` function against `null` ## [v0.9.0](https://github.com/itchyny/gojq/compare/v0.8.0..v0.9.0) (2020-03-15) * implement various functions (`infinite`, `isfinite`, `isinfinite`, `finites`, `isnormal`, `normals`) * implement environment variables loader as a compiler option * implement `$NAME::NAME` syntax for imported JSON variable * fix modify operator with empty against array (`[range(9)] | (.[] | select(. % 2 > 0)) |= empty`) * fix variable and function scopes (`{ x: 1 } | . as $x | (.x as $x | $x) | ., $x`) * fix path analyzer * fix type check in `startswith` and `endswith` * ignore type error of `ltrimstr` and `rtrimstr` * remove nano seconds from `mktime` output * trim newline at the end of error messages * improve documents and examples ## [v0.8.0](https://github.com/itchyny/gojq/compare/v0.7.0..v0.8.0) (2020-03-02) * implement format strings (`@text`, `@json`, `@html`, `@uri`, `@csv`, `@tsv`, `@sh`, `@base64`, `@base64d`) * implement modules feature (`-L` option for directory to search modules from) * implement options for binding variables from arguments (`--arg`, `--argjson`) * implement options for binding variables from files (`--slurpfile`, `--rawfile`) * implement an option for indentation count (`--indent`) * fix `isnan` for `null` * fix path analyzer * fix error after optional operator (`1? | .x`) * add `$ENV` variable * add zsh completion file ## [v0.7.0](https://github.com/itchyny/gojq/compare/v0.6.0..v0.7.0) (2019-12-22) * implement YAML input (`--yaml-input`) and output (`--yaml-output`) * fix pipe in object value * fix precedence of if, try, reduce and foreach expressions * release from GitHub Actions ## [v0.6.0](https://github.com/itchyny/gojq/compare/v0.5.0..v0.6.0) (2019-08-26) * implement arbitrary-precision integer calculation * implement various functions (`repeat`, `pow10`, `nan`, `isnan`, `nearbyint`, `halt`, `INDEX`, `JOIN`, `IN`) * implement long options (`--compact-output`, `--raw-output`, `--join-output`, `--color-output`, `--monochrome-output`, `--null-input`, `--raw-input`, `--slurp`, `--from-file`, `--version`) * implement join output options (`-j`, `--join-output`) * implement color/monochrome output options (`-C`, `--color-output`, `-M`, `--monochrome-output`) * refactor builtin functions ## [v0.5.0](https://github.com/itchyny/gojq/compare/v0.4.0..v0.5.0) (2019-08-03) * implement various functions (`with_entries`, `from_entries`, `leaf_paths`, `contains`, `inside`, `split`, `stream`, `fromstream`, `truncate_stream`, `bsearch`, `path`, `paths`, `map_values`, `del`, `delpaths`, `getpath`, `gmtime`, `localtime`, `mktime`, `strftime`, `strflocaltime`, `strptime`, `todate`, `fromdate`, `now`, `match`, `test`, `capture`, `scan`, `splits`, `sub`, `gsub`, `debug`, `stderr`) * implement assignment operator (`=`) * implement modify operator (`|=`) * implement update operators (`+=`, `-=`, `*=`, `/=`, `%=`, `//=`) * implement destructuring alternative operator (`?//`) * allow function declaration inside query * implement `-f` flag for loading query from file * improve error message for parsing multiple line query ## [v0.4.0](https://github.com/itchyny/gojq/compare/v0.3.0..v0.4.0) (2019-07-20) * improve performance significantly * rewrite from recursive interpreter to stack machine based interpreter * allow debugging with `make install-debug` and `export GOJQ_DEBUG=1` * parse built-in functions and generate syntax trees before compilation * optimize tail recursion * fix behavior of optional operator * fix scopes of arguments of recursive function call * fix duplicate function argument names * implement `setpath` function ## [v0.3.0](https://github.com/itchyny/gojq/compare/v0.2.0..v0.3.0) (2019-06-05) * implement `reduce`, `foreach`, `label`, `break` syntax * improve binding variable syntax to bind to an object or an array * implement string interpolation * implement object index by string (`."example"`) * implement various functions (`add`, `flatten`, `min`, `min_by`, `max`, `max_by`, `sort`, `sort_by`, `group_by`, `unique`, `unique_by`, `tostring`, `indices`, `index`, `rindex`, `walk`, `transpose`, `first`, `last`, `nth`, `limit`, `all`, `any`, `isempty`, `error`, `builtins`, `env`) * implement math functions (`sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`, `floor`, `round`, `rint`, `ceil`, `trunc`, `fabs`, `sqrt`, `cbrt`, `exp`, `exp10`, `exp2`, `expm1`, `frexp`, `modf`, `log`, `log10`, `log1p`, `log2`, `logb`, `gamma`, `tgamma`, `lgamma`, `erf`, `erfc`, `j0`, `j1`, `y0`, `y1`, `atan2/2`, `copysign/2`, `drem/2`, `fdim/2`, `fmax/2`, `fmin/2`, `fmod/2`, `hypot/2`, `jn/2`, `ldexp/2`, `nextafter/2`, `nexttoward/2`, `remainder/2`, `scalb/2`, `scalbln/2`, `pow/2`, `yn/2`, `fma/3`) * support object construction with variables * support indexing against strings * fix function evaluation for recursive call * fix error handling of `//` operator * fix string representation of NaN and Inf * implement `-R` flag for reading input as raw strings * implement `-c` flag for compact output * implement `-n` flag for using null as input value * implement `-r` flag for outputting raw string * implement `-s` flag for reading all inputs into an array ## [v0.2.0](https://github.com/itchyny/gojq/compare/v0.1.0..v0.2.0) (2019-05-06) * implement binding variable syntax (`... as $var`) * implement `try` `catch` syntax * implement alternative operator (`//`) * implement various functions (`in`, `to_entries`, `startswith`, `endswith`, `ltrimstr`, `rtrimstr`, `combinations`, `ascii_downcase`, `ascii_upcase`, `tojson`, `fromjson`) * support query for object indexing * support object construction with variables * support indexing against strings ## [v0.1.0](https://github.com/itchyny/gojq/compare/v0.0.1..v0.1.0) (2019-05-02) * implement binary operators (`+`, `-`, `*`, `/`, `%`, `==`, `!=`, `>`, `<`, `>=`, `<=`, `and`, `or`) * implement unary operators (`+`, `-`) * implement booleans (`false`, `true`), `null`, number and string constant values * implement `empty` value * implement conditional syntax (`if` `then` `elif` `else` `end`) * implement various functions (`length`, `utf8bytelength`, `not`, `keys`, `has`, `map`, `select`, `recurse`, `while`, `until`, `range`, `tonumber`, `type`, `arrays`, `objects`, `iterables`, `booleans`, `numbers`, `strings`, `nulls`, `values`, `scalars`, `reverse`, `explode`, `implode`, `join`) * support function declaration * support iterators in object keys * support object construction shortcut * support query in array indices * support negative number indexing against arrays * support json file name arguments ## [v0.0.1](https://github.com/itchyny/gojq/compare/0fa3241..v0.0.1) (2019-04-14) * initial implementation golang-github-wader-gojq-0.0~git20220108.6a05b6c/Dockerfile000066400000000000000000000002761416642156300227330ustar00rootroot00000000000000FROM golang:1.17 AS builder WORKDIR /app COPY . . ENV CGO_ENABLED 0 RUN make build FROM gcr.io/distroless/static:debug COPY --from=builder /app/gojq / ENTRYPOINT ["/gojq"] CMD ["--help"] golang-github-wader-gojq-0.0~git20220108.6a05b6c/LICENSE000066400000000000000000000020671416642156300217460ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2019-2021 itchyny Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-wader-gojq-0.0~git20220108.6a05b6c/Makefile000066400000000000000000000054011416642156300223740ustar00rootroot00000000000000BIN := gojq VERSION := $$(make -s show-version) VERSION_PATH := cli CURRENT_REVISION := $(shell git rev-parse --short HEAD) BUILD_LDFLAGS := "-s -w -X github.com/itchyny/$(BIN)/cli.revision=$(CURRENT_REVISION)" GOBIN ?= $(shell go env GOPATH)/bin SHELL := /bin/bash .PHONY: all all: build .PHONY: build build: go build -ldflags=$(BUILD_LDFLAGS) -o $(BIN) ./cmd/$(BIN) .PHONY: build-dev build-dev: parser.go builtin.go go build -ldflags=$(BUILD_LDFLAGS) -o $(BIN) ./cmd/$(BIN) .PHONY: build-debug build-debug: parser.go builtin.go go build -tags debug -ldflags=$(BUILD_LDFLAGS) -o $(BIN) ./cmd/$(BIN) builtin.go: builtin.jq parser.go.y parser.go query.go operator.go _tools/* GOOS= GOARCH= go generate .SUFFIXES: parser.go: parser.go.y $(GOBIN)/goyacc goyacc -o $@ $< $(GOBIN)/goyacc: @go install golang.org/x/tools/cmd/goyacc@latest .PHONY: install install: go install -ldflags=$(BUILD_LDFLAGS) ./... .PHONY: install-dev install-dev: parser.go builtin.go go install -ldflags=$(BUILD_LDFLAGS) ./... .PHONY: install-debug install-debug: parser.go builtin.go go install -tags debug -ldflags=$(BUILD_LDFLAGS) ./... .PHONY: show-version show-version: $(GOBIN)/gobump @gobump show -r $(VERSION_PATH) $(GOBIN)/gobump: @go install github.com/x-motemen/gobump/cmd/gobump@latest .PHONY: cross cross: $(GOBIN)/goxz CREDITS goxz -n $(BIN) -pv=v$(VERSION) -include _$(BIN) -arch=amd64,arm64 \ -build-ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) $(GOBIN)/goxz: go install github.com/Songmu/goxz/cmd/goxz@latest CREDITS: $(GOBIN)/gocredits go.sum go mod tidy gocredits -w . $(GOBIN)/gocredits: go install github.com/Songmu/gocredits/cmd/gocredits@latest .PHONY: test test: build go test -v -race ./... .PHONY: lint lint: $(GOBIN)/staticcheck go vet ./... staticcheck -tags debug ./... $(GOBIN)/staticcheck: go install honnef.co/go/tools/cmd/staticcheck@latest .PHONY: check-tools check-tools: go run _tools/print_builtin.go .PHONY: clean clean: rm -rf $(BIN) goxz CREDITS go clean .PHONY: update update: export GOPROXY=direct update: rm -f go.sum && go get -u -d ./... && go get -d github.com/mattn/go-runewidth@v0.0.9 && go mod tidy sed -i.bak '/require (/,/)/d' go.dev.mod && rm -f go.dev.{sum,mod.bak} go get -u -d -modfile=go.dev.mod github.com/itchyny/{astgen,timefmt}-go && go generate .PHONY: bump bump: $(GOBIN)/gobump ifneq ($(shell git status --porcelain),) $(error git workspace is dirty) endif ifneq ($(shell git rev-parse --abbrev-ref HEAD),main) $(error current branch is not main) endif @gobump up -w "$(VERSION_PATH)" git commit -am "bump up version to $(VERSION)" git tag "v$(VERSION)" git push origin main git push origin "refs/tags/v$(VERSION)" .PHONY: upload upload: $(GOBIN)/ghr ghr "v$(VERSION)" goxz $(GOBIN)/ghr: go install github.com/tcnksm/ghr@latest golang-github-wader-gojq-0.0~git20220108.6a05b6c/README.md000066400000000000000000000272521416642156300222230ustar00rootroot00000000000000# gojq [![CI Status](https://github.com/itchyny/gojq/workflows/CI/badge.svg)](https://github.com/itchyny/gojq/actions) [![Go Report Card](https://goreportcard.com/badge/github.com/itchyny/gojq)](https://goreportcard.com/report/github.com/itchyny/gojq) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/itchyny/gojq/blob/main/LICENSE) [![release](https://img.shields.io/github/release/itchyny/gojq/all.svg)](https://github.com/itchyny/gojq/releases) [![pkg.go.dev](https://pkg.go.dev/badge/github.com/itchyny/gojq)](https://pkg.go.dev/github.com/itchyny/gojq) ### Pure Go implementation of [jq](https://github.com/stedolan/jq) This is an implementation of jq command written in Go language. You can also embed gojq as a library to your Go products. ## Usage ```sh $ echo '{"foo": 128}' | gojq '.foo' 128 $ echo '{"a": {"b": 42}}' | gojq '.a.b' 42 $ echo '{"id": "sample", "10": {"b": 42}}' | gojq '{(.id): .["10"].b}' { "sample": 42 } $ echo '[{"id":1},{"id":2},{"id":3}]' | gojq '.[] | .id' 1 2 3 $ echo '{"a":1,"b":2}' | gojq '.a += 1 | .b *= 2' { "a": 2, "b": 4 } $ echo '{"a":1} [2] 3' | gojq '. as {$a} ?// [$a] ?// $a | $a' 1 2 3 $ echo '{"foo": 4722366482869645213696}' | gojq .foo 4722366482869645213696 # keeps the precision of large numbers $ gojq -n 'def fact($n): if $n < 1 then 1 else $n * fact($n - 1) end; fact(50)' 30414093201713378043612608166064768844377641568960512000000000000 # arbitrary-precision integer calculation ``` Nice error messages. ```sh $ echo '[1,2,3]' | gojq '.foo & .bar' gojq: invalid query: .foo & .bar .foo & .bar ^ unexpected token "&" $ echo '{"foo": { bar: [] } }' | gojq '.' gojq: invalid json: {"foo": { bar: [] } } ^ invalid character 'b' looking for beginning of object key string ``` ## Installation ### Homebrew ```sh brew install gojq ``` ### Zero Install ```sh 0install add gojq https://apps.0install.net/utils/gojq.xml ``` ### Build from source ```sh go install github.com/itchyny/gojq/cmd/gojq@latest ``` ### Docker ```sh docker run -i --rm itchyny/gojq docker run -i --rm ghcr.io/itchyny/gojq ``` ## Difference to jq - gojq is purely implemented with Go language and is completely portable. jq depends on the C standard library so the availability of math functions depends on the library. jq also depends on the regular expression library and it makes build scripts complex. - gojq implements nice error messages for invalid query and JSON input. The error message of jq is sometimes difficult to tell where to fix the query. - 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. Also, gojq assumes only valid JSON input while jq deals with some JSON extensions; `NaN`, `Infinity` and `[000]`. - gojq supports arbitrary-precision integer calculation while jq does not. This is important to keep the precision of numeric IDs or nanosecond values. You can also use gojq to solve some mathematical problems which require big integers. Note that mathematical functions convert integers to floating-point numbers; only addition, subtraction, multiplication, modulo operation, and division (when divisible) keep integer precisions. When you want to calculate floor division of big integers, use `def intdiv($x; $y): ($x - $x % $y) / $y;`, instead of `$x / $y`. - 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 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)). - 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 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`. gojq normalizes floating-point numbers to fit to double-precision (64-bit) 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 supports reading from YAML input (`--yaml-input`) while jq does not. gojq also supports YAML output (`--yaml-output`). ### Color configuration The gojq command automatically disables coloring output when the output is not a tty. To force coloring output, specify `--color-output` (`-C`) option. When [`NO_COLOR` environment variable](https://no-color.org/) is present or `--monochrome-output` (`-M`) option is specified, gojq disables coloring output. Use `GOJQ_COLORS` environment variable to configure individual colors. The variable is a colon-separated list of ANSI escape sequences of `null`, `false`, `true`, numbers, strings, object keys, arrays, and objects. The default configuration is `90:33:33:36:32:34;1`. ## Usage as a library You can use the gojq parser and interpreter from your Go products. ```go package main import ( "fmt" "log" "github.com/wader/gojq" ) func main() { query, err := gojq.Parse(".foo | ..") if err != nil { log.Fatalln(err) } input := map[string]interface{}{"foo": []interface{}{1, 2, 3}} iter := query.Run(input) // or query.RunWithContext for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } } ``` - Firstly, use [`gojq.Parse(string) (*Query, error)`](https://pkg.go.dev/github.com/itchyny/gojq#Parse) to get the query from a string. - Secondly, get the result iterator - using [`query.Run`](https://pkg.go.dev/github.com/itchyny/gojq#Query.Run) or [`query.RunWithContext`](https://pkg.go.dev/github.com/itchyny/gojq#Query.RunWithContext) - or alternatively, compile the query using [`gojq.Compile`](https://pkg.go.dev/github.com/itchyny/gojq#Compile) and then [`code.Run`](https://pkg.go.dev/github.com/itchyny/gojq#Code.Run) or [`code.RunWithContext`](https://pkg.go.dev/github.com/itchyny/gojq#Code.RunWithContext). You can reuse the `*Code` against multiple inputs to avoid compilation of the same query. - In either case, you cannot use custom type values as the query input. The type should be `[]interface{}` for an array and `map[string]interface{}` for a map (just like decoded to an `interface{}` using the [encoding/json](https://golang.org/pkg/encoding/json/) package). You can't use `[]int` or `map[string]string`, for example. If you want to query your custom struct, marshal to JSON, unmarshal to `interface{}` and use it as the query input. - Thirdly, iterate through the results using [`iter.Next() (interface{}, bool)`](https://pkg.go.dev/github.com/itchyny/gojq#Iter). The iterator can emit an error so make sure to handle it. The method returns `true` with results, and `false` when the iterator terminates. - The return type is not `(interface{}, error)` because iterators can emit multiple errors and you can continue after an error. It is difficult for the iterator to tell the termination in this situation. - Note that the result iterator may emit infinite number of values; `repeat(0)` and `range(infinite)`. It may stuck with no output value; `def f: f; f`. Use `RunWithContext` when you want to limit the execution time. [`gojq.Compile`](https://pkg.go.dev/github.com/itchyny/gojq#Compile) allows to configure the following compiler options. - [`gojq.WithModuleLoader`](https://pkg.go.dev/github.com/itchyny/gojq#WithModuleLoader) allows to load modules. By default, the module feature is disabled. If you want to load modules from the file system, use [`gojq.NewModuleLoader`](https://pkg.go.dev/github.com/itchyny/gojq#NewModuleLoader). - [`gojq.WithEnvironLoader`](https://pkg.go.dev/github.com/itchyny/gojq#WithEnvironLoader) allows to configure the environment variables referenced by `env` and `$ENV`. By default, OS environment variables are not accessible due to security reasons. You can use `gojq.WithEnvironLoader(os.Environ)` if you want. - [`gojq.WithVariables`](https://pkg.go.dev/github.com/itchyny/gojq#WithVariables) allows to configure the variables which can be used in the query. Pass the values of the variables to [`code.Run`](https://pkg.go.dev/github.com/itchyny/gojq#Code.Run) in the same order. - [`gojq.WithFunction`](https://pkg.go.dev/github.com/itchyny/gojq#WithFunction) allows to add a custom internal function. An internal function can return a single value (which can be an error) each invocation. To add a jq function (which may include a comma operator to emit multiple values, `empty` function, accept a filter for its argument, or call another built-in function), use `LoadInitModules` of the module loader. - [`gojq.WithIterFunction`](https://pkg.go.dev/github.com/itchyny/gojq#WithIterFunction) allows to add a custom iterator function. An iterator function returns an iterator to emit multiple values. You cannot define both iterator and non-iterator functions of the same name (with possibly different arities). You can use [`gojq.NewIter`](https://pkg.go.dev/github.com/itchyny/gojq#NewIter) to convert values or an error to a [`gojq.Iter`](https://pkg.go.dev/github.com/itchyny/gojq#Iter). - [`gojq.WithInputIter`](https://pkg.go.dev/github.com/itchyny/gojq#WithInputIter) allows to use `input` and `inputs` functions. By default, these functions are disabled. ## Bug Tracker Report bug at [Issues・itchyny/gojq - GitHub](https://github.com/itchyny/gojq/issues). ## Author itchyny (https://github.com/itchyny) ## License This software is released under the MIT License, see LICENSE. golang-github-wader-gojq-0.0~git20220108.6a05b6c/_gojq000066400000000000000000000035411416642156300217610ustar00rootroot00000000000000#compdef gojq _gojq() { _arguments -C \ '(-c --compact-output)'{-c,--compact-output}'[compact output]' \ '(-r --raw-output)'{-r,--raw-output}'[output raw strings]' \ '(-j --join-output)'{-j,--join-output}'[stop printing a newline after each output]' \ '(-0 --nul-output)'{-0,--nul-output}'[print NUL after each output]' \ '(-C --color-output)'{-C,--color-output}'[colorize output even if piped]' \ '(-M --monochrome-output)'{-M,--monochrome-output}'[stop colorizing output]' \ '(--yaml-output)'--yaml-output'[output by YAML]' \ '(--indent)'--indent'[number of spaces for indentation]:indentation count' \ '(--tab)'--tab'[use tabs for indentation]' \ '(-n --null-input)'{-n,--null-input}'[use null as input value]' \ '(-R --raw-input)'{-R,--raw-input}'[read input as raw strings]' \ '(-s --slurp)'{-s,--slurp}'[read all inputs into an array]' \ '(--stream)'--stream'[parse input in stream fashion]' \ '(--yaml-input)'--yaml-input'[read input as YAML]' \ '(-f --from-file)'{-f,--from-file}'[load query from file]:filename of jq query:_files' \ '(-L)'-L'[directory to search modules from]:module directory:_directories' \ '(--arg)'--arg'[set variable to string value]:variable name:' \ '(--argjson)'--argjson'[set variable to JSON value]:variable name:' \ '(--slurpfile)'--slurpfile'[set variable to the JSON contents of the file]:variable name:' \ '(--rawfile)'--rawfile'[set variable to the contents of the file]:variable name:' \ '(--args)'--args'[consume remaining arguments as positional string values]' \ '(--jsonargs)'--jsonargs'[consume remaining arguments as positional JSON values]' \ '(-e --exit-status)'{-e,--exit-status}'[exit 1 when the last value is false or null]' \ '(-v --version)'{-v,--version}'[print version]' \ '(-h --help)'{-h,--help}'[print help]' \ && ret=0 } golang-github-wader-gojq-0.0~git20220108.6a05b6c/_tools/000077500000000000000000000000001416642156300222335ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/_tools/gen_builtin.go000066400000000000000000000042451416642156300250660ustar00rootroot00000000000000package main import ( "flag" "fmt" "go/ast" "go/printer" "go/token" "os" "regexp" "strings" "github.com/itchyny/astgen-go" "github.com/wader/gojq" ) const fileFormat = `// Code generated by _tools/gen_builtin.go; DO NOT EDIT. package gojq func init() {%s} ` func main() { var input, output string flag.StringVar(&input, "i", "", "input file") flag.StringVar(&output, "o", "", "output file") flag.Parse() if err := run(input, output); err != nil { fmt.Fprint(os.Stderr, err) os.Exit(1) } } func run(input, output string) error { cnt, err := os.ReadFile(input) if err != nil { return err } qs := make(map[string][]*gojq.FuncDef) q, err := gojq.Parse(string(cnt)) if err != nil { return err } for _, fd := range q.FuncDefs { name := fd.Name if name[0] == '_' { name = name[1:] } fd.Minify() qs[name] = append(qs[fd.Name], fd) } t, err := astgen.Build(qs) if err != nil { return err } var sb strings.Builder sb.WriteString("\n\tbuiltinFuncDefs = ") if err := printCompositeLit(&sb, t.(*ast.CompositeLit)); err != nil { return err } out := os.Stdout if output != "" { f, err := os.Create(output) if err != nil { return err } defer f.Close() out = f } _, err = fmt.Fprintf(out, fileFormat, sb.String()) return err } func printCompositeLit(out *strings.Builder, t *ast.CompositeLit) error { err := printer.Fprint(out, token.NewFileSet(), t.Type) if err != nil { return err } out.WriteString("{") for _, kv := range t.Elts { out.WriteString("\n\t\t") var sb strings.Builder err = printer.Fprint(&sb, token.NewFileSet(), kv) if err != nil { return err } str := sb.String() for op := gojq.OpPipe; op <= gojq.OpUpdateAlt; op++ { r := regexp.MustCompile(fmt.Sprintf(`\b((?:Update)?Op): %d\b`, op)) str = r.ReplaceAllString(str, fmt.Sprintf("$1: %#v", op)) } for termType := gojq.TermTypeIdentity; termType <= gojq.TermTypeQuery; termType++ { r := regexp.MustCompile(fmt.Sprintf(`(Term{Type): %d\b`, termType)) str = r.ReplaceAllString(str, fmt.Sprintf("$1: %#v", termType)) } out.WriteString(strings.ReplaceAll(str, "gojq.", "")) out.WriteString(",") } out.WriteString("\n\t}\n") return nil } golang-github-wader-gojq-0.0~git20220108.6a05b6c/_tools/print_builtin.go000066400000000000000000000020351416642156300254440ustar00rootroot00000000000000package main import ( "fmt" "os" "reflect" "sort" "strings" "github.com/wader/gojq" ) func main() { cnt, err := os.ReadFile("builtin.jq") if err != nil { panic(err) } fds := make(map[string][]*gojq.FuncDef) q, err := gojq.Parse(string(cnt)) if err != nil { panic(err) } for _, fd := range q.FuncDefs { name := fd.Name if name[0] == '_' { name = name[1:] } fd.Minify() fds[name] = append(fds[fd.Name], fd) } count := len(fds) names, i := make([]string, count), 0 for n := range fds { names[i] = n i++ } sort.Strings(names) for _, n := range names { var s strings.Builder for _, fd := range fds[n] { fmt.Fprintf(&s, "%s ", fd) } q, err := gojq.Parse(s.String()) if err != nil { panic(fmt.Sprintf("%s: %s", err, s.String())) } for _, fd := range q.FuncDefs { fd.Minify() } if !reflect.DeepEqual(q.FuncDefs, fds[n]) { fmt.Printf("failed: %s: %s %s\n", n, q.FuncDefs, fds[n]) continue } fmt.Printf("ok: %s: %s\n", n, s.String()) count-- } if count > 0 { os.Exit(1) } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/builtin.go000066400000000000000000001446561416642156300227510ustar00rootroot00000000000000// Code generated by _tools/gen_builtin.go; DO NOT EDIT. package gojq func init() { builtinFuncDefs = map[string][]*FuncDef{ "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: "."}}}}}}}, "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"}}}}}}}, "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"}}}}}, "all": []*FuncDef{&FuncDef{Name: "all", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "all", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &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{Left: &Query{Func: "y"}, Op: OpAnd, Right: &Query{Func: "empty"}}}}}}}}}, "any": []*FuncDef{&FuncDef{Name: "any", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, &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{Left: &Query{Func: "y"}, Op: OpOr, Right: &Query{Func: "empty"}}}}}}}, Op: OpPipe, Right: &Query{Func: "not"}}}}, "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"}}}}}}}}}}, "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"}}}}}, "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"}}}}}, "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"}}}}}}}}}}, "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"}}}}}}}}}}, "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{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Op: OpNe, Right: &Query{Func: "null"}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{KeyQuery: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "add"}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}}}}}}, "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: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, IsSlice: true}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "combinations"}, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$y"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "$x"}}}}, Op: OpAdd, Right: &Query{Func: "$y"}}}}}}}}}}}}}}}}}, &FuncDef{Name: "combinations", Args: []string{"n"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$dot"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "range", Args: []*Query{&Query{Func: "n"}}}}}, Op: OpPipe, Right: &Query{Func: "$dot"}}}}}, Op: OpPipe, Right: &Query{Func: "combinations"}}}}}}}}}, "del": []*FuncDef{&FuncDef{Name: "del", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "delpaths", Args: []*Query{&Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Func: "f"}}}}}}}}}}}}}}, "endswith": []*FuncDef{&FuncDef{Name: "endswith", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}, Then: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Func: "length"}}}}}}, IsSlice: true}}}, Op: OpEq, Right: &Query{Func: "$x"}}, Else: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_type_error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "endswith"}}}}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_type_error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "endswith"}}}}}}}}}}}}, "finites": []*FuncDef{&FuncDef{Name: "finites", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Func: "isfinite"}}}}}}}, "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"}}}}}}}}}, "flatten": []*FuncDef{&FuncDef{Name: "_flatten", Args: []string{"$x"}, Body: &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{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Op: OpAnd, Right: &Query{Left: &Query{Func: "$x"}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_flatten", Args: []*Query{&Query{Left: &Query{Func: "$x"}, Op: OpSub, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Func: "."}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "add"}}}, &FuncDef{Name: "flatten", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$x"}, Op: OpLt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "flatten depth must not be negative"}}}}}}}, Else: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_flatten", Args: []*Query{&Query{Func: "$x"}}}}}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}}}, &FuncDef{Name: "flatten", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_flatten", Args: []*Query{&Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}, "from_entries": []*FuncDef{&FuncDef{Name: "from_entries", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{&Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{KeyQuery: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "key"}}}, Op: OpAlt, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Key"}}}, Op: OpAlt, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Name"}}}}}}, Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "has", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "value"}}}}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "value"}}}, Else: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Value"}}}}}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "add"}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}}}}}, "fromdate": []*FuncDef{&FuncDef{Name: "fromdate", Body: &Query{Func: "fromdateiso8601"}}}, "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"}}}}, "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"}}}}}}}}}}}}}}, "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"}}}}}}}}}}}}}}, "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"}}}}}}}}}}, "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"}}}}}}}}}}}}}, "index": []*FuncDef{&FuncDef{Name: "index", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_lindex", Args: []*Query{&Query{Func: "$x"}}}}}}}, "indices": []*FuncDef{&FuncDef{Name: "indices", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_indices", Args: []*Query{&Query{Func: "$x"}}}}}}}, "inputs": []*FuncDef{&FuncDef{Name: "inputs", Body: &Query{Term: &Term{Type: TermTypeTry, Try: &Try{Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "repeat", Args: []*Query{&Query{Func: "input"}}}}}, Catch: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "break"}}}}, Then: &Query{Func: "empty"}, Else: &Query{Func: "error"}}}}}}}}}, "inside": []*FuncDef{&FuncDef{Name: "inside", 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: "contains", Args: []*Query{&Query{Func: "$x"}}}}}}}}}}}}}, "isempty": []*FuncDef{&FuncDef{Name: "isempty", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "first", Args: []*Query{&Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "g"}, Op: OpPipe, Right: &Query{Func: "false"}}}}, Op: OpComma, Right: &Query{Func: "true"}}}}}}}}, "iterables": []*FuncDef{&FuncDef{Name: "iterables", 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: 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"}}}}}}}}}}}}, "join": []*FuncDef{&FuncDef{Name: "join", Args: []string{"$x"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpNe, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}}, Then: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_join", Args: []*Query{&Query{Func: "$x"}}}}}}}}, "last": []*FuncDef{&FuncDef{Name: "last", Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}}, &FuncDef{Name: "last", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Func: "null"}, Update: &Query{Func: "$item"}}}}}}, "leaf_paths": []*FuncDef{&FuncDef{Name: "leaf_paths", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "paths", Args: []*Query{&Query{Func: "scalars"}}}}}}}, "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"}}}}}}, "ltrimstr": []*FuncDef{&FuncDef{Name: "ltrimstr", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Left: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}, Op: OpAnd, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}}}}, Op: OpAnd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "startswith", Args: []*Query{&Query{Func: "$x"}}}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Func: "length"}}, IsSlice: true}}}}}}}}, "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"}}}}}}}, "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"}}}}, "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}}}}}}}, "max": []*FuncDef{&FuncDef{Name: "max", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "max_by", Args: []*Query{&Query{Func: "."}}}}}}}, "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"}}}}}}}}}}}}}}, "min": []*FuncDef{&FuncDef{Name: "min", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "min_by", Args: []*Query{&Query{Func: "."}}}}}}}, "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"}}}}}}}}}}}}}}, "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"}}}}}}}}}}}}}}}}}}}}, "normals": []*FuncDef{&FuncDef{Name: "normals", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Func: "isnormal"}}}}}}}, "not": []*FuncDef{&FuncDef{Name: "not", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Func: "."}, Then: &Query{Func: "false"}, Else: &Query{Func: "true"}}}}}}, "nth": []*FuncDef{&FuncDef{Name: "nth", Args: []string{"$n"}, Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Func: "$n"}}}}}, &FuncDef{Name: "nth", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$n"}, Op: OpLt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "nth doesn't support negative indices"}}}}}}}, Else: &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{Left: &Query{Left: &Query{Func: "."}, Op: OpLt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, Op: OpOr, Right: &Query{Func: "empty"}}, Op: OpPipe, Right: &Query{Left: &Query{Func: "$item"}, Op: OpComma, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}}}}}}}}}}}}}}, "nulls": []*FuncDef{&FuncDef{Name: "nulls", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "."}, Op: OpEq, Right: &Query{Func: "null"}}}}}}}}, "numbers": []*FuncDef{&FuncDef{Name: "numbers", 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: "number"}}}}}}}}}}, "objects": []*FuncDef{&FuncDef{Name: "objects", 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: "object"}}}}}}}}}}, "paths": []*FuncDef{&FuncDef{Name: "paths", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "recurse", Args: []*Query{&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: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Func: "length"}, Op: OpGt, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}}}}, &FuncDef{Name: "paths", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Left: &Query{Func: "paths"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$p"}}, Body: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{&Query{Func: "$p"}}}}}, Op: OpPipe, Right: &Query{Func: "f"}}}}}}}}}}}}}}}}}}}}, "range": []*FuncDef{&FuncDef{Name: "range", Args: []string{"$end"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{&Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, &Query{Func: "$end"}, &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, &FuncDef{Name: "range", Args: []string{"$start", "$end"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{&Query{Func: "$start"}, &Query{Func: "$end"}, &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, &FuncDef{Name: "range", Args: []string{"$start", "$end", "$step"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{&Query{Func: "$start"}, &Query{Func: "$end"}, &Query{Func: "$step"}}}}}}}, "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"}}}, "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"}}}, "rindex": []*FuncDef{&FuncDef{Name: "rindex", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_rindex", Args: []*Query{&Query{Func: "$x"}}}}}}}, "rtrimstr": []*FuncDef{&FuncDef{Name: "rtrimstr", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Left: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}, Op: OpAnd, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}}}}, Op: OpAnd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "endswith", Args: []*Query{&Query{Func: "$x"}}}}}}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{End: &Query{Term: &Term{Type: TermTypeUnary, Unary: &Unary{Op: OpSub, Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Func: "length"}}}}}}, IsSlice: true}}}}}}}}, "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"}}}}}}}}}}}}, "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{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}, Op: OpAdd, Right: &Query{Func: "$flags"}}}}}}, 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"}}}}}}}}}, "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"}}}}}}, "sort": []*FuncDef{&FuncDef{Name: "sort", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sort_by", Args: []*Query{&Query{Func: "."}}}}}}}, "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"}}}}}}}}}}}}}}, "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}}}}}}}, "startswith": []*FuncDef{&FuncDef{Name: "startswith", Args: []string{"$x"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}, Then: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "type"}, Op: OpEq, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{End: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Func: "length"}}, IsSlice: true}}}, Op: OpEq, Right: &Query{Func: "$x"}}, Else: &Query{Left: &Query{Func: "$x"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_type_error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "startswith"}}}}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_type_error", Args: []*Query{&Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "startswith"}}}}}}}}}}}}, "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"}}}}}}}}}}, "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: "$in"}}, 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: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$x"}}, Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "matches"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, &Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$r"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "captures"}}, &Suffix{Iter: true}}}}, Op: OpPipe, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{&Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Op: OpNe, Right: &Query{Func: "null"}}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{KeyQuery: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Left: &Query{Left: &Query{Func: "add"}, Op: OpAlt, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}}, Op: OpPipe, Right: &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: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "string"}}}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$in"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "offset"}}}}}, End: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$r"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "offset"}}}}}, IsSlice: true}}}}}}, Op: OpAdd, Right: &Query{Func: "str"}}}}}}}, &ObjectKeyVal{Key: "offset", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeQuery, Query: &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"}}}}}}}}}}}, &ObjectKeyVal{Key: "matches", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Name: "matches"}}, &Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, IsSlice: true}}}}}}}}}}}}, Op: OpPipe, Right: &Query{Func: "_sub"}}}}}}}}}}}}}}, Else: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}, Op: OpAdd, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$in"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "offset"}}}, IsSlice: true}}}}}}}}}}}, 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: "offset", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, &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"}}}}}}}}}, "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"}}}}}}}, "to_entries": []*FuncDef{&FuncDef{Name: "to_entries", Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "keys"}, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$k"}}, Body: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{&ObjectKeyVal{Key: "key", Val: &ObjectVal{Queries: []*Query{&Query{Func: "$k"}}}}, &ObjectKeyVal{Key: "value", Val: &ObjectVal{Queries: []*Query{&Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Func: "$k"}}}}}}}}}}}}}}}}}}}}}, "todate": []*FuncDef{&FuncDef{Name: "todate", Body: &Query{Func: "todateiso8601"}}}, "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"}}}}}}}}}, "tostream": []*FuncDef{&FuncDef{Name: "tostream", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{FuncDefs: []*FuncDef{&FuncDef{Name: "r", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Optional: true}}}}, Op: OpPipe, Right: &Query{Func: "r"}}}}, Op: OpComma, Right: &Query{Func: "."}}}}, Func: "r"}}}, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$p"}}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{&Query{Func: "$p"}}}}}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{&Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Iter: true}, &Suffix{Optional: true}}}}}}}, Pattern: &Pattern{Name: "$q"}, Start: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "$p"}, Op: OpComma, Right: &Query{Func: "."}}}}}, Update: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Func: "$p"}, Op: OpAdd, Right: &Query{Func: "$q"}}}}}}}}}}}}}}}}, "truncate_stream": []*FuncDef{&FuncDef{Name: "truncate_stream", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$n"}}, Body: &Query{Left: &Query{Func: "null"}, Op: OpPipe, Right: &Query{Left: &Query{Func: "f"}, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{&Suffix{Bind: &Bind{Patterns: []*Pattern{&Pattern{Name: "$input"}}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, Op: OpPipe, Right: &Query{Func: "length"}}}}, Op: OpGt, Right: &Query{Func: "$n"}}, Then: &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: "0"}}}}}, &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$input"}, SuffixList: []*Suffix{&Suffix{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}, &Suffix{Index: &Index{Start: &Query{Func: "$n"}, IsSlice: true}}}}}}}}}, Else: &Query{Func: "empty"}}}}}}}}}}}}}}}}}}, "unique": []*FuncDef{&FuncDef{Name: "unique", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "unique_by", Args: []*Query{&Query{Func: "."}}}}}}}, "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"}}}}}}}}}}}}}}, "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"}}}, "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"}}}}}}}}, "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"}}}, "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"}}}, "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"}}}}}, } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/builtin.jq000066400000000000000000000155731416642156300227510ustar00rootroot00000000000000def not: if . then false else true end; def in(xs): . as $x | xs | has($x); def map(f): [.[] | f]; def to_entries: [keys[] as $k | {key: $k, value: .[$k]}]; def from_entries: map({ (.key // .Key // .name // .Name): (if has("value") then .value else .Value end) }) | add // {}; def with_entries(f): to_entries | map(f) | from_entries; def select(f): if f then . else empty end; def recurse: recurse(.[]?); def recurse(f): def r: ., (f | r); r; def recurse(f; cond): def r: ., (f | select(cond) | r); r; def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; def until(cond; next): def _until: if cond then . else next | _until end; _until; def repeat(f): def _repeat: f, _repeat; _repeat; def range($end): _range(0; $end; 1); def range($start; $end): _range($start; $end; 1); def range($start; $end; $step): _range($start; $end; $step); def _flatten($x): map(if type == "array" and $x != 0 then _flatten($x - 1) else [.] end) | add; def flatten($x): if $x < 0 then error("flatten depth must not be negative") else _flatten($x) // [] end; def flatten: _flatten(-1) // []; def min: min_by(.); def min_by(f): _min_by(map([f])); def max: max_by(.); def max_by(f): _max_by(map([f])); def sort: sort_by(.); def sort_by(f): _sort_by(map([f])); def group_by(f): _group_by(map([f])); def unique: unique_by(.); def unique_by(f): _unique_by(map([f])); def arrays: select(type == "array"); def objects: select(type == "object"); def iterables: select(type | . == "array" or . == "object"); def booleans: select(type == "boolean"); def numbers: select(type == "number"); def finites: select(isfinite); def normals: select(isnormal); def strings: select(type == "string"); def nulls: select(. == null); def values: select(. != null); def scalars: select(type | . != "array" and . != "object"); def leaf_paths: paths(scalars); def indices($x): _indices($x); def index($x): _lindex($x); def rindex($x): _rindex($x); def inside(xs): . as $x | xs | contains($x); def startswith($x): if type == "string" then if $x|type == "string" then .[:$x | length] == $x else $x | _type_error("startswith") end else _type_error("startswith") end; def endswith($x): if type == "string" then if $x|type == "string" then .[- ($x | length):] == $x else $x | _type_error("endswith") end else _type_error("endswith") end; def ltrimstr($x): if type == "string" and ($x|type == "string") and startswith($x) then .[$x | length:] end; def rtrimstr($x): if type == "string" and ($x|type == "string") and endswith($x) then .[:- ($x | length)] end; def combinations: if length == 0 then [] else .[0][] as $x | .[1:] | combinations as $y | [$x] + $y end; def combinations(n): . as $dot | [range(n) | $dot] | combinations; def join($x): if type != "array" then [.[]] end | _join($x); def ascii_downcase: explode | map(if 65 <= . and . <= 90 then . + 32 end) | implode; def ascii_upcase: explode | map(if 97 <= . and . <= 122 then . - 32 end) | implode; def walk(f): def _walk: if type | . == "array" or . == "object" then map_values(_walk) end | f; _walk; def first: .[0]; def first(g): label $out | g | ., break $out; def last: .[-1]; def last(g): reduce g as $item (null; $item); def isempty(g): first((g|false), true); def all: all(.[]; .); def all(y): all(.[]; y); def all(g; y): isempty(g|y and empty); def any: any(.[]; .); def any(y): any(.[]; y); def any(g; y): isempty(g|y or empty) | not; def limit($n; g): if $n > 0 then label $out | foreach g as $item ($n; .-1; $item, if . <= 0 then break $out else empty end) elif $n == 0 then empty else g end; def nth($n): .[$n]; def nth($n; g): if $n < 0 then error("nth doesn't support negative indices") else label $out | foreach g as $item ($n; .-1; . < 0 or empty | $item, break $out) end; def truncate_stream(f): . as $n | null | f | . as $input | if (.[0] | length) > $n then setpath([0]; $input[0][$n:]) else empty end; def fromstream(f): { x: null, e: false } as $init | foreach f as $i ( $init; if .e then $init else . end | if $i | length == 2 then setpath(["e"]; $i[0] | length==0) | setpath(["x"] + $i[0]; $i[1]) else setpath(["e"]; $i[0] | length==1) end; if .e then .x else empty end); def tostream: path(def r: (.[]? | r), .; r) as $p | getpath($p) | reduce path(.[]?) as $q ([$p, .]; [$p + $q]); def _assign(ps; $v): reduce path(ps) as $p (.; setpath($p; $v)); def _modify(ps; f): reduce path(ps) as $p ([., []]; label $out | (([0] + $p) as $q | setpath($q; getpath($q) | f) | ., break $out), setpath([1]; .[1] + [$p])) | . as $x | $x[0] | delpaths($x[1]); def map_values(f): .[] |= f; def del(f): delpaths([path(f)]); def paths: path(recurse(if type | . == "array" or . == "object" then .[] else empty end)) | select(length > 0); def paths(f): . as $x | paths | select(. as $p | $x | getpath($p) | f); def fromdateiso8601: strptime("%Y-%m-%dT%H:%M:%S%z") | mktime; def todateiso8601: strftime("%Y-%m-%dT%H:%M:%SZ"); def fromdate: fromdateiso8601; def todate: todateiso8601; def match($re): match($re; null); def match($re; $flags): _match($re; $flags; false) | .[]; def test($re): test($re; null); def test($re; $flags): _match($re; $flags; true); def capture($re): capture($re; null); def capture($re; $flags): match($re; $flags) | [.captures[] | select(.name != null) | { (.name): .string }] | add // {}; def scan($re): scan($re; null); def scan($re; $flags): match($re; "g" + $flags) | if .captures|length > 0 then [.captures[].string] else .string end; def splits($re): splits($re; null); def splits($re; $flags): split($re; $flags) | .[]; def sub($re; str): sub($re; str; null); def sub($re; str; $flags): . as $in | def _sub: if .matches|length > 0 then . as $x | .matches[0] as $r | [$r.captures[] | select(.name != null) | { (.name): .string }] | add // {} | { string: ($x.string + $in[$x.offset:$r.offset] + str), offset: ($r.offset + $r.length), matches: $x.matches[1:] } | _sub else .string + $in[.offset:] end; { string: "", offset: 0, matches: [match($re; $flags)] } | _sub; def gsub($re; str): sub($re; str; "g"); def gsub($re; str; $flags): sub($re; str; $flags + "g"); def inputs: try repeat(input) catch if . == "break" then empty else error end; def INDEX(stream; idx_expr): reduce stream as $row ({}; .[$row|idx_expr|tostring] = $row); def INDEX(idx_expr): INDEX(.[]; idx_expr); def JOIN($idx; idx_expr): [.[] | [., $idx[idx_expr]]]; def JOIN($idx; stream; idx_expr): stream | [., $idx[idx_expr]]; def JOIN($idx; stream; idx_expr; join_expr): stream | [., $idx[idx_expr]] | join_expr; def IN(s): any(s == .; .); def IN(src; s): any(src == s; .); golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/000077500000000000000000000000001416642156300215035ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/cli.go000066400000000000000000000304621416642156300226060ustar00rootroot00000000000000package cli import ( "errors" "fmt" "io" "os" "path/filepath" "runtime" "strings" "github.com/mattn/go-isatty" "github.com/wader/gojq" ) const name = "gojq" const version = "0.12.6" var revision = "HEAD" const ( exitCodeOK = iota exitCodeFalsyErr exitCodeFlagParseErr exitCodeCompileErr exitCodeNoValueErr exitCodeDefaultErr ) type cli struct { inStream io.Reader outStream io.Writer errStream io.Writer outputCompact bool outputRaw bool outputJoin bool outputNul bool outputYAML bool outputIndent *int outputTab bool inputRaw bool inputSlurp bool inputStream bool inputYAML bool argnames []string argvalues []interface{} outputYAMLSeparator bool exitCodeError error } type flagopts struct { OutputCompact bool `short:"c" long:"compact-output" description:"compact output"` OutputRaw bool `short:"r" long:"raw-output" description:"output raw strings"` OutputJoin bool `short:"j" long:"join-output" description:"stop printing a newline after each output"` OutputNul bool `short:"0" long:"nul-output" description:"print NUL after each output"` OutputColor bool `short:"C" long:"color-output" description:"colorize output even if piped"` OutputMono bool `short:"M" long:"monochrome-output" description:"stop colorizing output"` OutputYAML bool `long:"yaml-output" description:"output by YAML"` OutputIndent *int `long:"indent" description:"number of spaces for indentation"` OutputTab bool `long:"tab" description:"use tabs for indentation"` InputNull bool `short:"n" long:"null-input" description:"use null as input value"` InputRaw bool `short:"R" long:"raw-input" description:"read input as raw strings"` InputSlurp bool `short:"s" long:"slurp" description:"read all inputs into an array"` InputStream bool `long:"stream" description:"parse input in stream fashion"` InputYAML bool `long:"yaml-input" description:"read input as YAML"` FromFile string `short:"f" long:"from-file" description:"load query from file"` ModulePaths []string `short:"L" description:"directory to search modules from"` Arg map[string]string `long:"arg" description:"set variable to string value"` ArgJSON map[string]string `long:"argjson" description:"set variable to JSON value"` SlurpFile map[string]string `long:"slurpfile" description:"set variable to the JSON contents of the file"` RawFile map[string]string `long:"rawfile" description:"set variable to the contents of the file"` Args []interface{} `long:"args" positional:"" description:"consume remaining arguments as positional string values"` JSONArgs []interface{} `long:"jsonargs" positional:"" description:"consume remaining arguments as positional JSON values"` ExitStatus bool `short:"e" long:"exit-status" description:"exit 1 when the last value is false or null"` Version bool `short:"v" long:"version" description:"print version"` Help bool `short:"h" long:"help" description:"print this help"` } var addDefaultModulePaths = true func (cli *cli) run(args []string) int { if err := cli.runInternal(args); err != nil { cli.printError(err) if err, ok := err.(interface{ ExitCode() int }); ok { return err.ExitCode() } return exitCodeDefaultErr } return exitCodeOK } func (cli *cli) runInternal(args []string) (err error) { var opts flagopts args, err = parseFlags(args, &opts) if err != nil { return &flagParseError{err} } if opts.Help { fmt.Fprintf(cli.outStream, `%[1]s - Go implementation of jq Version: %s (rev: %s/%s) Synopsis: %% echo '{"foo": 128}' | %[1]s '.foo' Usage: %[1]s [OPTIONS] `, name, version, revision, runtime.Version()) fmt.Fprintln(cli.outStream, formatFlags(&opts)) return nil } if opts.Version { fmt.Fprintf(cli.outStream, "%s %s (rev: %s/%s)\n", name, version, revision, runtime.Version()) return nil } cli.outputCompact, cli.outputRaw, cli.outputJoin, cli.outputNul, cli.outputYAML, cli.outputIndent, cli.outputTab = opts.OutputCompact, opts.OutputRaw, opts.OutputJoin, opts.OutputNul, opts.OutputYAML, opts.OutputIndent, opts.OutputTab defer func(x bool) { noColor = x }(noColor) if opts.OutputColor || opts.OutputMono { noColor = opts.OutputMono } else if os.Getenv("NO_COLOR") != "" || os.Getenv("TERM") == "dumb" { noColor = true } else { f, ok := cli.outStream.(interface{ Fd() uintptr }) noColor = !(ok && (isatty.IsTerminal(f.Fd()) || isatty.IsCygwinTerminal(f.Fd()))) } if !noColor { if colors := os.Getenv("GOJQ_COLORS"); colors != "" { if err := setColors(colors); err != nil { return err } } } if i := cli.outputIndent; i != nil { if *i > 9 { return fmt.Errorf("too many indentation count: %d", *i) } else if *i < 0 { return fmt.Errorf("negative indentation count: %d", *i) } } if opts.OutputYAML && opts.OutputTab { return errors.New("cannot use tabs for YAML output") } cli.inputRaw, cli.inputSlurp, cli.inputStream, cli.inputYAML = opts.InputRaw, opts.InputSlurp, opts.InputStream, opts.InputYAML for k, v := range opts.Arg { cli.argnames = append(cli.argnames, "$"+k) cli.argvalues = append(cli.argvalues, v) } for k, v := range opts.ArgJSON { val, _ := newJSONInputIter(strings.NewReader(v), "$"+k).Next() if err, ok := val.(error); ok { return err } cli.argnames = append(cli.argnames, "$"+k) cli.argvalues = append(cli.argvalues, val) } for k, v := range opts.SlurpFile { val, err := slurpFile(v) if err != nil { return err } cli.argnames = append(cli.argnames, "$"+k) cli.argvalues = append(cli.argvalues, val) } for k, v := range opts.RawFile { val, err := os.ReadFile(v) if err != nil { return err } cli.argnames = append(cli.argnames, "$"+k) cli.argvalues = append(cli.argvalues, string(val)) } named := make(map[string]interface{}, len(cli.argnames)) for i, name := range cli.argnames { named[name[1:]] = cli.argvalues[i] } positional := opts.Args for i, v := range opts.JSONArgs { if v != nil { val, _ := newJSONInputIter(strings.NewReader(v.(string)), "--jsonargs").Next() if err, ok := val.(error); ok { return err } if i < len(positional) { positional[i] = val } else { positional = append(positional, val) } } } cli.argnames = append(cli.argnames, "$ARGS") cli.argvalues = append(cli.argvalues, map[string]interface{}{ "named": named, "positional": positional, }) var arg, fname string if opts.FromFile != "" { src, err := os.ReadFile(opts.FromFile) if err != nil { return err } arg, fname = string(src), opts.FromFile } else if len(args) == 0 { arg = "." } else { arg, fname = strings.TrimSpace(args[0]), "" args = args[1:] } if opts.ExitStatus { cli.exitCodeError = &exitCodeError{exitCodeNoValueErr} defer func() { if _, ok := err.(interface{ ExitCode() int }); !ok { err = cli.exitCodeError } }() } query, err := gojq.Parse(arg) if err != nil { return &queryParseError{"query", fname, arg, err} } modulePaths := opts.ModulePaths if len(modulePaths) == 0 && addDefaultModulePaths { modulePaths = listDefaultModulePaths() } iter := cli.createInputIter(args) defer iter.Close() code, err := gojq.Compile(query, gojq.WithModuleLoader(gojq.NewModuleLoader(modulePaths)), gojq.WithEnvironLoader(os.Environ), gojq.WithVariables(cli.argnames), gojq.WithFunction("debug", 0, 0, cli.funcDebug), gojq.WithFunction("stderr", 0, 0, cli.funcStderr), gojq.WithFunction("input_filename", 0, 0, func(iter inputIter) func(interface{}, []interface{}) interface{} { return func(interface{}, []interface{}) interface{} { if fname := iter.Name(); fname != "" && (len(args) > 0 || !opts.InputNull) { return fname } return nil } }(iter), ), gojq.WithInputIter(iter), ) if err != nil { if err, ok := err.(interface { QueryParseError() (string, string, string, error) }); ok { typ, name, query, err := err.QueryParseError() if _, err := os.Stat(name); os.IsNotExist(err) { name = fname + ":" + name } return &queryParseError{typ, name, query, err} } if err, ok := err.(interface { JSONParseError() (string, string, error) }); ok { fname, contents, err := err.JSONParseError() return &compileError{&jsonParseError{fname, contents, 0, err}} } return &compileError{err} } if opts.InputNull { iter = newNullInputIter() } return cli.process(iter, code) } func listDefaultModulePaths() []string { modulePaths := []string{"", "../lib/gojq", "lib"} if executable, err := os.Executable(); err == nil { if executable, err := filepath.EvalSymlinks(executable); err == nil { origin := filepath.Dir(executable) modulePaths[1] = filepath.Join(origin, modulePaths[1]) modulePaths[2] = filepath.Join(origin, modulePaths[2]) } } if homeDir, err := os.UserHomeDir(); err == nil { modulePaths[0] = filepath.Join(homeDir, ".jq") } else { modulePaths = modulePaths[1:] } return modulePaths } func slurpFile(name string) (interface{}, error) { iter := newSlurpInputIter( newFilesInputIter(newJSONInputIter, []string{name}, nil), ) defer iter.Close() val, _ := iter.Next() if err, ok := val.(error); ok { return nil, err } return val, nil } func (cli *cli) createInputIter(args []string) (iter inputIter) { var newIter func(io.Reader, string) inputIter switch { case cli.inputRaw: if cli.inputSlurp { newIter = newReadAllIter } else { newIter = newRawInputIter } case cli.inputStream: newIter = newStreamInputIter case cli.inputYAML: newIter = newYAMLInputIter default: newIter = newJSONInputIter } if cli.inputSlurp { defer func() { if cli.inputRaw { iter = newSlurpRawInputIter(iter) } else { iter = newSlurpInputIter(iter) } }() } if len(args) == 0 { return newIter(cli.inStream, "") } return newFilesInputIter(newIter, args, cli.inStream) } func (cli *cli) process(iter inputIter, code *gojq.Code) error { var err error for { v, ok := iter.Next() if !ok { return err } if er, ok := v.(error); ok { cli.printError(er) err = &emptyError{er} continue } if er := cli.printValues(code.Run(v, cli.argvalues...)); er != nil { cli.printError(er) err = &emptyError{er} } } } func (cli *cli) printValues(iter gojq.Iter) error { m := cli.createMarshaler() for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { return err } if cli.outputYAMLSeparator { cli.outStream.Write([]byte("---\n")) } else { cli.outputYAMLSeparator = cli.outputYAML } if err := m.marshal(v, cli.outStream); err != nil { return err } if cli.exitCodeError != nil { if v == nil || v == false { cli.exitCodeError = &exitCodeError{exitCodeFalsyErr} } else { cli.exitCodeError = &exitCodeError{exitCodeOK} } } if !cli.outputJoin && !cli.outputYAML { if cli.outputNul { cli.outStream.Write([]byte{'\x00'}) } else { cli.outStream.Write([]byte{'\n'}) } } } return nil } func (cli *cli) createMarshaler() marshaler { if cli.outputYAML { return yamlFormatter(cli.outputIndent) } indent := 2 if cli.outputCompact { indent = 0 } else if cli.outputTab { indent = 1 } else if i := cli.outputIndent; i != nil { indent = *i } f := newEncoder(cli.outputTab, indent) if cli.outputRaw || cli.outputJoin || cli.outputNul { return &rawMarshaler{f} } return f } func (cli *cli) funcDebug(v interface{}, _ []interface{}) interface{} { newEncoder(false, 0).marshal([]interface{}{"DEBUG:", v}, cli.errStream) cli.errStream.Write([]byte{'\n'}) return v } func (cli *cli) funcStderr(v interface{}, _ []interface{}) interface{} { newEncoder(false, 0).marshal(v, cli.errStream) return v } func (cli *cli) printError(err error) { if er, ok := err.(interface{ IsEmptyError() bool }); !ok || !er.IsEmptyError() { if er, ok := err.(interface{ IsHaltError() bool }); !ok || !er.IsHaltError() { fmt.Fprintf(cli.errStream, "%s: %s\n", name, err) } else if er, ok := err.(gojq.ValueError); ok { v := er.Value() if str, ok := v.(string); ok { cli.errStream.Write([]byte(str)) } else { bs, _ := gojq.Marshal(v) cli.errStream.Write(bs) cli.errStream.Write([]byte{'\n'}) } } } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/cli_test.go000066400000000000000000000063451416642156300236500ustar00rootroot00000000000000package cli import ( "os" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "gopkg.in/yaml.v3" ) func init() { addDefaultModulePaths = false os.Setenv("NO_COLOR", "") os.Setenv("GOJQ_COLORS", "") } func setLocation(loc *time.Location) func() { orig := time.Local time.Local = loc return func() { time.Local = orig } } func TestCliRun(t *testing.T) { f, err := os.Open("test.yaml") if err != nil { t.Fatal(err) } defer f.Close() defer setLocation(time.FixedZone("UTC-7", -7*60*60))() errorReplacer := strings.NewReplacer( name+": ", "", "testdata\\", "testdata/", "flag `/", "flag `--", ) var testCases []struct { Name string Args []string Input string Env []string Expected string Error string ExitCode int `yaml:"exit_code"` } if err = yaml.NewDecoder(f).Decode(&testCases); err != nil { t.Fatal(err) } for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { defer func() { if err := recover(); err != nil { t.Fatal(err) } }() var outStream, errStream strings.Builder cli := cli{ inStream: strings.NewReader(tc.Input), outStream: &outStream, errStream: &errStream, } for _, env := range tc.Env { xs := strings.SplitN(env, "=", 2) k, v := xs[0], xs[1] defer func(v string) { os.Setenv(k, v) }(os.Getenv(k)) if k == "GOJQ_COLORS" { defer func(colors [][]byte) { nullColor, falseColor, trueColor, numberColor, stringColor, objectKeyColor, arrayColor, objectColor = colors[0], colors[1], colors[2], colors[3], colors[4], colors[5], colors[6], colors[7] }([][]byte{ nullColor, falseColor, trueColor, numberColor, stringColor, objectKeyColor, arrayColor, objectColor, }) } os.Setenv(k, v) } code := cli.run(tc.Args) if tc.Error == "" { if code != tc.ExitCode { t.Errorf("exit code: got: %v, expected: %v", code, tc.ExitCode) } if diff := cmp.Diff(tc.Expected, outStream.String()); diff != "" { t.Error("standard output:\n" + diff) } if diff := cmp.Diff("", errStream.String()); diff != "" { t.Error("standard error output:\n" + diff) } } else { errStr := errStream.String() if strings.Contains(errStr, "DEBUG:") { if code != exitCodeOK { t.Errorf("exit code: got: %v, expected: %v", code, exitCodeOK) } } else if tc.ExitCode != 0 { if code != tc.ExitCode { t.Errorf("exit code: got: %v, expected: %v", code, tc.ExitCode) } } else { if code != exitCodeDefaultErr { t.Errorf("exit code: got: %v, expected: %v", code, exitCodeDefaultErr) } } if diff := cmp.Diff(tc.Expected, outStream.String()); diff != "" { t.Error("standard output:\n" + diff) } if got, expected := errorReplacer.Replace(errStr), strings.TrimSpace(tc.Error); !strings.Contains(got, expected) { t.Error("standard error output:\n" + cmp.Diff(expected, got)) } if !strings.HasSuffix(errStr, "\n") && !strings.Contains(tc.Name, "stderr") && !strings.Contains(tc.Name, "halt_error") { t.Error(`standard error output should end with "\n"`) } if strings.HasSuffix(errStr, "\n\n") { t.Error(`standard error output should not end with "\n\n"`) } } }) } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/color.go000066400000000000000000000027531416642156300231570ustar00rootroot00000000000000package cli import ( "bytes" "fmt" "strings" ) var noColor bool func newColor(c string) []byte { return []byte("\x1b[" + c + "m") } func setColor(buf *bytes.Buffer, color []byte) { if !noColor { buf.Write(color) } } var ( resetColor = newColor("0") // Reset nullColor = newColor("90") // Bright black falseColor = newColor("33") // Yellow trueColor = newColor("33") // Yellow numberColor = newColor("36") // Cyan stringColor = newColor("32") // Green objectKeyColor = newColor("34;1") // Bold Blue arrayColor = []byte(nil) // No color objectColor = []byte(nil) // No color ) func validColor(x string) bool { var num bool for _, c := range x { if '0' <= c && c <= '9' { num = true } else if c == ';' { if !num { return false } num = false } else { return false } } return num || x == "" } func setColors(colors string) error { var i int var color string for _, target := range []*[]byte{ &nullColor, &falseColor, &trueColor, &numberColor, &stringColor, &objectKeyColor, &arrayColor, &objectColor, } { if i < len(colors) { if j := strings.IndexByte(colors[i:], ':'); j >= 0 { color = colors[i : i+j] i += j + 1 } else { color = colors[i:] i = len(colors) } if !validColor(color) { return fmt.Errorf("invalid color: %q", color) } if color == "" { *target = nil } else { *target = newColor(color) } } else { *target = nil } } return nil } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/encoder.go000066400000000000000000000115321416642156300234530ustar00rootroot00000000000000package cli import ( "bytes" "fmt" "io" "math" "math/big" "sort" "strconv" "unicode/utf8" "github.com/wader/gojq" ) type encoder struct { out io.Writer w *bytes.Buffer tab bool indent int depth int buf [64]byte } func newEncoder(tab bool, indent int) *encoder { // reuse the buffer in multiple calls of marshal return &encoder{w: new(bytes.Buffer), tab: tab, indent: indent} } func (e *encoder) marshal(v interface{}, w io.Writer) error { e.out = w e.encode(v) _, err := w.Write(e.w.Bytes()) e.w.Reset() return err } func (e *encoder) encode(v interface{}) { switch v := v.(type) { case nil: e.write([]byte("null"), nullColor) case bool: if v { e.write([]byte("true"), trueColor) } else { e.write([]byte("false"), falseColor) } case int: e.write(strconv.AppendInt(e.buf[:0], int64(v), 10), numberColor) case float64: e.encodeFloat64(v) case *big.Int: e.write(v.Append(e.buf[:0], 10), numberColor) case string: e.encodeString(v, stringColor) case []interface{}: e.encodeArray(v) case map[string]interface{}: e.encodeMap(v) case gojq.JQValue: e.encode(v.JQValueToGoJQ()) default: panic(fmt.Sprintf("invalid value: %v", v)) } if e.w.Len() > 8*1024 { e.out.Write(e.w.Bytes()) e.w.Reset() } } // ref: floatEncoder in encoding/json func (e *encoder) encodeFloat64(f float64) { if math.IsNaN(f) { e.write([]byte("null"), nullColor) return } if f >= math.MaxFloat64 { f = math.MaxFloat64 } else if f <= -math.MaxFloat64 { f = -math.MaxFloat64 } fmt := byte('f') if x := math.Abs(f); x != 0 && x < 1e-6 || x >= 1e21 { fmt = 'e' } buf := strconv.AppendFloat(e.buf[:0], f, fmt, -1, 64) if fmt == 'e' { // clean up e-09 to e-9 if n := len(buf); n >= 4 && buf[n-4] == 'e' && buf[n-3] == '-' && buf[n-2] == '0' { buf[n-2] = buf[n-1] buf = buf[:n-1] } } e.write(buf, numberColor) } // ref: encodeState#string in encoding/json func (e *encoder) encodeString(s string, color []byte) { if color != nil { setColor(e.w, color) } e.w.WriteByte('"') start := 0 for i := 0; i < len(s); { if b := s[i]; b < utf8.RuneSelf { if ']' <= b && b <= '~' || '#' <= b && b <= '[' || b == ' ' || b == '!' { i++ continue } if start < i { e.w.WriteString(s[start:i]) } e.w.WriteByte('\\') switch b { case '\\', '"': e.w.WriteByte(b) case '\b': e.w.WriteByte('b') case '\f': e.w.WriteByte('f') case '\n': e.w.WriteByte('n') case '\r': e.w.WriteByte('r') case '\t': e.w.WriteByte('t') default: const hex = "0123456789abcdef" e.w.WriteString("u00") e.w.WriteByte(hex[b>>4]) e.w.WriteByte(hex[b&0xF]) } i++ start = i continue } c, size := utf8.DecodeRuneInString(s[i:]) if c == utf8.RuneError && size == 1 { if start < i { e.w.WriteString(s[start:i]) } e.w.WriteString(`\ufffd`) i += size start = i continue } i += size } if start < len(s) { e.w.WriteString(s[start:]) } e.w.WriteByte('"') if color != nil { setColor(e.w, resetColor) } } func (e *encoder) encodeArray(vs []interface{}) { e.writeByte('[', arrayColor) e.depth += e.indent for i, v := range vs { if i > 0 { e.writeByte(',', arrayColor) } if e.indent != 0 { e.writeIndent() } e.encode(v) } e.depth -= e.indent if len(vs) > 0 && e.indent != 0 { e.writeIndent() } e.writeByte(']', arrayColor) } func (e *encoder) encodeMap(vs map[string]interface{}) { e.writeByte('{', objectColor) e.depth += e.indent type keyVal struct { key string val interface{} } kvs := make([]keyVal, len(vs)) var i int for k, v := range vs { kvs[i] = keyVal{k, v} i++ } sort.Slice(kvs, func(i, j int) bool { return kvs[i].key < kvs[j].key }) for i, kv := range kvs { if i > 0 { e.writeByte(',', objectColor) } if e.indent != 0 { e.writeIndent() } e.encodeString(kv.key, objectKeyColor) e.writeByte(':', objectColor) if e.indent != 0 { e.w.WriteByte(' ') } e.encode(kv.val) } e.depth -= e.indent if len(vs) > 0 && e.indent != 0 { e.writeIndent() } e.writeByte('}', objectColor) } func (e *encoder) writeIndent() { e.w.WriteByte('\n') if n := e.depth; n > 0 { if e.tab { const tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" for n > len(tabs) { e.w.Write([]byte(tabs)) n -= len(tabs) } e.w.Write([]byte(tabs)[:n]) } else { const spaces = " " for n > len(spaces) { e.w.Write([]byte(spaces)) n -= len(spaces) } e.w.Write([]byte(spaces)[:n]) } } } func (e *encoder) writeByte(b byte, color []byte) { if color == nil { e.w.WriteByte(b) } else { setColor(e.w, color) e.w.WriteByte(b) setColor(e.w, resetColor) } } func (e *encoder) write(bs []byte, color []byte) { if color == nil { e.w.Write(bs) } else { setColor(e.w, color) e.w.Write(bs) setColor(e.w, resetColor) } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/error.go000066400000000000000000000126041416642156300231660ustar00rootroot00000000000000package cli import ( "encoding/json" "fmt" "strconv" "strings" "unicode/utf8" "github.com/mattn/go-runewidth" ) type emptyError struct { err error } func (*emptyError) Error() string { return "" } func (*emptyError) IsEmptyError() bool { return true } func (err *emptyError) ExitCode() int { if err, ok := err.err.(interface{ ExitCode() int }); ok { return err.ExitCode() } return exitCodeDefaultErr } type exitCodeError struct { code int } func (err *exitCodeError) Error() string { return "exit code: " + strconv.Itoa(err.code) } func (err *exitCodeError) IsEmptyError() bool { return true } func (err *exitCodeError) ExitCode() int { return err.code } type flagParseError struct { err error } func (err *flagParseError) Error() string { return err.err.Error() } func (err *flagParseError) ExitCode() int { return exitCodeFlagParseErr } type compileError struct { err error } func (err *compileError) Error() string { return "compile error: " + err.err.Error() } func (err *compileError) ExitCode() int { return exitCodeCompileErr } type queryParseError struct { typ, fname, contents string err error } func (err *queryParseError) Error() string { if er, ok := err.err.(interface{ Token() (string, int) }); ok { _, offset := er.Token() linestr, line, col := getLineByOffset(err.contents, offset) var fname, prefix string if !strings.ContainsAny(err.contents, "\n\r") && strings.HasPrefix(err.fname, "") { fname = err.contents } else { fname = err.fname + ":" + strconv.Itoa(line) prefix = strconv.Itoa(line) + " | " } return "invalid " + err.typ + ": " + fname + "\n" + " " + prefix + linestr + "\n" + " " + strings.Repeat(" ", len(prefix)+col) + "^ " + err.err.Error() } return "invalid " + err.typ + ": " + err.fname + ": " + err.err.Error() } func (err *queryParseError) ExitCode() int { return exitCodeCompileErr } type jsonParseError struct { fname, contents string line int err error } func (err *jsonParseError) Error() string { var prefix, linestr string var line, col int fname, errmsg := err.fname, err.err.Error() if errmsg == "unexpected EOF" { linestr = strings.TrimRight(err.contents, "\n\r") if i := strings.LastIndexAny(linestr, "\n\r"); i >= 0 { linestr = linestr[i:] } col = runewidth.StringWidth(linestr) } else if er, ok := err.err.(*json.SyntaxError); ok { linestr, line, col = getLineByOffset( trimLastInvalidRune(err.contents), int(er.Offset), ) if i := strings.IndexAny(err.contents, "\n\r"); i >= 0 && i < len(err.contents)-1 { line += err.line fname += ":" + strconv.Itoa(line) prefix = strconv.Itoa(line) + " | " } } return "invalid json: " + fname + "\n" + " " + prefix + linestr + "\n" + " " + strings.Repeat(" ", len(prefix)+col) + "^ " + errmsg } type yamlParseError struct { fname, contents string err error } func (err *yamlParseError) Error() string { var line int msg := err.err.Error() fmt.Sscanf(msg, "yaml: line %d:", &line) if line > 0 { msg = msg[7+strings.IndexRune(msg[5:], ':'):] // trim "yaml: line N:" } else { if !strings.HasPrefix(msg, "yaml: unmarshal errors:\n") { return "invalid yaml: " + err.fname } msg = strings.Split(msg, "\n")[1] fmt.Sscanf(msg, " line %d: ", &line) if line > 0 { msg = msg[2+strings.IndexRune(msg, ':'):] // trim "line N:" } else { return "invalid yaml: " + err.fname } } var ss strings.Builder var i, j int var cr bool for _, r := range trimLastInvalidRune(err.contents) { i += len(string(r)) if r == '\n' || r == '\r' { if !cr || r != '\n' { j++ } cr = r == '\r' if j == line { break } ss.Reset() } else { cr = false ss.WriteRune(r) } } linestr := strconv.Itoa(line) return "invalid yaml: " + err.fname + ":" + linestr + "\n" + " " + linestr + " | " + ss.String() + "\n" + " " + strings.Repeat(" ", len(linestr)) + " ^ " + msg } func getLineByOffset(str string, offset int) (string, int, int) { var pos, col int var cr bool line, total := 1, len(str) for offset > 128 && offset <= total { diff := offset / 2 for i := 0; i < utf8.UTFMax; i++ { if r, _ := utf8.DecodeLastRuneInString(str[:diff+i]); r != utf8.RuneError { diff += i break } } for _, r := range str[:diff] { if r == '\n' || r == '\r' { if !cr || r != '\n' { line++ } cr = r == '\r' } } str = str[diff:] offset -= diff } var ss strings.Builder for _, r := range str { if k := utf8.RuneLen(r); k > 0 { pos += k } else { pos += len(string(r)) } if pos < offset { col += runewidth.RuneWidth(r) } if r == '\n' || r == '\r' { if pos >= offset { break } else if pos < total { col = 0 if !cr || r != '\n' { line++ } cr = r == '\r' ss.Reset() } } else { cr = false ss.WriteRune(r) if ss.Len() > 64 { if pos > offset { break } s, i := ss.String(), 48 ss.Reset() for j := 0; j < utf8.UTFMax; j++ { if r, _ := utf8.DecodeRuneInString(s[i+j:]); r != utf8.RuneError { i += j break } } col -= runewidth.StringWidth(s[:i]) ss.WriteString(s[i:]) } } } return ss.String(), line, col } func trimLastInvalidRune(s string) string { for i := 0; i < utf8.UTFMax && i < len(s); i++ { if r, _ := utf8.DecodeLastRuneInString(s[:len(s)-i]); r != utf8.RuneError { return s[:len(s)-i] } } return s } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/flags.go000066400000000000000000000120601416642156300231250ustar00rootroot00000000000000package cli import ( "fmt" "reflect" "strconv" "strings" ) func parseFlags(args []string, opts interface{}) ([]string, error) { rest := make([]string, 0, len(args)) val := reflect.ValueOf(opts).Elem() typ := val.Type() longToValue := map[string]reflect.Value{} longToPositional := map[string]struct{}{} shortToValue := map[string]reflect.Value{} for i, l := 0, val.NumField(); i < l; i++ { if flag, ok := typ.Field(i).Tag.Lookup("long"); ok { longToValue[flag] = val.Field(i) if _, ok := typ.Field(i).Tag.Lookup("positional"); ok { longToPositional[flag] = struct{}{} } } if flag, ok := typ.Field(i).Tag.Lookup("short"); ok { shortToValue[flag] = val.Field(i) } } mapKeys := map[string]struct{}{} var positionalVal reflect.Value for i := 0; i < len(args); i++ { arg := args[i] var ( val reflect.Value ok bool shortopts string ) if arg == "--" { if positionalVal.IsValid() { for _, arg := range args[i+1:] { positionalVal.Set(reflect.Append(positionalVal, reflect.ValueOf(arg))) } } else { rest = append(rest, args[i+1:]...) } break } if strings.HasPrefix(arg, "--") { if val, ok = longToValue[arg[2:]]; !ok { if j := strings.IndexByte(arg, '='); j >= 0 { if val, ok = longToValue[arg[2:j]]; ok { if val.Kind() == reflect.Bool { return nil, fmt.Errorf("boolean flag `%s' cannot have an argument", arg[:j]) } args[i] = arg[j+1:] arg = arg[:j] i-- } } if !ok { return nil, fmt.Errorf("unknown flag `%s'", arg) } } } else if arg > "-" && arg[0] == '-' { if val, ok = shortToValue[arg[1:]]; !ok { var skip bool for i := 1; i < len(arg); i++ { opt := arg[i : i+1] if val, ok = shortToValue[opt]; ok { if val.Kind() != reflect.Bool { break } } else if !("A" <= opt && opt <= "Z" || "a" <= opt && opt <= "z") { skip = true break } } if !skip { shortopts = arg[1:] goto L } } } if !ok { if positionalVal.IsValid() && len(rest) > 0 { positionalVal.Set(reflect.Append(positionalVal, reflect.ValueOf(arg))) } else { rest = append(rest, arg) } continue } S: switch val.Kind() { case reflect.Bool: val.SetBool(true) case reflect.String: if i++; i >= len(args) { return nil, fmt.Errorf("expected argument for flag `%s'", arg) } val.SetString(args[i]) case reflect.Ptr: if val.Type().Elem().Kind() == reflect.Int { if i++; i >= len(args) { return nil, fmt.Errorf("expected argument for flag `%s'", arg) } v, err := strconv.Atoi(args[i]) if err != nil { return nil, fmt.Errorf("invalid argument for flag `%s': %w", arg, err) } val.Set(reflect.New(val.Type().Elem())) val.Elem().SetInt(int64(v)) } case reflect.Slice: if _, ok := longToPositional[arg[2:]]; ok { if positionalVal.IsValid() { for positionalVal.Len() > val.Len() { val.Set(reflect.Append(val, reflect.Zero(val.Type().Elem()))) } } positionalVal = val } else { if i++; i >= len(args) { return nil, fmt.Errorf("expected argument for flag `%s'", arg) } val.Set(reflect.Append(val, reflect.ValueOf(args[i]))) } case reflect.Map: if i += 2; i >= len(args) { return nil, fmt.Errorf("expected 2 arguments for flag `%s'", arg) } if val.IsNil() { val.Set(reflect.MakeMap(val.Type())) } name := args[i-1] if _, ok := mapKeys[name]; !ok { mapKeys[name] = struct{}{} val.SetMapIndex(reflect.ValueOf(name), reflect.ValueOf(args[i])) } } L: if shortopts != "" { opt := shortopts[:1] if val, ok = shortToValue[opt]; !ok { return nil, fmt.Errorf("unknown flag `%s'", opt) } if val.Kind() != reflect.Bool && len(shortopts) > 1 { args[i] = shortopts[1:] i-- shortopts = "" } else { shortopts = shortopts[1:] } arg = "-" + opt goto S } } return rest, nil } func formatFlags(opts interface{}) string { val := reflect.ValueOf(opts).Elem() typ := val.Type() var sb strings.Builder sb.WriteString("Command Options:\n") for i, l := 0, typ.NumField(); i < l; i++ { tag := typ.Field(i).Tag if i == l-1 { sb.WriteString("\nHelp Option:\n") } sb.WriteString(" ") var short bool if flag, ok := tag.Lookup("short"); ok { sb.WriteString("-") sb.WriteString(flag) short = true } else { sb.WriteString(" ") } m := sb.Len() if flag, ok := tag.Lookup("long"); ok { if short { sb.WriteString(", ") } else { sb.WriteString(" ") } sb.WriteString("--") sb.WriteString(flag) switch val.Field(i).Kind() { case reflect.Bool: sb.WriteString(" ") case reflect.Map: if strings.HasSuffix(flag, "file") { sb.WriteString(" name file") } else { sb.WriteString(" name value") } default: if _, ok = tag.Lookup("positional"); !ok { sb.WriteString("=") } } } else { sb.WriteString("=") } sb.WriteString(" "[:24-sb.Len()+m]) sb.WriteString(tag.Get("description")) sb.WriteString("\n") } return sb.String() } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/inputs.go000066400000000000000000000206051416642156300233570ustar00rootroot00000000000000package cli import ( "bufio" "bytes" "encoding/json" "io" "os" "strings" "gopkg.in/yaml.v3" "github.com/wader/gojq" ) type inputReader struct { io.Reader file *os.File buf *bytes.Buffer } func newInputReader(r io.Reader) *inputReader { if r, ok := r.(*os.File); ok { if _, err := r.Seek(0, io.SeekCurrent); err == nil { return &inputReader{r, r, nil} } } var buf bytes.Buffer // do not use strings.Builder because we need to Reset return &inputReader{io.TeeReader(r, &buf), nil, &buf} } func (ir *inputReader) getContents(offset *int64, line *int) string { if buf := ir.buf; buf != nil { return buf.String() } if current, err := ir.file.Seek(0, io.SeekCurrent); err == nil { defer func() { ir.file.Seek(current, io.SeekStart) }() } ir.file.Seek(0, io.SeekStart) const bufSize = 16 * 1024 var buf bytes.Buffer // do not use strings.Builder because we need to Reset if offset != nil && *offset > bufSize { buf.Grow(bufSize) for *offset > bufSize { n, err := io.Copy(&buf, io.LimitReader(ir.file, bufSize)) *offset -= int64(n) *line += bytes.Count(buf.Bytes(), []byte{'\n'}) buf.Reset() if err != nil || n == 0 { break } } } var r io.Reader if offset == nil { r = ir.file } else { r = io.LimitReader(ir.file, bufSize*2) } io.Copy(&buf, r) return buf.String() } type inputIter interface { gojq.Iter io.Closer Name() string } type jsonInputIter struct { dec *json.Decoder ir *inputReader fname string offset int64 line int err error } func newJSONInputIter(r io.Reader, fname string) inputIter { ir := newInputReader(r) dec := json.NewDecoder(ir) dec.UseNumber() return &jsonInputIter{dec: dec, ir: ir, fname: fname} } func (i *jsonInputIter) Next() (interface{}, bool) { if i.err != nil { return nil, false } var v interface{} if err := i.dec.Decode(&v); err != nil { if err == io.EOF { i.err = err return nil, false } var offset *int64 var line *int if err, ok := err.(*json.SyntaxError); ok { err.Offset -= i.offset offset, line = &err.Offset, &i.line } i.err = &jsonParseError{i.fname, i.ir.getContents(offset, line), i.line, err} return i.err, true } if buf := i.ir.buf; buf != nil && buf.Len() >= 16*1024 { i.offset += int64(buf.Len()) i.line += bytes.Count(buf.Bytes(), []byte{'\n'}) buf.Reset() } return v, true } func (i *jsonInputIter) Close() error { i.err = io.EOF return nil } func (i *jsonInputIter) Name() string { return i.fname } type nullInputIter struct { err error } func newNullInputIter() inputIter { return &nullInputIter{} } func (i *nullInputIter) Next() (interface{}, bool) { if i.err != nil { return nil, false } i.err = io.EOF return nil, true } func (i *nullInputIter) Close() error { i.err = io.EOF return nil } func (i *nullInputIter) Name() string { return "" } type filesInputIter struct { newIter func(io.Reader, string) inputIter fnames []string stdin io.Reader iter inputIter file io.Reader err error } func newFilesInputIter( newIter func(io.Reader, string) inputIter, fnames []string, stdin io.Reader, ) inputIter { return &filesInputIter{newIter: newIter, fnames: fnames, stdin: stdin} } func (i *filesInputIter) Next() (interface{}, bool) { if i.err != nil { return nil, false } for { if i.file == nil { if len(i.fnames) == 0 { i.err = io.EOF if i.iter != nil { i.iter.Close() i.iter = nil } return nil, false } fname := i.fnames[0] i.fnames = i.fnames[1:] if fname == "-" && i.stdin != nil { i.file, fname = i.stdin, "" } else { file, err := os.Open(fname) if err != nil { return err, true } i.file = file } if i.iter != nil { i.iter.Close() } i.iter = i.newIter(i.file, fname) } if v, ok := i.iter.Next(); ok { return v, ok } if r, ok := i.file.(io.Closer); ok && i.file != i.stdin { r.Close() } i.file = nil } } func (i *filesInputIter) Close() error { if i.file != nil { if r, ok := i.file.(io.Closer); ok && i.file != i.stdin { r.Close() } i.file = nil i.err = io.EOF } return nil } func (i *filesInputIter) Name() string { if i.iter != nil { return i.iter.Name() } return "" } type rawInputIter struct { scanner *bufio.Scanner fname string err error } func newRawInputIter(r io.Reader, fname string) inputIter { return &rawInputIter{scanner: bufio.NewScanner(r), fname: fname} } func (i *rawInputIter) Next() (interface{}, bool) { if i.err != nil { return nil, false } if i.scanner.Scan() { return i.scanner.Text(), true } if i.err = i.scanner.Err(); i.err != nil { return i.err, true } i.err = io.EOF return nil, false } func (i *rawInputIter) Close() error { i.err = io.EOF return nil } func (i *rawInputIter) Name() string { return i.fname } type streamInputIter struct { stream *jsonStream ir *inputReader fname string offset int64 line int err error } func newStreamInputIter(r io.Reader, fname string) inputIter { ir := newInputReader(r) dec := json.NewDecoder(ir) dec.UseNumber() return &streamInputIter{stream: newJSONStream(dec), ir: ir, fname: fname} } func (i *streamInputIter) Next() (interface{}, bool) { if i.err != nil { return nil, false } v, err := i.stream.next() if err != nil { if err == io.EOF { i.err = err return nil, false } var offset *int64 var line *int if err, ok := err.(*json.SyntaxError); ok { err.Offset -= i.offset offset, line = &err.Offset, &i.line } i.err = &jsonParseError{i.fname, i.ir.getContents(offset, line), i.line, err} return i.err, true } if buf := i.ir.buf; buf != nil && buf.Len() >= 16*1024 { i.offset += int64(buf.Len()) i.line += bytes.Count(buf.Bytes(), []byte{'\n'}) buf.Reset() } return v, true } func (i *streamInputIter) Close() error { i.err = io.EOF return nil } func (i *streamInputIter) Name() string { return i.fname } type yamlInputIter struct { dec *yaml.Decoder ir *inputReader fname string err error } func newYAMLInputIter(r io.Reader, fname string) inputIter { ir := newInputReader(r) dec := yaml.NewDecoder(ir) return &yamlInputIter{dec: dec, ir: ir, fname: fname} } func (i *yamlInputIter) Next() (interface{}, bool) { if i.err != nil { return nil, false } var v interface{} if err := i.dec.Decode(&v); err != nil { if err == io.EOF { i.err = err return nil, false } i.err = &yamlParseError{i.fname, i.ir.getContents(nil, nil), err} return i.err, true } return normalizeYAML(v), true } func (i *yamlInputIter) Close() error { i.err = io.EOF return nil } func (i *yamlInputIter) Name() string { return i.fname } type slurpInputIter struct { iter inputIter err error } func newSlurpInputIter(iter inputIter) inputIter { return &slurpInputIter{iter: iter} } func (i *slurpInputIter) Next() (interface{}, bool) { if i.err != nil { return nil, false } var vs []interface{} var v interface{} var ok bool for { v, ok = i.iter.Next() if !ok { i.err = io.EOF return vs, true } if i.err, ok = v.(error); ok { return i.err, true } vs = append(vs, v) } } func (i *slurpInputIter) Close() error { if i.iter != nil { i.iter.Close() i.iter = nil i.err = io.EOF } return nil } func (i *slurpInputIter) Name() string { return i.iter.Name() } type readAllIter struct { r io.Reader fname string err error } func newReadAllIter(r io.Reader, fname string) inputIter { return &readAllIter{r: r, fname: fname} } func (i *readAllIter) Next() (interface{}, bool) { if i.err != nil { return nil, false } i.err = io.EOF cnt, err := io.ReadAll(i.r) if err != nil { return err, true } return string(cnt), true } func (i *readAllIter) Close() error { i.err = io.EOF return nil } func (i *readAllIter) Name() string { return i.fname } type slurpRawInputIter struct { iter inputIter err error } func newSlurpRawInputIter(iter inputIter) inputIter { return &slurpRawInputIter{iter: iter} } func (i *slurpRawInputIter) Next() (interface{}, bool) { if i.err != nil { return nil, false } var vs []string var v interface{} var ok bool for { v, ok = i.iter.Next() if !ok { i.err = io.EOF return strings.Join(vs, ""), true } if i.err, ok = v.(error); ok { return i.err, true } vs = append(vs, v.(string)) } } func (i *slurpRawInputIter) Close() error { if i.iter != nil { i.iter.Close() i.iter = nil i.err = io.EOF } return nil } func (i *slurpRawInputIter) Name() string { return i.iter.Name() } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/marshaler.go000066400000000000000000000013161416642156300240110ustar00rootroot00000000000000package cli import ( "io" "gopkg.in/yaml.v3" ) type marshaler interface { marshal(interface{}, io.Writer) error } type rawMarshaler struct { m marshaler } func (m *rawMarshaler) marshal(v interface{}, w io.Writer) error { if s, ok := v.(string); ok { _, err := w.Write([]byte(s)) return err } return m.m.marshal(v, w) } func yamlFormatter(indent *int) *yamlMarshaler { return &yamlMarshaler{indent} } type yamlMarshaler struct { indent *int } func (m *yamlMarshaler) marshal(v interface{}, w io.Writer) error { enc := yaml.NewEncoder(w) if i := m.indent; i != nil { enc.SetIndent(*i) } else { enc.SetIndent(2) } if err := enc.Encode(v); err != nil { return err } return enc.Close() } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/run.go000066400000000000000000000002451416642156300226370ustar00rootroot00000000000000package cli import "os" // Run gojq. func Run() int { return (&cli{ inStream: os.Stdin, outStream: os.Stdout, errStream: os.Stderr, }).run(os.Args[1:]) } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/stream.go000066400000000000000000000055501416642156300233320ustar00rootroot00000000000000package cli import "encoding/json" type jsonStream struct { dec *json.Decoder path []interface{} states []int } func newJSONStream(dec *json.Decoder) *jsonStream { return &jsonStream{dec: dec, states: []int{jsonStateTopValue}, path: []interface{}{}} } const ( jsonStateTopValue = iota jsonStateArrayStart jsonStateArrayValue jsonStateArrayEnd jsonStateArrayEmptyEnd jsonStateObjectStart jsonStateObjectKey jsonStateObjectValue jsonStateObjectEnd jsonStateObjectEmptyEnd ) func (s *jsonStream) next() (interface{}, error) { switch s.states[len(s.states)-1] { case jsonStateArrayEnd, jsonStateObjectEnd: s.path = s.path[:len(s.path)-1] fallthrough case jsonStateArrayEmptyEnd, jsonStateObjectEmptyEnd: s.states = s.states[:len(s.states)-1] } if s.dec.More() { switch s.states[len(s.states)-1] { case jsonStateArrayValue: s.path[len(s.path)-1] = s.path[len(s.path)-1].(int) + 1 case jsonStateObjectValue: s.path = s.path[:len(s.path)-1] } } for { token, err := s.dec.Token() if err != nil { return nil, err } if d, ok := token.(json.Delim); ok { switch d { case '[', '{': switch s.states[len(s.states)-1] { case jsonStateArrayStart: s.states[len(s.states)-1] = jsonStateArrayValue case jsonStateObjectKey: s.states[len(s.states)-1] = jsonStateObjectValue } if d == '[' { s.states = append(s.states, jsonStateArrayStart) s.path = append(s.path, 0) } else { s.states = append(s.states, jsonStateObjectStart) } case ']': if s.states[len(s.states)-1] == jsonStateArrayStart { s.states[len(s.states)-1] = jsonStateArrayEmptyEnd s.path = s.path[:len(s.path)-1] return []interface{}{s.copyPath(), []interface{}{}}, nil } s.states[len(s.states)-1] = jsonStateArrayEnd return []interface{}{s.copyPath()}, nil case '}': if s.states[len(s.states)-1] == jsonStateObjectStart { s.states[len(s.states)-1] = jsonStateObjectEmptyEnd return []interface{}{s.copyPath(), map[string]interface{}{}}, nil } s.states[len(s.states)-1] = jsonStateObjectEnd return []interface{}{s.copyPath()}, nil default: panic(d) } } else { switch s.states[len(s.states)-1] { case jsonStateArrayStart: s.states[len(s.states)-1] = jsonStateArrayValue fallthrough case jsonStateArrayValue: return []interface{}{s.copyPath(), token}, nil case jsonStateObjectStart, jsonStateObjectValue: s.states[len(s.states)-1] = jsonStateObjectKey s.path = append(s.path, token) case jsonStateObjectKey: s.states[len(s.states)-1] = jsonStateObjectValue return []interface{}{s.copyPath(), token}, nil default: s.states[len(s.states)-1] = jsonStateTopValue return []interface{}{s.copyPath(), token}, nil } } } } func (s *jsonStream) copyPath() []interface{} { return append(make([]interface{}, 0, len(s.path)), s.path...) } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/test.yaml000066400000000000000000004676341416642156300233720ustar00rootroot00000000000000- name: empty input input: '' expected: '' - name: empty query args: - '' input: '128' expected: | 128 - name: comment only query args: - '# comment' input: '128' expected: | 128 - name: number input args: - '.' input: '128' expected: | 128 - name: number query args: - '0, 128, 3.14, 1.2e3, 1E+3, 1E-9' input: '0' expected: | 0 128 3.14 1200 1000 1e-9 - name: number literal error args: - '0..[empty]' input: '0' error: | invalid query: 0..[empty] 0..[empty] ^ invalid token "0.." exit_code: 3 - name: string input args: - '.' input: '"Hello, world!\r\n\t\f\b\uff06"' expected: | "Hello, world!\r\n\t\f\b&" - name: string query args: - '" Hello, world! \r\n\t\f\b\uff10 "' input: '0' expected: | " Hello, world! \r\n\t\f\b0 " - name: string query including newlines args: - "\"a\n\\(\"\nb\nc\n\")\nd\n\"" input: '0' expected: | "a\n\nb\nc\n\nd\n" - name: string query error args: - '. + "foo' input: '0' error: | invalid query: . + "foo . + "foo ^ invalid token "foo exit_code: 3 - name: string query error args: - '"foo\,bar"' input: '0' error: | invalid query: "foo\,bar" "foo\,bar" ^ invalid token "\," exit_code: 3 - name: string query error args: - '"\u "' input: '0' error: | invalid query: "\u " "\u " ^ invalid token "\u " exit_code: 3 - name: string query error args: - '"\(1) \x "' input: '0' error: | invalid query: "\(1) \x " "\(1) \x " ^ invalid token " \x " exit_code: 3 - name: object input input: '{"foo": 128}' expected: | { "foo": 128 } - name: object input and string escape input: '{"foo\\u0000\\t\\n\\r\\u001f !\"#$/09:;<>?@AZ[\\\\]^_`az{|}~\\u007f": "<\\t\\n\\r>"}' expected: | { "foo\\u0000\\t\\n\\r\\u001f !\"#$/09:;<>?@AZ[\\\\]^_`az{|}~\\u007f": "<\\t\\n\\r>" } - name: object index args: - '.foo' input: '{"foo": 128}' expected: | 128 - name: object index with large number value args: - '.foo' input: '{"foo": 4722366482869645213696}' expected: | 4722366482869645213696 - name: object index by keywords args: - '.and,.or,.try' input: '{"and":1,"or":2,"try":3}' expected: | 1 2 3 - name: object index by brackets args: - '.["foo"]' input: '{"foo": 128}' expected: | 128 - name: object index by string args: - '."2\(.foo[])3"' input: '{"foo":[1,2],"213":3,"223":6}' expected: | 3 6 - name: object index by query args: - '.c[.a + .b]' input: '{ "a": "b", "b": "c", "c": {"bc": 128} }' expected: | 128 - name: object nested index by query args: - '.d[.a][.b][.c]' input: '{ "a": "b", "b": "c", "c": "d", "d": { "b": { "c": { "d": 128 } } } }' expected: | 128 - name: object index by iterator after iterator args: - '.c[][.a[] + .b[]]' input: '{ "a": ["a", "b"], "b": ["c", "d"], "c": [{"ac": 1, "bc": 2, "ad": 3, "bd": 4}, {"ac": 5, "bc": 6, "ad": 7, "bd": 8}] }' expected: | 1 5 2 6 3 7 4 8 - name: object index by string iterator after iterator args: - '.c[]."\(.a[])"' input: '{ "a": [0,1], "b": [0,2], "c": [{"0":1,"1":2},{"0":3,"1":4}] }' expected: | 1 3 2 4 - name: object index in object value args: - '{ message: .[].message }' input: '[ {"message": "Hello, world" } ]' expected: | { "message": "Hello, world" } - name: object index error args: - '.[.a + .b]' input: '{"a": 10, "b": 20}' error: | expected an array but got: object ({"a":10,"b":20}) - name: object index syntax error args: - '. a' input: '{}' error: | invalid query: . a . a ^ unexpected token "a" exit_code: 3 - name: expected object but got number error args: - '.foo.bar' input: '{"foo": 128}' error: | expected an object but got: number (128) - name: expected object but got string error args: - '"a"+"☆"*100 | .foo' input: '0' error: | expected an object but got: string ("a☆☆☆☆☆☆☆☆ ...") - name: expected object error with pipe operator args: - '.foo|.bar' input: '{"foo": 128}' error: | expected an object but got: number (128) - name: expected object error after iterator args: - '.[]|.foo|.bar' input: '[128]' error: | expected an object but got: number (128) - name: object index against null args: - '.foo.bar.baz' input: 'null' expected: | null - name: object optional index args: - '.foo?.bar?.baz?' input: '{"foo": 128}' - name: object optional index error args: - '.foo.bar.baz?' input: '{"foo": 128}' error: | expected an object but got: number (128) - name: object optional index after iterator error args: - '.[].foo?' input: '128' error: | cannot iterate over: number (128) - name: error after optional operator args: - '1? | .foo' input: 'null' error: | expected an object but got: number (1) - name: error after optional operator with fork args: - '(1,2)? | .foo' input: 'null' error: | expected an object but got: number (1) - name: array index args: - '.[-5], .[-1], .[0], .[2], .[3], .[4], .[nan]' input: '[16, 32, 48, 64]' expected: | null 64 16 48 64 null null - name: array slice args: - -c - '.[2:], .[:2], .[1:3], .[0:4], .[2:1], .[-2:], .[:-2], .[-2:-1], .[-10:], .[:10], .[10:], .[:-10]' input: '[16, 32, 48, 64]' expected: | [48,64] [16,32] [32,48] [16,32,48,64] [] [48,64] [16,32] [48] [16,32,48,64] [16,32,48,64] [] [] - name: array index and slice by large number args: - -c - '.[1e300], .[-1e300], .[-1e300:], .[1e300:], .[:-1e300], .[:1e300], .[-1e300:1e300], .[1e300:-1e300]' input: '[16, 32, 48, 64]' expected: | null null [16,32,48,64] [] [] [16,32,48,64] [16,32,48,64] [] - name: array index and slice by infinite args: - -c - '.[infinite], .[-infinite], .[-infinite:], .[infinite:], .[:-infinite], .[:infinite], .[-infinite:infinite], .[infinite:-infinite]' input: '[16, 32, 48, 64]' expected: | null null [16,32,48,64] [] [] [16,32,48,64] [16,32,48,64] [] - name: array and string slice by object args: - -c - '[.[{"start": (null,range(-3;4)), "end": (null,range(-3;4))}]]' input: '[0, 1, 2] "abcde"' expected: | [[0,1,2],[],[0],[0,1],[],[0],[0,1],[0,1,2],[0,1,2],[],[0],[0,1],[],[0],[0,1],[0,1,2],[1,2],[],[],[1],[],[],[1],[1,2],[2],[],[],[],[],[],[],[2],[0,1,2],[],[0],[0,1],[],[0],[0,1],[0,1,2],[1,2],[],[],[1],[],[],[1],[1,2],[2],[],[],[],[],[],[],[2],[],[],[],[],[],[],[],[]] ["abcde","ab","abc","abcd","","a","ab","abc","cde","","c","cd","","","","c","de","","","d","","","","","e","","","","","","","","abcde","ab","abc","abcd","","a","ab","abc","bcde","b","bc","bcd","","","b","bc","cde","","c","cd","","","","c","de","","","d","","","",""] - name: array slice by object against null args: - '.[{"start": 1, "end": 2}]' input: 'null' expected: | null - name: array slice by object error args: - '.[{"start": 1}]' input: '[]' error: | expected "start" and "end" for slicing but got: object ({"start":1}) - name: array slice against string error args: - '""[:{}]' input: '0' error: | expected a number for indexing an array but got: object ({}) - name: array index and slice by large integer args: - -c - '. as $x | 4722366482869645213696 as $y | $x[-$y], $x[$y], $x[-$y:], $x[:$y], $x[-$y:$y], $x[$y-$y], $x[$y/$y]' input: '[16, 32, 48, 64]' expected: | null null [16,32,48,64] [16,32,48,64] [16,32,48,64] 16 32 - name: array nested index by iterator args: - -c - '[.[.[]],.[.[]:],.[:.[]],.[.[]:.[]],.[-.[]],.[--.[]]]' input: '[0, 1, 2]' expected: | [0,1,2,[0,1,2],[1,2],[2],[],[0],[0,1],[],[0],[0,1],[],[],[1],[],[],[],0,2,1,0,1,2] - name: array index by iterator args: - '.c[][.a[] + .b[]]' input: '{ "a": [0,1], "b": [0,2], "c": [[1,2,3,4],[5,6,7,8]] }' expected: | 1 5 2 6 3 7 4 8 - name: array slice by iterator args: - -c - '.c[][.a[] : .b[]]' input: '{ "a": [0,1], "b": [0,2], "c": [[1,2,3,4],[5,6,7,8]] }' expected: | [] [] [1,2] [5,6] [] [] [2] [6] - name: expected array but got object error args: - '.[0:1]' input: '{"foo": 128}' error: | expected an array but got: object ({"foo":128}) - name: array index against null args: - '.[0]' input: 'null' expected: | null - name: array slice against null args: - '.[0:1]' input: 'null' expected: | null - name: array index by array args: - -c - '.[[1],[5,7,9],[],5,[3]]' input: '[1,3,5,7,9,8,6,5,4,3,2,1]' expected: | [0,11] [2] [] 8 [1,9] - name: array index error args: - '.["foo"]' input: '[]' error: | expected an object but got: array ([]) - name: array slice error args: - '.[:{}]' input: '[]' error: | expected a number for indexing an array but got: object ({}) - name: string index and slice args: - -c - '[.[-5],.[-1],.[0],.[3],.[4],.[6],.[2:4],.[-4:-2]]' input: '"12345"' expected: | ["1","5","1","4","5","","34","23"] - name: array construction args: - -c - '.,[3,[[2]]],[1],[[.],3]|[.,.]' input: 'null' expected: | [null,null] [[3,[[2]]],[3,[[2]]]] [[1],[1]] [[[null],3],[[null],3]] - name: array construction with error function args: - -c - '[error], [0, error, 1]' input: 'null' expected: | [] [0,1] - name: array construction with halt_error function args: - -c - '[halt_error], [0, halt_error, 1]' input: 'null' exit_code: 5 - name: array iterator args: - '.foo | .[] | .' input: '{"foo": [1,2,{"bar":[]},[3,4,5]]}' expected: | 1 2 { "bar": [] } [ 3, 4, 5 ] - name: iterator with optional operator args: - '.[]?' input: '10' - name: nested iterator args: - '.[][]' input: '[[10]]' expected: | 10 - name: nested iterator error args: - '.[][]' input: '[10]' error: | cannot iterate over: number (10) - name: nested iterator with optional operator args: - '.[][]?' input: '[10]' - name: object iterator args: - '.[]' input: '{"foo": 128}' expected: | 128 - name: object optional index after iterator args: - '[.[].name?]' input: '[ 1, { "name": 2 }, 3, { "name": 4 } ]' expected: | [ 2, 4 ] - name: pipe args: - '.foo | .bar' input: '{"foo": {"bar": {"baz": 128}}}' expected: | { "baz": 128 } - name: null value args: - '.[] | null' input: '["a", 1, [], {}]' expected: | null null null null - name: boolean values args: - '.[] | true, false' input: '["a", 1]' expected: | true false true false - name: empty function args: - '.[] | empty, empty, . + [empty,.][], [empty,.][] / 1, empty < ., . and empty, empty or .' input: '[0, 1]' expected: | 0 0 2 1 - name: empty function in array construction args: - '[empty], [0, empty, 1]' input: 'null' expected: | [] [ 0, 1 ] - name: empty function in function declaration args: - 'def f: 1,empty,empty,2; f,f' input: '[0, 1]' expected: | 1 2 1 2 - name: unary operators args: - -c - 'map(-.[]), map(+.[])' input: '[[1,2,3,4722366482869645213696]]' expected: | [-1,-2,-3,-4722366482869645213696] [1,2,3,4722366482869645213696] - name: unary operators against string args: - '(-.)' input: '"abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde"' error: | cannot negate: string ("abcdeabcdeabcdeabcdeabcd ...") - name: object construction args: - -c - '{ foo: .foo.bar, "bar": .foo, "": false }' input: '{"foo":{"bar":["a","b"]}}' expected: | {"":false,"bar":{"bar":["a","b"]},"foo":["a","b"]} - name: object construction shortcuts args: - '{foo,"bar",qux,quxx:.foo}' input: '{"foo": 1, "bar": 2, "baz": 3}' expected: | { "bar": 2, "foo": 1, "qux": null, "quxx": 1 } - name: object construction with trailing comma args: - '{foo,bar,}' input: '{"foo": 1, "bar": 2, "baz": 3}' expected: | { "bar": 2, "foo": 1 } - name: object construction by keywords args: - '{as, and, null, module}' input: '{"and": 1, "as": 2, "module": 3, "null": 4}' expected: | { "and": 1, "as": 2, "module": 3, "null": 4 } - name: object construction with pipe in value args: - '{ "bar": { "baz": . } | . | select(true) }' input: '{ "foo": 100 }' expected: | { "bar": { "baz": { "foo": 100 } } } - name: empty object args: - '{}' input: '{"foo":{"bar":["a","b"]}}' expected: | {} - name: unary operators in object values args: - '{ x: -128, y: +128 }' input: '0' expected: | { "x": -128, "y": 128 } - name: iterator in object values args: - -c - '{ foo: .foo[], bar: .bar[] }' input: '{"foo":[1,2],"bar":["a","b"]}' expected: | {"bar":"a","foo":1} {"bar":"b","foo":1} {"bar":"a","foo":2} {"bar":"b","foo":2} - name: iterator in object key args: - -c - '{ (.[]): .. }' input: '["a", "b"]' expected: | {"a":["a","b"]} {"a":"a"} {"a":"b"} {"b":["a","b"]} {"b":"a"} {"b":"b"} - name: binary operators in object key and value args: - -c - '{ (.[] + .[]): (.[] + .[]) }' input: '["foo", "bar"]' expected: | {"foofoo":"foofoo"} {"foofoo":"barfoo"} {"foofoo":"foobar"} {"foofoo":"barbar"} {"barfoo":"foofoo"} {"barfoo":"barfoo"} {"barfoo":"foobar"} {"barfoo":"barbar"} {"foobar":"foofoo"} {"foobar":"barfoo"} {"foobar":"foobar"} {"foobar":"barbar"} {"barbar":"foofoo"} {"barbar":"barfoo"} {"barbar":"foobar"} {"barbar":"barbar"} - name: error in object key args: - -c - '{ (.[]): .. }' input: '["a", 1]' expected: | {"a":["a",1]} {"a":"a"} {"a":1} error: | expected a string for object key but got: number (1) - name: error in object value args: - -c - '.[] | { (.k): .a.b }' input: '[{"a":{"b":10},"k":"a"}, {"a":10,"k":"b"}]' expected: | {"a":10} error: | expected an object but got: number (10) - name: variable in object keys args: - '. as [$x, $y, $z] | {$x: 1, $y: 2, $z: 3}' input: '["a", "b", "c"]' expected: | { "a": 1, "b": 2, "c": 3 } - name: pipe in object key args: - '{ (.foo|.bar): .foo.bar }' input: '{"foo":{"bar":"baz"}}' expected: | { "baz": "baz" } - name: number in object key args: - '{ (.foo): .foo }' input: '{"foo":10}' error: | expected a string for object key but got: number (10) - name: null in object key args: - '{ (.foo): .foo }' input: '{}' error: | expected a string for object key but got: null - name: keyword in object keys args: - -c - '{if:0,and:1,or:2,then:3,else:4,elif:5,end:6,as:7,def:8,reduce:9,foreach:10,try:11,catch:12,label:13,import:14,include:15,module:16}' input: 'null' expected: | {"and":1,"as":7,"catch":12,"def":8,"elif":5,"else":4,"end":6,"foreach":10,"if":0,"import":14,"include":15,"label":13,"module":16,"or":2,"reduce":9,"then":3,"try":11} - name: array args: - '[.foo, ., false]' input: '{"foo": {"bar": 128}}' expected: | [ { "bar": 128 }, { "foo": { "bar": 128 } }, false ] - name: empty array args: - '[]' input: '{"foo": {"bar": 128}}' expected: | [] - name: pipe in array args: - '[.foo|.bar]' input: '{"foo": {"bar": 128}}' expected: | [ 128 ] - name: iterator in array args: - '[.foo|.bar[][]]' input: '{"foo": {"bar": [[1],[2],[3]]}}' expected: | [ 1, 2, 3 ] - name: error after iterator args: - '[ .[] | .foo ]' input: '[ [1, 2, 3], 4, 5]' error: | expected an object but got: array ([1,2,3]) - name: function after iterator error args: - '.[] | .foo | null' input: '[1, 2, 3]' error: | expected an object but got: number (1) - name: multiple iterators in array args: - -c - '[.[],..,.[]]' input: '[1, 2, 3]' expected: | [1,2,3,[1,2,3],1,2,3,1,2,3] - name: recurse args: - -c - '..' input: '{"foo":{"bar":["a","b"]}}' expected: | {"foo":{"bar":["a","b"]}} {"bar":["a","b"]} ["a","b"] "a" "b" - name: recurse after iterator args: - '.[] | ..' input: '{"foo":[{"bar":128}]}' expected: | [ { "bar": 128 } ] { "bar": 128 } 128 - name: recurse and pipe args: - '.. | .foo?' input: '{"foo":{"bar":["a","b"]}}' expected: | { "bar": [ "a", "b" ] } null - name: recurse in object value args: - -c - '{ foo: .. }' input: '{"foo":[1,2,3]}' expected: | {"foo":{"foo":[1,2,3]}} {"foo":[1,2,3]} {"foo":1} {"foo":2} {"foo":3} - name: length function args: - 'length' input: | 42 -42 {"a":1,"b":2} [3,4,5] "Hello, world" "12345" null -4722366482869645213696 expected: | 42 42 2 3 12 5 0 4722366482869645213696 - name: length function error args: - 'length' input: 'false' error: | length cannot be applied to: boolean (false) - name: keys function args: - 'keys' input: | {"a":1,"b":2} [3,4,5] expected: | [ "a", "b" ] [ 0, 1, 2 ] - name: utf8bytelength function args: - 'utf8bytelength' input: '"" "☆12345☆"' expected: | 0 21 - name: has function on objects args: - 'has(.c[])' input: | {"a":{"b":1},"c":["a","b"]} {"c":["b","c"]} expected: | true false false true - name: has function on arrays args: - 'has(2, nan, infinite, -infinite)' input: | [1, 2, 3] [1, 3] expected: | true false false false false false false false - name: has function against null args: - 'has(2, nan, infinite, -infinite)' input: 'null' expected: | false false false false - name: has/0 is not defined args: - 'has' input: '[0]' error: | function not defined: has/0 exit_code: 3 - name: has/2 is not defined args: - 'has(1; 2)' input: '[0]' error: | function not defined: has/2 exit_code: 3 - name: has function type error args: - 'has(0)' input: '{}' error: | cannot check whether object ({}) has a key: number (0) - name: in function for object args: - 'in({"foo": 42})' input: '"foo" "bar"' expected: | true false - name: in function for array args: - 'in([3, 4])' input: '-1 0 1 2' expected: | false true true false - name: in function for null args: - 'in(null)' input: '0' expected: | false - name: in function type error args: - 'in([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])' input: '"x"' error: | cannot check whether array ([0,0,0,0,0,0,0,0,0,0,0,0, ...]) has a key: string ("x") - name: map function args: - -c - 'map(..)' input: '[{"a":{"b":1}}, [2,3,4]]' expected: | [{"a":{"b":1}},{"b":1},1,[2,3,4],2,3,4] - name: to_entries function args: - -c - 'to_entries' input: | [10, 20] {"a": 10, "b": []} expected: | [{"key":0,"value":10},{"key":1,"value":20}] [{"key":"a","value":10},{"key":"b","value":[]}] - name: from_entries function args: - -c - 'from_entries' input: '[{"key":"a", "value":1}, {"Key":"b", "Value":2}, {"name":"c", "value":3}, {"Name":"d", "Value":4}]' expected: | {"a":1,"b":2,"c":3,"d":4} - name: with_entries function args: - -c - 'with_entries(.key |= "x" + . | .value += 1)' input: '{"a": 1, "b": 2}' expected: | {"xa":2,"xb":3} - name: add function args: - -c - 'add' input: | [] [1,2,3] ["a","b","c"] [[3],[4,5],[6]] [{"a":1}, {"b":2}, {"a":3}] {} {"a":1,"b":9} {"a":"a","b":"b","c":"c"} {"a":[1],"b":[2],"c":[3],"d":[4]} expected: | null 6 "abc" [3,4,5,6] {"a":3,"b":2} null 10 "abc" [1,2,3,4] - name: add function error args: - 'add' input: '[{}, [], {}]' error: | cannot add: object ({}) and array ([]) - name: flatten/0 function args: - -c - 'flatten' input: | [] [0, [[[[[1]]]]]] [0, [1, [2]], [1, [[3], 2]]] {"x": [1], "y": [[2]], "z": [[[3]]]} expected: | [] [0,1] [0,1,2,1,3,2] [1,2,3] - name: flatten/1 function args: - -c - 'flatten(2)' input: | [] [0, [[[[[1]]]]]] [0, [1, [2]], [1, [[3], 2]]] {"x": [1], "y": [[2]], "z": [[[3]]]} expected: | [] [0,[[[1]]]] [0,1,2,1,[3],2] [1,2,[3]] - name: min, min_by, max, max_by functions args: - -c - '[min, min_by(.[1]), min_by(.[2]), max, max_by(.[1]), max_by(.[2])]' input: | [] [[4,2,"a"],[3,1,"a"],[2,4,"a"],[1,3,"a"]] expected: | [null,null,null,null,null,null] [[1,3,"a"],[3,1,"a"],[4,2,"a"],[4,2,"a"],[2,4,"a"],[1,3,"a"]] - name: sort, sort_by, group_by functions args: - -c - 'sort, sort_by(.a), sort_by(.b), sort_by(.b, .c), group_by(.b), group_by(.c, .a), group_by(.a + .b - .c == 2)' input: '[{"a": 1, "b": 4, "c": 5}, {"a": 4, "b": 1, "c": 2}, {"a": 1, "b": 4, "c": 3}, {"a": 3, "b": 2, "c": 1}]' expected: | [{"a":1,"b":4,"c":3},{"a":1,"b":4,"c":5},{"a":3,"b":2,"c":1},{"a":4,"b":1,"c":2}] [{"a":1,"b":4,"c":5},{"a":1,"b":4,"c":3},{"a":3,"b":2,"c":1},{"a":4,"b":1,"c":2}] [{"a":4,"b":1,"c":2},{"a":3,"b":2,"c":1},{"a":1,"b":4,"c":5},{"a":1,"b":4,"c":3}] [{"a":4,"b":1,"c":2},{"a":3,"b":2,"c":1},{"a":1,"b":4,"c":3},{"a":1,"b":4,"c":5}] [[{"a":4,"b":1,"c":2}],[{"a":3,"b":2,"c":1}],[{"a":1,"b":4,"c":5},{"a":1,"b":4,"c":3}]] [[{"a":3,"b":2,"c":1}],[{"a":4,"b":1,"c":2}],[{"a":1,"b":4,"c":3}],[{"a":1,"b":4,"c":5}]] [[{"a":1,"b":4,"c":5},{"a":4,"b":1,"c":2},{"a":3,"b":2,"c":1}],[{"a":1,"b":4,"c":3}]] - name: unique, unique_by functions args: - -c - 'unique, unique_by(length)' input: | [7,3,5,1,4,2,5,3,6,1,7,4,3] ["cat", "dog", "hello", "world", "banana", "apple"] expected: | [1,2,3,4,5,6,7] [1,2,3,4,5,6,7] ["apple","banana","cat","dog","hello","world"] ["cat","hello","banana"] - name: arrays, objects, iterables, booleans, numbers, strings, nulls, values, scalars functions args: - -c - 'map(arrays), map(objects), map(iterables), map(booleans), map(numbers), map(strings), map(nulls), map(values), map(scalars)' input: '[null, true, false, 0, 1, "", "foo", [], [2, []], {}, { "foo": [] }, 4722366482869645213696]' expected: | [[],[2,[]]] [{},{"foo":[]}] [[],[2,[]],{},{"foo":[]}] [true,false] [0,1,4722366482869645213696] ["","foo"] [null] [true,false,0,1,"","foo",[],[2,[]],{},{"foo":[]},4722366482869645213696] [null,true,false,0,1,"","foo",4722366482869645213696] - name: leaf_paths function args: - -c - 'leaf_paths' input: '[null,[],{},{"a":[1,[],{},true]},[3,[],1]]' expected: | [3,"a",0] [3,"a",3] [4,0] [4,2] - name: recurse/0 function args: - -c - 'recurse' input: '{"a":{"b":[1]}}' expected: | {"a":{"b":[1]}} {"b":[1]} [1] 1 - name: recurse/1 function args: - -c - 'recurse(.a[])' input: '{"a":[{"a": []}, {"a":[{"a":[]}]}]}' expected: | {"a":[{"a":[]},{"a":[{"a":[]}]}]} {"a":[]} {"a":[{"a":[]}]} {"a":[]} - name: recurse/2 function args: - -c - '[recurse(. + .; . < 1000)]' input: '1' expected: | [1,2,4,8,16,32,64,128,256,512] - name: while function args: - -c - '[while(.[0]<100; [.[1], .[0]+.[1]]) | .[0]]' input: '[0, 1]' expected: | [0,1,1,2,3,5,8,13,21,34,55,89] - name: until function args: - '[.,1] | until(.[0]==0; [.[0]-1, .[0]*.[1]]) | .[1]' input: '20' expected: | 2432902008176640000 - name: repeat function args: - -c - '[limit(.; repeat(1,2,3))]' input: '10' expected: | [1,2,3,1,2,3,1,2,3,1] - name: range function args: - -c - '[range(.)], [range(range(.))], [range(3.5)], [range(3; 5)], [range(1.5; 4)], [range(-5; -1.5)], [range(3.5; 5.5)], [range(-5.5; -3.5)], [range(0; 10; 3)], [range(0; 10; -1)], [range(5; -5; -3)], [range(0.25; 9.5; 2.25)], [range(-5; -9.5; -1)]' input: '5' expected: | [0,1,2,3,4] [0,0,1,0,1,2,0,1,2,3] [0,1,2,3] [3,4] [1.5,2.5,3.5] [-5,-4,-3,-2] [3.5,4.5] [-5.5,-4.5] [0,3,6,9] [] [5,2,-1,-4] [0.25,2.5,4.75,7,9.25] [-5,-6,-7,-8,-9] - name: range function overflow args: - 'range(9223372036854775700; 9223372036854775805; 10)' input: 'null' expected: | 9223372036854775700 9223372036854775710 9223372036854775720 9223372036854775730 9223372036854775740 9223372036854775750 9223372036854775760 9223372036854775770 9223372036854775780 9223372036854775790 9223372036854775800 - name: range function with large numbers args: - 'range(0; 20000000000000000000; 2000000000000000000)' input: 'null' expected: | 0 2000000000000000000 4000000000000000000 6000000000000000000 8000000000000000000 10000000000000000000 12000000000000000000 14000000000000000000 16000000000000000000 18000000000000000000 - name: range function with iterators in arguments args: - -c - '[range(-.; .+1)] | [range(.[]; .[]; .[])] | recurse(.[26:];.[0])[:26] | map(.+3)' input: '3' expected: | [0,0,0,0,1,0,0,0,1,2,0,2,0,0,1,2,3,0,2,0,3,0,1,2,3,4] [0,2,4,0,3,0,1,2,3,4,5,0,2,4,0,3,1,1,1,1,1,1,1,2,1,1] [1,2,3,1,3,1,1,2,3,4,1,3,1,4,1,2,3,4,5,1,3,5,1,4,2,2] [2,1,2,2,2,2,2,2,2,3,2,2,2,3,4,2,4,2,2,3,4,5,2,4,2,5] [3,3,1,3,2,1,3,3,3,2,3,3,3,3,3,3,3,4,3,3,3,4,5,3,5,3] [4,1,4,2,4,3,2,1,4,4,2,4,3,2,4,4,4,3,4,4,4,4,4,4,4,5] [4,4,5,2,5,3,1,5,4,3,2,1,5,2,5,3,5,4,3,2,5,5,3,5,4,3] [5,5,5,4,5,5,5,5,5,5,6,3,6,4,2,6,5,4,3,2,1,6,3,6,4,2] [6,5,4,3,2,6,3,6,4,6,5,4,3,6,6,4,6,5,4,6,6,6,5,6,6,6] - name: range function error args: - '(null, "", [], {} | try range(.) catch .), try range(""; 0) catch ., try range(0; 1; []) catch .' input: 'null' expected: | "range cannot be applied to: null" "range cannot be applied to: string (\"\")" "range cannot be applied to: array ([])" "range cannot be applied to: object ({})" "range cannot be applied to: string (\"\")" "range cannot be applied to: array ([])" - name: tonumber function args: - 'tonumber' input: | "0" "12345" "-43210" "+4.2" "05" "9." "+.123" "-.1" "1e-2" "2.e-2" 3.14 expected: | 0 12345 -43210 4.2 5 9 0.123 -0.1 0.01 0.02 3.14 - name: tonumber function error args: - 'try tonumber catch .' input: | "" "." " " "1a" ".e1" "-.e" expected: | "invalid number: \"\"" "invalid number: \".\"" "invalid number: \" \"" "invalid number: \"1a\"" "invalid number: \".e1\"" "invalid number: \"-.e\"" - name: tostring function args: - 'tostring' input: | null false true 3.14 "foo bar" {"foo": "-43210"} [1,2,3] ["<&><>"] expected: | "null" "false" "true" "3.14" "foo bar" "{\"foo\":\"-43210\"}" "[1,2,3]" "[\"<&><>\"]" - name: type function args: - 'type' input: | null false true 0 3.14 4722366482869645213696 "foo" [] {} expected: | "null" "boolean" "boolean" "number" "number" "number" "string" "array" "object" - name: reverse function args: - 'reverse' input: '[0, 1, 2, 3, 4]' expected: | [ 4, 3, 2, 1, 0 ] - name: contains function args: - -c - '.[] as $x | (null,false,true,[],[3,4],[3,1],"foo","ll",{},{"a":1},{"b":[5]}) as $y | $x | [$x,$y,contains($y)]?' input: '[null,false,true,[],[1,2,3],"","hello",{"a":3,"b":[4,5,6]}]' expected: | [null,null,true] [false,false,true] [true,true,true] [[],[],true] [[],[3,4],false] [[],[3,1],false] [[1,2,3],[],true] [[1,2,3],[3,4],false] [[1,2,3],[3,1],true] ["","foo",false] ["","ll",false] ["hello","foo",false] ["hello","ll",true] [{"a":3,"b":[4,5,6]},{},true] [{"a":3,"b":[4,5,6]},{"a":1},false] [{"a":3,"b":[4,5,6]},{"b":[5]},true] - name: contains function against unicode strings args: - -c - '. as $x | ("@", "\u0000@", "\u0000dc", "b\u0000c", "b\u0000cd", "b\u0000cd") as $y | $x | [$x,$y,contains($y)]' input: '"ab\u0000cd"' expected: | ["ab\u0000cd","@",false] ["ab\u0000cd","\u0000@",false] ["ab\u0000cd","\u0000dc",false] ["ab\u0000cd","b\u0000c",true] ["ab\u0000cd","b\u0000cd",true] ["ab\u0000cd","b\u0000cd",true] - name: contains function error args: - 'nan | contains([nan])' input: '0' error: | cannot check contains([null]): number (null) - name: indices function for array args: - 'indices(1), indices([]), indices([1,2]), indices([3,4,5])' input: '[0, 1, 2, 3, 4, 3, 2, 1, 2, 1, 2, 3, 4]' expected: | [ 1, 7, 9 ] [] [ 1, 7, 9 ] [] - name: indices function for string args: - 'indices(""), indices("ell"), indices("wor"), indices("ol"), indices("34")' input: '"hello, world, hello, word 12345"' expected: | # different from jq due to multibyte characters [] [ 1, 15 ] [ 7, 21 ] [] [ 28 ] - name: index, rindex functions on arrays args: - '[index(0, [1, 2]), rindex(0, [1, 2]), indices(0, [1, 2])]' input: | null [] [0] [0, 1, 2, 1, 2, 1, 0, 1, 2] expected: | [ null, null, null, null, null, null ] [ null, null, null, null, [], [] ] [ 0, null, 0, null, [ 0 ], [] ] [ 0, 1, 6, 7, [ 0, 6 ], [ 1, 3, 7 ] ] - name: index, rindex functions on strings args: - 'index(","), rindex(","), indices(",")' input: '"a,bc,def,ghij,klmno"' expected: | 1 13 [ 1, 4, 8, 13 ] - name: inside function args: - -c - '.[] as $x | (false,"abc",[],["foobaz","foobar"],{},{"a":10,"b":20,"c":30}) as $y | $x | [$x,$y,inside($y)]?' input: '["a",["bar","baz"],{"a":10,"b":20}]' expected: | ["a","abc",true] [["bar","baz"],[],false] [["bar","baz"],["foobaz","foobar"],true] [{"a":10,"b":20},{},false] [{"a":10,"b":20},{"a":10,"b":20,"c":30},true] - name: startswith function args: - 'startswith("foo")' input: | "" "fo" "foo" "fooo" "barfoo" expected: | false false true true false - name: startswith function error args: - '("x" | try startswith({}) catch .), (10 | try startswith("x") catch .)' input: '0' expected: | "startswith cannot be applied to: object ({})" "startswith cannot be applied to: number (10)" - name: endswith function args: - 'endswith("foo")' input: | "" "fo" "foo" "fooo" "barfoo" expected: | false false true false true - name: endswith function error args: - '("x" | try endswith({}) catch .), (10 | try endswith("x") catch .)' input: '0' expected: | "endswith cannot be applied to: object ({})" "endswith cannot be applied to: number (10)" - name: ltrimstr function args: - 'map(ltrimstr("foo")), ("x"|ltrimstr(0, null, {}))' input: '["", "fo", "foo", "fooo", "barfoo", 0, null, true, {}, []]' expected: | [ "", "fo", "", "o", "barfoo", 0, null, true, {}, [] ] "x" "x" "x" - name: rtrimstr function args: - 'map(rtrimstr("foo")), ("x"|rtrimstr(0, null, {}))' input: '["", "fo", "foo", "fooo", "barfoo", 0, null, true, {}, []]' expected: | [ "", "fo", "", "fooo", "bar", 0, null, true, {}, [] ] "x" "x" "x" - name: combinations/0 function args: - -c - '[combinations]' input: '[[1, 2, 3], [4, 5, 6]]' expected: | [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]] - name: combinations/1 function args: - -c - '[combinations(3)]' input: '[0, 1]' expected: | [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]] - name: join function args: - 'join(",")' input: '[null, false, true, 0, 1, "", "a", "b", "abc"]' expected: | ",false,true,0,1,,a,b,abc" - name: join function with iterator argument args: - '. / "" | join(.[])' input: '"abcde"' expected: | "aabacadae" "abbbcbdbe" "acbcccdce" "adbdcddde" "aebecedee" - name: join object args: - 'join(",")' input: '{"a": null, "b": false, "c": true, "d": 0, "e": 1, "f": "", "g": "a", "h": "b", "i": "abc"}' expected: | ",false,true,0,1,,a,b,abc" - name: join object order args: - 'join(",")' input: '{"d":4, "c":3, "b":2, "a":1}' expected: | "1,2,3,4" - name: join function error item args: - 'try join(",") catch .' input: '["1","2",[3,4,5]]' expected: | "cannot join: array ([3,4,5])" - name: join function error input args: - '.[] | try join(",") catch .' input: '[null, false, true, 1, "a"]' expected: | "cannot iterate over: null" "cannot iterate over: boolean (false)" "cannot iterate over: boolean (true)" "cannot iterate over: number (1)" "cannot iterate over: string (\"a\")" - name: join function non-string separator args: - '.[] | try join(1) catch .' input: '[[],[1],[1,2]]' expected: | "" "1" "join cannot be applied to: number (1)" - name: ascii_downcase function args: - 'ascii_downcase' input: '"@ABC XYZ[] `abc xyz{} ☆"' expected: | "@abc xyz[] `abc xyz{} ☆" - name: ascii_upcase function args: - 'ascii_upcase' input: '"@ABC XYZ[] `abc xyz{} ☆"' expected: | "@ABC XYZ[] `ABC XYZ{} ☆" - name: walk function args: - -c - 'walk(if type == "boolean" then not else . + . end) | .[]' input: '[1, ["a", {"b": []}], {"c": [null, false, true]}]' expected: | 2 ["aa",{"b":[]},"aa",{"b":[]}] {"c":[null,true,false,null,true,false]} 2 ["aa",{"b":[]},"aa",{"b":[]}] {"c":[null,true,false,null,true,false]} - name: transpose function args: - -c - 'transpose' input: | [] [[1,2,3,[]],[4,"foo",{}],[5,6]] expected: | [] [[1,4,5],[2,"foo",6],[3,{},null],[[],null,null]] - name: function not defined args: - 'abc' input: '{}' error: | function not defined: abc/0 exit_code: 3 - name: variable not defined args: - '$abc' input: '{}' error: | variable not defined: $abc exit_code: 3 - name: argument count error args: - 'map(.;.)' input: '{}' error: | function not defined: map/2 exit_code: 3 - name: function declaration with an argument args: - 'def f(g): g | g; f(..)' input: '[0, 1]' expected: | [ 0, 1 ] 0 1 0 1 - name: function nested declaration args: - -c - 'def f: def g: .; g; f' input: '[0, 1]' expected: | [0,1] - name: function declaration with call of another declared function args: - -c - 'def f: .,.; def g: [f,f]; f,g' input: '[0, 1]' expected: | [0,1] [0,1] [[0,1],[0,1],[0,1],[0,1]] - name: function declaration and recursive call args: - 'def f(g): if type == "array" then map(f(g * 2)) else g end; f(.)' input: '[[[1,2]]]' expected: | [ [ [ 8, 16 ] ] ] - name: function declaration and recursive call args: - 'def f(g): if . < 1000 then .,(g|f(g|g)) else g end; f(.*2)' input: '1' expected: | 1 2 8 128 2147483648 - name: function declaration and recursive call args: - 'def f: . as $x | $x, ($x + 1 | f); last(limit(100000; f))' input: '0' expected: | 99999 - name: function declaration and recursive call args: - 'def f: . as $x | $x, (if $x < 3 then $x + 1 | f else empty end), $x; f' input: '0' expected: | 0 1 2 3 3 2 1 0 - name: function declaration and recursive call args: - 'def f: ., if . < 2 then . + (1,2) | f else empty end; f' input: '0' expected: | 0 1 2 3 2 - name: function declaration and recursive call args: - '100000 as $x | def f: if . == $x then . else . + 1 | f end; f' input: '0' expected: | 100000 - name: function declaration and recursive call args: - 'def f: if .a then .a | f end; {}, {a:{}} | f | .,0' input: '0' expected: | {} 0 {} 0 - name: function declaration with duplicate function names args: - 'def f(g): def f: def g: 3; g * 2; g * f; def f: . * 5; f(f)' input: '1' expected: | 30 - name: function declaration with duplicate function names args: - '(def f(g): def g: 1; g; f(2)), (def g: 3; def f(g): g; f(4))' input: '0' expected: | 1 4 - name: function declaration with duplicate argument names args: - 'def f(g;g): g; f(1;2)' input: 'null' expected: | 2 - name: function declaration with duplicate variable names args: - 'def f($g;$g): $g; f(1;2,3)' input: 'null' expected: | 2 3 - name: function declaration with argument name conflict args: - 'def update: .[1:]; [range(3)] | while(length>0; update)' input: 'null' expected: | [ 0, 1, 2 ] [ 1, 2 ] [ 2 ] - name: function declaration with argument name conflict args: - 'def f($keys): with_entries(.key *= $keys); {x: 1} | f(3)' input: 'null' expected: | { "xxx": 1 } - name: function declaration with mixed argument types args: - -c - 'def f0( a; b; c): a*100 + b*10 + c; def f1( a;$b; c): a*100 + $b*10 + c; def f2($a; b;$c): $a*100 + b*10 + $c; def f3($a;$b;$c): $a*100 + $b*10 + $c; [f0(.[];.[];.[])], [f1(.[];.[];.[])], [f2(.[];.[];.[])], [f3(.[];.[];.[])]' input: '[1,2]' expected: | [111,211,121,221,112,212,122,222] [111,211,112,212,121,221,122,222] [111,121,112,122,211,221,212,222] [111,112,121,122,211,212,221,222] - name: function declaration with function reference to value argument args: - -c - 'def f($x; $y): [$x, x, $y, y]; f(1,2; 3,4)' input: 'null' expected: | [1,1,2,3,3,4] [1,1,2,4,3,4] [2,1,2,3,3,4] [2,1,2,4,3,4] - name: function declaration inside query args: - -c - '[0, def f: 1, def g: 2; g, 3 as $x | def h: $x + 1; h * g; f, f + 1, 5]' input: 'null' expected: | [0,1,2,8,2,3,9,5] - name: argument count error for custom function args: - 'def f(g): g | g; f' input: '{}' error: | function not defined: f/0 exit_code: 3 - name: function declaration name error args: - -c - 'def $f: .; $f' input: 'null' error: | invalid query: def $f: .; $f def $f: .; $f ^ unexpected token "$f" exit_code: 3 - name: add, subtract, multiply, divide, modulo numbers args: - -c - '[.[] + .[]], [.[] - .[]], [.[] * .[]], [.[] / .[]], [.[] % .[]]' input: '[-1, 5, 16]' expected: | [-2,4,15,4,10,21,15,21,32] [0,6,17,-6,0,11,-17,-11,0] [1,-5,-16,-5,25,80,-16,80,256] [1,-5,-16,-0.2,1,3.2,-0.0625,0.3125,1] [0,0,0,-1,0,1,-1,5,0] - name: add, subtract, multiply, divide, modulo large numbers args: - -c - '[.[] + .[]], [.[] - .[]], [.[] * .[]], [.[] / .[]], [.[] % .[]]' input: '[-1, 47, 4722366482869645213696, 557702036893939360447070208]' expected: | [-2,46,4722366482869645213695,557702036893939360447070207,46,94,4722366482869645213743,557702036893939360447070255,4722366482869645213695,4722366482869645213743,9444732965739290427392,557706759260422230092283904,557702036893939360447070207,557702036893939360447070255,557706759260422230092283904,1115404073787878720894140416] [0,48,4722366482869645213697,557702036893939360447070209,-48,0,4722366482869645213649,557702036893939360447070161,-4722366482869645213697,-4722366482869645213649,0,557697314527456490801856512,-557702036893939360447070209,-557702036893939360447070161,-557697314527456490801856512,0] [1,-47,-4722366482869645213696,-557702036893939360447070208,-47,2209,221951224694873325043712,26211995734015149941012299776,-4722366482869645213696,221951224694873325043712,22300745198530623141535718272648361505980416,2633673406456069531769085256563226197133275168768,-557702036893939360447070208,26211995734015149941012299776,2633673406456069531769085256563226197133275168768,311031561955648899562865430629603887429045530881163264] [1,-47,-4722366482869645213696,-557702036893939360447070208,-0.02127659574468085,1,100475882614247770000,1.1866000784977433e+25,-2.117582368135751e-22,9.952637130238029e-21,1,118098,-1.7930721672981344e-27,8.427439186301232e-26,0.000008467543904215143,1] [0,0,0,0,-1,0,8,37,-1,47,0,0,-1,47,4722366482869645213696,0] - name: add, subtract, multiply, divide, modulo operators precedence args: - -c - '[.[] + .[] % .[] * .[] - .[] / .[] % .[] + .[]] | while(length>0; .[32:])[:32]' input: '[3, 7]' expected: | [5,9,8,12,14,18,5,9,5,9,12,16,26,30,5,9,4,8,7,11,13,17,4,8,4,8,11,15,25,29,4,8] [6,10,9,13,15,19,6,10,6,10,13,17,27,31,6,10,5,9,8,12,14,18,5,9,5,9,12,16,26,30,5,9] [5,9,8,12,14,18,5,9,5,9,12,16,26,30,5,9,4,8,7,11,13,17,4,8,4,8,11,15,25,29,4,8] [6,10,9,13,15,19,6,10,6,10,13,17,27,31,6,10,5,9,8,12,14,18,5,9,5,9,12,16,26,30,5,9] [9,13,12,16,18,22,9,13,9,13,16,20,30,34,9,13,8,12,11,15,17,21,8,12,8,12,15,19,29,33,8,12] [10,14,13,17,19,23,10,14,10,14,17,21,31,35,10,14,9,13,12,16,18,22,9,13,9,13,16,20,30,34,9,13] [9,13,12,16,18,22,9,13,9,13,16,20,30,34,9,13,8,12,11,15,17,21,8,12,8,12,15,19,29,33,8,12] [10,14,13,17,19,23,10,14,10,14,17,21,31,35,10,14,9,13,12,16,18,22,9,13,9,13,16,20,30,34,9,13] - name: integer division precision args: - '72 / (1, -2, 3, -4, 6, -8) * 100000000000000000000' input: '0' expected: | 7200000000000000000000 -3600000000000000000000 2400000000000000000000 -1800000000000000000000 1200000000000000000000 -900000000000000000000 - name: optional and division operator args: - '1?/1' input: '0' expected: | 1 - name: optional and division operator parse error args: - '.?/' input: '0' error: | invalid query: .?/ .?/ ^ unexpected token exit_code: 3 - name: factorial calculation args: - -n - -r - 'def fact($n): if $n < 1 then 1 else $n * fact($n - 1) end; (range(30;51) | "\(.)! = \(fact(.))"), fact(50) / fact(30), fact(50) % (fact(30) + 1), fact(100), fact(1000)*1.0, 1/fact(1000), -1/fact(1000), 1.0/fact(1000)' expected: | 30! = 265252859812191058636308480000000 31! = 8222838654177922817725562880000000 32! = 263130836933693530167218012160000000 33! = 8683317618811886495518194401280000000 34! = 295232799039604140847618609643520000000 35! = 10333147966386144929666651337523200000000 36! = 371993326789901217467999448150835200000000 37! = 13763753091226345046315979581580902400000000 38! = 523022617466601111760007224100074291200000000 39! = 20397882081197443358640281739902897356800000000 40! = 815915283247897734345611269596115894272000000000 41! = 33452526613163807108170062053440751665152000000000 42! = 1405006117752879898543142606244511569936384000000000 43! = 60415263063373835637355132068513997507264512000000000 44! = 2658271574788448768043625811014615890319638528000000000 45! = 119622220865480194561963161495657715064383733760000000000 46! = 5502622159812088949850305428800254892961651752960000000000 47! = 258623241511168180642964355153611979969197632389120000000000 48! = 12413915592536072670862289047373375038521486354677760000000000 49! = 608281864034267560872252163321295376887552831379210240000000000 50! = 30414093201713378043612608166064768844377641568960512000000000000 114660755112113373922453094400000 150592104700077684713855385600001 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000 1.7976931348623157e+308 0 -0 0 - name: negative large numbers args: - -n - 'def f($n): if $n < 1 then 1 else (-$n) * (-f($n - 1)) end; -f(range(30;40))' expected: | -265252859812191058636308480000000 -8222838654177922817725562880000000 -263130836933693530167218012160000000 -8683317618811886495518194401280000000 -295232799039604140847618609643520000000 -10333147966386144929666651337523200000000 -371993326789901217467999448150835200000000 -13763753091226345046315979581580902400000000 -523022617466601111760007224100074291200000000 -20397882081197443358640281739902897356800000000 - name: large number literal args: - -n - | 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, -100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0, -100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0, 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, -10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0, -10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0 expected: | 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1e+308 -1e+308 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 1.7976931348623157e+308 -1.7976931348623157e+308 - name: zero division args: - '.[] / .[]' input: '[1, 0]' expected: | 1 0 error: | cannot divide number (1) by: number (0) - name: zero modulo args: - '.[] % .[]' input: '[1, 0]' expected: | 0 0 error: | cannot modulo number (1) by: number (0) - name: modulo with near 0.0 args: - '.[] % .[]' input: '[1, 0.9]' expected: | 0 0 error: | cannot modulo number (1) by: number (0.9) - name: zero division and modulo for large number args: - -c - '[try .[] / .[] catch .], [try .[] % .[] catch .]' input: '[1267650600228229401496703205376, 0]' expected: | [1,0,"cannot divide number (12676506002282294014967032 ...) by: number (0)"] [0,0,"cannot modulo number (12676506002282294014967032 ...) by: number (0)"] - name: add strings args: - '.[] + .[] + "c"' input: '["a", "b"]' expected: | "aac" "bac" "abc" "bbc" - name: multiply constant args: - '3 * .[], 2.5 * .[], .054e2 * .[]' input: '[1, 2.5, "abc"]' expected: | 3 7.5 "abcabcabc" 2.5 6.25 "abcabc" 5.4 13.5 "abcabcabcabcabc" - name: multiply strings args: - '[-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 3.7, 10, 9444732965739290427392 / 4722366482869645213696, 1000000000, infinite, -infinite, nan, -1e300, 1e300][] * "abc"' input: 'null' expected: | null null null "abc" "abc" "abc" "abcabcabc" "abcabcabcabcabcabcabcabcabcabc" "abcabc" null null null null null null - name: multiply empty string args: - '7 * ""' input: 'null' expected: | "" - name: multiply objects args: - -c - '.[] * .[]' input: '[{"foo": 10, "bar": {"qux": 20}}, {"foo": 20, "bar": {"baz": "quux"}}]' expected: | {"bar":{"qux":20},"foo":10} {"bar":{"baz":"quux","qux":20},"foo":10} {"bar":{"baz":"quux","qux":20},"foo":20} {"bar":{"baz":"quux"},"foo":20} - name: divide strings args: - -c - '.[] / .[]' input: '["abc", "a", "b"]' expected: | ["",""] ["a"] ["b"] ["","bc"] ["",""] ["b"] ["a","c"] ["a"] ["",""] - name: add and subtract arrays args: - -c - '.[] + .[] - .[]' input: '[[1, 2], [3, 4]]' expected: | [] [3,4] [3,4] [3,4,3,4] [1,2,1,2] [1,2] [1,2] [] - name: ensure arrays are immutable during addition args: - '. as $first | .[:0] + [6, 7, 8] | ., $first' input: '[1, 2, 3, 4, 5]' expected: | [ 6, 7, 8 ] [ 1, 2, 3, 4, 5 ] - name: subtract arrays args: - -c - '[1,2,3,4,1,4722366482869645213696] - [.,3,4722366482869645213696]' input: '1' expected: | [2,4] - name: subtract arrays with large numbers args: - -c - '. as $x | [$x-$x] - [0]' input: '10000000000000' expected: | [] - name: subtract arrays with nan args: - -c - '[nan,{x:nan}] - [nan,{x:nan}]' input: '1' expected: | [null,{"x":null}] - name: add object args: - -c - '.[] + .[]' input: '[{"a":1},{"b":2}]' expected: | {"a":1} {"a":1,"b":2} {"a":1,"b":2} {"b":2} - name: add null args: - '.[] + .[]' input: '[1, 2, null]' expected: | 2 3 1 3 4 2 1 2 null - name: subtract null args: - '.[] - .[]' input: '[1, 2, null]' expected: | 0 1 error: | cannot subtract: null and number (1) - name: subtract object args: - '.[] - .[]' input: '[{"a":1},{"b":2}]' error: | cannot subtract: object ({"a":1}) and object ({"a":1}) - name: comparison operators args: - '[.[] > .[], .[] < .[], .[] >= .[], .[] <= .[], .[] == .[], .[] != .[]] | while(length>0; .[120:]) | .[:120] | map(if . then 1 else 0 end) | join("")' input: '[null, false, true, 0, 1, "", "a", "b", "abc", [], [1], [2], [3, 4], ["a", "b"], [[]], [[], []], [{}], {}, {"a": ""}, {"a":[1]}, {"b":[1]}, {"b": []}, {"b": [[]]}, {"a": 0, "b": 1}]' expected: | "011111111111111111111111001111111111111111111111000111111111111111111111000011111111111111111111000001111111111111111111" "000000111111111111111111000000011111111111111111000000000111111111111111000000010111111111111111000000000011111111111111" "000000000001111111111111000000000000111111111111000000000000011111111111000000000000001111111111000000000000000111111111" "000000000000000011111111000000000000000001111111000000000000000000111111000000000000000000011111000000000000000000001111" "000000000000000000000010000000000000000000001010000000000000000000000000000000000000000000001110000000000000000000000000" "100000000000000000000000110000000000000000000000111000000000000000000000111100000000000000000000111110000000000000000000" "111111000000000000000000111111101000000000000000111111100000000000000000111111111000000000000000111111111100000000000000" "111111111110000000000000111111111111000000000000111111111111100000000000111111111111110000000000111111111111111000000000" "111111111111111100000000111111111111111110000000111111111111111111000000111111111111111111100000111111111111111111110101" "111111111111111111110001111111111111111111111101111111111111111111110000111111111111111111111111011111111111111111111111" "001111111111111111111111000111111111111111111111000011111111111111111111000001111111111111111111000000111111111111111111" "000000010111111111111111000000011111111111111111000000000111111111111111000000000011111111111111000000000001111111111111" "000000000000111111111111000000000000011111111111000000000000001111111111000000000000000111111111000000000000000011111111" "000000000000000001111111000000000000000000111111000000000000000000011111000000000000000000001010000000000000000000001110" "000000000000000000000010000000000000000000001111100000000000000000000000110000000000000000000000111000000000000000000000" "111100000000000000000000111110000000000000000000111111000000000000000000111111100000000000000000111111111000000000000000" "111111101000000000000000111111111100000000000000111111111110000000000000111111111111000000000000111111111111100000000000" "111111111111110000000000111111111111111000000000111111111111111100000000111111111111111110000000111111111111111111000000" "111111111111111111100000111111111111111111110000111111111111111111111101111111111111111111110101111111111111111111111111" "111111111111111111110001100000000000000000000000010000000000000000000000001000000000000000000000000100000000000000000000" "000010000000000000000000000001000000000000000000000000100000000000000000000000010000000000000000000000001000000000000000" "000000000100000000000000000000000010000000000000000000000001000000000000000000000000100000000000000000000000010000000000" "000000000000001000000000000000000000000100000000000000000000000010000000000000000000000001000000000000000000000000100000" "000000000000000000010000000000000000000000001000000000000000000000000100000000000000000000000010000000000000000000000001" "011111111111111111111111101111111111111111111111110111111111111111111111111011111111111111111111111101111111111111111111" "111110111111111111111111111111011111111111111111111111101111111111111111111111110111111111111111111111111011111111111111" "111111111101111111111111111111111110111111111111111111111111011111111111111111111111101111111111111111111111110111111111" "111111111111111011111111111111111111111101111111111111111111111110111111111111111111111111011111111111111111111111101111" "111111111111111111110111111111111111111111111011111111111111111111111101111111111111111111111110" - name: comparison and arithmetic operators args: - '[.[] + .[] > .[] - .[], .[] * .[] < .[] / .[] + .[] / .[], .[] == .[] * .[]] | while(length>0; .[120:]) | .[:120] | map(if . then 1 else 0 end) | join("")' input: '[1, 2, 3]' expected: | "111111111111111111011111111111111111111111111111111111111111111111111111111111111100000000110100000111100100100000000100" "000000110100000100000000100000000100000000110100000111100100111110100110100000110100000111100100110100000110100000110100" "000111100100111110100111110100111100100111100100111110100111100100111100100111100100100000000110100000111100100000000000" "100000000100000000000000000100000000100000000100000000110100000111100100100000000100000000110100000100000000100000000100" "000000110100000111100100111110100100000000110100000110100000100000000110100000110100000100000000110100000111100100000000" "000100000000100000000000000000000000000100000000100000000110100000111100100100000000100000000110100000000000000100000000" "100000000100000000110100000111100100100000000100000000110100000100000000100000000100000000100010001010000000001000000" - name: comparison operators with large numbers args: - '[.[] > .[], .[] < .[], .[] >= .[], .[] <= .[], .[] == .[], .[] != .[]] | while(length>0; .[120:]) | .[:120] | map(if . then 1 else 0 end) | join("")' input: '[-9999999999999999999999999999999999, -4722366482869645213697, -2, 0, 47, 4722366482869645213696, 4722366482869645213697, 99999999999999999999999999999999999]' expected: | "011111110011111100011111000011110000011100000011000000010000000000000000100000001100000011100000111100001111100011111100" "111111101111111101111111001111110001111100001111000001110000001100000001100000001100000011100000111100001111100011111100" "111111101111111110000000010000000010000000010000000010000000010000000010000000010111111110111111110111111110111111110111" "111110111111110111111110" - name: comparison operators does not associate args: - '. == . == .' input: '1' error: | invalid query: . == . == . . == . == . ^ unexpected token "==" exit_code: 3 - name: logical operators args: - '[.[] > .[] and .[] or .[] < .[]] | while(length>0; .[120:]) | .[:120] | map(if . then 1 else 0 end) | join("")' input: '[null, false, true, 0, "", [], {}]' expected: | "000000010000001100000111000011110001111100111111000000001000000110000011100001111000111110011111100000000100000011000001" "110000111100011111001111110111110000000100000011000001110000111100011111001111110000000010000001100000111000011110001111" "100111111011111000000010000001100000111000011110001111100111111000000001000000110000011100001111000111110011111101111100" "000001000000110000011100001111000111110011111100000000100000011000001110000111100011111001111110111110000000100000011000" "001110000111100011111001111110000000010000001100000111000011110001111100111111011111000000010000001100000111000011110001" "111100111111000000001000000110000011100001111000111110011111101111100000001000000110000011100001111000111110011111100000" "000100000011000001110000111100011111001111110000000010000001100000111000011110001111100111111000000001000000110000011100" "001111000111110011111101111100000001000000110000011100001111000111110011111100000000100000011000001110000111100011111001" "111110111110000000100000011000001110000111100011111001111110000000010000001100000111000011110001111100111111011111000000" "010000001100000111000011110001111100111111000000001000000110000011100001111000111110011111101111100000001000000110000011" "100001111000111110011111100000000100000011000001110000111100011111001111110111110000000100000011000001110000111100011111" "001111110000000010000001100000111000011110001111100111111000000001000000110000011100001111000111110011111100000000100000" "011000001110000111100011111001111110000000010000001100000111000011110001111100111111011111000000010000001100000111000011" "110001111100111111000000001000000110000011100001111000111110011111101111100000001000000110000011100001111000111110011111" "100000000100000011000001110000111100011111001111110111110000000100000011000001110000111100011111001111110000000010000001" "100000111000011110001111100111111011111000000010000001100000111000011110001111100111111000000001000000110000011100001111" "000111110011111100000000100000011000001110000111100011111001111110000000010000001100000111000011110001111100111111000000" "001000000110000011100001111000111110011111100000000100000011000001110000111100011111001111110111110000000100000011000001" "110000111100011111001111110000000010000001100000111000011110001111100111111011111000000010000001100000111000011110001111" "100111111000000001000000110000011100001111000111110011111101111100000001000000110000011100001111000111110011111100000000" "100000011000001110000111100011111001111110000000010000001100000111000011110001111100111111000000001000000110000011100001" "111000111110011111100000000100000011000001110000111100011111001111110000000010000001100000111000011110001111100111111000" "000001000000110000011100001111000111110011111101111100000001000000110000011100001111000111110011111100000000100000011000" "001110000111100011111001111110111110000000100000011000001110000111100011111001111110000000010000001100000111000011110001" "111100111111000000001000000110000011100001111000111110011111100000000100000011000001110000111100011111001111110000000010" "000001100000111000011110001111100111111000000001000000110000011100001111000111110011111100000000100000011000001110000111" "100011111001111110000000010000001100000111000011110001111100111111011111000000010000001100000111000011110001111100111111" "000000001000000110000011100001111000111110011111100000000100000011000001110000111100011111001111110000000010000001100000" "111000011110001111100111111000000001000000110000011100001111000111110011111100000000100000011000001110000111100011111001" "1111100000000100000011000001110000111100011111001111110" - name: alternative operator args: - -c - '[.a // .b, .b // null // .a, .c[] // .d[], .d[] // .c, .c // .a, .e.a // .c[]]' input: '{ "a": 5, "b": false, "c": [null,false], "d": [false,3], "e": null }' expected: | [5,5,false,3,3,[null,false],null,false] - name: explode function args: - -c - 'explode' input: | "" "foo bar" "12345" "\n\t" expected: | [] [102,111,111,32,98,97,114] [65297,65298,65299,65300,65301] [10,9] - name: implode function args: - 'implode' input: | [] [102,111,111,32,98,97,114] [65297,65298,65299,65300,65301] [10,9] expected: | "" "foo bar" "12345" "\n\t" - name: implode function error args: - 'try implode catch .' input: | [0] [-1] [1114112] [[]] [{}] expected: | "\u0000" "implode cannot be applied to: array ([-1])" "implode cannot be applied to: array ([1114112])" "implode cannot be applied to: array ([[]])" "implode cannot be applied to: array ([{}])" - name: split/1 function args: - 'split(", ","") | join("/")' input: | "a,b, c, d, e,f" ", a,b, c, d, e,f, " expected: | "a,b/c/d/e,f" "a/,/b/,/ /c/,/ /d/,/ /e/,/f" "/a,b/c/d/e,f/" ",/ /a/,/b/,/ /c/,/ /d/,/ /e/,/f/,/ " - name: condition args: - '.[] | if . then . else empty end' input: '[false, true, 0, 1]' expected: | true 0 1 - name: condition with elif args: - '.[] | if . then . elif [] then . else empty end' input: '[false, true, 0, 1]' expected: | false true 0 1 - name: condition without else clause args: - 'map(if . > 2 then . / 2 end)' input: '[1, 2, 3, 4, 5]' expected: | [ 1, 2, 1.5, 2, 2.5 ] - name: condition precedence args: - '. + if 1 then 2 else 3 end + 4' input: '1' expected: | 7 - name: condition with optional operator args: - 'if . then tonumber end ?' input: '1' expected: | 1 - name: condition with suffix args: - 'if . then 1 end []' input: '1' error: | invalid query: if . then 1 end [] if . then 1 end [] ^ unexpected token "[" exit_code: 3 - name: not function args: - 'not' input: | true false expected: | false true - name: iterator in condition args: - 'if .. then [] else {} end' input: '{ "foo": false }' expected: | [] {} - name: try args: - 'map(try utf8bytelength)' input: '[[], {}, [1,2], "hello", 55, true, false]' expected: | [ 5 ] - name: try catch args: - 'map(try utf8bytelength catch .)' input: '[[], {}, [1,2], "hello", 55, true, false]' expected: | [ "utf8bytelength cannot be applied to: array ([])", "utf8bytelength cannot be applied to: object ({})", "utf8bytelength cannot be applied to: array ([1,2])", 5, "utf8bytelength cannot be applied to: number (55)", "utf8bytelength cannot be applied to: boolean (true)", "utf8bytelength cannot be applied to: boolean (false)" ] - name: try catch with function declaration args: - 'def f: fromjson; try {} | f catch ., 1' input: '0' expected: | "fromjson cannot be applied to: object ({})" 1 - name: try precedence args: - '1 + try 2 catch 3 + 4' input: '1' expected: | 7 - name: reduce iterator args: - '[range(.)] | reduce .[] as $i (0; . + $i)' input: '10' expected: | 45 - name: reduce arrays args: - 'reduce .[] as [$i,$j] (0; . + $i * $j)' input: '[[1,2,10], [3,4,10]]' expected: | 14 - name: reduce objects args: - 'reduce .[] as {$i, j:$j} (0; . + $i - $j)' input: '[{"i":2, "j":1}, {"i":5, "j":3}, {"i":6, "j":4}]' expected: | 5 - name: reduce null args: - 'reduce . as $n (.; .)' input: 'null' expected: | null - name: reduce precedence args: - '. + reduce . as $n (. + [2]; [.]) + .' input: '[1]' expected: | [ 1, [ 1, 2 ], 1 ] - name: reduce variable scope args: - '. as $x | reduce .[] as $x (.; . + .) | [., $x]' input: '[1]' expected: | [ [ 1, 1 ], [ 1 ] ] - name: foreach iterator args: - 'foreach range(5) as $item (0; $item)' input: 'null' expected: | 0 1 2 3 4 - name: foreach arrays args: - 'foreach .[] as [$i, $j] (0; . + $i - $j)' input: '[[2,1], [5,3], [6,4]]' expected: | 1 3 5 - name: foreach objects args: - 'foreach .[] as {a:$a} (0; . + $a; -.)' input: '[{"a":1}, {"b":2}, {"a":3, "b":4}]' expected: | -1 -1 -4 - name: foreach with iterator in update args: - 'foreach .[] as $i (1; ., . + $i, range(.))' input: '[1, 2, 3]' expected: | 1 2 0 0 2 2 5 0 1 - name: foreach variable scope args: - '. as $x | foreach .[] as $x (.; . + .) | [., $x]' input: '[1]' expected: | [ [ 1, 1 ], [ 1 ] ] - name: label and break syntax args: - '[(label $x | .[] | if . > 2 then break $x else . end), 0]' input: '[1,2,3,2,1]' expected: | [ 1, 2, 0 ] - name: label and break in foreach syntax args: - '[range(.)] | label $x | foreach .[] as $i (0; if . > 5 then break $x else . + $i end)' input: '10' expected: | 0 1 3 6 - name: label and break in function argument args: - 'range(.) | label $out | limit(3; 1, if . > 1 then break $out end, 2)' input: '3' expected: | 1 0 2 1 1 2 1 - name: label and break in function argument with foreach args: - 'label $out | last(foreach range(0; 10) as $i ({x: 1}; if .x > 10 then break $out else .x += 1 end))' input: '0' expected: | { "x": 11 } - name: label in recursive call args: - 'def f: label $x | if . < 5 then ., (. + 1 | f, break $x, .) else . end; f' input: '0' expected: | 0 1 2 3 4 5 - name: duplicate label names args: - 'label $x | (label $x | break $x), .' input: '1' expected: | 1 - name: invalid label name args: - 'label foo | .' input: '1' error: | invalid query: label foo | . label foo | . ^ unexpected token "foo" exit_code: 3 - name: label not defined error for break without label args: - 'break $test' input: '1' error: | label not defined: $test exit_code: 3 - name: label not defined error for break with label in parenthesis args: - '(label $x | 1) | break $x' input: '1' error: | label not defined: $x exit_code: 3 - name: invalid break name args: - 'label $x | break x' input: '1' error: | invalid query: label $x | break x label $x | break x ^ unexpected token "x" exit_code: 3 - name: invalid break syntax args: - 'label $x | break' input: '0' error: | invalid query: label $x | break label $x | break ^ unexpected token exit_code: 3 - name: variable and label name conflict args: - '. as $x | (label $x | break $x), $x' input: '0' expected: | 0 - name: string interpolation args: - -c - '["\(.foo[])_\(.bar[])_\(.baz[])"]' input: '{ "foo": [0, 1], "bar": ["x", "y"], "baz": [{"a": 1}, [1], null] }' expected: | ["0_x_{\"a\":1}","1_x_{\"a\":1}","0_y_{\"a\":1}","1_y_{\"a\":1}","0_x_[1]","1_x_[1]","0_y_[1]","1_y_[1]","0_x_null","1_x_null","0_y_null","1_y_null"] - name: string interpolation in object args: - -c - '[{a:"\(.foo[])"}, {"\(.foo[])": 1}, {"\(.bar[])"}]' input: '{ "foo": [0, 1], "bar": ["x", "y"], "x": 10, "y": 20 }' expected: | [{"a":"0"},{"a":"1"},{"0":1},{"1":1},{"x":10},{"y":20}] - name: string interpolation nested args: - -c - '" \("(") \("x" + "\(1 + 2)") \("\\") \("\("\\")") \(")") "' input: '0' expected: | " ( x3 \\ \\ ) " - name: string interpolation nested deeply args: - -c - '"\("\("\("\("\("\("\("\("\("\("\("\("\("\("\("\("\("\("\("\("\("\("\("\(1+2)")")")")")")")")")")")")")")")")")")")")")")")"' input: '0' expected: | "3" - name: string interpolation multiple lines args: - -c - | "\( # " "\( "\(2 * (4 # " +# 6))" )" # " # " + (def f: "\("\("(" + "\\\((1 + 2) * 3)\"" + ")")")"; f) # " # " # " )" input: '0' expected: | "20(\\9\")" - name: string interpolation parse error args: - -c - '"\(1 +)"' input: '0' error: | invalid query: "\(1 +)" "\(1 +)" ^ unexpected token ")" exit_code: 3 - name: tojson function args: - 'tojson' input: | 1 null false true "foo" ["foo"] {"a": [1,2,3]} expected: | "1" "null" "false" "true" "\"foo\"" "[\"foo\"]" "{\"a\":[1,2,3]}" - name: fromjson function args: - 'fromjson' input: | "1" "null" "false" "true" "\"foo\"" "[\"foo\"]" "{\"a\":[1,2,3]}" expected: | 1 null false true "foo" [ "foo" ] { "a": [ 1, 2, 3 ] } - name: variable definition args: - '.foo.bar as $foo | .foo.bar as $bar | {$foo} + {bar: ($bar / 2)} * { baz: 20 }' input: '{ "foo": { "bar": 10 } }' expected: | { "bar": 5, "baz": 20, "foo": 10 } - name: variable definition args: - '.ys as $ys | .xs[] | {a, b: $ys[.b]}' input: '{"xs": [{"a": "a0", "b": "b0"}, {"a": "a1", "b": "b1"}], "ys": {"b0": "bb0", "b1": "bb1"}}' expected: | { "a": "a0", "b": "bb0" } { "a": "a1", "b": "bb1" } - name: variable name error args: - '.foo as foo | {$bar}' input: 'null' error: | invalid query: .foo as foo | {$bar} .foo as foo | {$bar} ^ unexpected token "foo" exit_code: 3 - name: variable name not found error args: - '.foo as $foo | {$bar}' input: '{ "foo": 10 }' error: | variable not defined: $bar exit_code: 3 - name: binding an array args: - '. as [$i, $j] | [$j, $i]' input: | [] [10] [20, 30] expected: | [ null, null ] [ null, 10 ] [ 30, 20 ] - name: binding an array deeply args: - '. as [$i, [$j], [[$k]]] | [$i, $j, $k]' input: | [] [10] [20, [30], [[40]]] expected: | [ null, null, null ] [ 10, null, null ] [ 20, 30, 40 ] - name: binding an array error args: - '. as [$i, $j] | [$j, $i]' input: '{ "foo": 10 }' error: | expected an array but got: object ({"foo":10}) - name: binding an object args: - '. as {$foo, bar: $bar} | {$bar, $foo}' input: | {} {"foo": 10} {"foo": 20, "bar": 30} expected: | { "bar": null, "foo": null } { "bar": null, "foo": 10 } { "bar": 30, "foo": 20 } - name: binding an object deeply args: - '. as {$foo, bar: {"baz": {$qux}, "": $quy}} | {$foo, $qux, $quy}' input: | {} {"foo": 10} {"foo": 20, "bar": { "baz": { "qux": 30 }, "": -1 } } expected: | { "foo": null, "qux": null, "quy": null } { "foo": 10, "qux": null, "quy": null } { "foo": 20, "qux": 30, "quy": -1 } - name: binding an object with expression args: - '. as {as: $foo, ("e"+"x"+"p"): $bar, (.foo): $baz, "\(.qux)": $qux} | [$foo, $bar, $baz, $qux]' input: '{"as": 1, "exp": 2, "foo": "bar", "bar": 3, "qux": "quux", "quux": 4}' expected: | [ 1, 2, 3, 4 ] - name: binding an object error args: - '. as {$i} | [$i]' input: '[10]' error: | expected an object but got: array ([10]) - name: binding object and array deeply args: - '. as {$a, $b: [$c, $d]} | [$a, $b, $c, $d]' input: '{"a":1, "b": [2, {"d": 3}]}' expected: | [ 1, [ 2, { "d": 3 } ], 2, { "d": 3 } ] - name: binding variable scope in parenthesis args: - '. as $x | (.foo as $x | [$x]) | .+[$x]' input: '{"foo": 42}' expected: | [ 42, { "foo": 42 } ] - name: binding variable scope in array args: - '. as $x | [.foo as $x | $x] | $x' input: '{"foo": 42}' expected: | { "foo": 42 } - name: binding variable scope in object args: - '. as $x | {bar: (.foo as $x | $x)} | $x' input: '{"foo": "bar"}' expected: | { "foo": "bar" } - name: binding variable scope in object key args: - '. as $x | {(.foo as $x | $x): $x}' input: '{"foo": "bar"}' expected: | { "bar": { "foo": "bar" } } - name: binding variable scope in condition args: - -c - '. as $x | if (.foo,.bar) as $x | $x then 1 as $x | $x else 2 as $x | $x end | [., $x]' input: '{"foo": "bar"}' expected: | [1,{"foo":"bar"}] [2,{"foo":"bar"}] - name: binding variable scope in try catch args: - '. as $x | try .foo as $x | $x | error catch $x' input: '{"foo": 42}' expected: | { "foo": 42 } - name: binding variable scope in reduce args: - '[range(.)] | 3 as $x | reduce .[] as $i (.[5] as $x | $x; . + $x + $i)' input: '10' expected: | 80 - name: binding variable scope in foreach args: - -c - '[2 as $x | foreach range(.) as $item (. as $x | $x; . + $item * $x; . + $x)]' input: '5' expected: | [7,9,13,19,27] - name: binding variable scope error in parenthesis args: - '(. as $i | $i) | $i' input: '0' error: | variable not defined: $i exit_code: 3 - name: binding variable scope error in array args: - '[. as $i | $i] | $i' input: '0' error: | variable not defined: $i exit_code: 3 - name: binding variable scope error in object args: - '{x: (. as $i | $i)} | $i' input: '0' error: | variable not defined: $i exit_code: 3 - name: binding variable scope error in object key args: - '{(. as $i | $i): $i}' input: '"x"' error: | variable not defined: $i exit_code: 3 - name: function scope in parenthesis args: - 'def f: 1; . | (def f: 2; f) | f, .' input: '0' expected: | 1 2 - name: function scope in array args: - -c - 'def f: 1; . | [def f: 2; f] | f, .' input: '0' expected: | 1 [2] - name: function scope in object args: - -c - 'def f: 1; . | {bar: (def f: .foo; f)} | f, .' input: '{"foo": "bar"}' expected: | 1 {"bar":"bar"} - name: function scope in object key args: - 'def f: 1; . | {(def f: "foo"; f): f}' input: '0' expected: | { "foo": 1 } - name: function scope error in parenthesis args: - '(def f: 1; f) | f' input: '0' error: | function not defined: f exit_code: 3 - name: function scope error in array args: - '[def f: 1; f] | f' input: '0' error: | function not defined: f exit_code: 3 - name: function scope error in object args: - '{x: (def f: 1; f)} | f' input: '0' error: | function not defined: f exit_code: 3 - name: function scope error in object key args: - '{(def f: 1; f): f}' input: '"x"' error: | function not defined: f exit_code: 3 - name: first/0, last/0 functions args: - -c - '[first, last]' input: '[1,2,3,4,5]' expected: | [1,5] - name: first/1, last/1, nth functions args: - -c - '[first(.[]), last(.[]), nth(0), nth(3), nth(0;.[]), nth(3;.[]), nth(5;.[])]' input: '[1,2,3,4,5]' expected: | [1,5,1,4,1,4] - name: first/1 function with optional operator args: - 'first(.), first(.?), first(.,.), first(.?,.), first(.,.?), first(.?,.?)' input: 'null' expected: | null null null null null null - name: first/1 function in first/1 argument args: - 'first((null, null) | first(null)), isempty(first(null))' input: 'null' expected: | null false - name: all/2 function in first/1 argument args: - 'first(range(1; 200) | . as $x | select(all(range(1; 7); $x % . == 0)))' input: 'null' expected: | 60 - name: limit/2 function args: - -c - '[limit(0,3,13; range(10000000000000))]' input: 'null' expected: | [0,1,2,0,1,2,3,4,5,6,7,8,9,10,11,12] - name: all/0, any/0 functions args: - -c - '[all, any]' input: | [] [false, false] [false, true] [true, true] expected: | [true,false] [false,false] [false,true] [true,true] - name: all/1, any/1 functions args: - -c - '[all(not), any(not)]' input: | [] [false, false] [false, true] [true, true] expected: | [true,false] [true,true] [false,true] [false,false] - name: all/2, any/2 functions args: - -c - '[all(range(.); not), any(range(.); not)]' input: '0 1' expected: | [true,false] [false,false] - name: isempty function args: - -c - '[isempty(empty), isempty(range(10000000000000)), isempty(1,error("foo"))]' input: 'null' expected: | [true,false,false] - name: trigonometric functions args: - -c - 'map(sin),map(cos),map(tan) | map(. * 1000000000 | floor / 1000000000)' input: '[0, 1, 2]' expected: | [0,0.841470984,0.909297426] [1,0.540302305,-0.416146837] [0,1.557407724,-2.185039864] - name: inverse trigonometric functions args: - -c - 'map(asin),map(acos),map(atan) | map(. * 1000000000 | floor / 1000000000)' input: '[0, 1, 2]' expected: | [0,1.570796326,null] [1.570796326,0,null] [0,0.785398163,1.107148717] - name: hyperbolic functions args: - -c - 'map(sinh),map(cosh),map(tanh) | map(. * 1000000000 | floor / 1000000000)' input: '[0, 1, 2]' expected: | [0,1.175201193,3.626860407] [1,1.543080634,3.762195691] [0,0.761594155,0.96402758] - name: inverse hyperbolic functions args: - -c - 'map(asinh),map(acosh),map(atanh) | map(. * 1000000000 | floor / 1000000000)' input: '[0, 1, 2]' expected: | [0,0.881373587,1.443635475] [null,0,1.316957896] [0,1.7976931348623157e+308,null] - name: floor, round, nearbyint, rint, ceil, trunc, fabs, sqrt and cbrt functions args: - -c - 'range(.) | sin | (fabs,sqrt,cbrt,sin) * 100000 | [(floor,round,nearbyint,rint,ceil,trunc) / 100000]' input: '5' expected: | [0,0,0,0,0,0] [0,0,0,0,0,0] [0,0,0,0,0,0] [0,0,0,0,0,0] [0.84147,0.84147,0.84147,0.84147,0.84148,0.84147] [0.91731,0.91732,0.91732,0.91732,0.91732,0.91731] [0.94408,0.94409,0.94409,0.94409,0.94409,0.94408] [0.74562,0.74562,0.74562,0.74562,0.74563,0.74562] [0.90929,0.9093,0.9093,0.9093,0.9093,0.90929] [0.95357,0.95357,0.95357,0.95357,0.95358,0.95357] [0.9688,0.9688,0.9688,0.9688,0.96881,0.9688] [0.78907,0.78907,0.78907,0.78907,0.78908,0.78907] [0.14112,0.14112,0.14112,0.14112,0.14113,0.14112] [0.37565,0.37566,0.37566,0.37566,0.37566,0.37565] [0.52063,0.52063,0.52063,0.52063,0.52064,0.52063] [0.14065,0.14065,0.14065,0.14065,0.14066,0.14065] [0.7568,0.7568,0.7568,0.7568,0.75681,0.7568] [null,null,null,null,null,null] [-0.9113,-0.9113,-0.9113,-0.9113,-0.91129,-0.91129] [-0.68661,-0.6866,-0.6866,-0.6866,-0.6866,-0.6866] - name: significand function args: - -c - '(range(10)|tan),0/0 | significand | . * 100000 | floor / 100000' input: '0' expected: | 0 1.5574 -1.09252 -1.14038 1.15782 -1.69026 -1.16403 1.74289 -1.69993 -1.80927 null - name: exp, exp10, exp2, expm1, pow10 functions args: - -c - '[range(.)] | map(exp),map(exp10),map(exp2),map(expm1),map(pow10) | map(. * 100000 | floor / 100000)' input: '10' expected: | [1,2.71828,7.38905,20.08553,54.59815,148.41315,403.42879,1096.63315,2980.95798,8103.08392] [1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000] [1,2,4,8,16,32,64,128,256,512] [0,1.71828,6.38905,19.08553,53.59815,147.41315,402.42879,1095.63315,2979.95798,8102.08392] [1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000] - name: frexp, modf function args: - -c - '[range(.) | tan] | map(frexp),map(modf) | map(map(. * 100000 | floor / 100000))' input: '10' expected: | [[0,0],[0.7787,1],[-0.54626,2],[-0.57019,-2],[0.57891,1],[-0.84513,2],[-0.58202,-1],[0.87144,0],[-0.84997,3],[-0.90464,-1]] [[0,0],[0.5574,1],[-0.18504,-2],[-0.14255,-0],[0.15782,1],[-0.38052,-3],[-0.29101,-0],[0.87144,0],[-0.79972,-6],[-0.45232,-0]] - name: log, log10, log1p, log2, logb functions args: - -c - '[range(.) | tan] | map(log),map(log10),map(log1p),map(log2),map(logb) | map(. * 100000 | floor / 100000)' input: '10' expected: | [-1.7976931348623157e+308,0.44302,null,null,0.14654,null,null,-0.1376,null,null] [-1.7976931348623157e+308,0.1924,null,null,0.06364,null,null,-0.05976,null,null] [0,0.93899,null,-0.15379,0.76909,null,-0.34391,0.62671,null,-0.60206] [-1.7976931348623157e+308,0.63914,null,null,0.21141,null,null,-0.19852,null,null] [-1.7976931348623157e+308,0,1,-3,0,1,-2,-1,2,-2] - name: gamma, tgamma, lgamma functions args: - -c - '[range(.) | tan] | map(gamma),map(tgamma),map(lgamma) | map(. * 100000 | floor / 100000)' input: '10' expected: | [1.7976931348623157e+308,0.88943,-2.39565,-7.75522,0.93049,0.34209,-4.41241,1.09278,-0.00159,-3.58744] [1.7976931348623157e+308,0.88943,-2.39565,-7.75522,0.93049,0.34209,-4.41241,1.09278,-0.00159,-3.58744] [1.7976931348623157e+308,-0.11718,0.87365,2.04836,-0.07204,-1.07268,1.48441,0.08872,-6.44927,1.27743] - name: erf, erfc, j0, j1, y0, y1 functions args: - -c - '[range(.) | tan] | map(erf),map(erfc),map(j0),map(j1),map(y0),map(y1) | map(. * 100000 | floor / 100000)' input: '10' expected: | [0,0.97237,-0.998,-0.15977,0.89845,-1,-0.31933,0.7822,-1,-0.47762] [1,0.02762,1.99799,1.15976,0.10154,1.99999,1.31932,0.21779,2,1.47761] [1,0.47958,0.11869,0.99492,0.69191,-0.36073,0.97894,0.81896,0.29307,0.9495] [0,0.56529,-0.55806,-0.0711,0.48717,-0.18735,-0.14397,0.39565,0.0653,-0.22043] [-1.7976931348623157e+308,0.40504,null,null,0.20121,null,null,-0.01971,null,null] [-1.7976931348623157e+308,-0.37491,null,null,-0.653,null,null,-0.90154,null,null] - name: atan2/2, copysign/2, drem/2, fdim/2, fmax/2, fmin/2, fmod/2, hypot/2, jn/2, ldexp/2, nextafter/2, nexttoward/2, remainder/2, scalb/2, scalbln/2, pow/2, yn/2 functions args: - -c - '[range(-.;.)] | [atan2(.[]; .[])], [copysign(.[]; .[])], [drem(.[]; .[])], [fdim(.[]; .[])], [fmax(.[]; .[])], [fmin(.[]; .[])], [fmod(.[]; .[])], [hypot(.[]; .[])], [jn(.[]; .[])], [ldexp(.[]; .[])], [nextafter(.[]; .[])], [nexttoward(.[]; .[])], [remainder(.[]; .[])], [scalb(.[]; .[])], [scalbln(.[]; .[])], [pow(.[]; .[])], [yn(.[]; .[])] | map(. * 10000 | round | . / 10000)' input: '3' expected: | [-2.3562,-2.5536,-2.8198,3.1416,2.8198,2.5536,-2.1588,-2.3562,-2.6779,3.1416,2.6779,2.3562,-1.8925,-2.0344,-2.3562,3.1416,2.3562,2.0344,-1.5708,-1.5708,-1.5708,0,1.5708,1.5708,-1.249,-1.1071,-0.7854,0,0.7854,1.1071,-0.9828,-0.7854,-0.4636,0,0.4636,0.7854] [-3,-2,-1,-0,-1,-2,-3,-2,-1,-0,-1,-2,-3,-2,-1,-0,-1,-2,3,2,1,0,1,2,3,2,1,0,1,2,3,2,1,0,1,2] [-0,1,-1,0,1,-1,1,-0,-1,0,1,0,-0,-0,-0,0,0,0,null,null,null,null,null,null,-0,-0,-0,0,0,0,1,-0,-1,0,1,0] [0,1,2,3,4,5,0,0,1,2,3,4,0,0,0,1,2,3,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0] [-3,-2,-1,0,1,2,-2,-2,-1,0,1,2,-1,-1,-1,0,1,2,0,0,0,0,1,2,1,1,1,1,1,2,2,2,2,2,2,2] [-3,-3,-3,-3,-3,-3,-3,-2,-2,-2,-2,-2,-3,-2,-1,-1,-1,-1,-3,-2,-1,0,0,0,-3,-2,-1,0,1,1,-3,-2,-1,0,1,2] [-0,-2,-1,0,1,2,-1,-0,-1,0,1,0,-0,-0,-0,0,0,0,null,null,null,null,null,null,-0,-0,-0,0,0,0,-1,-0,-1,0,1,0] [4.2426,3.6056,3.1623,3,3.1623,3.6056,3.6056,2.8284,2.2361,2,2.2361,2.8284,3.1623,2.2361,1.4142,1,1.4142,2.2361,3,2,1,0,1,2,3.1623,2.2361,1.4142,1,1.4142,2.2361,3.6056,2.8284,2.2361,2,2.2361,2.8284] [0.3091,0.4861,0.3391,-0.2601,-0.3391,0.4861,0.1289,0.3528,0.5767,0.2239,-0.5767,0.3528,0.0196,0.1149,0.4401,0.7652,-0.4401,0.1149,0,0,0,1,0,0,-0.0196,0.1149,-0.4401,0.7652,0.4401,0.1149,-0.1289,0.3528,-0.5767,0.2239,0.5767,0.3528] [-0.375,-0.25,-0.125,0,0.125,0.25,-0.75,-0.5,-0.25,0,0.25,0.5,-1.5,-1,-0.5,0,0.5,1,-3,-2,-1,0,1,2,-6,-4,-2,0,2,4,-12,-8,-4,0,4,8] [-3,-2,-1,-0,1,2,-3,-2,-1,-0,1,2,-3,-2,-1,-0,1,2,-3,-2,-1,0,1,2,-3,-2,-1,0,1,2,-3,-2,-1,0,1,2] [-3,-2,-1,-0,1,2,-3,-2,-1,-0,1,2,-3,-2,-1,-0,1,2,-3,-2,-1,0,1,2,-3,-2,-1,0,1,2,-3,-2,-1,0,1,2] [-0,1,-1,0,1,-1,1,-0,-1,0,1,0,-0,-0,-0,0,0,0,null,null,null,null,null,null,-0,-0,-0,0,0,0,1,-0,-1,0,1,0] [-0.375,-0.25,-0.125,0,0.125,0.25,-0.75,-0.5,-0.25,0,0.25,0.5,-1.5,-1,-0.5,0,0.5,1,-3,-2,-1,0,1,2,-6,-4,-2,0,2,4,-12,-8,-4,0,4,8] [-0.375,-0.25,-0.125,0,0.125,0.25,-0.75,-0.5,-0.25,0,0.25,0.5,-1.5,-1,-0.5,0,0.5,1,-3,-2,-1,0,1,2,-6,-4,-2,0,2,4,-12,-8,-4,0,4,8] [-0.037,-0.125,-1,1.7976931348623157e+308,1,0.125,0.1111,0.25,1,1.7976931348623157e+308,1,0.25,-0.3333,-0.5,-1,1.7976931348623157e+308,1,0.5,1,1,1,1,1,1,-3,-2,-1,0,1,2,9,4,1,0,1,4] [null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,1.7976931348623157e+308,-1.7976931348623157e+308,1.7976931348623157e+308,-1.7976931348623157e+308,-1.7976931348623157e+308,-1.7976931348623157e+308,5.8215,-1.6507,0.7812,0.0883,-0.7812,-1.6507,1.1278,-0.6174,0.107,0.5104,-0.107,-0.6174] - name: fma/3 function args: - -c - '[range(.) | . * 2 | sin] | [fma(.[]; .[]; .[])] | map(. * 100000 | floor / 100000)' input: '3' expected: | [0,0,0,0,0.82682,-0.68816,0,-0.68816,0.57275,0.90929,0.90929,0.90929,0.90929,1.73611,0.22113,0.90929,0.22113,1.48204,-0.75681,-0.75681,-0.75681,-0.75681,0.07001,-1.44497,-0.75681,-1.44497,-0.18406] - name: infinite function args: - -c - '[infinite, -infinite] | (.[], .[]+.[], .[]-.[], .[]*.[], .[]/.[], .[]%.[]%256, 1/.[], -1/.[], .[]-1e300, .[]+1e300) | [., . == infinite, . == -infinite, contains(infinite)]' input: 'null' expected: | [1.7976931348623157e+308,true,false,true] [-1.7976931348623157e+308,false,true,false] [1.7976931348623157e+308,true,false,true] [null,false,false,false] [null,false,false,false] [-1.7976931348623157e+308,false,true,false] [null,false,false,false] [-1.7976931348623157e+308,false,true,false] [1.7976931348623157e+308,true,false,true] [null,false,false,false] [1.7976931348623157e+308,true,false,true] [-1.7976931348623157e+308,false,true,false] [-1.7976931348623157e+308,false,true,false] [1.7976931348623157e+308,true,false,true] [null,false,false,false] [null,false,false,false] [null,false,false,false] [null,false,false,false] [0,false,false,false] [-1,false,false,false] [255,false,false,false] [0,false,false,false] [0,false,false,false] [-0,false,false,false] [-0,false,false,false] [0,false,false,false] [1.7976931348623157e+308,true,false,true] [-1.7976931348623157e+308,false,true,false] [1.7976931348623157e+308,true,false,true] [-1.7976931348623157e+308,false,true,false] - name: isfinite and isinfinite functions args: - -c - '[infinite, pow(9; 999) | ., -., 1/., 1/(-.), nan, tostring | isfinite, isinfinite]' input: 'null' expected: | [false,true,false,true,true,false,true,false,true,false,false,false,false,true,false,true,true,false,true,false,true,false,false,false] - name: finites function args: - -c - 'infinite, pow(9; 999), nan | ., -., 1/. | finites' input: 'null' expected: | 0 0 null null null - name: nan and isnan functions args: - -c - 'nan, null, 0/0, 0/0.0, infinite-infinite, infinite/infinite | [., .+1, isnan]' input: 'null' expected: | [null,null,true] [null,1,false] [null,null,true] [null,null,true] [null,null,true] [null,null,true] - name: nan and comparison args: - -c - 'nan | [.<., .>., .<0, .>0, .==., .!=.], ([., 1, ., 2, .] | [min, max], sort, group_by(.))' input: 'null' expected: | [true,false,true,false,false,true] [null,2] [null,null,null,1,2] [[null],[null],[null],[1],[2]] - name: isnormal function args: - -c - 'infinite, pow(9; 999), nan, 9 | ., 1/. | isnormal' input: 'null' expected: | false false false false false false true true - name: normals function args: - -c - 'infinite, pow(9; 999), nan, "", [], {}, 0, 9 | normals' input: 'null' expected: | 9 - name: stringify nan and inf args: - '(2|asin),(1|atanh),(-1|atanh) | tojson,([]|join(",")),.' input: 'null' expected: | "null" "" null "1.7976931348623157e+308" "" 1.7976931348623157e+308 "-1.7976931348623157e+308" "" -1.7976931348623157e+308 - name: truncate_stream function args: - -c - '[truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]])]' input: '0 1 2' expected: | [[[0],1],[[1,0],2],[[1,0]],[[1]]] [[[0],2],[[0]]] [] - name: fromstream function args: - -c - 'fromstream(0,1,2|truncate_stream([[0],1],[[1,0],2],[[1,0]],[[1]]))' input: 'null' expected: | [1,[2]] [2] - name: stream function args: - -c - 'tostream' input: '[0,[1,{"a":1},{"b":2}]]' expected: | [[0],0] [[1,0],1] [[1,1,"a"],1] [[1,1,"a"]] [[1,2,"b"],2] [[1,2,"b"]] [[1,2]] [[1]] - name: setpath function args: - -c - 'setpath([["a","b",0,1,2],[1,2,"a","b"]][]; 1,2)' input: 'null' expected: | {"a":{"b":[[null,[null,null,1]]]}} [null,[null,null,{"a":{"b":1}}]] {"a":{"b":[[null,[null,null,2]]]}} [null,[null,null,{"a":{"b":2}}]] - name: setpath function deeply args: - -c - 'setpath(["a","b",0,1]; 1,2)' input: '{"a":{"b":[]}}' expected: | {"a":{"b":[[null,1]]}} {"a":{"b":[[null,2]]}} - name: setpath function negative index args: - -c - 'setpath([-2,0,2][] as $x | [$x]; 4,5)' input: '[1,2,3]' expected: | [1,4,3] [4,2,3] [1,2,4] [1,5,3] [5,2,3] [1,2,5] - name: setpath function empty path args: - -c - 'setpath([]; 10)' input: 'null' expected: | 10 - name: setpath function error args: - -c - 'setpath(["a","b","c"]; 10)' input: '{"a":{"b":[]}}' error: | expected an object but got: array ([]) - name: setpath function error args: - -c - 'setpath(["a",1]; 10)' input: '{"a":{"b":[]}}' error: | expected an array but got: object ({"b":[]}) - name: setpath function error args: - -c - 'setpath(["a",{}]; 10)' input: 'null' error: | expected a string for object key but got: object ({}) - name: delpaths function args: - -c - 'delpaths([["a",1,"b",1]])' input: '{"a":[{},{"b":[1,2,3]}]}' expected: | {"a":[{},{"b":[1,3]}]} - name: delpaths function against array args: - -c - 'delpaths([[2],[6],[4]])' input: '[1,2,3,4,5]' expected: | [1,2,4] - name: delpaths function not found key args: - -c - 'delpaths([["a","b","c"]])' input: '{}' expected: | {} - name: delpaths function out of index args: - -c - 'delpaths([[2],[-10],[10],[-3]])' input: '[1,2,3,4,5]' expected: | [1,2,4,5] - name: delpaths function with overlapped paths args: - -c - 'delpaths([["x"],["x",0,"y"]], [["x",0],["x",0,"y"]], [["x"],["x",{"start":0}]])' input: '{"x":[{"y":0}]}' expected: | {} {"x":[]} {} - name: getpath function args: - -c - 'getpath(["a",1,"b",2])' input: '{"a":[{},{"b":[1,2,3]}]}' expected: | 3 - name: getpath function empty path args: - -c - 'getpath([])' input: '{"a":[{},{"b":[1,2,3]}]}' expected: | {"a":[{},{"b":[1,2,3]}]} - name: getpath function error args: - -c - 'getpath(["a",1,"b","c"])' input: '{"a":[{},{"b":[1,2,3,4,5,6,7]}]}' error: | cannot getpath with ["a",1,"b","c"] against: object ({"a":[{},{"b":[1,2,3,4,5, ...}) - name: setpath, delpaths, getpath functions args: - -c - '["foo",1] as $p | getpath($p), setpath($p; 2), delpaths([$p]), .' input: | {"foo":[0,1]} {"bar":false} expected: | 1 {"foo":[0,2]} {"foo":[0]} {"foo":[0,1]} null {"bar":false,"foo":[null,2]} {"bar":false} {"bar":false} - name: setpath to getpath args: - -c - '.[] | try (getpath(["a",0,"b"]) |= 5) catch .' input: '[null,{"b":0},{"a":0},{"a":null},{"a":[0,1]},{"a":{"b":1}},{"a":[{}]},{"a":[{"c":3}]}]' expected: | {"a":[{"b":5}]} {"a":[{"b":5}],"b":0} "cannot getpath with [\"a\",0,\"b\"] against: object ({\"a\":0})" {"a":[{"b":5}]} "cannot getpath with [\"a\",0,\"b\"] against: object ({\"a\":[0,1]})" "cannot getpath with [\"a\",0,\"b\"] against: object ({\"a\":{\"b\":1}})" {"a":[{"b":5}]} {"a":[{"b":5,"c":3}]} - name: map_values function args: - -c - 'map_values(.*2)' input: '[1,2,3]' expected: | [2,4,6] - name: map_values function with optional operator args: - -c - 'map_values(tonumber? // .)' input: '{"x": "1", "y": "1.5", "z": "z"}' expected: | {"x":1,"y":1.5,"z":"z"} - name: del function args: - -c - 'del(.), del(empty), del((.foo,.bar,.baz) | .[2,3,0]), del(.foo[0], .bar[0], .foo, .baz.bar[0].x), del(.foo[1:][:3][1:], .bar[:2][1:])' input: '{"foo": [0,1,2,3,4], "bar": [0,1]}' expected: | null {"bar":[0,1],"foo":[0,1,2,3,4]} {"bar":[1],"foo":[1,4]} {"bar":[1]} {"bar":[0],"foo":[0,1,4]} - name: del function with slice args: - -c - 'del(.[1], .[-6], .[2], .[-3:9])' input: '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]' expected: | [0,3,5,6,9] - name: assignment operator against object args: - -c - '.foo.bar = 1' input: 'null' expected: | {"foo":{"bar":1}} - name: assignment operator against array args: - -c - '.[-1,0] = 3' input: '[0,1,2]' expected: | [3,1,3] - name: assignment operator against array with slice args: - -c - '.[2:4] = ([], [0], [4,3,2]), .[1:-1] = [3,2], .[2:0] = [.[]], .[10:] = [1], .[-20:-10] = [1], .[0:5] = [9], .[1:][1:] = [9], .[1:][:3] = [6,7,8], .[:4][1:][:3] = [9], .[1:][1:-2] = [9], .[:4][-3:][:5] = [9], (null | .[:1][2:] = [9])' input: '[1,2,3,4,5]' expected: | [1,2,5] [1,2,0,5] [1,2,4,3,2,5] [1,3,2,5] [1,2,1,2,3,4,5,3,4,5] [1,2,3,4,5,1] [1,1,2,3,4,5] [9] [1,2,9] [1,6,7,8,5] [1,9,5] [1,2,9,4,5] [1,9,5] [9] - name: assignment operator deeply args: - -c - '.foo.[0].bar.[1] = 1' input: 'null' expected: | {"foo":[{"bar":[null,1]}]} - name: assignment operator deeply args: - -c - '.[2][2] = 1' input: '[1]' expected: | [1,null,[null,null,1]] - name: assignment operator deeply args: - -c - '.foo[2].bar = 2' input: '{"foo":[1], "bar":3}' expected: | {"bar":3,"foo":[1,null,{"bar":2}]} - name: assignment operator query args: - -c - '.foo = .bar' input: '{"bar":42}' expected: | {"bar":42,"foo":42} - name: assignment operator array index limit error args: - '.[100000000] = 1' input: '[]' error: | array index too large: 100000000 - name: assignment operator with error function args: - -c - 'error = 1' input: 'null' expected: | null - name: assignment operator with error function and valid paths args: - -c - '(.x,error,.y) = 1' input: 'null' expected: | {"x":1,"y":1} - name: assignment operator with select args: - -c - '(.[] | select(.k == "b") | .v) = 0' input: '[{"k": "a", "v": 1}, {"k": "b", "v": 2}, {"k": "c", "v": 3}]' expected: | [{"k":"a","v":1},{"k":"b","v":0},{"k":"c","v":3}] - name: modify operator args: - -c - '.foo |= .+1' input: '{"foo": 1}' expected: | {"foo":2} - name: modify operator deeply args: - -c - '.[0].a |= {"old":., "new":(.+1)}' input: '[{"a":1,"b":2}]' expected: | [{"a":{"new":2,"old":1},"b":2}] - name: modify operator in function args: - -c - 'def inc(x): x |= .+1; inc(.[].a)' input: '[{"a":1,"b":2},{"a":2,"b":4},{"a":7,"b":8}]' expected: | [{"a":2,"b":2},{"a":3,"b":4},{"a":8,"b":8}] - name: modify operator with empty args: - -c - '.foo |= empty' input: '{"foo": 42, "bar": 48}' expected: | {"bar":48} - name: modify operator on array with empty function args: - -c - '(.[] | select(. % 2 > 0)) |= empty' input: '[1,2,3,4,5]' expected: | [2,4] - name: modify operator on array with conditional empty function args: - -c - '[range(.)] | .[] |= if . % 2 == 0 then empty else [., -.] end' input: '10' expected: | [[1,-1],[3,-3],[5,-5],[7,-7],[9,-9]] - name: modify operator with optional operator args: - -c - '.foo |= .?' input: '{"foo": 42}' expected: | {"foo":42} - name: modify operator with try catch args: - -c - '. |= try . catch .' input: '1' expected: | 1 - name: modify operator with alternative operator args: - -c - '.foo |= (fromjson? // "x")' input: | {"foo": 42} {"foo": "42"} {"foo": "a"} {"foo": "true"} {"foo": "[{}]"} expected: | {"foo":"x"} {"foo":42} {"foo":"x"} {"foo":true} {"foo":[{}]} - name: update operators args: - -c - '.[] += 2, .[] -= 2, .[] *= 2, .[] /= 2, .[] %=2' input: '[1,2,3]' expected: | [3,4,5] [-1,0,1] [2,4,6] [0.5,1,1.5] [1,0,1] - name: update operator with query in right hand side args: - -c - '.foo += .foo, .bar //= .foo, .[] /= .foo, .[] = .foo' input: '{"foo":2}' expected: | {"foo":4} {"bar":2,"foo":2} {"foo":1} {"foo":2} - name: update operators does not associate args: - '. += 1 -= 2' input: '1' error: | invalid query: . += 1 -= 2 . += 1 -= 2 ^ unexpected token "-=" exit_code: 3 - name: error/0 function args: - '.foo[] | error' input: '{ "foo": ["error message", "bar"] }' error: | error: error message exit_code: 5 - name: error/0 function with null args: - 'error' input: 'null' - name: error/0 function on string and try catch args: - 'try error catch .' input: '"foo"' expected: | "foo" - name: error/0 function on values and try catch args: - 'null,1,[],{x:42} | try error catch .' input: '0' expected: | 1 [] { "x": 42 } - name: error/1 function args: - '.foo | error("error message")' input: '{ "foo": 1 }' error: | error: error message exit_code: 5 - name: error/1 function and try catch args: - 'try error({ x: 42 }) catch .' input: '0' expected: | { "x": 42 } - name: error/1 function with null args: - 'error(null)' input: '1' - name: path function identity args: - 'path(.)' input: '{}' expected: | [] - name: path function deep args: - -c - 'path(.a|.[1,2:3,4].b[5,6])' input: '{}' expected: | ["a",{"end":3,"start":1},"b",5] ["a",{"end":4,"start":1},"b",5] ["a",{"end":3,"start":2},"b",5] ["a",{"end":4,"start":2},"b",5] ["a",{"end":3,"start":1},"b",6] ["a",{"end":4,"start":1},"b",6] ["a",{"end":3,"start":2},"b",6] ["a",{"end":4,"start":2},"b",6] - name: path function select args: - -c - '[path(.[] | select(. > 3))]' input: '[1,3,5,7,3,8,9,1]' expected: | [[2],[3],[5],[6]] - name: path function recurse args: - -c - '[path(..)]' input: '{"a":{"b":[0,1,2]},"c":[3,4]}' expected: | [[],["a"],["a","b"],["a","b",0],["a","b",1],["a","b",2],["c"],["c",0],["c",1]] - name: path function and pattern args: - -c - '(.a as $x | (.b,.c)) = "b"' input: '{"a":null,"b":null}' expected: | {"a":null,"b":"b","c":"b"} - name: path function result args: - -c - '.a[path(.b)[0]]' input: '{"a":{"b":0}}' expected: | 0 - name: path function with select args: - -c - 'path(.[] | select(.id | startswith("x")) | .id)' input: '[{"id": "xy"}, {"id": "yx"}]' expected: | [0,"id"] - name: path function against nan args: - -c - '[nan,{x:nan}] | path(.[1].x)' input: 'null' expected: | [1,"x"] - name: path function nested args: - -c - 'path(.a[path(.b)[0]])' input: '{"a":{"b":0}}' expected: | ["a","b"] - name: path function error args: - 'path(. + 1)' input: '0' error: | invalid path against: number (1) - name: path function with error function args: - '[path(error)]' input: 'null' expected: | [] - name: path function with error function and valid paths args: - -c - '[path(.x,error,.y)]' input: 'null' expected: | [["x"],["y"]] - name: path function with error message args: - '[path("x"|error)]' input: 'null' error: | error: x - name: path function iterate error args: - 'try ((map(select(.a == 0))[].b) = 10) catch .' input: '[{"a":0},{"a":1}]' expected: | "invalid path on iterating against: array ([{\"a\":0}])" - name: paths/0 function args: - -c - 'paths' input: '[1,[[1,{"a":2},3],{"a":[2]}]]' expected: | [0] [1] [1,0] [1,0,0] [1,0,1] [1,0,1,"a"] [1,0,2] [1,1] [1,1,"a"] [1,1,"a",0] - name: paths/1 function args: - -c - 'paths(. > 2)' input: '[1,[[1,{"a":2},3],{"a":[2]}]]' expected: | [1] [1,0] [1,0,1] [1,0,2] [1,1] [1,1,"a"] - name: bsearch function args: - 'bsearch(0,1,2,3,4,5,6)' input: '[1,3,5]' expected: | -1 0 -2 1 -3 2 -4 - name: gmtime, localtime functions args: - -c - 'gmtime, localtime' input: '1500000000 1567890123.456 1600000000.111' expected: | # tested with UTC-7 [2017,6,14,2,40,0,5,194] [2017,6,13,19,40,0,4,193] [2019,8,7,21,2,3.4560000889999998,6,249] [2019,8,7,14,2,3.4560000889999998,6,249] [2020,8,13,12,26,40.111000061,0,256] [2020,8,13,5,26,40.111000061,0,256] - name: mktime, strftime, strflocaltime, todate functions args: - -c - 'mktime, ((.,mktime) | strftime("%Y-%m-%dT%H:%M:%SZ")), ((.,mktime) | strflocaltime("%Y-%m-%dT%H:%M:%S%z")), todate' input: | [2017,6,14,2,40,0,5,194] [2019,8,7,21,2,3.4560000889999998,6,249] [2020,8,13,12,26,40.111000061,0,256] expected: | 1500000000 "2017-07-14T02:40:00Z" "2017-07-14T02:40:00Z" "2017-07-14T02:40:00-0700" "2017-07-13T19:40:00-0700" "2017-07-14T02:40:00Z" 1567890123 "2019-09-07T21:02:03Z" "2019-09-07T21:02:03Z" "2019-09-07T21:02:03-0700" "2019-09-07T14:02:03-0700" "2019-09-07T21:02:03Z" 1600000000 "2020-09-13T12:26:40Z" "2020-09-13T12:26:40Z" "2020-09-13T12:26:40-0700" "2020-09-13T05:26:40-0700" "2020-09-13T12:26:40Z" - name: strptime, fromdate functions args: - -c - 'strptime("%Y-%m-%dT%H:%M:%S%z"), fromdate' input: | "2017-07-14T02:40:00Z" "2019-09-08T06:02:03+0900" "2020-09-13T07:26:40-05:00" expected: | [2017,6,14,2,40,0,5,194] 1500000000 [2019,8,7,21,2,3,6,249] 1567890123 [2020,8,13,12,26,40,0,256] 1600000000 - name: strftime, strptime functions args: - 'strptime("%c") | strftime("%c")' input: | "Thu Jul 23 20:41:06 2020" expected: | "Thu Jul 23 20:41:06 2020" - name: now function args: - -c - 'now | type' input: 'null' expected: | "number" - name: debug function args: - 'recurse(debug|.[]?)' input: | [{}] [1,{"a":2}] expected: | [ {} ] {} [ 1, { "a": 2 } ] 1 { "a": 2 } 2 error: | ["DEBUG:",[{}]] ["DEBUG:",{}] ["DEBUG:",[1,{"a":2}]] ["DEBUG:",1] ["DEBUG:",{"a":2}] ["DEBUG:",2] - name: stderr function args: - 'recurse(stderr|.[]?)' input: | [{}] [1,{"a":2}] "DEBUG:" expected: | [ {} ] {} [ 1, { "a": 2 } ] 1 { "a": 2 } 2 "DEBUG:" error: '[{}]{}[1,{"a":2}]1{"a":2}2"DEBUG:"' - name: debug and stderr in builtins args: - 'builtins[] | select(test("debug|stderr"))' input: 'null' expected: | "debug/0" "stderr/0" - name: input_filename function with stdin input args: - 'input_filename' input: '0 1 2' expected: | "" "" "" - name: input_filename function with null input option args: - -n - 'input_filename' input: '0' expected: | null - name: input_filename function with raw strings input option args: - --raw-input - 'input_filename' input: | 1 2 3 expected: | "" "" "" - name: input_filename function with slurp option args: - --slurp - 'input_filename' input: | 1 2 3 expected: | "" - name: input_filename function with input and slurp option args: - --raw-input - --slurp - 'input_filename' input: | 1 2 3 expected: | "" - name: input_filename function with json file arguments args: - 'input_filename' - 'testdata/1.json' - 'testdata/2.json' input: '0' expected: | "testdata/1.json" "testdata/2.json" - name: input_filename function with null input option and json file arguments args: - -n - '., inputs, (try input catch .) | input_filename' - 'testdata/1.json' - 'testdata/2.json' input: '0' expected: | null "testdata/1.json" "testdata/2.json" null - name: input_filename function with hyphen for stdin args: - 'input_filename' - '-' - 'testdata/1.json' - '-' - 'testdata/2.json' - '-' input: '0' expected: | "" "testdata/1.json" "testdata/2.json" - name: input_filename function with yaml input option args: - --yaml-input - 'input_filename' input: '0' expected: | "" - name: input_filename function with yaml file arguments args: - --yaml-input - 'input_filename' - 'testdata/1.yaml' input: '0' expected: | "testdata/1.yaml" "testdata/1.yaml" "testdata/1.yaml" - name: input_filename in builtins args: - 'builtins[] | select(test("input_filename"))' input: 'null' expected: | "input_filename/0" - name: halt function args: - 'def f($n): if $n > 0 then $n, f($n - 1) else halt end; f(.), .' input: '3' expected: | 3 2 1 - name: halt function and raw strings input option args: - -R - 'halt' input: | 1 2 3 - name: halt function and json file arguments args: - 'halt' - 'testdata/1.json' - 'testdata/2.json' - name: halt_error/0 function args: - 'halt_error' input: '1' error: | 1 exit_code: 5 - name: halt_error/0 function on nan args: - '0/0 | halt_error' input: 'null' error: | null exit_code: 5 - name: halt_error/0 function and try catch args: - 'range(5) | try halt_error catch .' input: 'null' error: | 0 exit_code: 5 - name: halt_error/0 function with null input and try catch args: - 'try halt_error catch .' input: 'null' exit_code: 5 - name: halt_error/0 function with multiple input values args: - 'halt_error' input: | 1 2 3 error: | 1 2 3 exit_code: 5 - name: halt_error/0 function and raw strings input option args: - -R - 'halt_error' input: | 1 2 3 error: | 123 exit_code: 5 - name: halt_error/1 function args: - 'halt_error(42)' input: 'null' exit_code: 42 - name: builtins function args: - 'builtins | length > 100' input: 'null' expected: | true - name: env function args: - 'env|length > 0' input: 'null' expected: | true - name: env in function args: - 'def f: env; f|length > 0' input: 'null' expected: | true - name: env in builtins args: - 'builtins[] | select(test("env"))' input: 'null' expected: | "env/0" - name: $ENV variable args: - '[$ENV|length > 0, 1 as $ENV | $ENV]' input: 'null' expected: | [ true, 1 ] - name: env arguments count error args: - 'env(0)' input: 'null' error: | function not defined: env/1 exit_code: 3 - name: input function args: - -n - 'input, input' input: '1' expected: | 1 error: | break - name: input function with binomial operator args: - -n - 'input + input[0]' input: | [1] 2 expected: | 3 - name: input function in function args: - -n - 'def f: [def f: input; range(3) | f]; f' input: '1 2 3' expected: | [ 1, 2, 3 ] - name: input function with files args: - -n - -c - 'range(5) | input' - 'testdata/1.json' - 'testdata/7.json' - 'testdata/1.json' expected: | {"foo":10} 1 2 {"foo":42} {"foo":10} - name: input function without null input option args: - '. + input' input: '1 2' expected: | 3 - name: input function with stream option args: - -n - -c - --stream - 'range(5) | input' input: '{"a":[1,2,3]}' expected: | [["a",0],1] [["a",1],2] [["a",2],3] [["a",2]] [["a"]] - name: inputs function args: - -n - 'reduce inputs as $x (0; . + $x)' input: '1 2 3 4 5' expected: | 15 - name: inputs function with stream option args: - -n - -c - --stream - 'range(5) | input' input: '{"a":[1,2,3]}' expected: | [["a",0],1] [["a",1],2] [["a",2],3] [["a",2]] [["a"]] - name: inputs function with stream option and truncate_stream function args: - -n - --stream - 'fromstream(1 | truncate_stream(inputs) | select(length > 1) | .[0] |= .[1:])' input: '{"a":[1,true,null,"x"]}' expected: | 1 true null "x" - name: inputs function with stream option args: - -n - -c - --stream - '[inputs][]' input: '{"a":1} {"b":2} {"c":3}' expected: | [["a"],1] [["a"]] [["b"],2] [["b"]] [["c"],3] [["c"]] - name: inputs function with stream and slurp option args: - -n - -c - --stream - --slurp - 'inputs[]' input: '{"a":1} {"b":2} {"c":3}' expected: | [["a"],1] [["a"]] [["b"],2] [["b"]] [["c"],3] [["c"]] - name: inputs function with raw input option args: - -n - -R - 'inputs' input: | a b c d 1 expected: | "a b" "c" "d" "1" - name: inputs function with raw input and slurp option args: - -n - -R - -s - 'inputs' input: | a b c expected: | "a b\nc\n" - name: inputs function with yaml input option args: - -n - --yaml-input - 'inputs' input: | foo: 1 --- bar: 2 --- baz: 3 expected: | { "foo": 1 } { "bar": 2 } { "baz": 3 } - name: inputs function with yaml input, slurp option args: - -n - --slurp - --yaml-input - 'inputs' input: | foo: 1 --- bar: 2 --- baz: 3 expected: | [ { "foo": 1 }, { "bar": 2 }, { "baz": 3 } ] - name: match function args: - -c - '[match("bcd")], [match("☆★"; "g")], (("","g","i","ig") as $flag | [match("bcd"; $flag)]), [match("ABC.?D.?E"; "mg")], [match("A(?B(C)(.?D))(?.?E)")], [match("b(?c)(d)"; "igm")], [match("bc(c(.*))?d")]' input: '"abcde☆★abcde☆★ABCDE☆ABC\nD\nE"' expected: | [{"captures":[],"length":3,"offset":1,"string":"bcd"}] [{"captures":[],"length":2,"offset":5,"string":"☆★"},{"captures":[],"length":2,"offset":12,"string":"☆★"}] [{"captures":[],"length":3,"offset":1,"string":"bcd"}] [{"captures":[],"length":3,"offset":1,"string":"bcd"},{"captures":[],"length":3,"offset":8,"string":"bcd"}] [{"captures":[],"length":3,"offset":1,"string":"bcd"}] [{"captures":[],"length":3,"offset":1,"string":"bcd"},{"captures":[],"length":3,"offset":8,"string":"bcd"},{"captures":[],"length":3,"offset":15,"string":"BCD"}] [{"captures":[],"length":5,"offset":14,"string":"ABCDE"},{"captures":[],"length":7,"offset":20,"string":"ABC\nD\nE"}] [{"captures":[{"length":3,"name":"bar","offset":15,"string":"BCD"},{"length":1,"name":null,"offset":16,"string":"C"},{"length":1,"name":null,"offset":17,"string":"D"},{"length":1,"name":"foo","offset":18,"string":"E"}],"length":5,"offset":14,"string":"ABCDE"}] [{"captures":[{"length":1,"name":"foo","offset":2,"string":"c"},{"length":1,"name":null,"offset":3,"string":"d"}],"length":3,"offset":1,"string":"bcd"},{"captures":[{"length":1,"name":"foo","offset":9,"string":"c"},{"length":1,"name":null,"offset":10,"string":"d"}],"length":3,"offset":8,"string":"bcd"},{"captures":[{"length":1,"name":"foo","offset":16,"string":"C"},{"length":1,"name":null,"offset":17,"string":"D"}],"length":3,"offset":15,"string":"BCD"}] [{"captures":[{"length":0,"name":null,"offset":-1,"string":null},{"length":0,"name":null,"offset":-1,"string":null}],"length":3,"offset":1,"string":"bcd"}] - name: match function flag error args: - 'match("x"; "a")' input: '""' error: | unsupported regular expression flag: "a" - name: test function args: - -c - '[test("xyz"), test("bcd"), test("efg"), test("efg"; "i"), test("☆★")]' input: '"abcde☆★abcde☆★ABCDEFG"' expected: | [false,true,false,true,true] - name: capture function args: - -c - 'capture("bcd"), capture("b(?c.?(?d..))"), [capture("(?b(?c).?(?d..))"; "gmi")]' input: '"abcdeABCDEabcde"' expected: | {} {"bar":"deA","foo":"cdeA"} [{"a":"bcdeA","bar":"deA","foo":"c"},{"a":"BCDEa","bar":"DEa","foo":"C"}] - name: scan function args: - -c - 'scan("abc"), scan("abc"; "i"), scan("(b(c*)(d*))"), scan("(b(c*)(d*))"; "i")' input: '"abcdeABCCDDEabcce"' expected: | "abc" "abc" "abc" "ABC" "abc" ["bcd","c","d"] ["bcc","cc",""] ["bcd","c","d"] ["BCCDD","CC","DD"] ["bcc","cc",""] - name: split/2 function args: - -c - 'split("a"), split("a+"), split("a+"; ""), split("a+"; "i")' input: '"abaacaAad"' expected: | ["","b","","c","A","d"] ["abaacaAad"] ["","b","c","A","d"] ["","b","c","d"] - name: splits function args: - -c - '[splits("a")], [splits("a+")], [splits("a+"; "")], [splits("a+"; "i")]' input: '"abaacaAad"' expected: | ["","b","","c","A","d"] ["","b","c","A","d"] ["","b","c","A","d"] ["","b","c","d"] - name: sub function args: - 'sub("a"; "b"), sub("aaa"; "b"), sub("a"; "b","c"), sub("a(?.)"; "\(.a)b\(.a)"; "ig"), sub("(?★)"; "\(.foo)☆\(.foo)"), sub("^"; "b")' input: '"abc☆★☆ABC"' expected: | "bbc☆★☆ABC" "abc☆★☆ABC" "bbc☆★☆ABC" "cbc☆★☆ABC" "bbbc☆★☆BbBC" "abc☆★☆★☆ABC" "babc☆★☆ABC" - name: gsub function args: - 'gsub("a"; "b"), gsub("a.c"; "b"), gsub("a"; "b","c"), gsub("a(?..)"; "\(.a+.a)"; "i"), gsub("(?★)"; "\(.foo)☆\(.foo)"), gsub("^"; "a")' input: '"abcABC☆★☆ABCabc"' expected: | "bbcABC☆★☆ABCbbc" "bABC☆★☆ABCb" "bbcABC☆★☆ABCbbc" "bbcABC☆★☆ABCcbc" "cbcABC☆★☆ABCbbc" "cbcABC☆★☆ABCcbc" "bcbcBCBC☆★☆BCBCbcbc" "abcABC☆★☆★☆ABCabc" "aabcABC☆★☆ABCabc" - name: INDEX function args: - -c - 'INDEX(range(5); .*2)' input: 'null' expected: | {"0":0,"2":1,"4":2,"6":3,"8":4} - name: JOIN function args: - -c - '[range(5)] | JOIN(map(.*3); .*2)' input: 'null' expected: | [[0,0],[1,6],[2,12],[3,null],[4,null]] - name: IN function args: - -c - '[range(5; 13)|IN(range(0; 10; 3))]' input: 'null' expected: | [false,true,false,false,true,false,false,false] - name: format function args: - -n - '("html","uri","csv","tsv","sh","base64") as $x | [1,2,3] | format($x)' expected: | "[1,2,3]" "%5B1%2C2%2C3%5D" "1,2,3" "1\t2\t3" "1 2 3" "WzEsMiwzXQ==" - name: format function error args: - -n - 'format("t")' error: | format not defined: @t - name: format strings @text args: - -n - '@text' expected: | "null" - name: format strings @json args: - '@json' input: '{"a":42}' expected: | "{\"a\":42}" - name: format strings @json with string interpolation args: - '@json "0\(.)1\(. + .)2\([2])3"' input: '[1]' expected: | "0[1]1[1,1]2[2]3" - name: format strings @html args: - '@html' input: | {"foo": "
&'\"
"} expected: | "{"foo":"<div>&'\\"</div>"}" - name: format strings @html with string interpolation args: - '@html "x\(.)x\(.+.)x\("<>")x"' input: | ["<>"] expected: | "x["<>"]x["<>","<>"]x<>x" - name: format strings @uri args: - '@uri' input: | [1, {"foo": "
&'\"()
"}] expected: | "%5B1%2C%7B%22foo%22%3A%22%3Cdiv%3E%26%27%5C%22%28%29%3C%2Fdiv%3E%22%7D%5D" - name: format strings @uri with string interpolation args: - '@uri "q=\(.)&q=\(. + .)"' input: | ["<>", "{&}"] expected: | "q=%5B%22%3C%3E%22%2C%22%7B%26%7D%22%5D&q=%5B%22%3C%3E%22%2C%22%7B%26%7D%22%2C%22%3C%3E%22%2C%22%7B%26%7D%22%5D" - name: format strings @csv args: - '@csv' input: | [1, "foo", null, "foo,\n\"bar\"\tbaz"] expected: | "1,\"foo\",,\"foo,\n\"\"bar\"\"\tbaz\"" - name: format strings @csv with string interpolation args: - '@csv "\(.),\(. + .)"' input: | [1, "foo", null, 2] expected: | "1,\"foo\",,2,1,\"foo\",,2,1,\"foo\",,2" - name: format strings @csv error args: - '@csv' input: | [1, "foo", null, {"foo":1}] error: | invalid csv row: object ({"foo":1}) - name: format strings @tsv args: - '@tsv' input: | [1, "foo", null, "foo,\n\"bar\"\tba\\z"] expected: | "1\tfoo\t\tfoo,\\n\"bar\"\\tba\\\\z" - name: format strings @tsv with string interpolation args: - '@tsv "\(.),\(. + .)"' input: | [1, "foo", null, 2] expected: | "1\tfoo\t\t2,1\tfoo\t\t2\t1\tfoo\t\t2" - name: format strings @tsv error args: - '@tsv' input: | [1, "foo", null, ["foo"]] error: | invalid tsv row: array (["foo"]) - name: format strings @sh args: - '@sh' input: | [1, "f'o'o", null, false] expected: | "1 'f'\\''o'\\''o' null false" - name: format strings @sh error args: - '@sh' input: | [{"foo": "<>"}] error: | cannot escape for shell: object ({"foo":"<>"}) - name: format strings @base64 args: - '@base64' input: '{"foo":"bar"}' expected: | "eyJmb28iOiJiYXIifQ==" - name: format strings @base64 with string interpolation args: - '@base64 "\(.),\(. + .),\(. + . + .)"' input: '[1,2]' expected: | "WzEsMl0=,WzEsMiwxLDJd,WzEsMiwxLDIsMSwyXQ==" - name: format strings @base64d args: - -R - '@base64d' input: 'eyJmb28iOiJiYXIifQ==' expected: | "{\"foo\":\"bar\"}" - name: format strings @base64d without padding args: - -R - '@base64d' input: 'eyJmb28iOiJiYXIifQ' expected: | "{\"foo\":\"bar\"}" - name: format strings @base64d with trailing characters after padding args: - -R - '@base64d' input: 'eyJmb28iOiJiYXIifQ=x=y' expected: | "{\"foo\":\"bar\"}" - name: format strings not defined error args: - -n - '@foo' error: | format not defined: @foo exit_code: 5 - name: format strings not defined error with numeric characters args: - -n - '@0' error: | format not defined: @0 exit_code: 5 - name: format strings not defined error with optional operator args: - -n - '@foo?, @bar"\(0|debug)"?, 1' expected: | 1 error: | ["DEBUG:",0] - name: destructuring alternative operator args: - '.[] | . as {a:$a} ?// [$a] ?// $a | $a' input: '[[1],[2],{"a":3},4]' expected: | 1 2 3 4 - name: destructuring alternative operator with error backtrack args: - -c - '.[] | . as {a:$a} ?// [$a] ?// $a | if 1 < $a and $a < 4 then $a|error else $a end' input: '[[1],[2],{"a":3},4]' expected: | 1 [2] {"a":3} 4 - name: destructuring alternative operator with multiple variables args: - -c - '.[] | . as {$a, b: [$c, {$d}]} ?// [$a, {$b}, $e] ?// $f | [$a, $b, $c, $d, $e, $f]' input: '[{"a":1,"b":[2,{"d":3}]},[4,{"b":5,"c":6},7,8,9],"foo"]' expected: | [1,null,2,3,null,null] [4,5,null,null,7,null] [null,null,null,null,null,"foo"] - name: compact output option args: - -c - '.' input: '{ "foo": ["hello", "world"] }' expected: | {"foo":["hello","world"]} - name: compact output long option args: - --compact-output - '.' input: '{ "foo": ["hello", "world"] }' expected: | {"foo":["hello","world"]} - name: compact output long option with value args: - --compact-output=1 - '.' input: '0' error: | boolean flag `--compact-output' cannot have an argument exit_code: 2 - name: raw string output option args: - -r - '.foo[]' input: '{ "foo": ["hello", "world", "multiple\nline"] }' expected: | hello world multiple line - name: raw string output long option args: - --raw-output - '.foo[]' input: '{ "foo": ["hello", "world", "multiple\nline"] }' expected: | hello world multiple line - name: raw string output option against non-string args: - -r - '.' input: '{ "foo": "multiple\nline" }' expected: | { "foo": "multiple\nline" } - name: join output option args: - -j - '.[]' input: '["foo",1,2,3]' expected: "foo123" - name: join output long option args: - --join-output - '.[]' input: '["foo",[{},{}],1,[2,3]]' expected: "foo[\n {},\n {}\n]1[\n 2,\n 3\n]" - name: nul output option args: - '-0' - '.[]' input: '["foo",1,2,3]' expected: "foo\x001\x002\x003\x00" - name: nul output long option args: - --nul-output - '.[]' input: '["foo",[{},{}],1,[2,3]]' expected: "foo\x00[\n {},\n {}\n]\x001\x00[\n 2,\n 3\n]\x00" - name: nul output option at the last argument args: - '.[]' - '-0' input: '["foo",1,2,3]' expected: "foo\x001\x002\x003\x00" - name: color output option args: - -C - '., null, nan, infinite' input: '{"x":"foo","y":128,"z":[null,false,true,3.14,"foo",{}]}' expected: "{\n\ \ \e[34;1m\"x\"\e[0m: \e[32m\"foo\"\e[0m,\n\ \ \e[34;1m\"y\"\e[0m: \e[36m128\e[0m,\n\ \ \e[34;1m\"z\"\e[0m: [\n\ \ \e[90mnull\e[0m,\n\ \ \e[33mfalse\e[0m,\n\ \ \e[33mtrue\e[0m,\n\ \ \e[36m3.14\e[0m,\n\ \ \e[32m\"foo\"\e[0m,\n\ \ {}\n\ \ ]\n\ }\n\ \e[90mnull\e[0m\n\ \e[90mnull\e[0m\n\ \e[36m1.7976931348623157e+308\e[0m\n" - name: color output option configured by environment variable args: - -C - '., null, nan, infinite' input: | { "x": "foo", "y": 128, "z": [null, false, true, 3.14, "foo", {}] } env: - GOJQ_COLORS=31:32:33:34:35:36:41:42 expected: "\e[42m{\e[0m\n\ \ \e[36m\"x\"\e[0m\e[42m:\e[0m \e[35m\"foo\"\e[0m\e[42m,\e[0m\n\ \ \e[36m\"y\"\e[0m\e[42m:\e[0m \e[34m128\e[0m\e[42m,\e[0m\n\ \ \e[36m\"z\"\e[0m\e[42m:\e[0m \e[41m[\e[0m\n\ \ \e[31mnull\e[0m\e[41m,\e[0m\n\ \ \e[32mfalse\e[0m\e[41m,\e[0m\n\ \ \e[33mtrue\e[0m\e[41m,\e[0m\n\ \ \e[34m3.14\e[0m\e[41m,\e[0m\n\ \ \e[35m\"foo\"\e[0m\e[41m,\e[0m\n\ \ \e[42m{\e[0m\e[42m}\e[0m\n\ \ \e[41m]\e[0m\n\ \e[42m}\e[0m\n\ \e[31mnull\e[0m\n\ \e[31mnull\e[0m\n\ \e[34m1.7976931348623157e+308\e[0m\n" - name: color output option configured by environment variable with fewer colors args: - -C - '.' input: | { "foo": [null, false, true, 3.14, "bar"] } env: - GOJQ_COLORS=31;1:::32;2 expected: "{\n\ \ \"foo\": [\n\ \ \e[31;1mnull\e[0m,\n\ \ false,\n\ \ true,\n\ \ \e[32;2m3.14\e[0m,\n\ \ \"bar\"\n\ \ ]\n\ }\n" - name: invalid colors environment variable args: - -C - '.' input: | 0 env: - GOJQ_COLORS=30:31;;:32 error: | invalid color: "31;;" - name: color output long option args: - --color-output - '.' input: '{"foo":42}' expected: "{\n\ \ \e[34;1m\"foo\"\e[0m: \e[36m42\e[0m\n\ }\n" - name: color output with compact option args: - -C - -c - '.' input: '{"x":42,"y":3.14,"z":[10000000000000000000]}' expected: "{\ \e[34;1m\"x\"\e[0m:\e[36m42\e[0m,\ \e[34;1m\"y\"\e[0m:\e[36m3.14\e[0m,\ \e[34;1m\"z\"\e[0m:[\e[36m10000000000000000000\e[0m]\ }\n" - name: monochrome output option args: - -C - -M - '.' input: '{"foo":42}' expected: | { "foo": 42 } - name: monochrome output long option args: - -C - --monochrome-output - '.' input: '{"foo":42}' expected: | { "foo": 42 } - name: NO_COLOR environment variable with color output option args: - --color-output - '.' input: '{"foo":42}' env: - NO_COLOR=1 expected: "{\n\ \ \e[34;1m\"foo\"\e[0m: \e[36m42\e[0m\n\ }\n" - name: indent option args: - --indent - '1' - '.' input: '{ "foo": ["hello", "world"] }' expected: | { "foo": [ "hello", "world" ] } - name: indent option with value 0 args: - --indent - '0' - '.' input: '{ "foo": ["hello", "world"] }' expected: | {"foo":["hello","world"]} - name: indent option with deeply nested array args: - --indent - '7' - '.' input: '{ "foo": ["hello", ["world", [[[[[[[[[]]]]]]]]]]] }' expected: | { "foo": [ "hello", [ "world", [ [ [ [ [ [ [ [ [] ] ] ] ] ] ] ] ] ] ] } - name: indent option too many indentation count error args: - --indent=10 - '.' input: '{ "foo": { "baz": ["hello", "world"] } }' error: | too many indentation count: 10 - name: indent option negative indentation count error args: - --indent - -1 - '.' input: '{ "foo": { "baz": ["hello", "world"] } }' error: | negative indentation count: -1 - name: indent option without argument args: - --indent input: '0' error: | expected argument for flag `--indent' exit_code: 2 - name: indent option parse error args: - --indent=foo input: '0' error: | invalid argument for flag `--indent': strconv.Atoi: parsing "foo": invalid syntax exit_code: 2 - name: tab option args: - --tab - '.' input: '{ "foo": { "baz": ["hello", ["world", [[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]] } }' expected: | { "foo": { "baz": [ "hello", [ "world", [ [ [ [ [ [ [ [ [ [ [ [ [ [ [ [] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] ] } } - name: raw strings input option args: - -R - '.' input: | { "foo": "bar" } expected: | "{" " \"foo\": \"bar\"" "}" - name: raw strings input long option args: - --raw-input - '.' input: | { "foo": "bar" } expected: | "{" " \"foo\": \"bar\"" "}" - name: raw strings input option error args: - -R - '. / 2' input: | { "foo": "bar" } error: | cannot divide: string ("{") and number (2) cannot divide: string (" \"foo\": \"bar\"") and number (2) cannot divide: string ("}") and number (2) - name: null input value option args: - -n - '[.]' expected: | [ null ] - name: null input value long option args: - --null-input - '[.]' expected: | [ null ] - name: null input value option with compact option args: - -n - -c - '[.,.]' expected: | [null,null] - name: null input value option with raw strings input option args: - -n - -R - '.' input: 'foo' expected: | null - name: slurp option args: - -s - '.' input: | null false [] { "foo": "bar" } expected: | [ null, false, [], { "foo": "bar" } ] - name: slurp long option args: - --slurp - --compact-output - '.' input: | null false expected: | [null,false] - name: slurp option with raw strings input option args: - -s - -R - '.' input: "null\nfalse\r\nfoo\nbar" expected: | "null\nfalse\r\nfoo\nbar" - name: slurp option with null input option args: - -s - -n - '.' expected: | null - name: slurp option with multiple json files args: - -s - '.' - 'testdata/1.json' - 'testdata/2.json' expected: | [ { "foo": 10 }, [ { "bar": [] } ] ] - name: slurp and raw input option with multiple json files args: - -R - -s - '.' - 'testdata/1.json' - 'testdata/2.json' expected: | "{\"foo\":10}\n[{\n\"bar\"\n:\n[]\n}]\n" - name: stream option args: - -c - --stream - '.' input: | 1 2 [] {} [3] {"x":1,"y":2} [1,2,{"x":{"y":[3,[{}],{"z":{}}]}}] {"a":"b","c":[1,[2,{}]],"d":null,"e":1.234,"x":{"y":{"z":[1,2],"q":2},"w":3},"z":[{}]} expected: | [[],1] [[],2] [[],[]] [[],{}] [[0],3] [[0]] [["x"],1] [["y"],2] [["y"]] [[0],1] [[1],2] [[2,"x","y",0],3] [[2,"x","y",1,0],{}] [[2,"x","y",1,0]] [[2,"x","y",2,"z"],{}] [[2,"x","y",2,"z"]] [[2,"x","y",2]] [[2,"x","y"]] [[2,"x"]] [[2]] [["a"],"b"] [["c",0],1] [["c",1,0],2] [["c",1,1],{}] [["c",1,1]] [["c",1]] [["d"],null] [["e"],1.234] [["x","y","z",0],1] [["x","y","z",1],2] [["x","y","z",1]] [["x","y","q"],2] [["x","y","q"]] [["x","w"],3] [["x","w"]] [["z",0],{}] [["z",0]] [["z"]] - name: stream option with null input option args: - -n - --stream - '.' input: '1' expected: | null - name: stream option with raw input option args: - -R - --stream - '.' input: | 1 2 3 expected: | "1" "2" "3" - name: stream option with slurp option args: - -c - --stream - --slurp - '.[]' input: '{"a":1} {"b":2} {"c":3}' expected: | [["a"],1] [["a"]] [["b"],2] [["b"]] [["c"],3] [["c"]] - name: yaml input option args: - --yaml-input - '.' input: | foo: bar: 42 baz: | a b qux: 100 --- foo: 1 --- bar expected: | { "foo": { "bar": 42, "baz": "a\nb\n" }, "qux": 100 } { "foo": 1 } "bar" - name: yaml file input args: - --yaml-input - '.' - 'testdata/1.yaml' expected: | { "foo": { "bar": 42, "baz": "a\nb\n" }, "qux": 100 } { "foo": 1 } "bar" - name: yaml input option with non-string keys args: - --yaml-input - '.' input: | foo: - 42: bar expected: | { "foo": [ { "42": "bar" } ] } - name: yaml input option with timestamps args: - --yaml-input - '.' input: | foo: 2021-01-02 bar: 2021-01-02t10:11:12Z baz: "2021-01-02" expected: | # it is impossible to keep the original timestamp strings { "bar": "2021-01-02T10:11:12Z", "baz": "2021-01-02", "foo": "2021-01-02T00:00:00Z" } - name: yaml input option with slurp option args: - --yaml-input - --slurp - '.' input: | foo: bar: 42 baz: | a b qux: 100 --- foo: 1 --- bar expected: | [ { "foo": { "bar": 42, "baz": "a\nb\n" }, "qux": 100 }, { "foo": 1 }, "bar" ] - name: yaml input option with hyphen for stdin args: - --yaml-input - '.' - '-' input: | foo: 1 expected: | { "foo": 1 } - name: yaml input option error args: - --yaml-input - '.' input: | foo: 10 10 bar: error: | invalid yaml: :2 2 | 10 ^ could not find expected ':' - name: yaml input option multiple errors args: - --yaml-input - '.' input: | foo: 1 bar: 2 foo: 3 error: | invalid yaml: :3 3 | foo: 3 ^ mapping key "foo" already defined at line 1 - name: yaml file input error args: - --yaml-input - '.' - 'testdata/2.yaml' error: | invalid yaml: testdata/2.yaml:5 5 | a: b: c ^ mapping values are not allowed in this context - name: yaml file input error in dos format args: - --yaml-input - '.' - 'testdata/3.yaml' error: | invalid yaml: testdata/3.yaml:5 5 | a: b: c ^ mapping values are not allowed in this context - name: yaml file input error in mac format args: - --yaml-input - '.' - 'testdata/4.yaml' error: | invalid yaml: testdata/4.yaml:5 5 | a: b: c ^ mapping values are not allowed in this context - name: yaml output option args: - --yaml-output - '.' input: '{"foo": 128, "bar": {"baz": [1,"qux", []]}}' expected: | bar: baz: - 1 - qux - [] foo: 128 - name: yaml output option with multiple values args: - --yaml-output - '..' input: '{"foo": 128, "bar": [1,2,"foo"]}' expected: | bar: - 1 - 2 - foo foo: 128 --- - 1 - 2 - foo --- 1 --- 2 --- foo --- 128 - name: yaml input and output option args: - --yaml-input - --yaml-output - '.' input: | foo: 42 --- foo: bar: 42 baz: | a b qux: 100 --- foo: 1 --- bar expected: | foo: 42 --- foo: bar: 42 baz: | a b qux: 100 --- foo: 1 --- bar - name: yaml output with indent option args: - --yaml-output - --indent - '8' - '.' input: '{ "foo": { "baz": ["hello", "world"] } }' expected: | foo: baz: - hello - world - name: yaml output with indent option error args: - --yaml-output - --indent - '10' - '.' input: '{ "foo": { "baz": ["hello", "world"] } }' error: | too many indentation count: 10 - name: yaml output with tab option error args: - --yaml-output - --tab - '.' input: '{"foo": 128, "bar": {"baz": [1,"qux", []]}}' error: | cannot use tabs for YAML output - name: source query from file args: - -n - -f - 'testdata/1.jq' expected: | null - name: source query from file args: - -nf - 'testdata/1.jq' expected: | null - name: source query from file long option args: - --null-input - --from-file - 'testdata/1.jq' expected: | null - name: invalid query args: - '>abc' input: '{}' error: | invalid query: >abc >abc ^ unexpected token ">" exit_code: 3 - name: invalid query args: - '.abc[' input: '{}' error: | invalid query: .abc[ .abc[ ^ unexpected token exit_code: 3 - name: invalid query args: - '[ .[] | { .id } ]' input: '{}' error: | invalid query: [ .[] | { .id } ] [ .[] | { .id } ] ^ unexpected token ".id" exit_code: 3 - name: invalid query args: - 'if 0then. else. end' input: '{}' error: | invalid query: if 0then. else. end if 0then. else. end ^ invalid token "0t" exit_code: 3 - name: invalid query args: - '.foo & .bar' input: '{}' error: | invalid query: .foo & .bar .foo & .bar ^ unexpected token "&" exit_code: 3 - name: invalid query args: - 'foo☆' input: '{}' error: | invalid query: foo☆ foo☆ ^ unexpected token "☆" exit_code: 3 - name: invalid query args: - "{\n[]}" input: '{}' error: | invalid query: :2 2 | []} ^ unexpected token "[" exit_code: 3 - name: invalid multiple line query args: - | [ .[] | { id } } ] input: '{}' error: | invalid query: :3 3 | | { id } } ^ unexpected token "}" exit_code: 3 - name: invalid multiple line query in file args: - -f - 'testdata/10.jq' error: | invalid query: testdata/10.jq:5 5 | bar ] ^ unexpected token "]" exit_code: 3 - name: invalid multiple line query in dos format args: - -f - 'testdata/11.jq' error: | invalid query: testdata/11.jq:5 5 | bar ] ^ unexpected token "]" exit_code: 3 - name: invalid multiple line query in mac format args: - -f - 'testdata/12.jq' error: | invalid query: testdata/12.jq:5 5 | bar ] ^ unexpected token "]" exit_code: 3 - name: invalid query in file args: - -f - 'testdata/2.jq' error: | invalid query: testdata/2.jq:3 3 | bar, [] ^ unexpected token "[" exit_code: 3 - name: query file not found args: - -f - 'testdata/3.jq' error: 'open testdata/3.jq:' - name: query file not specified args: - -f error: | expected argument for flag `-f' exit_code: 2 - name: invalid json eof input: '{' error: | invalid json: { ^ unexpected EOF - name: invalid json eof with multibyte characters input: '{ "12345":' error: | invalid json: { "12345": ^ unexpected EOF - name: invalid json invalid character with multibyte characters input: | { "12345" 100 } error: | invalid json: :2 2 | "12345" 100 ^ invalid character '1' after object key - name: invalid json string literal input: | { "123": n } error: | invalid json: :2 2 | "123": n ^ invalid character '\n' in literal null (expecting 'u') - name: multiple json in input input: '{}[]{"foo":10}{"bar":[]}' expected: | {} [] { "foo": 10 } { "bar": [] } - name: hyphen for stdin args: - '.' - '-' input: '{}' expected: | {} - name: json file arguments args: - '.' - 'testdata/1.json' - 'testdata/2.json' expected: | { "foo": 10 } [ { "bar": [] } ] - name: json file error json args: - '.' - 'testdata/3.json' error: | invalid json: testdata/3.json { ^ unexpected EOF - name: json file error json args: - '.' - 'testdata/4.json' error: | invalid json: testdata/4.json:3 3 | bar ^ invalid character 'b' looking for beginning of value - name: json file error json args: - '.' - 'testdata/8.json' error: | invalid json: testdata/8.json [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ,, 1, 1, 1, 1, 1, 1, 1 ^ invalid character ',' looking for beginning of value - name: json file error json args: - '.' - 'testdata/9.json' error: | invalid json: testdata/9.json:3 3 | ", "0", "0", "0", "0", , "0", "0"] ^ invalid character ',' looking for beginning of value - name: json file error json with multibyte character in the error line args: - '.' - 'testdata/5.json' error: | # file contents is read by inputReader so the entire error line is shown unlike the following input json test invalid json: testdata/5.json:3 3 | "abcde": :★★★★★★★★★★★★★★★ ^ invalid character ':' looking for beginning of value - name: json input error json with multibyte character in the error line args: - '.' input: | { "a": "This is a test for multibyte character in the error line of JSON. Since the first buffer size of JSON decoder is 512 (see encoding/json/stream.go) and the decoder stops reading bytes on encountering an error, the error line can be an invalid UTF-8 string even if the complete input is a valid UTF-8 string. Note that the error happens before 512 bytes and the corresponding line continues beyond 512 bytes with a multibyte character at the boundary.", "abcde": :★★★★★★★★★★★★★★★ error: | invalid json: :3 3 | "abcde": :★★★★★★★★★★★★ ^ invalid character ':' looking for beginning of value - name: json file not found error args: - '.' - 'testdata/1.json' - 'testdata/6.json' - 'testdata/2.json' expected: | { "foo": 10 } [ { "bar": [] } ] error: 'open testdata/6.json:' - name: module directory option args: - -c - -L - 'testdata' - 'include "4"; 1 | f | g' input: '0' expected: | {"foo":[1,2,3,4,[1,2,{"foo":42}]]} - name: module directory option args: - -c - -L - 'testdata' - 'import "4" as m1; 1 | m1::f | m1::g' input: '0' expected: | {"foo":[1,2,3,4,[1,2,{"foo":42}]]} - name: module directory option args: - -c - -L - 'testdata' - 'include "m1"; include "m3"; [f, g]' input: '0' expected: | [42,43,44,45] - name: module directory option args: - -c - -L - 'testdata' - 'import "7" as $foo; $foo, $foo::foo' input: '0' expected: | [1,2,{"foo":42}] [1,2,{"foo":42}] - name: module directory option args: - -c - -L - 'testdata' - 'import "m1" as $x; $x, $x::x, $x[1].m1*100000000000000000000' input: '0' expected: | [42,{"m1":42}] [42,{"m1":42}] 4200000000000000000000 - name: module directory option with argjson args: - -c - -L - 'testdata' - --argjson - 'x' - '{}' - 'import "m1" as $x; $x' input: '0' expected: | [42,{"m1":42}] - name: module directory option parse error args: - 'include "m"; f' input: '0' error: 'compile error: module not found: "m"' exit_code: 3 - name: module directory option function error args: - -L - 'testdata' - 'include "5"; i' input: '0' error: 'function not defined: i/0' exit_code: 3 - name: module directory option variable error args: - -L - 'testdata' - 'include "4"; $x' input: '0' error: 'compile error: variable not defined: $x' exit_code: 3 - name: module directory option variable name conflict args: - -c - -L - 'testdata' - 'include "6"; include "13"; . as $x | $x, g, h' input: '1' expected: | 1 [1,2,{"foo":42}] [[{"foo":10}],[1,2,{"foo":42}]] - name: module directory option json parse error args: - -L - 'testdata' - 'import "4" as $x; $x' input: '0' error: | compile error: invalid json: testdata/4.json:3 3 | bar ^ invalid character 'b' looking for beginning of value exit_code: 3 - name: module directory option with same alias name args: - -c - -Ltestdata - 'include "4"; import "6" as bar; bar::g' input: '1' expected: | [1,2,{"foo":42}] - name: module directory option without argument args: - -c - -L input: '0' error: | expected argument for flag `-L' exit_code: 2 - name: search directory in query args: - 'include "m2" { search: "testdata/m2" }; g' input: '0' expected: | 0 43 44 - name: empty module args: - -L - 'testdata' - 'include "8"; .' input: '0' expected: | 0 - name: invalid query in a module args: - -L - 'testdata' - 'include "7"; .' input: '0' error: | invalid query in module: testdata/7.jq:1 1 | def f: 1 + ^ unexpected token exit_code: 3 - name: module directive args: - -c - -L - 'testdata' - 'include "9"; f(2)' input: '3' expected: | 6 - name: module in query args: - -n - 'module {}; .' expected: | null - name: modulemeta function args: - -c - -L - 'testdata' - 'tostring | modulemeta' input: '4 5 6 8 9' expected: | {"deps":[{"is_data":false,"relpath":"5"},{"as":"bar","is_data":false,"relpath":"6"}]} {"deps":[{"as":"foo","is_data":false,"relpath":"6","test":"foo"}]} {"deps":[{"as":"x","is_data":true,"relpath":"7"}]} {"deps":[]} {"deps":[],"description":"This is a description","name":"sample-module","x":[[],{},{"y":10,"z":20}],"y":{"z":[128,"",false,true,null,[[]]]}} - name: modulemeta function error args: - -c - -L - 'testdata' - '"3" | modulemeta' input: '0' error: | module not found: "3" - name: arg option args: - --arg - 'foo' - 'value 1' - --arg - 'bar' - '42' - '{ $foo, $bar }' input: '123' expected: | { "bar": "42", "foo": "value 1" } - name: arg option arguments count error args: - --arg error: | expected 2 arguments for flag `--arg' exit_code: 2 - name: arg option arguments count error args: - --arg - 'foo' error: | expected 2 arguments for flag `--arg' exit_code: 2 - name: arg option variable name error args: - --arg - 'f.oo' - '10' - '.' error: | invalid variable name: $f.oo exit_code: 3 - name: argjson option args: - --argjson - 'foo' - '{ "foo": 42 }' - --argjson - 'bar' - '10' - '{ $foo, $bar }' input: '123' expected: | { "bar": 10, "foo": { "foo": 42 } } - name: argjson option with big integer args: - --argjson - 'foo' - '10000000000000000000000000000000' - '$foo * 2' input: '0' expected: | 20000000000000000000000000000000 - name: arg and argjson options args: - --arg - 'x' - '1' - --argjson - 'x' - '1' - --argjson - 'y' - '2' - --arg - 'y' - '2' - '{ $x, $y }' input: '0' expected: | { "x": "1", "y": 2 } - name: argjson option error args: - --argjson - '0x' - '0' - '.' error: | invalid variable name: $0x exit_code: 3 - name: argjson option parse error args: - --argjson - 'foo' - '{ foo }' - '.' error: | invalid json: $foo { foo } ^ invalid character 'f' looking for beginning of object key string - name: slurpfile option args: - --slurpfile - 'foo' - 'testdata/7.json' - '{ $foo }' input: 'null' expected: | { "foo": [ 1, 2, { "foo": 42 } ] } - name: slurpfile option invalid json error args: - --slurpfile - 'foo' - 'testdata/1.jq' - '{ $foo }' input: 'null' error: | invalid json: testdata/1.jq .foo.bar ^ invalid character '.' looking for beginning of value - name: slurpfile option invalid file error args: - --slurpfile - 'foo' - '-' - '{ $foo }' input: 'null' error: 'open -:' - name: rawfile option args: - --rawfile - 'foo' - 'testdata/7.json' - '{ $foo }' input: 'null' expected: | { "foo": "1\n2\n{ \"foo\": 42 }\n" } - name: rawfile option error args: - --rawfile - 'foo' - 'testdata/6.json' - '{ $foo }' input: 'null' error: 'open testdata/6.json:' - name: $ARGS variable args: - --arg - 'a' - '1' - --argjson - 'b' - '1' - --slurpfile - 'c' - 'testdata/1.json' - --rawfile - 'd' - 'testdata/1.json' - '$ARGS | ., .named.a' input: 'null' expected: | { "named": { "a": "1", "b": 1, "c": [ { "foo": 10 } ], "d": "{\"foo\":10}\n" }, "positional": [] } "1" - name: $ARGS variable with args option args: - --arg - 'foo' - 'value' - '$ARGS | ., .positional[]' - --args - '1' - '"bar"' - '{"x":1}' input: '0' expected: | { "named": { "foo": "value" }, "positional": [ "1", "\"bar\"", "{\"x\":1}" ] } "1" "\"bar\"" "{\"x\":1}" - name: args option without argument args: - '$ARGS' - --args input: '0' expected: | { "named": {}, "positional": [] } - name: query and options after args option args: - --args - '$ARGS' - 1 - -n - 2 - -c - -- - 3 - -- - -c input: '0' expected: | {"named":{},"positional":["1","2","3","--","-c"]} - name: double dash with args option args: - --args - -- - 1 - 2 - 3 input: '0' expected: | 0 - name: $ARGS variable with jsonargs option args: - '$ARGS | ., .positional[]' - --arg - 'foo' - 'value' - --jsonargs - '1' - '"bar"' - '{"x":1}' input: '0' expected: | { "named": { "foo": "value" }, "positional": [ 1, "bar", { "x": 1 } ] } 1 "bar" { "x": 1 } - name: jsonargs option without argument args: - '$ARGS' - --jsonargs input: '0' expected: | { "named": {}, "positional": [] } - name: jsonargs option parse error args: - '$ARGS' - --jsonargs - '1' - '"bar"' - '{"x":}' input: '0' error: | invalid json: --jsonargs {"x":} ^ invalid character '}' looking for beginning of value - name: args with jsonargs option args: - -c - '$ARGS' - --args - 1 - 2 - --jsonargs - 1 - 2 - --args - 1 - 2 input: '0' expected: | {"named":{},"positional":["1","2",1,2,"1","2"]} - name: args with jsonargs option args: - -c - '$ARGS' - --args - 1 - 2 - --jsonargs - 1 - 2 - --args - 1 - 2 - --jsonargs - 1 - 2 input: '0' expected: | {"named":{},"positional":["1","2",1,2,"1","2",1,2]} - name: exit status option with null result args: - -e - '.' input: 'null' expected: | null exit_code: 1 - name: exit status option with empty function args: - -e - 'empty' input: 'null' exit_code: 4 - name: exit status long option with false result args: - --exit-status - '.' input: 'false' expected: | false exit_code: 1 - name: exit status long option with true result args: - --exit-status - 'not' input: 'false' expected: | true - name: exit status long option with multiple results args: - --exit-status - '1,not,2' input: 'false' expected: | 1 true 2 - name: exit status long option with error function args: - --exit-status - 'error' input: 'false' error: | error: false exit_code: 5 - name: exit status long option with error function args: - --exit-status - 'error' input: 'null' exit_code: 4 - name: exit status long option with halt function args: - --exit-status - 'halt' input: 'false' exit_code: 0 - name: exit status long option with halt_error function args: - --exit-status - 'halt_error(0)' input: 'null' exit_code: 0 - name: exit status long option with halt_error function args: - --exit-status - 'halt_error(42)' input: 'null' exit_code: 42 - name: query with leading hyphen args: - '-.' input: '1' expected: | -1 - name: query with leading hyphen after a flag args: - -r - '-.' input: '1' expected: | -1 - name: query with leading -0 and spaces args: - '-0 + .' input: '1' expected: | 1 - name: query with leading -0 but no spaces args: - '-0.1' input: '1' expected: | -0.1 - name: query with leading -0 before json files args: - -c - '-0 * 1' - 'testdata/1.json' - 'testdata/2.json' input: '1' expected: | 0 0 - name: double dash args: - '--' - '.' input: '1' expected: | 1 - name: double dash with query with leading hyphen args: - '--' - '-.' input: '1' expected: | -1 - name: double dash with query with leading -0 args: - '--' - '-0' input: '1' expected: | 0 - name: double dash with nul output option args: - '-0' - '--' - '-0' input: '1' expected: "0\x00" - name: double double dash args: - '--' - '--' - '.' input: '1' error: | invalid query: -- -- ^ unexpected token exit_code: 3 - name: short flags args: - -cRr - '.' input: 'foo' expected: | foo - name: invalid short option error args: - -neq - '.' error: | unknown flag `q' exit_code: 2 - name: invalid long option error args: - --qqq - '.' error: | unknown flag `--qqq' exit_code: 2 golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/000077500000000000000000000000001416642156300233145ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/1.jq000066400000000000000000000000111416642156300240000ustar00rootroot00000000000000.foo.bar golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/1.json000066400000000000000000000000131416642156300243410ustar00rootroot00000000000000{"foo":10} golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/1.yaml000066400000000000000000000001001416642156300243270ustar00rootroot00000000000000foo: bar: 42 baz: | a b qux: 100 --- foo: 1 --- bar golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/10.jq000066400000000000000000000000221416642156300240620ustar00rootroot00000000000000.foo, [], bar ] golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/11.jq000066400000000000000000000000271416642156300240700ustar00rootroot00000000000000.foo, [], bar ] golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/12.jq000066400000000000000000000000221416642156300240640ustar00rootroot00000000000000.foo, [], bar ] golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/13.jq000066400000000000000000000000561416642156300240740ustar00rootroot00000000000000import "1" as $x; include "6"; def h: [$x,g]; golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/2.jq000066400000000000000000000000341416642156300240060ustar00rootroot00000000000000.foo | { foo, bar, [] } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/2.json000066400000000000000000000000211416642156300243410ustar00rootroot00000000000000[{ "bar" : [] }] golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/2.yaml000066400000000000000000000000441416642156300243370ustar00rootroot00000000000000foo: bar: a a: b: c baz: 1 golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/3.json000066400000000000000000000000021416642156300243410ustar00rootroot00000000000000{ golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/3.yaml000066400000000000000000000000521416642156300243370ustar00rootroot00000000000000foo: bar: a a: b: c baz: 1 golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/4.jq000066400000000000000000000001371416642156300240140ustar00rootroot00000000000000include "5"; import "6" as bar; def f: [., . * 2, h, (bar::i|.+1), bar::g]; def g: { foo: . }; golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/4.json000066400000000000000000000000271416642156300243510ustar00rootroot00000000000000{ "foo": bar } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/4.yaml000066400000000000000000000000441416642156300243410ustar00rootroot00000000000000foo: bar: a a: b: c baz: 1 golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/5.jq000066400000000000000000000000661416642156300240160ustar00rootroot00000000000000import "6" as foo { test: "foo" }; def h: . * foo::i; golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/5.json000066400000000000000000000010101416642156300243430ustar00rootroot00000000000000{ "a": "This is a test for multibyte character in the error line of JSON. Since the first buffer size of JSON decoder is 512 (see encoding/json/stream.go) and the decoder stops reading bytes on encountering an error, the error line can be an invalid UTF-8 string even if the complete input is a valid UTF-8 string. Note that the error happens before 512 bytes and the corresponding line continues beyond 512 bytes with a multibyte character at the boundary.", "abcde": :★★★★★★★★★★★★★★★ golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/6.jq000066400000000000000000000000471416642156300240160ustar00rootroot00000000000000import "7" as $x; def i: 3; def g: $x; golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/7.jq000066400000000000000000000000131416642156300240100ustar00rootroot00000000000000def f: 1 + golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/7.json000066400000000000000000000000221416642156300243470ustar00rootroot000000000000001 2 { "foo": 42 } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/8.jq000066400000000000000000000000011416642156300240060ustar00rootroot00000000000000 golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/8.json000066400000000000000000000001711416642156300243550ustar00rootroot00000000000000[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ,, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/9.jq000066400000000000000000000003141416642156300240160ustar00rootroot00000000000000module { "name": "sample-module", "description": "This is a description", x: [ [], {}, { y: 10, z: 20 } ], y: { z: [128, "", false, true, null, [[]]] }, }; def f($x): $x * .; golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/9.json000066400000000000000000000012371416642156300243620ustar00rootroot00000000000000["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", , "0", "0"] golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/m1/000077500000000000000000000000001416642156300236315ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/m1/m1.jq000066400000000000000000000000451416642156300245010ustar00rootroot00000000000000import "m2" as x; def f: 42 | x::g; golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/m1/m1.json000066400000000000000000000000221416642156300250330ustar00rootroot0000000000000042 { "m1": 42 } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/m2/000077500000000000000000000000001416642156300236325ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/m2/m2.jq000066400000000000000000000001361416642156300245040ustar00rootroot00000000000000import "m3" as x { search: "./" }; import "m3" as $x { search: "./" }; def g: ., x::g, $x[]; golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/m2/m3.jq000066400000000000000000000000131416642156300244770ustar00rootroot00000000000000def g: 43; golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/m2/m3.json000066400000000000000000000000031416642156300250350ustar00rootroot0000000000000044 golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/m3/000077500000000000000000000000001416642156300236335ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/testdata/m3/m3.jq000066400000000000000000000000131416642156300245000ustar00rootroot00000000000000def g: 45; golang-github-wader-gojq-0.0~git20220108.6a05b6c/cli/yaml.go000066400000000000000000000014111416642156300227710ustar00rootroot00000000000000package cli import ( "fmt" "time" ) // Workaround for https://github.com/go-yaml/yaml/issues/139 func normalizeYAML(v interface{}) interface{} { switch v := v.(type) { case map[interface{}]interface{}: w := make(map[string]interface{}, len(v)) for k, v := range v { w[fmt.Sprint(k)] = normalizeYAML(v) } return w case map[string]interface{}: w := make(map[string]interface{}, len(v)) for k, v := range v { w[k] = normalizeYAML(v) } return w case []interface{}: for i, w := range v { v[i] = normalizeYAML(w) } return v // go-yaml unmarshals timestamp string to time.Time but gojq cannot handle it. // It is impossible to keep the original timestamp strings. case time.Time: return v.Format(time.RFC3339) default: return v } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/cmd/000077500000000000000000000000001416642156300214775ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/cmd/gojq/000077500000000000000000000000001416642156300224375ustar00rootroot00000000000000golang-github-wader-gojq-0.0~git20220108.6a05b6c/cmd/gojq/main.go000066400000000000000000000001421416642156300237070ustar00rootroot00000000000000package main import ( "os" "github.com/wader/gojq/cli" ) func main() { os.Exit(cli.Run()) } golang-github-wader-gojq-0.0~git20220108.6a05b6c/code.go000066400000000000000000000026041416642156300221770ustar00rootroot00000000000000package gojq type code struct { v interface{} op opcode } type opcode int const ( opnop opcode = iota oppush oppop opdup opconst opload opstore opobject opappend opfork opforktrybegin opforktryend opforkalt opforklabel opbacktrack opjump opjumpifnot opcall opcallrec oppushpc opcallpc opscope opret opeach opexpbegin opexpend oppathbegin oppathend ) func (op opcode) String() string { switch op { case opnop: return "nop" case oppush: return "push" case oppop: return "pop" case opdup: return "dup" case opconst: return "const" case opload: return "load" case opstore: return "store" case opobject: return "object" case opappend: return "append" case opfork: return "fork" case opforktrybegin: return "forktrybegin" case opforktryend: return "forktryend" case opforkalt: return "forkalt" case opforklabel: return "forklabel" case opbacktrack: return "backtrack" case opjump: return "jump" case opjumpifnot: return "jumpifnot" case opcall: return "call" case opcallrec: return "callrec" case oppushpc: return "pushpc" case opcallpc: return "callpc" case opscope: return "scope" case opret: return "ret" case opeach: return "each" case opexpbegin: return "expbegin" case opexpend: return "expend" case oppathbegin: return "pathbegin" case oppathend: return "pathend" default: panic(op) } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/compare.go000066400000000000000000000032301416642156300227070ustar00rootroot00000000000000package gojq import ( "math" "math/big" ) func compare(l, r interface{}) int { return BinopTypeSwitch(l, r, nil, func(l, r int) interface{} { switch { case l < r: return -1 case l == r: return 0 default: return 1 } }, func(l, r float64) interface{} { switch { case l < r || math.IsNaN(l): return -1 case l == r: return 0 default: return 1 } }, func(l, r *big.Int) interface{} { return l.Cmp(r) }, func(l, r string) interface{} { switch { case l < r: return -1 case l == r: return 0 default: return 1 } }, func(l, r []interface{}) interface{} { for i := 0; ; i++ { if i >= len(l) { if i >= len(r) { return 0 } return -1 } if i >= len(r) { return 1 } if cmp := compare(l[i], r[i]); cmp != 0 { return cmp } } }, func(l, r map[string]interface{}) interface{} { lk, rk := funcKeys(l), funcKeys(r) if cmp := compare(lk, rk); cmp != 0 { return cmp } for _, k := range lk.([]interface{}) { if cmp := compare(l[k.(string)], r[k.(string)]); cmp != 0 { return cmp } } return 0 }, func(l, r interface{}) interface{} { ln, rn := getTypeOrdNum(l), getTypeOrdNum(r) switch { case ln < rn: return -1 case ln == rn: return 0 default: return 1 } }, ).(int) } func getTypeOrdNum(v interface{}) int { switch v := v.(type) { case nil: return 0 case bool: if v { return 2 } return 1 case int, float64, *big.Int: return 3 case string: return 4 case []interface{}: return 5 case map[string]interface{}: return 6 default: return -1 } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/compiler.go000066400000000000000000001137521416642156300231060ustar00rootroot00000000000000package gojq import ( "context" "encoding/json" "errors" "fmt" "sort" "strconv" "strings" ) type compiler struct { moduleLoader ModuleLoader environLoader func() []string variables []string customFuncs map[string]function inputIter Iter codes []*code codeinfos []codeinfo scopes []*scopeinfo scopecnt int } // Code is a compiled jq query. type Code struct { variables []string codes []*code codeinfos []codeinfo } // Run runs the code with the variable values (which should be in the // same order as the given variables using WithVariables) and returns // a result iterator. // // It is safe to call this method of a *Code in multiple goroutines. func (c *Code) Run(v interface{}, values ...interface{}) Iter { return c.RunWithContext(context.Background(), v, values...) } // RunWithContext runs the code with context. func (c *Code) RunWithContext(ctx context.Context, v interface{}, values ...interface{}) Iter { if len(values) > len(c.variables) { return NewIter(&tooManyVariableValuesError{}) } else if len(values) < len(c.variables) { return NewIter(&expectedVariableError{c.variables[len(values)]}) } for i, v := range values { values[i] = normalizeNumbers(v) } return newEnv(ctx).execute(c, normalizeNumbers(v), values...) } // ModuleLoader is an interface for loading modules. // // Implement following optional methods. Use NewModuleLoader to load local modules. // LoadModule(string) (*Query, error) // LoadModuleWithMeta(string, map[string]interface{}) (*Query, error) // LoadInitModules() ([]*Query, error) // LoadJSON(string) (interface{}, error) // LoadJSONWithMeta(string, map[string]interface{}) (interface{}, error) type ModuleLoader interface{} type scopeinfo struct { variables []*varinfo funcs []*funcinfo id int depth int variablecnt int } type varinfo struct { name string index [2]int depth int } type funcinfo struct { name string pc int argcnt int } // Compile compiles a query. func Compile(q *Query, options ...CompilerOption) (*Code, error) { c := &compiler{} for _, opt := range options { opt(c) } scope := c.newScope() c.scopes = []*scopeinfo{scope} setscope := c.lazy(func() *code { return &code{op: opscope, v: [3]int{scope.id, scope.variablecnt, 0}} }) if c.moduleLoader != nil { if moduleLoader, ok := c.moduleLoader.(interface { LoadInitModules() ([]*Query, error) }); ok { qs, err := moduleLoader.LoadInitModules() if err != nil { return nil, err } for _, q := range qs { if err := c.compileModule(q, ""); err != nil { return nil, err } } } } if err := c.compile(q); err != nil { return nil, err } setscope() c.optimizeTailRec() c.optimizeCodeOps() return &Code{ variables: c.variables, codes: c.codes, codeinfos: c.codeinfos, }, nil } func (c *compiler) compile(q *Query) error { for _, name := range c.variables { if !newLexer(name).validVarName() { return &variableNameError{name} } c.appendCodeInfo(name) c.append(&code{op: opstore, v: c.pushVariable(name)}) } for _, i := range q.Imports { if err := c.compileImport(i); err != nil { return err } } if err := c.compileQuery(q); err != nil { return err } c.append(&code{op: opret}) return nil } func (c *compiler) compileImport(i *Import) error { var path, alias string var err error if i.ImportPath != "" { path, alias = i.ImportPath, i.ImportAlias } else { path = i.IncludePath } if c.moduleLoader == nil { return fmt.Errorf("cannot load module: %q", path) } if strings.HasPrefix(alias, "$") { var vals interface{} if moduleLoader, ok := c.moduleLoader.(interface { LoadJSONWithMeta(string, map[string]interface{}) (interface{}, error) }); ok { if vals, err = moduleLoader.LoadJSONWithMeta(path, i.Meta.ToValue()); err != nil { return err } } else if moduleLoader, ok := c.moduleLoader.(interface { LoadJSON(string) (interface{}, error) }); ok { if vals, err = moduleLoader.LoadJSON(path); err != nil { return err } } else { return fmt.Errorf("module not found: %q", path) } vals = normalizeNumbers(vals) c.append(&code{op: oppush, v: vals}) c.append(&code{op: opstore, v: c.pushVariable(alias)}) c.append(&code{op: oppush, v: vals}) c.append(&code{op: opstore, v: c.pushVariable(alias + "::" + alias[1:])}) return nil } var q *Query if moduleLoader, ok := c.moduleLoader.(interface { LoadModuleWithMeta(string, map[string]interface{}) (*Query, error) }); ok { if q, err = moduleLoader.LoadModuleWithMeta(path, i.Meta.ToValue()); err != nil { return err } } else if moduleLoader, ok := c.moduleLoader.(interface { LoadModule(string) (*Query, error) }); ok { if q, err = moduleLoader.LoadModule(path); err != nil { return err } } c.appendCodeInfo("module " + path) defer c.appendCodeInfo("end of module " + path) return c.compileModule(q, alias) } func (c *compiler) compileModule(q *Query, alias string) error { scope := c.scopes[len(c.scopes)-1] scope.depth++ defer func(l int) { scope.depth-- scope.variables = scope.variables[:l] }(len(scope.variables)) if alias != "" { defer func(l int) { for _, f := range scope.funcs[l:] { f.name = alias + "::" + f.name } }(len(scope.funcs)) } for _, i := range q.Imports { if err := c.compileImport(i); err != nil { return err } } for _, fd := range q.FuncDefs { if err := c.compileFuncDef(fd, false); err != nil { return err } } return nil } func (c *compiler) newVariable() [2]int { return c.createVariable("") } func (c *compiler) pushVariable(name string) [2]int { s := c.scopes[len(c.scopes)-1] for _, v := range s.variables { if v.name == name && v.depth == s.depth { return v.index } } return c.createVariable(name) } func (c *compiler) createVariable(name string) [2]int { s := c.scopes[len(c.scopes)-1] v := [2]int{s.id, s.variablecnt} s.variablecnt++ s.variables = append(s.variables, &varinfo{name, v, s.depth}) return v } func (c *compiler) lookupVariable(name string) ([2]int, error) { for i := len(c.scopes) - 1; i >= 0; i-- { s := c.scopes[i] for j := len(s.variables) - 1; j >= 0; j-- { if w := s.variables[j]; w.name == name { return w.index, nil } } } return [2]int{}, &variableNotFoundError{name} } func (c *compiler) lookupFuncOrVariable(name string) (*funcinfo, *varinfo) { for i, isFunc := len(c.scopes)-1, name[0] != '$'; i >= 0; i-- { s := c.scopes[i] if isFunc { for j := len(s.funcs) - 1; j >= 0; j-- { if f := s.funcs[j]; f.name == name && f.argcnt == 0 { return f, nil } } } for j := len(s.variables) - 1; j >= 0; j-- { if v := s.variables[j]; v.name == name { return nil, v } } } return nil, nil } func (c *compiler) newScope() *scopeinfo { i := c.scopecnt // do not use len(c.scopes) because it pops c.scopecnt++ return &scopeinfo{id: i} } func (c *compiler) newScopeDepth() func() { scope := c.scopes[len(c.scopes)-1] l, m := len(scope.variables), len(scope.funcs) scope.depth++ return func() { scope.depth-- scope.variables = scope.variables[:l] scope.funcs = scope.funcs[:m] } } func (c *compiler) compileFuncDef(e *FuncDef, builtin bool) error { var scope *scopeinfo if builtin { scope = c.scopes[0] for i := len(scope.funcs) - 1; i >= 0; i-- { if f := scope.funcs[i]; f.name == e.Name && f.argcnt == len(e.Args) { return nil } } } else { scope = c.scopes[len(c.scopes)-1] } defer c.lazy(func() *code { return &code{op: opjump, v: c.pc()} })() c.appendCodeInfo(e.Name) defer c.appendCodeInfo("end of " + e.Name) pc := c.pc() scope.funcs = append(scope.funcs, &funcinfo{e.Name, pc, len(e.Args)}) defer func(scopes []*scopeinfo, variables []string) { c.scopes, c.variables = scopes, variables }(c.scopes, c.variables) c.variables = c.variables[len(c.variables):] scope = c.newScope() if builtin { c.scopes = []*scopeinfo{c.scopes[0], scope} } else { c.scopes = append(c.scopes, scope) } defer c.lazy(func() *code { return &code{op: opscope, v: [3]int{scope.id, scope.variablecnt, len(e.Args)}} })() if len(e.Args) > 0 { type varIndex struct { name string index [2]int } vis := make([]varIndex, 0, len(e.Args)) v := c.newVariable() c.append(&code{op: opstore, v: v}) for _, arg := range e.Args { if arg[0] == '$' { c.appendCodeInfo(arg[1:]) w := c.createVariable(arg[1:]) c.append(&code{op: opstore, v: w}) vis = append(vis, varIndex{arg, w}) } else { c.appendCodeInfo(arg) c.append(&code{op: opstore, v: c.createVariable(arg)}) } } for _, w := range vis { c.append(&code{op: opload, v: v}) c.append(&code{op: opload, v: w.index}) c.append(&code{op: opcallpc}) c.appendCodeInfo(w.name) c.append(&code{op: opstore, v: c.pushVariable(w.name)}) } c.append(&code{op: opload, v: v}) } return c.compile(e.Body) } func (c *compiler) compileQuery(e *Query) error { for _, fd := range e.FuncDefs { if err := c.compileFuncDef(fd, false); err != nil { return err } } if e.Func != "" { switch e.Func { case ".": return c.compileTerm(&Term{Type: TermTypeIdentity}) case "..": return c.compileTerm(&Term{Type: TermTypeRecurse}) case "null": return c.compileTerm(&Term{Type: TermTypeNull}) case "true": return c.compileTerm(&Term{Type: TermTypeTrue}) case "false": return c.compileTerm(&Term{Type: TermTypeFalse}) default: return c.compileFunc(&Func{Name: e.Func}) } } else if e.Term != nil { return c.compileTerm(e.Term) } switch e.Op { case OpPipe: if err := c.compileQuery(e.Left); err != nil { return err } return c.compileQuery(e.Right) case OpComma: return c.compileComma(e.Left, e.Right) case OpAlt: return c.compileAlt(e.Left, e.Right) case OpAssign, OpModify, OpUpdateAdd, OpUpdateSub, OpUpdateMul, OpUpdateDiv, OpUpdateMod, OpUpdateAlt: return c.compileQueryUpdate(e.Left, e.Right, e.Op) case OpOr: return c.compileIf( &If{ Cond: e.Left, Then: &Query{Term: &Term{Type: TermTypeTrue}}, Else: &Query{Term: &Term{Type: TermTypeIf, If: &If{ Cond: e.Right, Then: &Query{Term: &Term{Type: TermTypeTrue}}, Else: &Query{Term: &Term{Type: TermTypeFalse}}, }}}, }, ) case OpAnd: return c.compileIf( &If{ Cond: e.Left, Then: &Query{Term: &Term{Type: TermTypeIf, If: &If{ Cond: e.Right, Then: &Query{Term: &Term{Type: TermTypeTrue}}, Else: &Query{Term: &Term{Type: TermTypeFalse}}, }}}, Else: &Query{Term: &Term{Type: TermTypeFalse}}, }, ) default: return c.compileCall( e.Op.getFunc(), []*Query{e.Left, e.Right}, ) } } func (c *compiler) compileComma(l, r *Query) error { setfork := c.lazy(func() *code { return &code{op: opfork, v: c.pc() + 1} }) if err := c.compileQuery(l); err != nil { return err } setfork() defer c.lazy(func() *code { return &code{op: opjump, v: c.pc()} })() return c.compileQuery(r) } func (c *compiler) compileAlt(l, r *Query) error { c.append(&code{op: oppush, v: false}) found := c.newVariable() c.append(&code{op: opstore, v: found}) setfork := c.lazy(func() *code { return &code{op: opfork, v: c.pc()} // opload found }) if err := c.compileQuery(l); err != nil { return err } c.append(&code{op: opdup}) c.append(&code{op: opjumpifnot, v: c.pc() + 4}) // oppop c.append(&code{op: oppush, v: true}) // found some value c.append(&code{op: opstore, v: found}) defer c.lazy(func() *code { return &code{op: opjump, v: c.pc()} // ret })() c.append(&code{op: oppop}) c.append(&code{op: opbacktrack}) setfork() c.append(&code{op: opload, v: found}) c.append(&code{op: opjumpifnot, v: c.pc() + 3}) c.append(&code{op: opbacktrack}) // if found, backtrack c.append(&code{op: oppop}) return c.compileQuery(r) } func (c *compiler) compileQueryUpdate(l, r *Query, op Operator) error { switch op { case OpAssign: // .foo.bar = f => setpath(["foo", "bar"]; f) if xs := l.toIndices(); xs != nil { // ref: compileCall v := c.newVariable() c.append(&code{op: opstore, v: v}) c.append(&code{op: opload, v: v}) if err := c.compileQuery(r); err != nil { return err } c.append(&code{op: oppush, v: xs}) c.append(&code{op: opload, v: v}) c.append(&code{op: opcall, v: [3]interface{}{internalFuncs["setpath"].callback, 2, "setpath"}}) return nil } fallthrough case OpModify: return c.compileFunc( &Func{ Name: op.getFunc(), Args: []*Query{l, r}, }, ) default: name := "$%0" c.append(&code{op: opdup}) if err := c.compileQuery(r); err != nil { return err } c.append(&code{op: opstore, v: c.pushVariable(name)}) return c.compileFunc( &Func{ Name: "_modify", Args: []*Query{ l, {Term: &Term{ Type: TermTypeFunc, Func: &Func{ Name: op.getFunc(), Args: []*Query{ {Term: &Term{Type: TermTypeIdentity}}, {Func: name}, }, }, }}, }, }, ) } } func (c *compiler) compileBind(b *Bind) error { var pc int var vs [][2]int for i, p := range b.Patterns { var pcc int var err error if i < len(b.Patterns)-1 { defer c.lazy(func() *code { return &code{op: opforkalt, v: pcc} })() } if 0 < i { for _, v := range vs { c.append(&code{op: oppush, v: nil}) c.append(&code{op: opstore, v: v}) } } vs, err = c.compilePattern(p) if err != nil { return err } if i < len(b.Patterns)-1 { defer c.lazy(func() *code { return &code{op: opjump, v: pc} })() pcc = c.pc() } } if len(b.Patterns) > 1 { pc = c.pc() } if len(b.Patterns) == 1 && c.codes[len(c.codes)-2].op == opexpbegin { c.codes[len(c.codes)-2].op = opnop } else { c.append(&code{op: opexpend}) // ref: compileTermSuffix } return c.compileQuery(b.Body) } func (c *compiler) compilePattern(p *Pattern) ([][2]int, error) { c.appendCodeInfo(p) if p.Name != "" { v := c.pushVariable(p.Name) c.append(&code{op: opstore, v: v}) return [][2]int{v}, nil } else if len(p.Array) > 0 { var vs [][2]int v := c.newVariable() c.append(&code{op: opstore, v: v}) for i, p := range p.Array { c.append(&code{op: oppush, v: i}) c.append(&code{op: opload, v: v}) c.append(&code{op: opload, v: v}) // ref: compileCall c.append(&code{op: opcall, v: [3]interface{}{internalFuncs["_index"].callback, 2, "_index"}}) ns, err := c.compilePattern(p) if err != nil { return nil, err } vs = append(vs, ns...) } return vs, nil } else if len(p.Object) > 0 { var vs [][2]int v := c.newVariable() c.append(&code{op: opstore, v: v}) for _, kv := range p.Object { var key, name string if kv.KeyOnly != "" { key, name = kv.KeyOnly[1:], kv.KeyOnly c.append(&code{op: oppush, v: key}) } else if kv.Key != "" { key = kv.Key if key != "" && key[0] == '$' { key, name = key[1:], key } c.append(&code{op: oppush, v: key}) } else if kv.KeyString != nil { c.append(&code{op: opload, v: v}) if err := c.compileString(kv.KeyString, nil); err != nil { return nil, err } } else if kv.KeyQuery != nil { c.append(&code{op: opload, v: v}) if err := c.compileQuery(kv.KeyQuery); err != nil { return nil, err } } c.append(&code{op: opload, v: v}) c.append(&code{op: opload, v: v}) // ref: compileCall c.append(&code{op: opcall, v: [3]interface{}{internalFuncs["_index"].callback, 2, "_index"}}) if name != "" { if kv.Val != nil { c.append(&code{op: opdup}) } ns, err := c.compilePattern(&Pattern{Name: name}) if err != nil { return nil, err } vs = append(vs, ns...) } if kv.Val != nil { ns, err := c.compilePattern(kv.Val) if err != nil { return nil, err } vs = append(vs, ns...) } } return vs, nil } else { return nil, fmt.Errorf("invalid pattern: %s", p) } } func (c *compiler) compileIf(e *If) error { c.appendCodeInfo(e) c.append(&code{op: opdup}) // duplicate the value for then or else clause c.append(&code{op: opexpbegin}) pc := len(c.codes) f := c.newScopeDepth() if err := c.compileQuery(e.Cond); err != nil { return err } f() if pc == len(c.codes) { c.codes = c.codes[:pc-1] } else { c.append(&code{op: opexpend}) } pcc := len(c.codes) setjumpifnot := c.lazy(func() *code { return &code{op: opjumpifnot, v: c.pc() + 1} // if falsy, skip then clause }) f = c.newScopeDepth() if err := c.compileQuery(e.Then); err != nil { return err } f() setjumpifnot() defer c.lazy(func() *code { return &code{op: opjump, v: c.pc()} // jump to ret after else clause })() if len(e.Elif) > 0 { return c.compileIf(&If{e.Elif[0].Cond, e.Elif[0].Then, e.Elif[1:], e.Else}) } if e.Else != nil { defer c.newScopeDepth()() defer func() { // optimize constant results // opdup, ..., opjumpifnot, opconst, opjump, opconst // => opnop, ..., opjumpifnot, oppush, opjump, oppush if pcc+4 == len(c.codes) && c.codes[pcc+1] != nil && c.codes[pcc+1].op == opconst && c.codes[pcc+3] != nil && c.codes[pcc+3].op == opconst { c.codes[pc-2].op = opnop c.codes[pcc+1].op = oppush c.codes[pcc+3].op = oppush } }() return c.compileQuery(e.Else) } return nil } func (c *compiler) compileTry(e *Try) error { c.appendCodeInfo(e) setforktrybegin := c.lazy(func() *code { return &code{op: opforktrybegin, v: c.pc()} }) f := c.newScopeDepth() if err := c.compileQuery(e.Body); err != nil { return err } f() c.append(&code{op: opforktryend}) defer c.lazy(func() *code { return &code{op: opjump, v: c.pc()} })() setforktrybegin() if e.Catch != nil { defer c.newScopeDepth()() return c.compileQuery(e.Catch) } c.append(&code{op: opbacktrack}) return nil } func (c *compiler) compileReduce(e *Reduce) error { c.appendCodeInfo(e) defer c.newScopeDepth()() defer c.lazy(func() *code { return &code{op: opfork, v: c.pc() - 2} })() c.append(&code{op: opdup}) v := c.newVariable() f := c.newScopeDepth() if err := c.compileQuery(e.Start); err != nil { return err } f() c.append(&code{op: opstore, v: v}) if err := c.compileTerm(e.Term); err != nil { return err } if _, err := c.compilePattern(e.Pattern); err != nil { return err } c.append(&code{op: opload, v: v}) f = c.newScopeDepth() if err := c.compileQuery(e.Update); err != nil { return err } f() c.append(&code{op: opstore, v: v}) c.append(&code{op: opbacktrack}) c.append(&code{op: oppop}) c.append(&code{op: opload, v: v}) return nil } func (c *compiler) compileForeach(e *Foreach) error { c.appendCodeInfo(e) defer c.newScopeDepth()() c.append(&code{op: opdup}) v := c.newVariable() f := c.newScopeDepth() if err := c.compileQuery(e.Start); err != nil { return err } f() c.append(&code{op: opstore, v: v}) if err := c.compileTerm(e.Term); err != nil { return err } if _, err := c.compilePattern(e.Pattern); err != nil { return err } c.append(&code{op: opload, v: v}) f = c.newScopeDepth() if err := c.compileQuery(e.Update); err != nil { return err } f() c.append(&code{op: opdup}) c.append(&code{op: opstore, v: v}) if e.Extract != nil { defer c.newScopeDepth()() return c.compileQuery(e.Extract) } return nil } func (c *compiler) compileLabel(e *Label) error { c.appendCodeInfo(e) v := c.pushVariable("$%" + e.Ident[1:]) defer c.lazy(func() *code { return &code{op: opforklabel, v: v} })() return c.compileQuery(e.Body) } func (c *compiler) compileBreak(label string) error { v, err := c.lookupVariable("$%" + label[1:]) if err != nil { return &breakError{label, nil} } c.append(&code{op: oppop}) c.append(&code{op: opload, v: v}) c.append(&code{op: opcall, v: [3]interface{}{ func(v interface{}, _ []interface{}) interface{} { return &breakError{label, v} }, 0, "_break", }}) return nil } func (c *compiler) compileTerm(e *Term) error { if len(e.SuffixList) > 0 { s := e.SuffixList[len(e.SuffixList)-1] t := *e // clone without changing e (&t).SuffixList = t.SuffixList[:len(e.SuffixList)-1] return c.compileTermSuffix(&t, s) } switch e.Type { case TermTypeIdentity: return nil case TermTypeRecurse: return c.compileFunc(&Func{Name: "recurse"}) case TermTypeNull: c.append(&code{op: opconst, v: nil}) return nil case TermTypeTrue: c.append(&code{op: opconst, v: true}) return nil case TermTypeFalse: c.append(&code{op: opconst, v: false}) return nil case TermTypeIndex: return c.compileIndex(&Term{Type: TermTypeIdentity}, e.Index) case TermTypeFunc: return c.compileFunc(e.Func) case TermTypeObject: return c.compileObject(e.Object) case TermTypeArray: return c.compileArray(e.Array) case TermTypeNumber: v := normalizeNumbers(json.Number(e.Number)) if err, ok := v.(error); ok { return err } c.append(&code{op: opconst, v: v}) return nil case TermTypeUnary: return c.compileUnary(e.Unary) case TermTypeFormat: return c.compileFormat(e.Format, e.Str) case TermTypeString: return c.compileString(e.Str, nil) case TermTypeIf: return c.compileIf(e.If) case TermTypeTry: return c.compileTry(e.Try) case TermTypeReduce: return c.compileReduce(e.Reduce) case TermTypeForeach: return c.compileForeach(e.Foreach) case TermTypeLabel: return c.compileLabel(e.Label) case TermTypeBreak: return c.compileBreak(e.Break) case TermTypeQuery: defer c.newScopeDepth()() return c.compileQuery(e.Query) default: panic("invalid term: " + e.String()) } } func (c *compiler) compileIndex(e *Term, x *Index) error { c.appendCodeInfo(x) if x.Name != "" { return c.compileCall("_index", []*Query{{Term: e}, {Term: &Term{Type: TermTypeString, Str: &String{Str: x.Name}}}}) } if x.Str != nil { return c.compileCall("_index", []*Query{{Term: e}, {Term: &Term{Type: TermTypeString, Str: x.Str}}}) } if !x.IsSlice { return c.compileCall("_index", []*Query{{Term: e}, x.Start}) } if x.Start == nil { return c.compileCall("_slice", []*Query{{Term: e}, x.End, {Term: &Term{Type: TermTypeNull}}}) } if x.End == nil { return c.compileCall("_slice", []*Query{{Term: e}, {Term: &Term{Type: TermTypeNull}}, x.Start}) } return c.compileCall("_slice", []*Query{{Term: e}, x.End, x.Start}) } func (c *compiler) compileFunc(e *Func) error { name := e.Name if len(e.Args) == 0 { if f, v := c.lookupFuncOrVariable(name); f != nil { return c.compileCallPc(f, e.Args) } else if v != nil { if name[0] == '$' { c.append(&code{op: oppop}) c.append(&code{op: opload, v: v.index}) } else { c.append(&code{op: opload, v: v.index}) c.append(&code{op: opcallpc}) } return nil } else if name == "$ENV" || name == "env" { env := make(map[string]interface{}) if c.environLoader != nil { for _, kv := range c.environLoader() { if i := strings.IndexByte(kv, '='); i > 0 { env[kv[:i]] = kv[i+1:] } } } c.append(&code{op: opconst, v: env}) return nil } else if name[0] == '$' { return &variableNotFoundError{name} } } else { for i := len(c.scopes) - 1; i >= 0; i-- { s := c.scopes[i] for j := len(s.funcs) - 1; j >= 0; j-- { if f := s.funcs[j]; f.name == name && f.argcnt == len(e.Args) { return c.compileCallPc(f, e.Args) } } } } if name[0] == '_' { name = name[1:] } if fds, ok := builtinFuncDefs[name]; ok { for _, fd := range fds { if len(fd.Args) == len(e.Args) { if err := c.compileFuncDef(fd, true); err != nil { return err } } } s := c.scopes[0] for i := len(s.funcs) - 1; i >= 0; i-- { if f := s.funcs[i]; f.name == e.Name && f.argcnt == len(e.Args) { return c.compileCallPc(f, e.Args) } } } if fn, ok := internalFuncs[e.Name]; ok && fn.accept(len(e.Args)) { switch e.Name { case "empty": c.append(&code{op: opbacktrack}) return nil case "path": c.append(&code{op: oppathbegin}) if err := c.compileCall(e.Name, e.Args); err != nil { return err } c.codes[len(c.codes)-1] = &code{op: oppathend} return nil case "builtins": return c.compileCallInternal( [3]interface{}{c.funcBuiltins, 0, e.Name}, e.Args, true, false, ) case "scope": return c.compileCallInternal( [3]interface{}{c.funcScope, 0, e.Name}, e.Args, true, false, ) case "input": if c.inputIter == nil { return &inputNotAllowedError{} } return c.compileCallInternal( [3]interface{}{c.funcInput, 0, e.Name}, e.Args, true, false, ) case "modulemeta": return c.compileCallInternal( [3]interface{}{c.funcModulemeta, 0, e.Name}, e.Args, true, false, ) case "scopedump": c.append(&code{op: oppop}) n := 0 for i := len(c.scopes) - 1; i >= 0; i-- { s := c.scopes[i] for j := len(s.variables) - 1; j >= 0; j-- { v := s.variables[j] c.append(&code{op: oppush, v: v.name}) c.append(&code{op: opload, v: v.index}) n++ } } c.append(&code{op: opobject, v: n}) return nil default: return c.compileCall(e.Name, e.Args) } } if fn, ok := c.customFuncs[e.Name]; ok && fn.accept(len(e.Args)) { if err := c.compileCallInternal( [3]interface{}{fn.callback, len(e.Args), e.Name}, e.Args, true, false, ); err != nil { return err } if fn.iter { c.append(&code{op: opeach}) } return nil } return &funcNotFoundError{e} } func (c *compiler) funcBuiltins(interface{}, []interface{}) interface{} { type funcNameArity struct { name string arity int } var xs []*funcNameArity for _, fds := range builtinFuncDefs { for _, fd := range fds { if fd.Name[0] != '_' { xs = append(xs, &funcNameArity{fd.Name, len(fd.Args)}) } } } for name, fn := range internalFuncs { if name[0] != '_' { for i, cnt := 0, fn.argcount; cnt > 0; i, cnt = i+1, cnt>>1 { if cnt&1 > 0 { xs = append(xs, &funcNameArity{name, i}) } } } } for name, fn := range c.customFuncs { if name[0] != '_' { for i, cnt := 0, fn.argcount; cnt > 0; i, cnt = i+1, cnt>>1 { if cnt&1 > 0 { xs = append(xs, &funcNameArity{name, i}) } } } } sort.Slice(xs, func(i, j int) bool { return xs[i].name < xs[j].name || xs[i].name == xs[j].name && xs[i].arity < xs[j].arity }) ys := make([]interface{}, len(xs)) for i, x := range xs { ys[i] = x.name + "/" + strconv.Itoa(x.arity) } return ys } func (c *compiler) funcScope(interface{}, []interface{}) interface{} { var xs []interface{} for _, fds := range builtinFuncDefs { xs = append(xs, fds[0].Name) } for name := range internalFuncs { xs = append(xs, name) } for name := range c.customFuncs { xs = append(xs, name) } if c.environLoader != nil { xs = append(xs, "$ENV") } for i := len(c.scopes) - 1; i >= 0; i-- { s := c.scopes[i] for j := len(s.variables) - 1; j >= 0; j-- { v := s.variables[j] xs = append(xs, v.name) } for j := len(s.funcs) - 1; j >= 0; j-- { f := s.funcs[j] xs = append(xs, f.name) } } sort.Slice(xs, func(i, j int) bool { return xs[i].(string) < xs[j].(string) }) return xs } func (c *compiler) funcInput(interface{}, []interface{}) interface{} { v, ok := c.inputIter.Next() if !ok { return errors.New("break") } return normalizeNumbers(v) } func (c *compiler) funcModulemeta(v interface{}, _ []interface{}) interface{} { s, ok := v.(string) if !ok { return &funcTypeError{"modulemeta", v} } if c.moduleLoader == nil { return fmt.Errorf("cannot load module: %q", s) } var q *Query var err error if moduleLoader, ok := c.moduleLoader.(interface { LoadModuleWithMeta(string, map[string]interface{}) (*Query, error) }); ok { if q, err = moduleLoader.LoadModuleWithMeta(s, nil); err != nil { return err } } else if moduleLoader, ok := c.moduleLoader.(interface { LoadModule(string) (*Query, error) }); ok { if q, err = moduleLoader.LoadModule(s); err != nil { return err } } meta := q.Meta.ToValue() if meta == nil { meta = make(map[string]interface{}) } var deps []interface{} for _, i := range q.Imports { v := i.Meta.ToValue() if v == nil { v = make(map[string]interface{}) } else { for k := range v { // dirty hack to remove the internal fields if strings.HasPrefix(k, "$$") { delete(v, k) } } } if i.ImportPath == "" { v["relpath"] = i.IncludePath } else { v["relpath"] = i.ImportPath } if err != nil { return err } if i.ImportAlias != "" { v["as"] = strings.TrimPrefix(i.ImportAlias, "$") } v["is_data"] = strings.HasPrefix(i.ImportAlias, "$") deps = append(deps, v) } meta["deps"] = deps return meta } func (c *compiler) compileObject(e *Object) error { c.appendCodeInfo(e) if len(e.KeyVals) == 0 { c.append(&code{op: opconst, v: map[string]interface{}{}}) return nil } defer c.newScopeDepth()() v := c.newVariable() c.append(&code{op: opstore, v: v}) pc := len(c.codes) for _, kv := range e.KeyVals { if err := c.compileObjectKeyVal(v, kv); err != nil { return err } } c.append(&code{op: opobject, v: len(e.KeyVals)}) // optimize constant objects l := len(e.KeyVals) if pc+l*3+1 != len(c.codes) { return nil } for i := 0; i < l; i++ { if c.codes[pc+i*3].op != oppush || c.codes[pc+i*3+1].op != opload || c.codes[pc+i*3+2].op != opconst { return nil } } w := make(map[string]interface{}, l) for i := 0; i < l; i++ { w[c.codes[pc+i*3].v.(string)] = c.codes[pc+i*3+2].v } c.codes[pc-1] = &code{op: opconst, v: w} c.codes = c.codes[:pc] return nil } func (c *compiler) compileObjectKeyVal(v [2]int, kv *ObjectKeyVal) error { if kv.KeyOnly != "" { if kv.KeyOnly[0] == '$' { c.append(&code{op: oppush, v: kv.KeyOnly[1:]}) c.append(&code{op: opload, v: v}) return c.compileFunc(&Func{Name: kv.KeyOnly}) } c.append(&code{op: oppush, v: kv.KeyOnly}) c.append(&code{op: opload, v: v}) return c.compileIndex(&Term{Type: TermTypeIdentity}, &Index{Name: kv.KeyOnly}) } else if kv.KeyOnlyString != nil { c.append(&code{op: opload, v: v}) if err := c.compileString(kv.KeyOnlyString, nil); err != nil { return err } c.append(&code{op: opdup}) c.append(&code{op: opload, v: v}) c.append(&code{op: opload, v: v}) // ref: compileCall c.append(&code{op: opcall, v: [3]interface{}{internalFuncs["_index"].callback, 2, "_index"}}) return nil } else { if kv.KeyQuery != nil { c.append(&code{op: opload, v: v}) f := c.newScopeDepth() if err := c.compileQuery(kv.KeyQuery); err != nil { return err } f() } else if kv.KeyString != nil { c.append(&code{op: opload, v: v}) if err := c.compileString(kv.KeyString, nil); err != nil { return err } if d := c.codes[len(c.codes)-1]; d.op == opconst { c.codes[len(c.codes)-2] = &code{op: oppush, v: d.v} c.codes = c.codes[:len(c.codes)-1] } } else if kv.Key[0] == '$' { c.append(&code{op: opload, v: v}) if err := c.compileFunc(&Func{Name: kv.Key}); err != nil { return err } } else { c.append(&code{op: oppush, v: kv.Key}) } c.append(&code{op: opload, v: v}) return c.compileObjectVal(kv.Val) } } func (c *compiler) compileObjectVal(e *ObjectVal) error { for _, e := range e.Queries { if err := c.compileQuery(e); err != nil { return err } } return nil } func (c *compiler) compileArray(e *Array) error { c.appendCodeInfo(e) if e.Query == nil { c.append(&code{op: opconst, v: []interface{}{}}) return nil } c.append(&code{op: oppush, v: []interface{}{}}) arr := c.newVariable() c.append(&code{op: opstore, v: arr}) pc := len(c.codes) c.append(&code{op: opfork}) defer func() { if pc < len(c.codes) { c.codes[pc].v = c.pc() - 2 } }() defer c.newScopeDepth()() if err := c.compileQuery(e.Query); err != nil { return err } c.append(&code{op: opappend, v: arr}) c.append(&code{op: opbacktrack}) c.append(&code{op: oppop}) c.append(&code{op: opload, v: arr}) if e.Query.Op == OpPipe { return nil } // optimize constant arrays if (len(c.codes)-pc)%3 != 0 { return nil } l := (len(c.codes) - pc - 3) / 3 for i := 0; i < l; i++ { if c.codes[pc+i].op != opfork || c.codes[pc+i*2+l].op != opconst || (i < l-1 && c.codes[pc+i*2+l+1].op != opjump) { return nil } } v := make([]interface{}, l) for i := 0; i < l; i++ { v[i] = c.codes[pc+i*2+l].v } c.codes[pc-2] = &code{op: opconst, v: v} c.codes = c.codes[:pc-1] return nil } func (c *compiler) compileUnary(e *Unary) error { c.appendCodeInfo(e) if err := c.compileTerm(e.Term); err != nil { return err } switch e.Op { case OpAdd: return c.compileCall("_plus", nil) case OpSub: return c.compileCall("_negate", nil) default: return fmt.Errorf("unexpected operator in Unary: %s", e.Op) } } func (c *compiler) compileFormat(fmt string, str *String) error { f := formatToFunc(fmt) if f == nil { f = &Func{ Name: "format", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: fmt[1:]}}}}, } } if str == nil { return c.compileFunc(f) } return c.compileString(str, f) } func formatToFunc(fmt string) *Func { switch fmt { case "@text": return &Func{Name: "tostring"} case "@json": return &Func{Name: "tojson"} case "@html": return &Func{Name: "_tohtml"} case "@uri": return &Func{Name: "_touri"} case "@csv": return &Func{Name: "_tocsv"} case "@tsv": return &Func{Name: "_totsv"} case "@sh": return &Func{Name: "_tosh"} case "@base64": return &Func{Name: "_tobase64"} case "@base64d": return &Func{Name: "_tobase64d"} default: return nil } } func (c *compiler) compileString(s *String, f *Func) error { if s.Queries == nil { c.append(&code{op: opconst, v: s.Str}) return nil } if f == nil { f = &Func{Name: "tostring"} } var q *Query for _, e := range s.Queries { if e.Term.Str == nil { e = &Query{Left: e, Op: OpPipe, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: f}}} } if q == nil { q = e } else { q = &Query{Left: q, Op: OpAdd, Right: e} } } return c.compileQuery(q) } func (c *compiler) compileTermSuffix(e *Term, s *Suffix) error { if s.Index != nil { return c.compileIndex(e, s.Index) } else if s.Iter { if err := c.compileTerm(e); err != nil { return err } c.append(&code{op: opeach}) return nil } else if s.Optional { if len(e.SuffixList) > 1 || len(e.SuffixList) == 1 && !e.SuffixList[0].Iter { if u, ok := e.SuffixList[len(e.SuffixList)-1].toTerm(); ok { t := *e // clone without changing e (&t).SuffixList = t.SuffixList[:len(e.SuffixList)-1] if err := c.compileTerm(&t); err != nil { return err } return c.compileTermSuffix(u, s) } } return c.compileTry(&Try{Body: &Query{Term: e}}) } else if s.Bind != nil { c.append(&code{op: opdup}) c.append(&code{op: opexpbegin}) if err := c.compileTerm(e); err != nil { return err } return c.compileBind(s.Bind) } else { return fmt.Errorf("invalid suffix: %s", s) } } func (c *compiler) compileCall(name string, args []*Query) error { fn := internalFuncs[name] if err := c.compileCallInternal( [3]interface{}{fn.callback, len(args), name}, args, true, name == "_index" || name == "_slice", ); err != nil { return err } if fn.iter { c.append(&code{op: opeach}) } return nil } func (c *compiler) compileCallPc(fn *funcinfo, args []*Query) error { return c.compileCallInternal(fn.pc, args, false, false) } func (c *compiler) compileCallInternal( fn interface{}, args []*Query, internal, indexing bool) error { if len(args) == 0 { c.append(&code{op: opcall, v: fn}) return nil } idx := c.newVariable() c.append(&code{op: opstore, v: idx}) if indexing && len(args) > 1 { c.append(&code{op: opexpbegin}) } for i := len(args) - 1; i >= 0; i-- { pc := c.pc() + 1 // skip opjump (ref: compileFuncDef) name := "lambda:" + strconv.Itoa(pc) if err := c.compileFuncDef(&FuncDef{Name: name, Body: args[i]}, false); err != nil { return err } if internal { switch c.pc() - pc { case 2: // optimize identity argument (opscope, opret) j := len(c.codes) - 3 c.codes[j] = &code{op: opload, v: idx} c.codes = c.codes[:j+1] s := c.scopes[len(c.scopes)-1] s.funcs = s.funcs[:len(s.funcs)-1] c.deleteCodeInfo(name) case 3: // optimize one instruction argument (opscope, opX, opret) j := len(c.codes) - 4 if c.codes[j+2].op == opconst { c.codes[j] = &code{op: oppush, v: c.codes[j+2].v} c.codes = c.codes[:j+1] } else { c.codes[j] = &code{op: opload, v: idx} c.codes[j+1] = c.codes[j+2] c.codes = c.codes[:j+2] } s := c.scopes[len(c.scopes)-1] s.funcs = s.funcs[:len(s.funcs)-1] c.deleteCodeInfo(name) default: c.append(&code{op: opload, v: idx}) c.append(&code{op: oppushpc, v: pc}) c.append(&code{op: opcallpc}) } } else { c.append(&code{op: oppushpc, v: pc}) } if indexing && i == 1 { if c.codes[len(c.codes)-2].op == opexpbegin { c.codes[len(c.codes)-2] = c.codes[len(c.codes)-1] c.codes = c.codes[:len(c.codes)-1] } else { c.append(&code{op: opexpend}) } } } c.append(&code{op: opload, v: idx}) c.append(&code{op: opcall, v: fn}) return nil } func (c *compiler) append(code *code) { c.codes = append(c.codes, code) } func (c *compiler) pc() int { return len(c.codes) } func (c *compiler) lazy(f func() *code) func() { i := len(c.codes) c.codes = append(c.codes, nil) return func() { c.codes[i] = f() } } func (c *compiler) optimizeTailRec() { var pcs []int scopes := map[int]bool{} L: for i, l := 0, len(c.codes); i < l; i++ { switch c.codes[i].op { case opscope: pcs = append(pcs, i) if v := c.codes[i].v.([3]int); v[2] == 0 { scopes[i] = v[1] == 0 } case opcall: var canjump bool if j, ok := c.codes[i].v.(int); !ok || len(pcs) == 0 || pcs[len(pcs)-1] != j { break } else if canjump, ok = scopes[j]; !ok { break } for j := i + 1; j < l; { switch c.codes[j].op { case opjump: j = c.codes[j].v.(int) case opret: if canjump { c.codes[i].op = opjump c.codes[i].v = pcs[len(pcs)-1] + 1 } else { c.codes[i].op = opcallrec } continue L default: continue L } } case opret: if len(pcs) == 0 { break L } pcs = pcs[:len(pcs)-1] } } } func (c *compiler) optimizeCodeOps() { for i, next := len(c.codes)-1, (*code)(nil); i >= 0; i-- { code := c.codes[i] switch code.op { case oppush, opdup, opload: switch next.op { case oppop: code.op = opnop next.op = opnop case opconst: code.op = opnop next.op = oppush } case opjump, opjumpifnot: if j := code.v.(int); j-1 == i { code.op = opnop } else if next = c.codes[j]; next.op == opjump { code.v = next.v } } next = code } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/compiler_test.go000066400000000000000000000131201416642156300241310ustar00rootroot00000000000000package gojq_test import ( "context" "fmt" "log" "os" "reflect" "sync" "testing" "time" "unsafe" "github.com/wader/gojq" ) func ExampleCompile() { query, err := gojq.Parse(".[] | .foo") if err != nil { log.Fatalln(err) } code, err := gojq.Compile(query) if err != nil { log.Fatalln(err) } iter := code.Run([]interface{}{ nil, "string", 42, []interface{}{"foo"}, map[string]interface{}{"foo": 42}, }) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { fmt.Println(err) continue } fmt.Printf("%#v\n", v) } // Output: // // expected an object but got: string ("string") // expected an object but got: number (42) // expected an object but got: array (["foo"]) // 42 } func ExampleCode_Run() { query, err := gojq.Parse(".foo") if err != nil { log.Fatalln(err) } code, err := gojq.Compile(query) if err != nil { log.Fatalln(err) } input := map[string]interface{}{"foo": 42} iter := code.Run(input) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } // Output: // 42 } func ExampleCode_RunWithContext() { query, err := gojq.Parse("def f: f; f, f") if err != nil { log.Fatalln(err) } code, err := gojq.Compile(query) if err != nil { log.Fatalln(err) } ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() iter := code.RunWithContext(ctx, nil) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { fmt.Println(err) continue } _ = v } // Output: // context deadline exceeded } func TestCodeCompile_OptimizeConstants(t *testing.T) { query, err := gojq.Parse(`[1,{foo:2,"bar":3},[4]]`) if err != nil { t.Fatal(err) } code, err := gojq.Compile(query) if err != nil { t.Fatal(err) } if got, expected := reflect.ValueOf(code).Elem().FieldByName("codes").Len(), 3; expected != got { t.Errorf("expected: %v, got: %v", expected, got) } iter := code.Run(nil) for { got, ok := iter.Next() if !ok { break } if expected := []interface{}{ 1, map[string]interface{}{"foo": 2, "bar": 3}, []interface{}{4}, }; !reflect.DeepEqual(got, expected) { t.Errorf("expected: %v, got: %v", expected, got) } } } func TestCodeCompile_OptimizeTailRec_While(t *testing.T) { query, err := gojq.Parse("0 | while(. < 10; . + 1)") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query) if err != nil { t.Fatal(err) } codes := reflect.ValueOf(code).Elem().FieldByName("codes") if got, expected := codes.Len(), 48; expected != got { t.Errorf("expected: %v, got: %v", expected, got) } op1 := codes.Index(2).Elem().FieldByName("op") op2 := codes.Index(21).Elem().FieldByName("op") // test jump of call _while if got, expected := *(*int)(unsafe.Pointer(op2.UnsafeAddr())), *(*int)(unsafe.Pointer(op1.UnsafeAddr())); expected != got { t.Errorf("expected: %v, got: %v", expected, got) } iter := code.Run(nil) n := 0 for { got, ok := iter.Next() if !ok { break } if !reflect.DeepEqual(got, n) { t.Errorf("expected: %v, got: %v", n, got) } n++ } if expected := 10; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } } func TestCodeCompile_OptimizeTailRec_CallRec(t *testing.T) { query, err := gojq.Parse("def f: . as $x | $x, (if $x < 3 then $x + 1 | f else empty end), $x; f") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query) if err != nil { t.Fatal(err) } codes := reflect.ValueOf(code).Elem().FieldByName("codes") if got, expected := codes.Len(), 48; expected != got { t.Errorf("expected: %v, got: %v", expected, got) } op1 := codes.Index(39).Elem().FieldByName("op") // callrec f op2 := codes.Index(38).Elem().FieldByName("op") // call _add/2 if got, expected := *(*int)(unsafe.Pointer(op2.UnsafeAddr()))+1, *(*int)(unsafe.Pointer(op1.UnsafeAddr())); expected != got { t.Errorf("expected: %v, got: %v", expected, got) } } func TestCodeCompile_OptimizeJumps(t *testing.T) { query, err := gojq.Parse("def f: 1; def g: 2; def h: 3; f") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query) if err != nil { t.Fatal(err) } codes := reflect.ValueOf(code).Elem().FieldByName("codes") if got, expected := codes.Len(), 15; expected != got { t.Errorf("expected: %v, got: %v", expected, got) } v := codes.Index(1).Elem().FieldByName("v") if got, expected := *(*interface{})(unsafe.Pointer(v.UnsafeAddr())), 13; expected != got { t.Errorf("expected: %v, got: %v", expected, got) } iter := code.Run(nil) for { got, ok := iter.Next() if !ok { break } if expected := 1; !reflect.DeepEqual(got, expected) { t.Errorf("expected: %v, got: %v", expected, got) } } } func TestCodeRun_Race(t *testing.T) { query, err := gojq.Parse("range(10)") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query) if err != nil { t.Fatal(err) } var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() iter := code.Run(nil) n := 0 for { got, ok := iter.Next() if !ok { break } if got != n { t.Errorf("expected: %v, got: %v", n, got) } n++ } if expected := 10; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } }() } wg.Wait() } func BenchmarkCompile(b *testing.B) { cnt, err := os.ReadFile("builtin.jq") if err != nil { b.Fatal(err) } query, err := gojq.Parse(string(cnt)) if err != nil { b.Fatal(err) } for i := 0; i < b.N; i++ { _, err := gojq.Compile( query, gojq.WithInputIter(gojq.NewIter()), ) if err != nil { b.Fatal(err) } } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/debug.go000066400000000000000000000101661416642156300223550ustar00rootroot00000000000000//go:build debug // +build debug package gojq import ( "fmt" "io" "os" "strconv" "strings" ) var ( debug bool debugOut io.Writer ) func init() { if out := os.Getenv("GOJQ_DEBUG"); out != "" { debug = true if out == "stdout" { debugOut = os.Stdout } else { debugOut = os.Stderr } } } type codeinfo struct { name string pc int } func (c *compiler) appendCodeInfo(x interface{}) { if !debug { return } var name string switch x := x.(type) { case string: name = x default: name = fmt.Sprint(x) } var diff int if c.codes[len(c.codes)-1] != nil && c.codes[len(c.codes)-1].op == opret && strings.HasPrefix(name, "end of ") { diff = -1 } c.codeinfos = append(c.codeinfos, codeinfo{name, c.pc() + diff}) } func (c *compiler) deleteCodeInfo(name string) { for i := 0; i < len(c.codeinfos); i++ { if strings.HasSuffix(c.codeinfos[i].name, name) { copy(c.codeinfos[i:], c.codeinfos[i+1:]) c.codeinfos = c.codeinfos[:len(c.codeinfos)-1] i-- } } } func (env *env) lookupInfoName(pc int) string { var name string for _, ci := range env.codeinfos { if ci.pc == pc { if name != "" { name += ", " } name += ci.name } } return name } func (env *env) debugCodes() { if !debug { return } for i, c := range env.codes { pc := i switch c.op { case opcall, opcallrec: if x, ok := c.v.(int); ok { pc = x } case opjump: x := c.v.(int) if x > 0 && env.codes[x-1].op == opscope { pc = x - 1 } } var s string if name := env.lookupInfoName(pc); name != "" { switch c.op { case opcall, opcallrec, opjump: if !strings.HasPrefix(name, "module ") { s = "\t## call " + name break } fallthrough default: s = "\t## " + name } } fmt.Fprintf(debugOut, "\t%d\t%s%s%s\n", i, formatOp(c.op, false), debugOperand(c), s) } fmt.Fprintln(debugOut, "\t"+strings.Repeat("-", 40)+"+") } func (env *env) debugState(pc int, backtrack bool) { if !debug { return } var sb strings.Builder c := env.codes[pc] fmt.Fprintf(&sb, "\t%d\t%s%s\t|", pc, formatOp(c.op, backtrack), debugOperand(c)) var xs []int for i := env.stack.index; i >= 0; i = env.stack.data[i].next { xs = append(xs, i) } for i := len(xs) - 1; i >= 0; i-- { sb.WriteString("\t") sb.WriteString(debugValue(env.stack.data[xs[i]].value)) } switch c.op { case opcall, opcallrec: if x, ok := c.v.(int); ok { pc = x } case opjump: x := c.v.(int) if x > 0 && env.codes[x-1].op == opscope { pc = x - 1 } } if name := env.lookupInfoName(pc); name != "" { switch c.op { case opcall, opcallrec, opjump: if !strings.HasPrefix(name, "module ") { sb.WriteString("\t\t\t## call " + name) break } fallthrough default: sb.WriteString("\t\t\t## " + name) } } fmt.Fprintln(debugOut, sb.String()) } func formatOp(c opcode, backtrack bool) string { if backtrack { return c.String() + " " + strings.Repeat(" ", 13-len(c.String())) } return c.String() + strings.Repeat(" ", 25-len(c.String())) } func (env *env) debugForks(pc int, op string) { if !debug { return } var sb strings.Builder for i, v := range env.forks { if i > 0 { sb.WriteByte('\t') } if i == len(env.forks)-1 { sb.WriteByte('<') } fmt.Fprintf(&sb, "%d, %s", v.pc, debugValue(env.stack.data[v.stackindex].value)) if i == len(env.forks)-1 { sb.WriteByte('>') } } fmt.Fprintf(debugOut, "\t-\t%s%s%d\t|\t%s\n", op, strings.Repeat(" ", 22), pc, sb.String()) } func debugOperand(c *code) string { switch c.op { case opcall, opcallrec: switch v := c.v.(type) { case int: return strconv.Itoa(v) case [3]interface{}: return fmt.Sprintf("%s/%d", v[2], v[1]) default: panic(c) } default: return debugValue(c.v) } } func debugValue(v interface{}) string { switch v := v.(type) { case Iter: return fmt.Sprintf("gojq.Iter(%#v)", v) case JQValue: return fmt.Sprintf("gojq.JQValue(%s)", previewValue(v)) case [2]int: return fmt.Sprintf("[%d,%d]", v[0], v[1]) case [3]int: return fmt.Sprintf("[%d,%d,%d]", v[0], v[1], v[2]) case [3]interface{}: return fmt.Sprintf("[%v,%v,%v]", v[0], v[1], v[2]) default: return previewValue(v) } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/deepequal.go000066400000000000000000000015371416642156300232360ustar00rootroot00000000000000package gojq import ( "math" "math/big" ) func deepEqual(l, r interface{}) bool { return BinopTypeSwitch(l, r, binopIsHalfInt, func(l, r int) interface{} { return l == r }, func(l, r float64) interface{} { return l == r || math.IsNaN(l) && math.IsNaN(r) }, func(l, r *big.Int) interface{} { return l.Cmp(r) == 0 }, func(l, r string) interface{} { return l == r }, func(l, r []interface{}) interface{} { if len(l) != len(r) { return false } for i, v := range l { if !deepEqual(v, r[i]) { return false } } return true }, func(l, r map[string]interface{}) interface{} { if len(l) != len(r) { return false } for k, v := range l { if !deepEqual(v, r[k]) { return false } } return true }, func(l, r interface{}) interface{} { return l == r }, ).(bool) } golang-github-wader-gojq-0.0~git20220108.6a05b6c/encoder.go000066400000000000000000000074561416642156300227160ustar00rootroot00000000000000package gojq import ( "bytes" "fmt" "io" "math" "math/big" "sort" "strconv" "strings" "unicode/utf8" ) // Marshal returns the jq-flavored JSON encoding of v. // // This method only accepts limited types (nil, bool, int, float64, *big.Int, // string, []interface{} and map[string]interface{}) because these are the // possible types a gojq iterator can emit. This method marshals NaN to null, // truncates infinities to (+|-) math.MaxFloat64, uses \b and \f in strings, // and does not escape '<' and '>' for embedding in HTML. These behaviors are // based on the marshaler of jq command and different from Go standard library // method json.Marshal. func Marshal(v interface{}) ([]byte, error) { var b bytes.Buffer (&encoder{w: &b}).encode(v) return b.Bytes(), nil } func jsonMarshal(v interface{}) string { var sb strings.Builder (&encoder{w: &sb}).encode(v) return sb.String() } type encoder struct { w interface { io.Writer io.ByteWriter io.StringWriter } buf [64]byte } func (e *encoder) encode(v interface{}) { switch v := v.(type) { case nil: e.w.WriteString("null") case bool: if v { e.w.WriteString("true") } else { e.w.WriteString("false") } case int: e.w.Write(strconv.AppendInt(e.buf[:0], int64(v), 10)) case float64: e.encodeFloat64(v) case *big.Int: e.w.Write(v.Append(e.buf[:0], 10)) case string: e.encodeString(v) case []interface{}: e.encodeArray(v) case map[string]interface{}: e.encodeMap(v) case JQValue: e.encode(v.JQValueToGoJQ()) default: panic(fmt.Sprintf("invalid value: %v", v)) } } // ref: floatEncoder in encoding/json func (e *encoder) encodeFloat64(f float64) { if math.IsNaN(f) { e.w.WriteString("null") return } if f >= math.MaxFloat64 { f = math.MaxFloat64 } else if f <= -math.MaxFloat64 { f = -math.MaxFloat64 } fmt := byte('f') if x := math.Abs(f); x != 0 && x < 1e-6 || x >= 1e21 { fmt = 'e' } buf := strconv.AppendFloat(e.buf[:0], f, fmt, -1, 64) if fmt == 'e' { // clean up e-09 to e-9 if n := len(buf); n >= 4 && buf[n-4] == 'e' && buf[n-3] == '-' && buf[n-2] == '0' { buf[n-2] = buf[n-1] buf = buf[:n-1] } } e.w.Write(buf) } // ref: encodeState#string in encoding/json func (e *encoder) encodeString(s string) { e.w.WriteByte('"') start := 0 for i := 0; i < len(s); { if b := s[i]; b < utf8.RuneSelf { if ']' <= b && b <= '~' || '#' <= b && b <= '[' || b == ' ' || b == '!' { i++ continue } if start < i { e.w.WriteString(s[start:i]) } e.w.WriteByte('\\') switch b { case '\\', '"': e.w.WriteByte(b) case '\b': e.w.WriteByte('b') case '\f': e.w.WriteByte('f') case '\n': e.w.WriteByte('n') case '\r': e.w.WriteByte('r') case '\t': e.w.WriteByte('t') default: const hex = "0123456789abcdef" e.w.WriteString("u00") e.w.WriteByte(hex[b>>4]) e.w.WriteByte(hex[b&0xF]) } i++ start = i continue } c, size := utf8.DecodeRuneInString(s[i:]) if c == utf8.RuneError && size == 1 { if start < i { e.w.WriteString(s[start:i]) } e.w.WriteString(`\ufffd`) i += size start = i continue } i += size } if start < len(s) { e.w.WriteString(s[start:]) } e.w.WriteByte('"') } func (e *encoder) encodeArray(vs []interface{}) { e.w.WriteByte('[') for i, v := range vs { if i > 0 { e.w.WriteByte(',') } e.encode(v) } e.w.WriteByte(']') } func (e *encoder) encodeMap(vs map[string]interface{}) { e.w.WriteByte('{') type keyVal struct { key string val interface{} } kvs := make([]keyVal, len(vs)) var i int for k, v := range vs { kvs[i] = keyVal{k, v} i++ } sort.Slice(kvs, func(i, j int) bool { return kvs[i].key < kvs[j].key }) for i, kv := range kvs { if i > 0 { e.w.WriteByte(',') } e.encodeString(kv.key) e.w.WriteByte(':') e.encode(kv.val) } e.w.WriteByte('}') } golang-github-wader-gojq-0.0~git20220108.6a05b6c/encoder_test.go000066400000000000000000000030561416642156300237450ustar00rootroot00000000000000package gojq_test import ( "math" "math/big" "testing" "github.com/wader/gojq" ) func TestMarshal(t *testing.T) { testCases := []struct { name string value interface{} expected string }{ { name: "nil", value: nil, expected: "null", }, { name: "booleans", value: []interface{}{false, true}, expected: "[false,true]", }, { name: "numbers", value: []interface{}{ 42, 3.14, 1e-6, 1e-7, -1e-9, 1e-10, math.NaN(), math.Inf(1), math.Inf(-1), new(big.Int).SetBytes([]byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")), }, expected: "[42,3.14,0.000001,1e-7,-1e-9,1e-10,null,1.7976931348623157e+308,-1.7976931348623157e+308,340282366920938463463374607431768211455]", }, { name: "strings", value: []interface{}{"", "abcde", "foo\x00\x1f\r\n\t\f\b<=>!\"#$%'& \\\x7fbar"}, expected: `["","abcde","foo\u0000\u001f\r\n\t\f\b<=>!\"#$%'& \\\u007fbar"]`, }, { name: "arrays", value: []interface{}{1, []interface{}{2, []interface{}{3, []interface{}{map[string]interface{}{}}}}}, expected: `[1,[2,[3,[{}]]]]`, }, { name: "objects", value: map[string]interface{}{"x": []interface{}{100}, "y": map[string]interface{}{"z": 42}}, expected: `{"x":[100],"y":{"z":42}}`, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got, err := gojq.Marshal(tc.value) if err != nil { t.Fatal(err) } if string(got) != tc.expected { t.Errorf("expected: %s, got: %s", tc.expected, string(got)) } }) } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/env.go000066400000000000000000000013601416642156300220530ustar00rootroot00000000000000package gojq import "context" type env struct { pc int stack *stack paths *stack scopes *scopeStack values []interface{} codes []*code codeinfos []codeinfo forks []fork backtrack bool offset int expdepth int label int args [32]interface{} // len(env.args) > maxarity ctx context.Context } func newEnv(ctx context.Context) *env { return &env{ stack: newStack(), paths: newStack(), scopes: newScopeStack(), ctx: ctx, } } type scope struct { id int offset int pc int saveindex int outerindex int } type fork struct { pc int stackindex int stacklimit int scopeindex int scopelimit int pathindex int pathlimit int expdepth int } golang-github-wader-gojq-0.0~git20220108.6a05b6c/error.go000066400000000000000000000210151416642156300224130ustar00rootroot00000000000000package gojq import ( "fmt" "math/big" "strconv" "strings" ) // ValueError is an interface for errors with a value for internal function. // Return an error implementing this interface when you want to catch error // values (not error messages) by try-catch, just like built-in error function. // Refer to WithFunction to add a custom internal function. type ValueError interface { error Value() interface{} } type expectedObjectError struct { v interface{} } func (err *expectedObjectError) Error() string { return "expected an object but got: " + typeErrorPreview(err.v) } type expectedArrayError struct { v interface{} } func (err *expectedArrayError) Error() string { return "expected an array but got: " + typeErrorPreview(err.v) } type expectedStringError struct { v interface{} } func (err *expectedStringError) Error() string { return "expected a string but got: " + typeErrorPreview(err.v) } type iteratorError struct { v interface{} } func (err *iteratorError) Error() string { return "cannot iterate over: " + typeErrorPreview(err.v) } type arrayIndexTooLargeError struct { v interface{} } func (err *arrayIndexTooLargeError) Error() string { return "array index too large: " + previewValue(err.v) } type objectKeyNotStringError struct { v interface{} } func (err *objectKeyNotStringError) Error() string { return "expected a string for object key but got: " + typeErrorPreview(err.v) } type arrayIndexNotNumberError struct { v interface{} } func (err *arrayIndexNotNumberError) Error() string { return "expected a number for indexing an array but got: " + typeErrorPreview(err.v) } type expectedStartEndError struct { v interface{} } func (err *expectedStartEndError) Error() string { return `expected "start" and "end" for slicing but got: ` + typeErrorPreview(err.v) } type lengthMismatchError struct { name string v, x []interface{} } func (err *lengthMismatchError) Error() string { return "length mismatch in " + err.name + ": " + typeErrorPreview(err.v) + ", " + typeErrorPreview(err.x) } type inputNotAllowedError struct{} func (*inputNotAllowedError) Error() string { return "input(s)/0 is not allowed" } type funcNotFoundError struct { f *Func } func (err *funcNotFoundError) Error() string { return "function not defined: " + err.f.Name + "/" + strconv.Itoa(len(err.f.Args)) } type funcTypeError struct { name string v interface{} } func (err *funcTypeError) Error() string { return err.name + " cannot be applied to: " + typeErrorPreview(err.v) } type exitCodeError struct { value interface{} code int halt bool } func (err *exitCodeError) Error() string { if s, ok := err.value.(string); ok { return "error: " + s } return "error: " + jsonMarshal(err.value) } func (err *exitCodeError) IsEmptyError() bool { return err.value == nil } func (err *exitCodeError) Value() interface{} { return err.value } func (err *exitCodeError) ExitCode() int { return err.code } func (err *exitCodeError) IsHaltError() bool { return err.halt } type funcContainsError struct { l, r interface{} } func (err *funcContainsError) Error() string { return "cannot check contains(" + previewValue(err.r) + "): " + typeErrorPreview(err.l) } type hasKeyTypeError struct { l, r interface{} } func (err *hasKeyTypeError) Error() string { return "cannot check whether " + typeErrorPreview(err.l) + " has a key: " + typeErrorPreview(err.r) } type unaryTypeError struct { name string v interface{} } func (err *unaryTypeError) Error() string { return "cannot " + err.name + ": " + typeErrorPreview(err.v) } type binopTypeError struct { name string l, r interface{} } func (err *binopTypeError) Error() string { return "cannot " + err.name + ": " + typeErrorPreview(err.l) + " and " + typeErrorPreview(err.r) } type zeroDivisionError struct { l, r interface{} } func (err *zeroDivisionError) Error() string { return "cannot divide " + typeErrorPreview(err.l) + " by: " + typeErrorPreview(err.r) } type zeroModuloError struct { l, r interface{} } func (err *zeroModuloError) Error() string { return "cannot modulo " + typeErrorPreview(err.l) + " by: " + typeErrorPreview(err.r) + "" } type formatNotFoundError struct { n string } func (err *formatNotFoundError) Error() string { return "format not defined: " + err.n } type formatCsvTsvRowError struct { typ string v interface{} } func (err *formatCsvTsvRowError) Error() string { return "invalid " + err.typ + " row: " + typeErrorPreview(err.v) } type formatShError struct { v interface{} } func (err *formatShError) Error() string { return "cannot escape for shell: " + typeErrorPreview(err.v) } type tooManyVariableValuesError struct{} func (err *tooManyVariableValuesError) Error() string { return "too many variable values provided" } type expectedVariableError struct { n string } func (err *expectedVariableError) Error() string { return "variable defined but not bound: " + err.n } type variableNotFoundError struct { n string } func (err *variableNotFoundError) Error() string { return "variable not defined: " + err.n } type variableNameError struct { n string } func (err *variableNameError) Error() string { return "invalid variable name: " + err.n } type breakError struct { n string v interface{} } func (err *breakError) Error() string { return "label not defined: " + err.n } func (err *breakError) ExitCode() int { return 3 } type tryEndError struct { err error } func (err *tryEndError) Error() string { return err.err.Error() } type invalidPathError struct { v interface{} } func (err *invalidPathError) Error() string { return "invalid path against: " + typeErrorPreview(err.v) } type invalidPathIterError struct { v interface{} } func (err *invalidPathIterError) Error() string { return "invalid path on iterating against: " + typeErrorPreview(err.v) } type getpathError struct { v, path interface{} } func (err *getpathError) Error() string { return "cannot getpath with " + previewValue(err.path) + " against: " + typeErrorPreview(err.v) + "" } type queryParseError struct { typ, fname, contents string err error } func (err *queryParseError) QueryParseError() (string, string, string, error) { return err.typ, err.fname, err.contents, err.err } func (err *queryParseError) Error() string { return "invalid " + err.typ + ": " + err.fname + ": " + err.err.Error() } type jsonParseError struct { fname, contents string err error } func (err *jsonParseError) JSONParseError() (string, string, error) { return err.fname, err.contents, err.err } func (err *jsonParseError) Error() string { return "invalid json: " + err.fname + ": " + err.err.Error() } func typeErrorPreview(v interface{}) string { if _, ok := v.(Iter); ok { return "gojq.Iter" } p := preview(v) if p != "" { p = " (" + p + ")" } return typeof(v) + p } func typeof(v interface{}) string { switch v := v.(type) { case nil: return "null" case bool: return "boolean" case int, float64, *big.Int: return "number" case string: return "string" case []interface{}: return "array" case map[string]interface{}: return "object" case JQValue: return fmt.Sprintf("JQValue(%s)", v.JQValueType()) default: panic(fmt.Sprintf("invalid value: %v", v)) } } type limitedWriter struct { buf []byte off int } func (w *limitedWriter) Write(bs []byte) (int, error) { n := copy(w.buf[w.off:], bs) if w.off += n; w.off == len(w.buf) { panic(nil) } return n, nil } func (w *limitedWriter) WriteByte(b byte) error { w.buf[w.off] = b if w.off++; w.off == len(w.buf) { panic(nil) } return nil } func (w *limitedWriter) WriteString(s string) (int, error) { n := copy(w.buf[w.off:], s) if w.off += n; w.off == len(w.buf) { panic(nil) } return n, nil } func (w *limitedWriter) String() string { return string(w.buf[:w.off]) } func jsonLimitedMarshal(v interface{}, n int) (s string) { w := &limitedWriter{buf: make([]byte, n)} defer func() { recover() s = w.String() }() (&encoder{w: w}).encode(v) return } func preview(v interface{}) string { if v == nil { return "" } s := jsonLimitedMarshal(v, 32) if l := 30; len(s) > l { var trailing string switch v.(type) { case string: trailing = ` ..."` case []interface{}: trailing = " ...]" case map[string]interface{}: trailing = " ...}" default: trailing = " ..." } var sb strings.Builder sb.Grow(l + 5) for _, c := range s { sb.WriteRune(c) if sb.Len() >= l-len(trailing) { sb.WriteString(trailing) break } } s = sb.String() } return s } func previewValue(v interface{}) string { if v == nil { return "null" } return preview(v) } golang-github-wader-gojq-0.0~git20220108.6a05b6c/execute.go000066400000000000000000000237401416642156300227330ustar00rootroot00000000000000package gojq import ( "context" "fmt" "sort" ) func (env *env) execute(bc *Code, v interface{}, vars ...interface{}) Iter { env.codes = bc.codes env.codeinfos = bc.codeinfos env.push(v) for i := len(vars) - 1; i >= 0; i-- { env.push(vars[i]) } env.debugCodes() return env } func (env *env) Next() (interface{}, bool) { var err error pc, callpc, index := env.pc, len(env.codes)-1, -1 backtrack, hasCtx := env.backtrack, env.ctx != context.Background() defer func() { env.pc, env.backtrack = pc, true }() loop: for ; pc < len(env.codes); pc++ { env.debugState(pc, backtrack) code := env.codes[pc] if hasCtx { select { case <-env.ctx.Done(): pc, env.forks = len(env.codes), nil return env.ctx.Err(), true default: } } switch code.op { case opnop: // nop case oppush: env.push(code.v) case oppop: env.pop() case opdup: x := env.pop() env.push(x) env.push(x) case opconst: env.pop() env.push(code.v) case opload: env.push(env.values[env.index(code.v.([2]int))]) case opstore: env.values[env.index(code.v.([2]int))] = env.pop() case opobject: if backtrack { break loop } n := code.v.(int) m := make(map[string]interface{}, n) for i := 0; i < n; i++ { v, k := env.pop(), env.pop() if jv, ok := k.(JQValue); ok { k = jv.JQValueToString() } s, ok := k.(string) if !ok { err = &objectKeyNotStringError{k} break loop } m[s] = v } env.push(m) case opappend: i := env.index(code.v.([2]int)) env.values[i] = append(env.values[i].([]interface{}), env.pop()) case opfork: if backtrack { if err != nil { break loop } pc, backtrack = code.v.(int), false goto loop } else { env.pushfork(pc) } case opforktrybegin: if backtrack { if err == nil { break loop } switch er := err.(type) { case *tryEndError: err = er.err break loop case ValueError: if er, ok := er.(*exitCodeError); ok && er.halt { break loop } if v := er.Value(); v != nil { env.pop() env.push(v) } else { err = nil break loop } default: env.pop() env.push(err.Error()) } pc, backtrack, err = code.v.(int), false, nil goto loop } else { env.pushfork(pc) } case opforktryend: if backtrack { if err != nil { err = &tryEndError{err} } break loop } else { env.pushfork(pc) } case opforkalt: if backtrack { if err == nil { break loop } pc, backtrack, err = code.v.(int), false, nil goto loop } else { env.pushfork(pc) } case opforklabel: if backtrack { label := env.pop() if e, ok := err.(*breakError); ok && e.v == label { err = nil } break loop } else { env.push(env.label) env.pushfork(pc) env.pop() env.values[env.index(code.v.([2]int))] = env.label env.label++ } case opbacktrack: break loop case opjump: pc = code.v.(int) goto loop case opjumpifnot: v := env.pop() b, bOk := toBoolean(v) if isNull(v) || (bOk && !b) { pc = code.v.(int) goto loop } case opcall: if backtrack { break loop } switch v := code.v.(type) { case int: pc, callpc, index = v, pc, env.scopes.index goto loop case [3]interface{}: argcnt := v[1].(int) x, args := env.pop(), env.args[:argcnt] for i := 0; i < argcnt; i++ { args[i] = env.pop() } w := v[0].(func(interface{}, []interface{}) interface{})(x, args) if e, ok := w.(error); ok { if er, ok := e.(*exitCodeError); !ok || er.value != nil || er.halt { err = e } break loop } env.push(w) if !env.paths.empty() { var ps []interface{} ps, err = env.pathEntries(v[2].(string), x, args) if err != nil { break loop } for _, p := range ps { env.paths.push(PathValue{Path: p, Value: w}) } } default: panic(v) } case opcallrec: pc, callpc, index = code.v.(int), -1, env.scopes.index goto loop case oppushpc: env.push([2]int{code.v.(int), env.scopes.index}) case opcallpc: xs := env.pop().([2]int) pc, callpc, index = xs[0], pc, xs[1] goto loop case opscope: xs := code.v.([3]int) var saveindex, outerindex, limit int if index == env.scopes.index { if callpc >= 0 { saveindex = index } else { callpc, saveindex = env.popscope() } } else { env.scopes.save(&saveindex, &limit) env.scopes.index = index } if outerindex = index; outerindex >= 0 { if s := env.scopes.data[outerindex].value; s.id == xs[0] { outerindex = s.outerindex } } env.scopes.push(scope{xs[0], env.offset, callpc, saveindex, outerindex}) env.offset += xs[1] if env.offset > len(env.values) { vs := make([]interface{}, env.offset*2) copy(vs, env.values) env.values = vs } case opret: if backtrack { break loop } pc, env.scopes.index = env.popscope() if env.scopes.empty() { return env.pop(), true } case opeach: if err != nil { break loop } backtrack = false var xs []PathValue switch v := env.pop().(type) { case []PathValue: xs = v case []interface{}: if !env.paths.empty() && env.expdepth == 0 && !deepEqual(v, env.paths.top().(PathValue).Value) { err = &invalidPathIterError{v} break loop } if len(v) == 0 { break loop } xs = make([]PathValue, len(v)) for i, v := range v { xs[i] = PathValue{Path: i, Value: v} } case map[string]interface{}: if !env.paths.empty() && env.expdepth == 0 && !deepEqual(v, env.paths.top().(PathValue).Value) { err = &invalidPathIterError{v} break loop } if len(v) == 0 { break loop } xs = make([]PathValue, len(v)) var i int for k, v := range v { xs[i] = PathValue{Path: k, Value: v} i++ } sort.Slice(xs, func(i, j int) bool { return xs[i].Path.(string) < xs[j].Path.(string) }) case Iter: if !env.paths.empty() && env.expdepth == 0 { err = &invalidPathIterError{v} break loop } if w, ok := v.Next(); ok { env.push(v) env.pushfork(pc) env.pop() if e, ok := w.(error); ok { err = e break loop } env.push(w) continue } break loop case JQValue: // TODO: not sure about the compare here? compare JQValue instance somehow? _, isJQValue := v.(JQValue) if !env.paths.empty() && env.expdepth == 0 && !isJQValue { err = &invalidPathIterError{v} break loop } xsv := v.JQValueEach() if e, ok := xsv.(error); ok { err = e break loop } var ok bool xs, ok = xsv.([]PathValue) if !ok { err = &iteratorError{xsv} break loop } if len(xs) == 0 { break loop } default: err = &iteratorError{v} break loop } if len(xs) > 1 { env.push(xs[1:]) env.pushfork(pc) env.pop() } env.push(xs[0].Value) if !env.paths.empty() && env.expdepth == 0 { env.paths.push(xs[0]) } case opexpbegin: env.expdepth++ case opexpend: env.expdepth-- case oppathbegin: env.paths.push(env.expdepth) env.paths.push(PathValue{Value: env.stack.top()}) env.expdepth = 0 case oppathend: if backtrack { break loop } if env.expdepth > 0 { panic(fmt.Sprintf("unexpected expdepth: %d", env.expdepth)) } env.pop() x := env.pop() // TODO: not sure about the compare here? compare JQValue instance somehow? _, isJQValue := x.(JQValue) if deepEqual(x, env.paths.top().(PathValue).Value) || isJQValue { env.push(env.poppaths()) env.expdepth = env.paths.pop().(int) } else { err = &invalidPathError{x} break loop } default: panic(code.op) } } if len(env.forks) > 0 { pc, backtrack = env.popfork(), true goto loop } if err != nil { return err, true } return nil, false } func (env *env) push(v interface{}) { env.stack.push(v) } func (env *env) pop() interface{} { return env.stack.pop() } func (env *env) popscope() (int, int) { free := env.scopes.index > env.scopes.limit s := env.scopes.pop() if free { env.offset = s.offset } return s.pc, s.saveindex } func (env *env) pushfork(pc int) { f := fork{pc: pc, expdepth: env.expdepth} env.stack.save(&f.stackindex, &f.stacklimit) env.scopes.save(&f.scopeindex, &f.scopelimit) env.paths.save(&f.pathindex, &f.pathlimit) env.forks = append(env.forks, f) env.debugForks(pc, ">>>") } func (env *env) popfork() int { f := env.forks[len(env.forks)-1] env.debugForks(f.pc, "<<<") env.forks, env.expdepth = env.forks[:len(env.forks)-1], f.expdepth env.stack.restore(f.stackindex, f.stacklimit) env.scopes.restore(f.scopeindex, f.scopelimit) env.paths.restore(f.pathindex, f.pathlimit) return f.pc } func (env *env) index(v [2]int) int { for id, i := v[0], env.scopes.index; i >= 0; { s := env.scopes.data[i].value if s.id == id { return s.offset + v[1] } i = s.outerindex } panic("env.index") } func (env *env) pathEntries(name string, x interface{}, args []interface{}) ([]interface{}, error) { switch name { case "_index": if env.expdepth > 0 { return nil, nil } else if !deepEqual(args[0], env.paths.top().(PathValue).Value) { return nil, &invalidPathError{x} } return []interface{}{args[1]}, nil case "_slice": if env.expdepth > 0 { return nil, nil } else if !deepEqual(args[0], env.paths.top().(PathValue).Value) { return nil, &invalidPathError{x} } return []interface{}{map[string]interface{}{"start": args[2], "end": args[1]}}, nil case "getpath": if env.expdepth > 0 { return nil, nil } else if !deepEqual(x, env.paths.top().(PathValue).Value) { return nil, &invalidPathError{x} } return args[0].([]interface{}), nil default: return nil, nil } } func (env *env) poppaths() []interface{} { var xs []interface{} for { p := env.paths.pop().(PathValue) if p.Path == nil { break } xs = append(xs, p.Path) } for i, j := 0, len(xs)-1; i < j; i, j = i+1, j-1 { xs[i], xs[j] = xs[j], xs[i] } return xs } golang-github-wader-gojq-0.0~git20220108.6a05b6c/func.go000066400000000000000000001223121416642156300222170ustar00rootroot00000000000000package gojq import ( "encoding/base64" "encoding/json" "fmt" "math" "math/big" "net/url" "regexp" "sort" "strconv" "strings" "time" "unicode/utf8" "github.com/itchyny/timefmt-go" ) //go:generate go run -modfile=go.dev.mod _tools/gen_builtin.go -i builtin.jq -o builtin.go var builtinFuncDefs map[string][]*FuncDef const ( argcount0 = 1 << iota argcount1 argcount2 argcount3 ) type function struct { argcount int iter bool callback func(interface{}, []interface{}) interface{} } func (fn function) accept(cnt int) bool { return fn.argcount&(1<= 0 { return v } return -v case float64: return math.Abs(v) case *big.Int: if v.Sign() >= 0 { return v } return new(big.Int).Abs(v) case nil: return 0 case JQValue: return v.JQValueLength() default: return &funcTypeError{"length", v} } } func funcUtf8ByteLength(v interface{}) interface{} { if s, ok := toString(v); ok { return len(s) } return &funcTypeError{"utf8bytelength", v} } func funcKeys(v interface{}) interface{} { switch v := v.(type) { case []interface{}: w := make([]interface{}, len(v)) for i := range v { w[i] = i } return w case map[string]interface{}: w := make([]string, len(v)) var i int for k := range v { w[i] = k i++ } sort.Strings(w) u := make([]interface{}, len(v)) for i, x := range w { u[i] = x } return u case JQValue: return v.JQValueKeys() default: return &funcTypeError{"keys", v} } } func funcHas(v, x interface{}) interface{} { switch v := v.(type) { case []interface{}: if x, ok := toInt(x); ok { return 0 <= x && x < len(v) } return &hasKeyTypeError{v, x} case map[string]interface{}: switch x := x.(type) { case string: _, ok := v[x] return ok default: return &hasKeyTypeError{v, x} } case nil: return false case JQValue: return v.JQValueHas(x) default: return &hasKeyTypeError{v, x} } } func funcAdd(v interface{}) interface{} { if vs, ok := toObject(v); ok { xs := make([]string, len(vs)) var i int for k := range vs { xs[i] = k i++ } sort.Strings(xs) us := make([]interface{}, len(vs)) for i, x := range xs { us[i] = vs[x] } v = us } vs, ok := toArray(v) if !ok { return &funcTypeError{"add", v} } v = nil for _, x := range vs { switch y := x.(type) { case map[string]interface{}: switch w := v.(type) { case nil: m := make(map[string]interface{}, len(y)) for k, e := range y { m[k] = e } v = m continue case map[string]interface{}: for k, e := range y { w[k] = e } continue } case []interface{}: switch w := v.(type) { case nil: s := make([]interface{}, len(y)) copy(s, y) v = s continue case []interface{}: v = append(w, y...) continue } } v = funcOpAdd(nil, v, x) if err, ok := v.(error); ok { return err } } return v } func funcToNumber(v interface{}) interface{} { switch v := v.(type) { case int, float64, *big.Int: return v case string: return NormalizeNumbers(v) case JQValue: return v.JQValueToNumber() default: return &funcTypeError{"tonumber", v} } } func funcToString(v interface{}) interface{} { switch v := v.(type) { case string: return v case JQValue: return v.JQValueToString() default: return funcToJSON(v) } } func funcType(v interface{}) interface{} { switch v := v.(type) { case JQValue: return v.JQValueType() default: return typeof(v) } } func funcReverse(v interface{}) interface{} { vs, ok := toArray(v) if !ok { return &expectedArrayError{v} } ws := make([]interface{}, len(vs)) for i, v := range vs { ws[len(ws)-i-1] = v } return ws } func funcContains(v, x interface{}) interface{} { if isNull(v) { if isNull(x) { return true } } if vb, ok := toBoolean(v); ok { if xb, ok := toBoolean(x); ok { if vb == xb { return true } } } return BinopTypeSwitch(v, x, nil, func(l, r int) interface{} { return l == r }, func(l, r float64) interface{} { return l == r }, func(l, r *big.Int) interface{} { return l.Cmp(r) == 0 }, func(l, r string) interface{} { return strings.Contains(l, r) }, func(l, r []interface{}) interface{} { for _, x := range r { var found bool for _, y := range l { if funcContains(y, x) == true { found = true break } } if !found { return false } } return true }, func(l, r map[string]interface{}) interface{} { for k, rk := range r { lk, ok := l[k] if !ok { return false } c := funcContains(lk, rk) if _, ok := c.(error); ok { return false } if c == false { return false } } return true }, func(l, r interface{}) interface{} { return &funcContainsError{l, r} }, ) } func funcExplode(v interface{}) interface{} { x, ok := toString(v) if !ok { return &funcTypeError{"implode", v} } return explode(x) } func explode(s string) []interface{} { rs := []int32(s) xs := make([]interface{}, len(rs)) for i, r := range rs { xs[i] = int(r) } return xs } func funcImplode(v interface{}) interface{} { x, ok := toArray(v) if !ok { return &funcTypeError{"implode", v} } return implode(x) } func implode(v []interface{}) interface{} { var sb strings.Builder sb.Grow(len(v)) for _, r := range v { if r, ok := toInt(r); ok && 0 <= r && r <= utf8.MaxRune { sb.WriteRune(rune(r)) } else { return &funcTypeError{"implode", v} } } return sb.String() } func funcSplit(v interface{}, args []interface{}) interface{} { s, ok := toString(v) if !ok { return &funcTypeError{"split", v} } x, ok := toString(args[0]) if !ok { return &funcTypeError{"split", x} } var ss []string if len(args) == 1 { ss = strings.Split(s, x) } else { var flags string if args[1] != nil { v, ok := toString(args[1]) if !ok { return &funcTypeError{"split", args[1]} } flags = v } r, err := compileRegexp(x, flags) if err != nil { return err } ss = r.Split(s, -1) } xs := make([]interface{}, len(ss)) for i, s := range ss { xs[i] = s } return xs } func funcToJSON(v interface{}) interface{} { return jsonMarshal(v) } func funcFromJSON(v interface{}) interface{} { x, ok := toString(v) if !ok { return &funcTypeError{"fromjson", v} } var w interface{} err := json.Unmarshal([]byte(x), &w) if err != nil { return err } return w } func funcFormat(v, x interface{}) interface{} { sx, ok := toString(x) if !ok { return &funcTypeError{"format", x} } fmt := "@" + sx f := formatToFunc(fmt) if f == nil { return &formatNotFoundError{fmt} } return internalFuncs[f.Name].callback(v, nil) } var htmlEscaper = strings.NewReplacer( `<`, "<", `>`, ">", `&`, "&", `'`, "'", `"`, """, ) func funcToHTML(v interface{}) interface{} { switch x := funcToString(v).(type) { case string: return htmlEscaper.Replace(x) default: return x } } func funcToURI(v interface{}) interface{} { switch x := funcToString(v).(type) { case string: return url.QueryEscape(x) default: return x } } func funcToCSV(v interface{}) interface{} { return funcToCSVTSV("csv", v, ",", func(s string) string { return `"` + strings.ReplaceAll(s, `"`, `""`) + `"` }) } var tsvEscaper = strings.NewReplacer( "\t", `\t`, "\r", `\r`, "\n", `\n`, "\\", `\\`, ) func funcToTSV(v interface{}) interface{} { return funcToCSVTSV("tsv", v, "\t", func(s string) string { return tsvEscaper.Replace(s) }) } func funcToCSVTSV(typ string, v interface{}, sep string, escape func(string) string) interface{} { xs, ok := toArray(v) if !ok { return &expectedArrayError{v} } ys := make([]string, len(xs)) for i, x := range xs { y, err := toCSVTSV(typ, x, escape) if err != nil { return err } ys[i] = y } return strings.Join(ys, sep) } func toCSVTSV(typ string, v interface{}, escape func(string) string) (string, error) { if jqv, ok := v.(JQValue); ok { v = jqv.JQValueToGoJQ() } switch v := v.(type) { case map[string]interface{}, []interface{}: return "", &formatCsvTsvRowError{typ, v} case string: return escape(v), nil default: if s := jsonMarshal(v); s != "null" { return s, nil } return "", nil } } func funcToSh(v interface{}) interface{} { if jqv, ok := v.(JQValue); ok { v = jqv.JQValueToGoJQ() } var xs []interface{} if w, ok := v.([]interface{}); ok { xs = w } else { xs = []interface{}{v} } var s strings.Builder for i, x := range xs { if i > 0 { s.WriteByte(' ') } if jqv, ok := x.(JQValue); ok { x = jqv.JQValueToGoJQ() } switch x := x.(type) { case map[string]interface{}, []interface{}: return &formatShError{x} case string: s.WriteByte('\'') s.WriteString(strings.ReplaceAll(x, "'", `'\''`)) s.WriteByte('\'') default: s.WriteString(jsonMarshal(x)) } } return s.String() } func funcToBase64(v interface{}) interface{} { switch x := funcToString(v).(type) { case string: return base64.StdEncoding.EncodeToString([]byte(x)) default: return x } } func funcToBase64d(v interface{}) interface{} { switch x := funcToString(v).(type) { case string: if i := strings.IndexRune(x, base64.StdPadding); i >= 0 { x = x[:i] } y, err := base64.RawStdEncoding.DecodeString(x) if err != nil { return err } return string(y) default: return x } } func funcIndex(_, v, x interface{}) interface{} { if xv, ok := x.(JQValue); ok { x = xv.JQValueToGoJQ() } switch x := x.(type) { case string: switch v := v.(type) { case nil: return nil case map[string]interface{}: return v[x] case JQValue: return v.JQValueKey(x) default: return &expectedObjectError{v} } case int, float64, *big.Int: idx, _ := toInt(x) switch v := v.(type) { case nil: return nil case []interface{}, JQValue: return funcIndexSlice(nil, nil, &idx, v) case string: switch v := funcIndexSlice(nil, nil, &idx, explode(v)).(type) { case []interface{}: return implode(v) case int: return implode([]interface{}{v}) case nil: return "" default: panic(v) } default: return &expectedArrayError{v} } case []interface{}: switch v := v.(type) { case nil: return nil case []interface{}: return indices(v, x) default: return &expectedArrayError{v} } case map[string]interface{}: if v == nil { return nil } start, ok := x["start"] if !ok { return &expectedStartEndError{x} } end, ok := x["end"] if !ok { return &expectedStartEndError{x} } return funcSlice(nil, v, end, start) default: return &objectKeyNotStringError{x} } } func indices(vs, xs []interface{}) interface{} { var rs []interface{} if len(xs) == 0 { return rs } for i := 0; i < len(vs) && i < len(vs)-len(xs)+1; i++ { var neq bool for j, y := range xs { if neq = compare(vs[i+j], y) != 0; neq { break } } if !neq { rs = append(rs, i) } } return rs } func funcSlice(_, v, end, start interface{}) (r interface{}) { // does not use toString to let JQValue:s do slicing if w, ok := v.(string); ok { v = explode(w) defer func() { // TODO: JQValue check needed? switch s := r.(type) { case []interface{}: r = implode(s) case int: r = implode([]interface{}{s}) case nil: r = "" case error: default: panic(r) } }() } switch v := v.(type) { case nil: return nil case []interface{}, JQValue: if start != nil { if start, ok := toInt(start); ok { if end != nil { if end, ok := toInt(end); ok { return funcIndexSlice(&start, &end, nil, v) } return &arrayIndexNotNumberError{end} } return funcIndexSlice(&start, nil, nil, v) } return &arrayIndexNotNumberError{start} } if end != nil { if end, ok := toInt(end); ok { return funcIndexSlice(nil, &end, nil, v) } return &arrayIndexNotNumberError{end} } return v default: return &expectedArrayError{v} } } func funcIndexSlice(start, end, index *int, v interface{}) interface{} { var l int switch v := v.(type) { case []interface{}: l = len(v) case JQValue: var ok bool lv := v.JQValueSliceLen() l, ok = lv.(int) // can be error if slicing not supported if !ok { return lv } } if index != nil { i := toIndex(l, *index) switch v := v.(type) { case []interface{}: if i < 0 { return nil } return v[i] case JQValue: return v.JQValueIndex(i) } } endIdx := l var startIdx int if end != nil { endIdx = toIndex(l, *end) if endIdx == -1 { endIdx = l } else if endIdx == -2 { endIdx = 0 } } if start != nil { startIdx = toIndex(l, *start) if startIdx == -1 || startIdx > endIdx { startIdx = endIdx } else if startIdx == -2 { startIdx = 0 } } switch v := v.(type) { case []interface{}: return v[startIdx:endIdx] case JQValue: return v.JQValueSlice(startIdx, endIdx) default: panic("unreachable") } } func toIndex(l int, i int) int { switch { case i < -l: return -2 case i < 0: return l + i case i < l: return i default: return -1 } } func funcIndices(v, x interface{}) interface{} { return indexFunc(v, x, indices) } func funcLindex(v, x interface{}) interface{} { return indexFunc(v, x, func(vs, xs []interface{}) interface{} { if len(xs) == 0 { return nil } for i := 0; i < len(vs) && i < len(vs)-len(xs)+1; i++ { var neq bool for j, y := range xs { if neq = compare(vs[i+j], y) != 0; neq { break } } if !neq { return i } } return nil }) } func funcRindex(v, x interface{}) interface{} { return indexFunc(v, x, func(vs, xs []interface{}) interface{} { if len(xs) == 0 { return nil } i := len(vs) - 1 if j := len(vs) - len(xs); j < i { i = j } for ; i >= 0; i-- { var neq bool for j, y := range xs { if neq = compare(vs[i+j], y) != 0; neq { break } } if !neq { return i } } return nil }) } func indexFunc(v, x interface{}, f func(_, _ []interface{}) interface{}) interface{} { switch v := v.(type) { case nil: return nil case []interface{}: switch x := x.(type) { case []interface{}: return f(v, x) default: return f(v, []interface{}{x}) } case string: if x, ok := toString(x); ok { return f(explode(v), explode(x)) } return &expectedStringError{x} case JQValue: return indexFunc(v.JQValueToGoJQ(), x, f) default: return &expectedArrayError{v} } } type rangeIter struct { value, end, step interface{} } func (iter *rangeIter) Next() (interface{}, bool) { if compare(iter.step, 0)*compare(iter.value, iter.end) >= 0 { return nil, false } v := iter.value iter.value = funcOpAdd(nil, v, iter.step) return v, true } func funcRange(_ interface{}, xs []interface{}) interface{} { for _, x := range xs { switch x.(type) { case int, float64, *big.Int: default: return &funcTypeError{"range", x} } } return &rangeIter{xs[0], xs[1], xs[2]} } func funcMinBy(v, x interface{}) interface{} { return funcMinMaxBy(v, x, "min_by", true) } func funcMaxBy(v, x interface{}) interface{} { return funcMinMaxBy(v, x, "max_by", false) } func funcMinMaxBy(v, x interface{}, name string, isMin bool) interface{} { var ok bool var vs []interface{} vs, ok = toArray(v) if !ok { return &expectedArrayError{v} } xs, ok := x.([]interface{}) if !ok { return &expectedArrayError{x} } if len(vs) != len(xs) { return &lengthMismatchError{name, vs, xs} } if len(vs) == 0 { return nil } i, j, x := 0, 0, xs[0] for i++; i < len(xs); i++ { if (compare(x, xs[i]) > 0) == isMin { j, x = i, xs[i] } } return vs[j] } type sortItem struct { value, key interface{} } func funcSortBy(v, x interface{}) interface{} { items, err := sortItems("sort_by", v, x) if err != nil { return err } rs := make([]interface{}, len(items)) for i, x := range items { rs[i] = x.value } return rs } func funcGroupBy(v, x interface{}) interface{} { items, err := sortItems("group_by", v, x) if err != nil { return err } var rs []interface{} var last interface{} for i, r := range items { if i == 0 || compare(last, r.key) != 0 { rs, last = append(rs, []interface{}{r.value}), r.key } else { rs[len(rs)-1] = append(rs[len(rs)-1].([]interface{}), r.value) } } return rs } func funcUniqueBy(v, x interface{}) interface{} { items, err := sortItems("unique_by", v, x) if err != nil { return err } var rs []interface{} var last interface{} for i, r := range items { if i == 0 || compare(last, r.key) != 0 { rs, last = append(rs, r.value), r.key } } return rs } func funcJoin(v, x interface{}) interface{} { vs, ok := toArray(v) if !ok { return &expectedArrayError{v} } if len(vs) == 0 { return "" } sep, ok := toString(x) if len(vs) > 1 && !ok { return &funcTypeError{"join", x} } ss := make([]string, len(vs)) for i, e := range vs { if isNull(e) { // nop } else if s, ok := toString(e); ok { ss[i] = s } else if b, ok := toBoolean(e); ok { if b { ss[i] = "true" } else { ss[i] = "false" } } else if _, ok := toInt(e); ok { ss[i] = jsonMarshal(e) } else { return &unaryTypeError{"join", e} } } return strings.Join(ss, sep) } func sortItems(name string, v, x interface{}) ([]*sortItem, interface{}) { var ok bool var vs []interface{} vs, ok = toArray(v) if !ok { return nil, &expectedArrayError{v} } xs, ok := x.([]interface{}) if !ok { return nil, &expectedArrayError{x} } if len(vs) != len(xs) { return nil, &lengthMismatchError{name, vs, xs} } items := make([]*sortItem, len(vs)) for i, v := range vs { items[i] = &sortItem{v, xs[i]} } sort.SliceStable(items, func(i, j int) bool { return compare(items[i].key, items[j].key) < 0 }) return items, nil } func funcSignificand(v float64) float64 { if math.IsNaN(v) || math.IsInf(v, 0) || v == 0.0 { return v } return math.Float64frombits((math.Float64bits(v) & 0x800fffffffffffff) | 0x3ff0000000000000) } func funcExp10(v float64) float64 { return math.Pow(10, v) } func funcFrexp(v interface{}) interface{} { x, ok := toFloat(v) if !ok { return &funcTypeError{"frexp", v} } f, e := math.Frexp(x) return []interface{}{f, e} } func funcModf(v interface{}) interface{} { x, ok := toFloat(v) if !ok { return &funcTypeError{"modf", v} } i, f := math.Modf(x) return []interface{}{f, i} } func funcLgamma(v float64) float64 { v, _ = math.Lgamma(v) return v } func funcDrem(l, r float64) float64 { x := math.Remainder(l, r) if x == 0.0 { return math.Copysign(x, l) } return x } func funcJn(l, r float64) float64 { return math.Jn(int(l), r) } func funcLdexp(l, r float64) float64 { return math.Ldexp(l, int(r)) } func funcScalb(l, r float64) float64 { return l * math.Pow(2, r) } func funcScalbln(l, r float64) float64 { return l * math.Pow(2, r) } func funcYn(l, r float64) float64 { return math.Yn(int(l), r) } func funcInfinite(interface{}) interface{} { return math.Inf(1) } func funcIsfinite(v interface{}) interface{} { x, ok := toFloat(v) return ok && !math.IsInf(x, 0) } func funcIsinfinite(v interface{}) interface{} { x, ok := toFloat(v) return ok && math.IsInf(x, 0) } func funcNan(interface{}) interface{} { return math.NaN() } func funcIsnan(v interface{}) interface{} { x, ok := toFloat(v) if !ok { if v == nil { return false } return &funcTypeError{"isnan", v} } return math.IsNaN(x) } func funcIsnormal(v interface{}) interface{} { x, ok := toFloat(v) return ok && !math.IsNaN(x) && !math.IsInf(x, 0) && x != 0.0 } func funcSetpath(v, p, w interface{}) interface{} { path, ok := toArray(p) if !ok { return &funcTypeError{"setpath", p} } var err error if v, err = updatePaths(v, path, w, false); err != nil { if err, ok := err.(*funcTypeError); ok { err.name = "setpath" } return err } return v } func funcDelpaths(v, p interface{}) interface{} { paths, ok := toArray(p) if !ok { return &funcTypeError{"delpaths", p} } // Fills the paths with an empty value and then delete them. We cannot delete // in each loop because array indices should not change. For example, // jq -n "[0, 1, 2, 3] | delpaths([[1], [2]])" #=> [0, 3]. var empty struct{} var err error for _, p := range paths { path, ok := toArray(p) if !ok { return &funcTypeError{"delpaths", p} } if v, err = updatePaths(v, path, empty, true); err != nil { return err } } return deleteEmpty(v) } func updatePaths(v interface{}, path []interface{}, w interface{}, delpaths bool) (interface{}, error) { if len(path) == 0 { return w, nil } p0 := path[0] switch x := p0.(type) { case struct{}: return v, nil case JQValue: p0 = x.JQValueToGoJQ() } switch x := p0.(type) { case string: if v == nil { // nil | delpaths(["a", ...]) if delpaths { return v, nil } v = make(map[string]interface{}) } switch v := v.(type) { case JQValue: if len(path) == 1 { uu := v.JQValueUpdate(x, w, delpaths) if err, ok := uu.(error); ok { return nil, err } return uu, nil } case struct{}: return v, nil } uu, ok := toObject(v) if !ok { return nil, &expectedObjectError{v} } if _, ok := uu[x]; !ok && delpaths { // nop, delete key that do not exist return v, nil } u, err := updatePaths(uu[x], path[1:], w, delpaths) if err != nil { return nil, err } vs := make(map[string]interface{}, len(uu)) for k, v := range uu { vs[k] = v } vs[x] = u return vs, nil case int, float64, *big.Int: if v == nil { // nil | delpaths([123, ...]) if delpaths { return v, nil } v = []interface{}{} } switch v := v.(type) { case JQValue: if len(path) == 1 { uu := v.JQValueUpdate(x, w, delpaths) if err, ok := uu.(error); ok { return nil, err } return uu, nil } case struct{}: return v, nil } uu, ok := toArray(v) if !ok { return nil, &expectedArrayError{v} } y, _ := toInt(x) l := len(uu) var copied bool if copied = y >= l; copied { if delpaths { return v, nil } if y > 0x3ffffff { return nil, &arrayIndexTooLargeError{y} } l = y + 1 ys := make([]interface{}, l) copy(ys, uu) uu = ys } else if y < -l { if delpaths { return v, nil } return nil, &funcTypeError{v: y} } else if y < 0 { y += l } u, err := updatePaths(uu[y], path[1:], w, delpaths) if err != nil { return nil, err } if copied { uu[y] = u return uu, nil } vs := make([]interface{}, l) copy(vs, uu) vs[y] = u return vs, nil case map[string]interface{}: if len(x) == 0 { switch v.(type) { case []interface{}: return nil, &arrayIndexNotNumberError{x} default: return nil, &objectKeyNotStringError{x} } } if v == nil { v = []interface{}{} } switch v := v.(type) { case JQValue: if len(path) == 1 { uu := v.JQValueUpdate(x, w, delpaths) if err, ok := uu.(error); ok { return nil, err } return uu, nil } case struct{}: return v, nil } uu, ok := toArray(v) if !ok { return nil, &expectedArrayError{v} } var start, end int if x, ok := toInt(x["start"]); ok { x := toIndex(len(uu), x) if x > len(uu) || x == -1 { start = len(uu) } else if x == -2 { start = 0 } else { start = x } } if x, ok := toInt(x["end"]); ok { x := toIndex(len(uu), x) if x == -1 { end = len(uu) } else if x < start { end = start } else { end = x } } else { end = len(uu) } if delpaths { if start >= end { return uu, nil } if len(path) > 1 { u, err := updatePaths(uu[start:end], path[1:], w, delpaths) if err != nil { return nil, err } switch us := u.(type) { case []interface{}: vs := make([]interface{}, len(uu)) copy(vs, uu) copy(vs[start:end], us) return vs, nil default: return nil, &expectedArrayError{u} } } vs := make([]interface{}, len(uu)) copy(vs, uu) for y := start; y < end; y++ { vs[y] = w } return vs, nil } if len(path) > 1 { u, err := updatePaths(uu[start:end], path[1:], w, delpaths) if err != nil { return nil, err } w = u } switch v := w.(type) { case []interface{}: vs := make([]interface{}, start+len(v)+len(uu)-end) copy(vs, uu[:start]) copy(vs[start:], v) copy(vs[start+len(v):], uu[end:]) return vs, nil default: return nil, &expectedArrayError{v} } default: switch v.(type) { case []interface{}: return nil, &arrayIndexNotNumberError{x} default: return nil, &objectKeyNotStringError{x} } } } func funcGetpath(v, p interface{}) interface{} { keys, ok := toArray(p) if !ok { return &funcTypeError{"getpath", p} } u := v for _, x := range keys { switch v.(type) { case map[string]interface{}: case []interface{}: case nil: case JQValue: default: return &getpathError{u, p} } v = funcIndex(nil, v, x) if _, ok := v.(error); ok { return &getpathError{u, p} } } return v } func funcTranspose(v interface{}) interface{} { vss, ok := v.([]interface{}) if !ok { return &funcTypeError{"transpose", v} } if len(vss) == 0 { return []interface{}{} } var l int for _, vs := range vss { vs, ok := vs.([]interface{}) if !ok { return &funcTypeError{"transpose", v} } if k := len(vs); l < k { l = k } } wss := make([][]interface{}, l) xs := make([]interface{}, l) for i, k := 0, len(vss); i < l; i++ { s := make([]interface{}, k) wss[i] = s xs[i] = s } for i, vs := range vss { for j, v := range vs.([]interface{}) { wss[j][i] = v } } return xs } func funcBsearch(v, t interface{}) interface{} { vs, ok := toArray(v) if !ok { return &funcTypeError{"bsearch", v} } i := sort.Search(len(vs), func(i int) bool { return compare(vs[i], t) >= 0 }) if i < len(vs) && compare(vs[i], t) == 0 { return i } return -i - 1 } func funcGmtime(v interface{}) interface{} { if v, ok := toFloat(v); ok { return epochToArray(v, time.UTC) } return &funcTypeError{"gmtime", v} } func funcLocaltime(v interface{}) interface{} { if v, ok := toFloat(v); ok { return epochToArray(v, time.Local) } return &funcTypeError{"localtime", v} } func epochToArray(v float64, loc *time.Location) []interface{} { t := time.Unix(int64(v), int64((v-math.Floor(v))*1e9)).In(loc) return []interface{}{ t.Year(), int(t.Month()) - 1, t.Day(), t.Hour(), t.Minute(), float64(t.Second()) + float64(t.Nanosecond())/1e9, int(t.Weekday()), t.YearDay() - 1, } } func funcMktime(v interface{}) interface{} { if a, ok := toArray(v); ok { t, err := arrayToTime("mktime", a, time.UTC) if err != nil { return err } return float64(t.Unix()) } return &funcTypeError{"mktime", v} } func funcStrftime(v, x interface{}) interface{} { if w, ok := toFloat(v); ok { v = epochToArray(w, time.UTC) } if a, ok := toArray(v); ok { if format, ok := toString(x); ok { t, err := arrayToTime("strftime", a, time.UTC) if err != nil { return err } return timefmt.Format(t, format) } return &funcTypeError{"strftime", x} } return &funcTypeError{"strftime", v} } func funcStrflocaltime(v, x interface{}) interface{} { if w, ok := toFloat(v); ok { v = epochToArray(w, time.Local) } if a, ok := toArray(v); ok { if format, ok := toString(x); ok { t, err := arrayToTime("strflocaltime", a, time.Local) if err != nil { return err } return timefmt.Format(t, format) } return &funcTypeError{"strflocaltime", x} } return &funcTypeError{"strflocaltime", v} } func funcStrptime(v, x interface{}) interface{} { if v, ok := toString(v); ok { if format, ok := toString(x); ok { t, err := timefmt.Parse(v, format) if err != nil { return err } var s time.Time if t == s { return &funcTypeError{"strptime", v} } return epochToArray(float64(t.Unix())+float64(t.Nanosecond())/1e9, time.UTC) } return &funcTypeError{"strptime", x} } return &funcTypeError{"strptime", v} } func arrayToTime(name string, a []interface{}, loc *time.Location) (time.Time, error) { var t time.Time if len(a) != 8 { return t, &funcTypeError{name, a} } var y, m, d, h, min, sec, nsec int if x, ok := toInt(a[0]); ok { y = x } else { return t, &funcTypeError{name, a} } if x, ok := toInt(a[1]); ok { m = x + 1 } else { return t, &funcTypeError{name, a} } if x, ok := toInt(a[2]); ok { d = x } else { return t, &funcTypeError{name, a} } if x, ok := toInt(a[3]); ok { h = x } else { return t, &funcTypeError{name, a} } if x, ok := toInt(a[4]); ok { min = x } else { return t, &funcTypeError{name, a} } if x, ok := toFloat(a[5]); ok { sec = int(x) nsec = int((x - math.Floor(x)) * 1e9) } else { return t, &funcTypeError{name, a} } return time.Date(y, time.Month(m), d, h, min, sec, nsec, loc), nil } func funcNow(interface{}) interface{} { t := time.Now() return float64(t.Unix()) + float64(t.Nanosecond())/1e9 } func funcMatch(v, re, fs, testing interface{}) interface{} { var flags string if fs != nil { v, ok := toString(fs) if !ok { return &funcTypeError{"match", fs} } flags = v } s, ok := toString(v) if !ok { return &funcTypeError{"match", v} } restr, ok := toString(re) if !ok { return &funcTypeError{"match", v} } r, err := compileRegexp(restr, flags) if err != nil { return err } var xs [][]int if strings.ContainsRune(flags, 'g') && testing != true { xs = r.FindAllStringSubmatchIndex(s, -1) } else { got := r.FindStringSubmatchIndex(s) if testing == true { return got != nil } if got != nil { xs = [][]int{got} } } res, names := make([]interface{}, len(xs)), r.SubexpNames() for i, x := range xs { captures := make([]interface{}, (len(x)-2)/2) for j := 1; j < len(x)/2; j++ { var name interface{} if n := names[j]; n != "" { name = n } if x[j*2] < 0 { captures[j-1] = map[string]interface{}{ "name": name, "offset": -1, "length": 0, "string": nil, } continue } captures[j-1] = map[string]interface{}{ "name": name, "offset": len([]rune(s[:x[j*2]])), "length": len([]rune(s[:x[j*2+1]])) - len([]rune(s[:x[j*2]])), "string": s[x[j*2]:x[j*2+1]], } } res[i] = map[string]interface{}{ "offset": len([]rune(s[:x[0]])), "length": len([]rune(s[:x[1]])) - len([]rune(s[:x[0]])), "string": s[x[0]:x[1]], "captures": captures, } } return res } func compileRegexp(re, flags string) (*regexp.Regexp, error) { if strings.IndexFunc(flags, func(r rune) bool { return r != 'g' && r != 'i' && r != 'm' }) >= 0 { return nil, fmt.Errorf("unsupported regular expression flag: %q", flags) } re = strings.ReplaceAll(re, "(?<", "(?P<") if strings.ContainsRune(flags, 'i') { re = "(?i)" + re } if strings.ContainsRune(flags, 'm') { re = "(?s)" + re } r, err := regexp.Compile(re) if err != nil { return nil, fmt.Errorf("invalid regular expression %q: %s", re, err) } return r, nil } func funcError(v interface{}, args []interface{}) interface{} { if len(args) > 0 { v = args[0] } code := 5 if v == nil { code = 0 } return &exitCodeError{v, code, false} } func funcHalt(interface{}) interface{} { return &exitCodeError{nil, 0, true} } func funcHaltError(v interface{}, args []interface{}) interface{} { code := 5 if len(args) > 0 { var ok bool if code, ok = toInt(args[0]); !ok { return &funcTypeError{"halt_error", args[0]} } } return &exitCodeError{v, code, true} } func internalfuncTypeError(v, x interface{}) interface{} { if x, ok := toString(x); ok { return &funcTypeError{x, v} } return &funcTypeError{"_type_error", v} } func toString(x interface{}) (string, bool) { switch x := x.(type) { case string: return x, true case JQValue: return toString(x.JQValueToGoJQ()) default: return "", false } } func toObject(x interface{}) (map[string]interface{}, bool) { switch x := x.(type) { case map[string]interface{}: return x, true case JQValue: return toObject(x.JQValueToGoJQ()) default: return nil, false } } func toArray(x interface{}) ([]interface{}, bool) { switch x := x.(type) { case []interface{}: return x, true case JQValue: return toArray(x.JQValueToGoJQ()) default: return nil, false } } func toBoolean(x interface{}) (bool, bool) { switch x := x.(type) { case bool: return x, true case JQValue: return toBoolean(x.JQValueToGoJQ()) default: return false, false } } func isNull(x interface{}) bool { switch x := x.(type) { case nil: return true case JQValue: return isNull(x.JQValueToGoJQ()) default: return false } } func toInt(x interface{}) (int, bool) { switch x := x.(type) { case int: return x, true case float64: return floatToInt(x), true case *big.Int: if x.IsInt64() { if i := x.Int64(); minInt <= i && i <= maxInt { return int(i), true } } if x.Sign() > 0 { return maxInt, true } return minInt, true case JQValue: return toInt(x.JQValueToGoJQ()) default: return 0, false } } func floatToInt(x float64) int { if minInt <= x && x <= maxInt { return int(x) } if x > 0 { return maxInt } return minInt } func toFloat(x interface{}) (float64, bool) { switch x := x.(type) { case int: return float64(x), true case float64: return x, true case *big.Int: return bigToFloat(x), true case JQValue: return toFloat(x.JQValueToGoJQ()) default: return 0.0, false } } func bigToFloat(x *big.Int) float64 { if x.IsInt64() { return float64(x.Int64()) } if f, err := strconv.ParseFloat(x.String(), 64); err == nil { return f } return math.Inf(x.Sign()) } golang-github-wader-gojq-0.0~git20220108.6a05b6c/go.dev.mod000066400000000000000000000002641416642156300226210ustar00rootroot00000000000000module github.com/itchyny/gojq go 1.16 require ( github.com/itchyny/astgen-go v0.0.0-20210914105503-cc8fccf6f972 // indirect github.com/itchyny/timefmt-go v0.1.3 // indirect ) golang-github-wader-gojq-0.0~git20220108.6a05b6c/go.dev.sum000066400000000000000000000006301416642156300226430ustar00rootroot00000000000000github.com/itchyny/astgen-go v0.0.0-20210914105503-cc8fccf6f972 h1:XYWolmPDLTY9B1O5o/Ad811/mtVkaHWMiZdbPLm/nDA= github.com/itchyny/astgen-go v0.0.0-20210914105503-cc8fccf6f972/go.mod h1:jTXcxGeQMJfFN3wWjtzb4aAaWDDN+QbezE0HjH1XfNk= github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= golang-github-wader-gojq-0.0~git20220108.6a05b6c/go.mod000066400000000000000000000006431416642156300220450ustar00rootroot00000000000000// fork of ithub.com/itchyny/gojq // renames module to make it easier to depend on without using replace module github.com/wader/gojq go 1.16 require ( github.com/google/go-cmp v0.5.4 github.com/itchyny/timefmt-go v0.1.3 github.com/mattn/go-isatty v0.0.14 github.com/mattn/go-runewidth v0.0.9 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) golang-github-wader-gojq-0.0~git20220108.6a05b6c/go.sum000066400000000000000000000031471416642156300220740ustar00rootroot00000000000000github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-github-wader-gojq-0.0~git20220108.6a05b6c/gojq.go000066400000000000000000000037641416642156300222350ustar00rootroot00000000000000// Package gojq provides the parser and interpreter of gojq. // // Please refer to https://github.com/itchyny/gojq#usage-as-a-library for // introduction of the usage as a library. package gojq // JQValue represents something that can be a jq value type JQValue interface { // JQValueLength is length of value, ex: value | length JQValueLength() interface{} // JQValueSliceLen slice length // return int or error if slicing not supported JQValueSliceLen() interface{} // JQValueIndex look up index for value, ex: value[index] // index -1 outside after slice, -2 outside before slice JQValueIndex(index int) interface{} // JQValueSlice slices value, ex: value[start:end] // start and end indexes are translated and clamped using JQValueSliceLen JQValueSlice(start int, end int) interface{} // JQValueKey look up key value of value: ex: value[name] JQValueKey(name string) interface{} // JQValueUpdate set or delete key // ex: value.key = ..., value or setpath(...; ...) // ex: value.key |= empty or value | delpaths(...; ...) JQValueUpdate(key interface{}, u interface{}, delpaths bool) interface{} // JQValueEach each of value, ex: value[] // return []PathValue or error JQValueEach() interface{} // JQValueKeys keys for value, ex: value | keys JQValueKeys() interface{} // JQValueHas check if value has key, ex: value | has(key) JQValueHas(key interface{}) interface{} // JQValueType type of value, ex: value | type // Usually "array", "boolean", "object", "number", "string" or "null" JQValueType() string // JQValueToNumber is value represented as a number, ex: value | tonumber JQValueToNumber() interface{} // JQValueToString is value represented as a string, ex: value | tostring JQValueToString() interface{} // JQValue value represented as a gojq compatible value // Should be a int, float64, *big.Int, bool, string, []interface{}, map[string]interface{} or error JQValueToGoJQ() interface{} } // PathValue is a part of a jq path expression type PathValue struct { Path, Value interface{} } golang-github-wader-gojq-0.0~git20220108.6a05b6c/iter.go000066400000000000000000000014741416642156300222340ustar00rootroot00000000000000package gojq // Iter is an interface for an iterator. type Iter interface { Next() (interface{}, bool) } // NewIter creates a new Iter from values. func NewIter(values ...interface{}) Iter { switch len(values) { case 0: return emptyIter{} case 1: return &unitIter{value: values[0]} default: iter := sliceIter(values) return &iter } } type emptyIter struct{} func (emptyIter) Next() (interface{}, bool) { return nil, false } type unitIter struct { value interface{} done bool } func (iter *unitIter) Next() (interface{}, bool) { if iter.done { return nil, false } iter.done = true return iter.value, true } type sliceIter []interface{} func (iter *sliceIter) Next() (interface{}, bool) { if len(*iter) == 0 { return nil, false } value := (*iter)[0] *iter = (*iter)[1:] return value, true } golang-github-wader-gojq-0.0~git20220108.6a05b6c/lexer.go000066400000000000000000000242161416642156300224070ustar00rootroot00000000000000package gojq import ( "strconv" "strings" "unicode/utf8" ) type lexer struct { source string offset int result *Query token string tokenType int inString bool err error } func newLexer(src string) *lexer { return &lexer{source: src} } const eof = -1 var keywords = map[string]int{ "or": tokOrOp, "and": tokAndOp, "module": tokModule, "import": tokImport, "include": tokInclude, "def": tokDef, "as": tokAs, "label": tokLabel, "break": tokBreak, "null": tokNull, "true": tokTrue, "false": tokFalse, "if": tokIf, "then": tokThen, "elif": tokElif, "else": tokElse, "end": tokEnd, "try": tokTry, "catch": tokCatch, "reduce": tokReduce, "foreach": tokForeach, } func (l *lexer) Lex(lval *yySymType) (tokenType int) { defer func() { l.tokenType = tokenType }() if len(l.source) == l.offset { l.token = "" return eof } if l.inString { tok, str := l.scanString(l.offset) lval.token = str return tok } ch, iseof := l.next() if iseof { l.token = "" return eof } switch { case isIdent(ch, false): i := l.offset - 1 j, isModule := l.scanIdentOrModule() l.token = l.source[i:j] lval.token = l.token if isModule { return tokModuleIdent } if tok, ok := keywords[l.token]; ok { return tok } return tokIdent case isNumber(ch): if ch == '0' { // TODO: fractions etc? // TODO: allow _ separator? decimal numbers? switch l.peek() { case 'b', 'o', 'x': i := l.offset - 1 l.offset++ for isInteger(l.peek()) { l.offset++ } l.token = string(l.source[i:l.offset]) lval.token = l.token return tokNumber } } i := l.offset - 1 j := l.scanNumber(numberStateLead) if j < 0 { l.token = l.source[i:-j] return tokInvalid } l.token = l.source[i:j] lval.token = l.token return tokNumber } switch ch { case '.': ch := l.peek() switch { case ch == '.': l.offset++ l.token = ".." return tokRecurse case isIdent(ch, false): l.token = l.source[l.offset-1 : l.scanIdent()] lval.token = l.token[1:] return tokIndex case isNumber(ch): i := l.offset - 1 j := l.scanNumber(numberStateFloat) if j < 0 { l.token = l.source[i:-j] return tokInvalid } l.token = l.source[i:j] lval.token = l.token return tokNumber default: return '.' } case '$': if isIdent(l.peek(), false) { i := l.offset - 1 j, isModule := l.scanIdentOrModule() l.token = l.source[i:j] lval.token = l.token if isModule { return tokModuleVariable } return tokVariable } case '|': if l.peek() == '=' { l.offset++ l.token = "|=" lval.operator = OpModify return tokUpdateOp } case '?': if l.peek() == '/' { l.offset++ if l.peek() == '/' { l.offset++ l.token = "?//" return tokDestAltOp } l.offset-- } case '+': if l.peek() == '=' { l.offset++ l.token = "+=" lval.operator = OpUpdateAdd return tokUpdateOp } case '-': if l.peek() == '=' { l.offset++ l.token = "-=" lval.operator = OpUpdateSub return tokUpdateOp } case '*': if l.peek() == '=' { l.offset++ l.token = "*=" lval.operator = OpUpdateMul return tokUpdateOp } case '/': switch l.peek() { case '=': l.offset++ l.token = "/=" lval.operator = OpUpdateDiv return tokUpdateOp case '/': l.offset++ if l.peek() == '=' { l.offset++ l.token = "//=" lval.operator = OpUpdateAlt return tokUpdateOp } l.token = "//" lval.operator = OpAlt return tokAltOp } case '%': if l.peek() == '=' { l.offset++ l.token = "%=" lval.operator = OpUpdateMod return tokUpdateOp } case '=': if l.peek() == '=' { l.offset++ l.token = "==" lval.operator = OpEq return tokCompareOp } l.token = "=" lval.operator = OpAssign return tokUpdateOp case '!': if l.peek() == '=' { l.offset++ l.token = "!=" lval.operator = OpNe return tokCompareOp } case '>': if l.peek() == '=' { l.offset++ l.token = ">=" lval.operator = OpGe return tokCompareOp } l.token = ">" lval.operator = OpGt return tokCompareOp case '<': if l.peek() == '=' { l.offset++ l.token = "<=" lval.operator = OpLe return tokCompareOp } l.token = "<" lval.operator = OpLt return tokCompareOp case '@': if isIdent(l.peek(), true) { l.token = l.source[l.offset-1 : l.scanIdent()] lval.token = l.token return tokFormat } case '"': tok, str := l.scanString(l.offset - 1) lval.token = str return tok default: if ch >= utf8.RuneSelf { r, _ := utf8.DecodeRuneInString(l.source[l.offset-1:]) l.token = string(r) l.offset += len(l.token) } } return int(ch) } func (l *lexer) next() (byte, bool) { for { ch := l.source[l.offset] l.offset++ if ch == '#' { if len(l.source) == l.offset { return 0, true } for !isNewLine(l.source[l.offset]) { l.offset++ if len(l.source) == l.offset { return 0, true } } } else if !isWhite(ch) { return ch, false } else if len(l.source) == l.offset { return 0, true } } } func (l *lexer) peek() byte { if len(l.source) == l.offset { return 0 } return l.source[l.offset] } func (l *lexer) scanIdent() int { for isIdent(l.peek(), true) { l.offset++ } return l.offset } func (l *lexer) scanIdentOrModule() (int, bool) { index := l.scanIdent() var isModule bool if l.peek() == ':' { l.offset++ if l.peek() == ':' { l.offset++ if isIdent(l.peek(), false) { l.offset++ index = l.scanIdent() isModule = true } else { l.offset -= 2 } } else { l.offset-- } } return index, isModule } func (l *lexer) validVarName() bool { if l.peek() != '$' { return false } l.offset++ return isIdent(l.peek(), false) && l.scanIdent() == len(l.source) } const ( numberStateLead = iota numberStateFloat numberStateExpLead numberStateExp ) func (l *lexer) scanNumber(state int) int { for { switch state { case numberStateLead, numberStateFloat: if ch := l.peek(); isNumber(ch) { l.offset++ } else { switch ch { case '.': if state != numberStateLead { l.offset++ return -l.offset } l.offset++ state = numberStateFloat case 'e', 'E': l.offset++ switch l.peek() { case '-', '+': l.offset++ } state = numberStateExpLead default: if isIdent(ch, false) { l.offset++ return -l.offset } return l.offset } } case numberStateExpLead, numberStateExp: if ch := l.peek(); !isNumber(ch) { if isIdent(ch, false) { l.offset++ return -l.offset } if state == numberStateExpLead { return -l.offset } return l.offset } l.offset++ state = numberStateExp default: panic(state) } } } func (l *lexer) validNumber() bool { ch := l.peek() switch ch { case '+', '-': l.offset++ ch = l.peek() } state := numberStateLead if ch == '.' { l.offset++ ch = l.peek() state = numberStateFloat } return isNumber(ch) && l.scanNumber(state) == len(l.source) } func (l *lexer) scanString(start int) (int, string) { var quote, newline bool unquote := func(src string, quote bool) (string, error) { if quote { src = "\"" + src + "\"" } if newline { src = strings.ReplaceAll(src, "\n", "\\n") } return strconv.Unquote(src) } for i, m := l.offset, len(l.source); i < m; i++ { ch := l.source[i] switch ch { case '\\': quote = !quote case '\n': newline = true case '"': if !quote { if !l.inString { l.offset = i + 1 l.token = l.source[start:l.offset] str, err := unquote(l.token, false) if err != nil { return tokInvalid, "" } return tokString, str } if i > l.offset { l.offset = i l.token = l.source[start:l.offset] str, err := unquote(l.token, true) if err != nil { return tokInvalid, "" } return tokString, str } l.inString = false l.offset = i + 1 return tokStringEnd, "" } quote = false case '(': if quote { if l.inString { if i > l.offset+1 { l.offset = i - 1 l.token = l.source[start:l.offset] str, err := unquote(l.token, true) if err != nil { return tokInvalid, "" } return tokString, str } l.offset = i + 1 l.inString = false return tokStringQuery, "" } l.inString = true return tokStringStart, "" } default: if quote { if !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || '0' <= ch && ch <= '9' || ch == '\'' || ch == '"') { l.offset = i + 1 l.token = l.source[l.offset-2 : l.offset] return tokInvalid, "" } quote = false } } } l.offset = len(l.source) l.token = l.source[start:l.offset] return tokInvalid, "" } type parseError struct { offset int token string tokenType int } func (err *parseError) Error() string { var message string prefix := "unexpected" switch { case err.tokenType == eof: message = "" case err.tokenType == tokInvalid: prefix = "invalid" fallthrough case err.tokenType >= utf8.RuneSelf: if strings.HasPrefix(err.token, "\"") { message = err.token } else { message = "\"" + err.token + "\"" } default: message = strconv.Quote(string(rune(err.tokenType))) } return prefix + " token " + message } func (err *parseError) Token() (string, int) { return err.token, err.offset } func (l *lexer) Error(string) { offset, token := l.offset, l.token switch { case l.tokenType == eof: offset++ case l.tokenType >= utf8.RuneSelf: offset -= len(token) - 1 default: token = string(rune(l.tokenType)) } l.err = &parseError{offset, token, l.tokenType} } func isWhite(ch byte) bool { switch ch { case '\t', '\n', '\r', ' ': return true default: return false } } func isIdent(ch byte, tail bool) bool { return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || tail && isNumber(ch) } func isNumber(ch byte) bool { return '0' <= ch && ch <= '9' } func isInteger(ch byte) bool { return '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' } func isNewLine(ch byte) bool { switch ch { case '\n', '\r': return true default: return false } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/math.go000066400000000000000000000005301416642156300222120ustar00rootroot00000000000000package gojq import "math/bits" const ( maxInt = 1<<(bits.UintSize-1) - 1 // math.MaxInt64 or math.MaxInt32 minInt = -maxInt - 1 // math.MinInt64 or math.MinInt32 maxHalfInt = 1<<(bits.UintSize/2-1) - 1 // math.MaxInt32 or math.MaxInt16 minHalfInt = -maxHalfInt - 1 // math.MinInt32 or math.MinInt16 ) golang-github-wader-gojq-0.0~git20220108.6a05b6c/module_loader.go000066400000000000000000000072711416642156300241050ustar00rootroot00000000000000package gojq import ( "encoding/json" "fmt" "io" "os" "path/filepath" "strings" ) type moduleLoader struct { paths []string } // NewModuleLoader creates a new ModuleLoader reading local modules in the paths. func NewModuleLoader(paths []string) ModuleLoader { return &moduleLoader{expandHomeDir(paths)} } func (l *moduleLoader) LoadInitModules() ([]*Query, error) { var qs []*Query for _, path := range l.paths { if filepath.Base(path) != ".jq" { continue } fi, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { continue } return nil, err } if fi.IsDir() { continue } cnt, err := os.ReadFile(path) if err != nil { return nil, err } q, err := parseModule(path, string(cnt)) if err != nil { return nil, &queryParseError{"query in module", path, string(cnt), err} } qs = append(qs, q) } return qs, nil } func (l *moduleLoader) LoadModuleWithMeta(name string, meta map[string]interface{}) (*Query, error) { path, err := l.lookupModule(name, ".jq", meta) if err != nil { return nil, err } cnt, err := os.ReadFile(path) if err != nil { return nil, err } q, err := parseModule(path, string(cnt)) if err != nil { return nil, &queryParseError{"query in module", path, string(cnt), err} } return q, nil } func (l *moduleLoader) LoadJSONWithMeta(name string, meta map[string]interface{}) (interface{}, error) { path, err := l.lookupModule(name, ".json", meta) if err != nil { return nil, err } f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() var vals []interface{} dec := json.NewDecoder(f) dec.UseNumber() for { var val interface{} if err := dec.Decode(&val); err != nil { if err == io.EOF { break } if _, err := f.Seek(0, io.SeekStart); err != nil { return nil, err } cnt, er := io.ReadAll(f) if er != nil { return nil, er } return nil, &jsonParseError{path, string(cnt), err} } vals = append(vals, val) } return vals, nil } func (l *moduleLoader) lookupModule(name, extension string, meta map[string]interface{}) (string, error) { paths := l.paths if path := searchPath(meta); path != "" { paths = append([]string{path}, paths...) } for _, base := range paths { path := filepath.Clean(filepath.Join(base, name+extension)) if _, err := os.Stat(path); err == nil { return path, err } path = filepath.Clean(filepath.Join(base, name, filepath.Base(name)+extension)) if _, err := os.Stat(path); err == nil { return path, err } } return "", fmt.Errorf("module not found: %q", name) } // This is a dirty hack to implement the "search" field. func parseModule(path, cnt string) (*Query, error) { q, err := Parse(cnt) if err != nil { return nil, err } for _, i := range q.Imports { if i.Meta == nil { continue } i.Meta.KeyVals = append( i.Meta.KeyVals, &ConstObjectKeyVal{ Key: "$$path", Val: &ConstTerm{Str: path}, }, ) } return q, nil } func searchPath(meta map[string]interface{}) string { x, ok := meta["search"] if !ok { return "" } s, ok := x.(string) if !ok { return "" } if filepath.IsAbs(s) { return s } if strings.HasPrefix(s, "~") { if homeDir, err := os.UserHomeDir(); err == nil { return filepath.Join(homeDir, s[1:]) } } var path string if x, ok := meta["$$path"]; ok { path, _ = x.(string) } if path == "" { return s } return filepath.Join(filepath.Dir(path), s) } func expandHomeDir(paths []string) []string { var homeDir string var err error for i, path := range paths { if strings.HasPrefix(path, "~") { if homeDir == "" && err == nil { homeDir, err = os.UserHomeDir() } if homeDir != "" { paths[i] = filepath.Join(homeDir, path[1:]) } } } return paths } golang-github-wader-gojq-0.0~git20220108.6a05b6c/normalize.go000066400000000000000000000042651416642156300232720ustar00rootroot00000000000000package gojq import ( "encoding/json" "fmt" "math" "math/big" "strings" ) func NormalizeNumbers(s string) interface{} { if !newLexer(s).validNumber() { return fmt.Errorf("invalid number: %q", s) } return normalizeNumbers(json.Number(s)) } func normalizeNumbers(v interface{}) interface{} { switch v := v.(type) { case json.Number: if i, err := v.Int64(); err == nil && minInt <= i && i <= maxInt { return int(i) } if strings.ContainsAny(v.String(), ".eE") { if f, err := v.Float64(); err == nil { return f } } if bi, ok := new(big.Int).SetString(v.String(), 0); ok { return bi } if strings.HasPrefix(v.String(), "-") { return math.Inf(-1) } return math.Inf(1) case *big.Int: if v.IsInt64() { if i := v.Int64(); minInt <= i && i <= maxInt { return int(i) } } return v case int64: if v > maxInt || v < minInt { return new(big.Int).SetInt64(v) } return int(v) case int32: return int(v) case int16: return int(v) case int8: return int(v) case uint: if v > maxInt { return new(big.Int).SetUint64(uint64(v)) } return int(v) case uint64: if v > maxInt { return new(big.Int).SetUint64(v) } return int(v) case uint32: if uint64(v) > maxInt { return new(big.Int).SetUint64(uint64(v)) } return int(v) case uint16: return int(v) case uint8: return int(v) case float32: return float64(v) case map[string]interface{}: for k, x := range v { v[k] = normalizeNumbers(x) } return v case []interface{}: for i, x := range v { v[i] = normalizeNumbers(x) } return v default: return v } } // It's ok to delete destructively because this function is used right after // updatePaths, where it shallow-copies maps or slices on updates. func deleteEmpty(v interface{}) interface{} { switch v := v.(type) { case struct{}: return nil case map[string]interface{}: for k, w := range v { if w == struct{}{} { delete(v, k) } else { v[k] = deleteEmpty(w) } } return v case []interface{}: var j int for _, w := range v { if w != struct{}{} { v[j] = deleteEmpty(w) j++ } } for i := j; i < len(v); i++ { v[i] = nil } return v[:j] default: return v } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/operator.go000066400000000000000000000303641416642156300231240ustar00rootroot00000000000000package gojq import ( "encoding/json" "fmt" "math" "math/big" "strings" ) // Operator ... type Operator int // Operators ... const ( OpPipe Operator = iota + 1 OpComma OpAdd OpSub OpMul OpDiv OpMod OpEq OpNe OpGt OpLt OpGe OpLe OpAnd OpOr OpAlt OpAssign OpModify OpUpdateAdd OpUpdateSub OpUpdateMul OpUpdateDiv OpUpdateMod OpUpdateAlt ) // String implements Stringer. func OperatorFromString(s string) Operator { switch s { case "|": return OpPipe case ",": return OpComma case "+": return OpAdd case "-": return OpSub case "*": return OpMul case "/": return OpDiv case "%": return OpMod case "==": return OpEq case "!=": return OpNe case ">": return OpGt case "<": return OpLt case ">=": return OpGe case "<=": return OpLe case "and": return OpAnd case "or": return OpOr case "//": return OpAlt case "=": return OpAssign case "|=": return OpModify case "+=": return OpUpdateAdd case "-=": return OpUpdateSub case "*=": return OpUpdateMul case "/=": return OpUpdateDiv case "%=": return OpUpdateMod case "//=": return OpUpdateAlt default: return 0 } } // String implements Stringer. func (op Operator) String() string { switch op { case OpPipe: return "|" case OpComma: return "," case OpAdd: return "+" case OpSub: return "-" case OpMul: return "*" case OpDiv: return "/" case OpMod: return "%" case OpEq: return "==" case OpNe: return "!=" case OpGt: return ">" case OpLt: return "<" case OpGe: return ">=" case OpLe: return "<=" case OpAnd: return "and" case OpOr: return "or" case OpAlt: return "//" case OpAssign: return "=" case OpModify: return "|=" case OpUpdateAdd: return "+=" case OpUpdateSub: return "-=" case OpUpdateMul: return "*=" case OpUpdateDiv: return "/=" case OpUpdateMod: return "%=" case OpUpdateAlt: return "//=" default: return "" } } // GoString implements GoStringer. func (op Operator) GoString() (str string) { defer func() { str = "gojq." + str }() switch op { case Operator(0): return "Operator(0)" case OpPipe: return "OpPipe" case OpComma: return "OpComma" case OpAdd: return "OpAdd" case OpSub: return "OpSub" case OpMul: return "OpMul" case OpDiv: return "OpDiv" case OpMod: return "OpMod" case OpEq: return "OpEq" case OpNe: return "OpNe" case OpGt: return "OpGt" case OpLt: return "OpLt" case OpGe: return "OpGe" case OpLe: return "OpLe" case OpAnd: return "OpAnd" case OpOr: return "OpOr" case OpAlt: return "OpAlt" case OpAssign: return "OpAssign" case OpModify: return "OpModify" case OpUpdateAdd: return "OpUpdateAdd" case OpUpdateSub: return "OpUpdateSub" case OpUpdateMul: return "OpUpdateMul" case OpUpdateDiv: return "OpUpdateDiv" case OpUpdateMod: return "OpUpdateMod" case OpUpdateAlt: return "OpUpdateAlt" default: panic(op) } } func (op Operator) getFunc() string { switch op { case OpPipe: panic("unreachable") case OpComma: panic("unreachable") case OpAdd: return "_add" case OpSub: return "_subtract" case OpMul: return "_multiply" case OpDiv: return "_divide" case OpMod: return "_modulo" case OpEq: return "_equal" case OpNe: return "_notequal" case OpGt: return "_greater" case OpLt: return "_less" case OpGe: return "_greatereq" case OpLe: return "_lesseq" case OpAnd: panic("unreachable") case OpOr: panic("unreachable") case OpAlt: panic("unreachable") case OpAssign: return "_assign" case OpModify: return "_modify" case OpUpdateAdd: return "_add" case OpUpdateSub: return "_subtract" case OpUpdateMul: return "_multiply" case OpUpdateDiv: return "_divide" case OpUpdateMod: return "_modulo" case OpUpdateAlt: return "_alternative" default: panic(op) } } func (op Operator) MarshalJSON() ([]byte, error) { if op == 0 { return json.Marshal(nil) } return json.Marshal(op.String()) } func (op *Operator) UnmarshalJSON(text []byte) error { var s string err := json.Unmarshal(text, &s) if err != nil { return err } *op = OperatorFromString(s) if *op == 0 { return fmt.Errorf("unknown operator %v", s) } return nil } func binopIsHalfInt(l, r int) bool { return minHalfInt <= l && l <= maxHalfInt && minHalfInt <= r && r <= maxHalfInt } func BinopTypeSwitch( l, r interface{}, intSafe func(l, r int) bool, callbackInts func(_, _ int) interface{}, callbackFloats func(_, _ float64) interface{}, callbackBigInts func(_, _ *big.Int) interface{}, callbackStrings func(_, _ string) interface{}, callbackArrays func(_, _ []interface{}) interface{}, callbackMaps func(_, _ map[string]interface{}) interface{}, fallback func(_, _ interface{}) interface{}) interface{} { if lj, ok := l.(JQValue); ok { l = lj.JQValueToGoJQ() } if rj, ok := r.(JQValue); ok { r = rj.JQValueToGoJQ() } switch l := l.(type) { case int: switch r := r.(type) { case int: if intSafe != nil && intSafe(l, r) { return callbackInts(l, r) } return callbackBigInts(big.NewInt(int64(l)), big.NewInt(int64(r))) case float64: return callbackFloats(float64(l), r) case *big.Int: return callbackBigInts(big.NewInt(int64(l)), r) default: return fallback(l, r) } case float64: switch r := r.(type) { case int: return callbackFloats(l, float64(r)) case float64: return callbackFloats(l, r) case *big.Int: return callbackFloats(l, bigToFloat(r)) default: return fallback(l, r) } case *big.Int: switch r := r.(type) { case int: return callbackBigInts(l, big.NewInt(int64(r))) case float64: return callbackFloats(bigToFloat(l), r) case *big.Int: return callbackBigInts(l, r) default: return fallback(l, r) } case string: switch r := r.(type) { case string: return callbackStrings(l, r) default: return fallback(l, r) } case []interface{}: switch r := r.(type) { case []interface{}: return callbackArrays(l, r) default: return fallback(l, r) } case map[string]interface{}: switch r := r.(type) { case map[string]interface{}: return callbackMaps(l, r) default: return fallback(l, r) } default: return fallback(l, r) } } func funcOpPlus(v interface{}) interface{} { switch v := v.(type) { case int: return v case float64: return v case *big.Int: return v case JQValue: return funcOpPlus(v.JQValueToGoJQ()) default: return &unaryTypeError{"plus", v} } } func funcOpNegate(v interface{}) interface{} { switch v := v.(type) { case int: return -v case float64: return -v case *big.Int: return new(big.Int).Neg(v) case JQValue: return funcOpNegate(v.JQValueToGoJQ()) default: return &unaryTypeError{"negate", v} } } func funcOpAdd(_, l, r interface{}) interface{} { if isNull(l) { return r } else if isNull(r) { return l } return BinopTypeSwitch(l, r, binopIsHalfInt, func(l, r int) interface{} { return l + r }, func(l, r float64) interface{} { return l + r }, func(l, r *big.Int) interface{} { return new(big.Int).Add(l, r) }, func(l, r string) interface{} { return l + r }, func(l, r []interface{}) interface{} { if len(r) == 0 { return l } else if len(l) == 0 { return r } v := make([]interface{}, 0, len(l)+len(r)) return append(append(v, l...), r...) }, func(l, r map[string]interface{}) interface{} { m := make(map[string]interface{}, len(l)+len(r)) for k, v := range l { m[k] = v } for k, v := range r { m[k] = v } return m }, func(l, r interface{}) interface{} { return &binopTypeError{"add", l, r} }, ) } func funcOpSub(_, l, r interface{}) interface{} { return BinopTypeSwitch(l, r, binopIsHalfInt, func(l, r int) interface{} { return l - r }, func(l, r float64) interface{} { return l - r }, func(l, r *big.Int) interface{} { return new(big.Int).Sub(l, r) }, func(l, r string) interface{} { return &binopTypeError{"subtract", l, r} }, func(l, r []interface{}) interface{} { a := make([]interface{}, 0, len(l)) for _, v := range l { var found bool for _, w := range r { if compare(v, w) == 0 { found = true break } } if !found { a = append(a, v) } } return a }, func(l, r map[string]interface{}) interface{} { return &binopTypeError{"subtract", l, r} }, func(l, r interface{}) interface{} { return &binopTypeError{"subtract", l, r} }, ) } func funcOpMul(_, l, r interface{}) interface{} { return BinopTypeSwitch(l, r, binopIsHalfInt, func(l, r int) interface{} { return l * r }, func(l, r float64) interface{} { return l * r }, func(l, r *big.Int) interface{} { return new(big.Int).Mul(l, r) }, func(l, r string) interface{} { return &binopTypeError{"multiply", l, r} }, func(l, r []interface{}) interface{} { return &binopTypeError{"multiply", l, r} }, deepMergeObjects, func(l, r interface{}) interface{} { multiplyString := func(s string, cnt float64) interface{} { if cnt <= 0.0 || cnt > float64(maxHalfInt/(16*(len(s)+1))) || math.IsNaN(cnt) { return nil } if cnt < 1.0 { return s } return strings.Repeat(s, int(cnt)) } if l, ok := l.(string); ok { if f, ok := toFloat(r); ok { return multiplyString(l, f) } } if r, ok := r.(string); ok { if f, ok := toFloat(l); ok { return multiplyString(r, f) } } return &binopTypeError{"multiply", l, r} }, ) } func deepMergeObjects(l, r map[string]interface{}) interface{} { m := make(map[string]interface{}, len(l)+len(r)) for k, v := range l { m[k] = v } for k, v := range r { if mk, ok := m[k]; ok { if mk, ok := mk.(map[string]interface{}); ok { if w, ok := v.(map[string]interface{}); ok { v = deepMergeObjects(mk, w) } } } m[k] = v } return m } func funcOpDiv(_, l, r interface{}) interface{} { return BinopTypeSwitch(l, r, binopIsHalfInt, func(l, r int) interface{} { if r == 0 { if l == 0 { return math.NaN() } return &zeroDivisionError{l, r} } if l%r == 0 { return l / r } return float64(l) / float64(r) }, func(l, r float64) interface{} { if r == 0.0 { if l == 0.0 { return math.NaN() } return &zeroDivisionError{l, r} } return l / r }, func(l, r *big.Int) interface{} { if r.Sign() == 0 { if l.Sign() == 0 { return math.NaN() } return &zeroDivisionError{l, r} } d, m := new(big.Int).DivMod(l, r, new(big.Int)) if m.Sign() == 0 { return d } return bigToFloat(l) / bigToFloat(r) }, func(l, r string) interface{} { if l == "" { return []interface{}{} } xs := strings.Split(l, r) vs := make([]interface{}, len(xs)) for i, x := range xs { vs[i] = x } return vs }, func(l, r []interface{}) interface{} { return &binopTypeError{"divide", l, r} }, func(l, r map[string]interface{}) interface{} { return &binopTypeError{"divide", l, r} }, func(l, r interface{}) interface{} { return &binopTypeError{"divide", l, r} }, ) } func funcOpMod(_, l, r interface{}) interface{} { return BinopTypeSwitch(l, r, binopIsHalfInt, func(l, r int) interface{} { if r == 0 { return &zeroModuloError{l, r} } return l % r }, func(l, r float64) interface{} { ri := floatToInt(r) if ri == 0 { return &zeroModuloError{l, r} } return floatToInt(l) % ri }, func(l, r *big.Int) interface{} { if r.Sign() == 0 { return &zeroModuloError{l, r} } return new(big.Int).Rem(l, r) }, func(l, r string) interface{} { return &binopTypeError{"modulo", l, r} }, func(l, r []interface{}) interface{} { return &binopTypeError{"modulo", l, r} }, func(l, r map[string]interface{}) interface{} { return &binopTypeError{"modulo", l, r} }, func(l, r interface{}) interface{} { return &binopTypeError{"modulo", l, r} }, ) } func funcOpAlt(_, l, r interface{}) interface{} { if isNull(l) { return r } else if lb, ok := toBoolean(l); ok && !lb { return r } return l } func funcOpEq(_, l, r interface{}) interface{} { return compare(l, r) == 0 } func funcOpNe(_, l, r interface{}) interface{} { return compare(l, r) != 0 } func funcOpGt(_, l, r interface{}) interface{} { return compare(l, r) > 0 } func funcOpLt(_, l, r interface{}) interface{} { return compare(l, r) < 0 } func funcOpGe(_, l, r interface{}) interface{} { return compare(l, r) >= 0 } func funcOpLe(_, l, r interface{}) interface{} { return compare(l, r) <= 0 } golang-github-wader-gojq-0.0~git20220108.6a05b6c/option.go000066400000000000000000000074361416642156300226050ustar00rootroot00000000000000package gojq import "fmt" // CompilerOption is a compiler option. type CompilerOption func(*compiler) // WithModuleLoader is a compiler option for module loader. // If you want to load modules from the filesystem, use NewModuleLoader. func WithModuleLoader(moduleLoader ModuleLoader) CompilerOption { return func(c *compiler) { c.moduleLoader = moduleLoader } } // WithEnvironLoader is a compiler option for environment variables loader. // The OS environment variables are not accessible by default due to security // reason. You can pass os.Environ if you allow to access it. func WithEnvironLoader(environLoader func() []string) CompilerOption { return func(c *compiler) { c.environLoader = environLoader } } // WithVariables is a compiler option for variable names. The variables can be // used in the query. You have to give the values to code.Run in the same order. func WithVariables(variables []string) CompilerOption { return func(c *compiler) { c.variables = variables } } // WithFunction is a compiler option for adding a custom internal function. // Specify the minimum and maximum count of the function arguments. These // values should satisfy 0 <= minarity <= maxarity <= 30, otherwise panics. // On handling numbers, you should take account to int, float64 and *big.Int. // These are the number types you are allowed to return, so do not return int64. // Refer to ValueError to return a value error just like built-in error function. // If you want to emit multiple values, call the empty function, accept a filter // for its argument, or call another built-in function, then use LoadInitModules // of the module loader. func WithFunction(name string, minarity, maxarity int, f func(interface{}, []interface{}) interface{}) CompilerOption { return withFunction(name, minarity, maxarity, false, f) } // WithIterFunction is a compiler option for adding a custom iterator function. // This is like the WithFunction option, but you can add a function which // returns an Iter to emit multiple values. You cannot define both iterator and // non-iterator functions of the same name (with possibly different arities). // See also NewIter, which can be used to convert values or an error to an Iter. func WithIterFunction(name string, minarity, maxarity int, f func(interface{}, []interface{}) Iter) CompilerOption { return withFunction(name, minarity, maxarity, true, func(v interface{}, args []interface{}) interface{} { return f(v, args) }, ) } func withFunction(name string, minarity, maxarity int, iter bool, f func(interface{}, []interface{}) interface{}) CompilerOption { if !(0 <= minarity && minarity <= maxarity && maxarity <= 30) { panic(fmt.Sprintf("invalid arity for %q: %d, %d", name, minarity, maxarity)) } argcount := 1<<(maxarity+1) - 1<= iter.max { return nil, false } v := iter.value iter.value++ return v, true } func ExampleWithIterFunction() { query, err := gojq.Parse("f(3; 7)") if err != nil { log.Fatalln(err) } code, err := gojq.Compile( query, gojq.WithIterFunction("f", 2, 2, func(_ interface{}, xs []interface{}) gojq.Iter { if x, ok := xs[0].(int); ok { if y, ok := xs[1].(int); ok { return &rangeIter{x, y} } } return gojq.NewIter(fmt.Errorf("f cannot be applied to: %v", xs)) }), ) if err != nil { log.Fatalln(err) } iter := code.Run(nil) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } // Output: // 3 // 4 // 5 // 6 } golang-github-wader-gojq-0.0~git20220108.6a05b6c/option_module_loader_test.go000066400000000000000000000016271416642156300265330ustar00rootroot00000000000000package gojq_test import ( "fmt" "log" "github.com/wader/gojq" ) type moduleLoader struct{} func (*moduleLoader) LoadModule(name string) (*gojq.Query, error) { switch name { case "module1": return gojq.Parse(` module { name: "module1", test: 42 }; import "module2" as foo; def f: foo::f; `) case "module2": return gojq.Parse(` def f: .foo; `) } return nil, fmt.Errorf("module not found: %q", name) } func ExampleWithModuleLoader() { query, err := gojq.Parse(` import "module1" as m; m::f `) if err != nil { log.Fatalln(err) } code, err := gojq.Compile( query, gojq.WithModuleLoader(&moduleLoader{}), ) if err != nil { log.Fatalln(err) } input := map[string]interface{}{"foo": 42} iter := code.Run(input) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } // Output: // 42 } golang-github-wader-gojq-0.0~git20220108.6a05b6c/option_test.go000066400000000000000000000430731416642156300236410ustar00rootroot00000000000000package gojq_test import ( "encoding/json" "errors" "fmt" "math/big" "reflect" "testing" "github.com/wader/gojq" ) func TestWithModuleLoaderError(t *testing.T) { query, err := gojq.Parse(` import "module1" as m; m::f `) if err != nil { t.Fatal(err) } _, err = gojq.Compile(query) if got, expected := err.Error(), `cannot load module: "module1"`; got != expected { t.Errorf("expected: %v, got: %v", expected, got) } query, err = gojq.Parse("modulemeta") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query) if err != nil { t.Fatal(err) } iter := code.Run("m") v, ok := iter.Next() if !ok { t.Fatal("should emit an error but got no output") } err, expected := v.(error), `cannot load module: "m"` if got := err.Error(); got != expected { t.Errorf("expected: %v, got: %v", expected, got) } v, ok = iter.Next() if ok { t.Errorf("should not emit a value but got: %v", v) } } func TestWithModuleLoader_modulemeta(t *testing.T) { query, err := gojq.Parse(` "module1" | modulemeta `) if err != nil { t.Fatal(err) } code, err := gojq.Compile( query, gojq.WithModuleLoader(&moduleLoader{}), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) for { got, ok := iter.Next() if !ok { break } if expected := map[string]interface{}{ "deps": []interface{}{ map[string]interface{}{ "relpath": "module2", "as": "foo", "is_data": false, }, }, "name": "module1", "test": 42, }; !reflect.DeepEqual(got, expected) { t.Errorf("expected: %v, got: %v", expected, got) } } } type moduleLoaderJSON struct{} func (*moduleLoaderJSON) LoadJSON(name string) (interface{}, error) { switch name { case "module1": return []interface{}{1.0, 42, json.Number("123")}, nil } return nil, fmt.Errorf("module not found: %q", name) } func TestWithModuleLoader_JSON(t *testing.T) { query, err := gojq.Parse(` import "module1" as $m; [$m, $m[1]*$m[2]*1000000000000] `) if err != nil { t.Fatal(err) } code, err := gojq.Compile( query, gojq.WithModuleLoader(&moduleLoaderJSON{}), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) for { got, ok := iter.Next() if !ok { break } if expected := []interface{}{ []interface{}{1.0, 42, 123}, big.NewInt(5166000000000000), }; !reflect.DeepEqual(got, expected) { t.Errorf("expected: %v, got: %v", expected, got) } } } type moduleLoaderInitModules struct{} func (*moduleLoaderInitModules) LoadInitModules() ([]*gojq.Query, error) { query, err := gojq.Parse(` def f: 42; def g: f * f; `) if err != nil { return nil, err } return []*gojq.Query{query}, nil } func TestWithModuleLoader_LoadInitModules(t *testing.T) { query, err := gojq.Parse("g") if err != nil { t.Fatal(err) } code, err := gojq.Compile( query, gojq.WithModuleLoader(&moduleLoaderInitModules{}), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) for { got, ok := iter.Next() if !ok { break } if expected := 42 * 42; !reflect.DeepEqual(got, expected) { t.Errorf("expected: %v, got: %v", expected, got) } } } func TestWithEnvironLoader(t *testing.T) { query, err := gojq.Parse("env") if err != nil { t.Fatal(err) } code, err := gojq.Compile( query, gojq.WithEnvironLoader(func() []string { return []string{"foo=42", "bar=128", "baz", "qux=", "=0"} }), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) for { got, ok := iter.Next() if !ok { break } expected := map[string]interface{}{"foo": "42", "bar": "128", "qux": ""} if !reflect.DeepEqual(got, expected) { t.Errorf("expected: %#v, got: %#v", expected, got) } } } func TestWithEnvironLoaderEmpty(t *testing.T) { query, err := gojq.Parse("env") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query) if err != nil { t.Fatal(err) } iter := code.Run(nil) for { got, ok := iter.Next() if !ok { break } if expected := map[string]interface{}{}; !reflect.DeepEqual(got, expected) { t.Errorf("expected: %v, got: %v", expected, got) } } } func TestWithVariablesError0(t *testing.T) { query, err := gojq.Parse(".") if err != nil { t.Fatal(err) } for _, name := range []string{"", "$", "_x", "x", "$0x", "$$", "$x ", " $x"} { _, err = gojq.Compile( query, gojq.WithVariables([]string{name}), ) if err == nil { t.Fatalf("%q is not a valid variable name", name) } if got, expected := err.Error(), "invalid variable name: "+name; got != expected { t.Errorf("expected: %v, got: %v", expected, got) } } } func TestWithVariablesError1(t *testing.T) { query, err := gojq.Parse(".") if err != nil { t.Fatal(err) } code, err := gojq.Compile( query, gojq.WithVariables([]string{"$x"}), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) v, ok := iter.Next() if !ok { t.Fatal("should emit an error but got no output") } err, expected := v.(error), "variable defined but not bound: $x" if got := err.Error(); got != expected { t.Errorf("expected: %v, got: %v", expected, got) } v, ok = iter.Next() if ok { t.Errorf("should not emit a value but got: %v", v) } } func TestWithVariablesError2(t *testing.T) { query, err := gojq.Parse(".") if err != nil { t.Fatal(err) } code, err := gojq.Compile( query, gojq.WithVariables([]string{"$x"}), ) if err != nil { t.Fatal(err) } iter := code.Run(nil, 1, 2) v, ok := iter.Next() if !ok { t.Fatal("should emit an error but got no output") } err, expected := v.(error), "too many variable values provided" if got := err.Error(); got != expected { t.Errorf("expected: %v, got: %v", expected, got) } v, ok = iter.Next() if ok { t.Errorf("should not emit a value but got: %v", v) } } func TestWithFunction(t *testing.T) { options := []gojq.CompilerOption{ gojq.WithFunction("f", 0, 0, func(x interface{}, _ []interface{}) interface{} { if x, ok := x.(int); ok { return x * 2 } return fmt.Errorf("f cannot be applied to: %v", x) }), gojq.WithFunction("g", 1, 1, func(x interface{}, xs []interface{}) interface{} { if x, ok := x.(int); ok { if y, ok := xs[0].(int); ok { return x + y } } return fmt.Errorf("g cannot be applied to: %v, %v", x, xs) }), } query, err := gojq.Parse(".[] | f | g(3)") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query, options...) if err != nil { t.Fatal(err) } iter := code.Run([]interface{}{0, 1, 2, 3, 4}) n := 0 for { v, ok := iter.Next() if !ok { break } if expected := n*2 + 3; v != expected { t.Errorf("expected: %v, got: %v", expected, v) } n++ } if expected := 5; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } query, err = gojq.Parse( `("f/0", "f/1", "g/0", "g/1") as $f | builtins | any(. == $f)`, ) if err != nil { t.Fatal(err) } code, err = gojq.Compile(query, options...) if err != nil { t.Fatal(err) } iter = code.Run(nil) n = 0 for { v, ok := iter.Next() if !ok { break } if expected := n == 0 || n == 3; v != expected { t.Errorf("expected: %v, got: %v (n = %d)", expected, v, n) } n++ } if expected := 4; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } } func TestWithFunctionDuplicateName(t *testing.T) { options := []gojq.CompilerOption{ gojq.WithFunction("f", 0, 0, func(x interface{}, _ []interface{}) interface{} { if x, ok := x.(int); ok { return x * 2 } return fmt.Errorf("f cannot be applied to: %v", x) }), gojq.WithFunction("f", 1, 1, func(x interface{}, xs []interface{}) interface{} { if x, ok := x.(int); ok { if y, ok := xs[0].(int); ok { return x + y } } return fmt.Errorf("f cannot be applied to: %v, %v", x, xs) }), gojq.WithFunction("f", 0, 0, func(x interface{}, _ []interface{}) interface{} { if x, ok := x.(int); ok { return x * 4 } return fmt.Errorf("f cannot be applied to: %v", x) }), gojq.WithFunction("f", 2, 2, func(x interface{}, xs []interface{}) interface{} { if x, ok := x.(int); ok { if y, ok := xs[0].(int); ok { if z, ok := xs[1].(int); ok { return x * y * z } } } return fmt.Errorf("f cannot be applied to: %v, %v", x, xs) }), } query, err := gojq.Parse(".[] | f | f(3) | f(4; 5)") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query, options...) if err != nil { t.Fatal(err) } iter := code.Run([]interface{}{0, 1, 2, 3, 4}) n := 0 for { v, ok := iter.Next() if !ok { break } if expected := (n*4 + 3) * 4 * 5; v != expected { t.Errorf("expected: %v, got: %v", expected, v) } n++ } if expected := 5; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } query, err = gojq.Parse( `("f/0", "f/1", "f/2", "f/3") as $f | builtins | any(. == $f)`, ) if err != nil { t.Fatal(err) } code, err = gojq.Compile(query, options...) if err != nil { t.Fatal(err) } iter = code.Run(nil) n = 0 for { v, ok := iter.Next() if !ok { break } if expected := n < 3; v != expected { t.Errorf("expected: %v, got: %v (n = %d)", expected, v, n) } n++ } if expected := 4; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } } func TestWithFunctionMultipleArities(t *testing.T) { options := []gojq.CompilerOption{ gojq.WithFunction("f", 0, 4, func(x interface{}, xs []interface{}) interface{} { if x, ok := x.(int); ok { x *= 2 for _, y := range xs { if y, ok := y.(int); ok { x += y } } return x } return fmt.Errorf("f cannot be applied to: %v, %v", x, xs) }), gojq.WithFunction("f", 2, 3, func(x interface{}, xs []interface{}) interface{} { if x, ok := x.(int); ok { for _, y := range xs { if y, ok := y.(int); ok { x *= y } } return x } return fmt.Errorf("f cannot be applied to: %v, %v", x, xs) }), gojq.WithFunction("g", 0, 30, func(x interface{}, xs []interface{}) interface{} { return nil }), } query, err := gojq.Parse(".[] | f | f(1) | f(2; 3) | f(4; 5; 6) | f(7; 8; 9; 10)") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query, options...) if err != nil { t.Fatal(err) } iter := code.Run([]interface{}{0, 1, 2, 3, 4}) n := 0 for { v, ok := iter.Next() if !ok { break } if expected := (((n*2*2+1)*2*3)*4*5*6)*2 + 7 + 8 + 9 + 10; v != expected { t.Errorf("expected: %v, got: %v", expected, v) } n++ } if expected := 5; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } query, err = gojq.Parse( `("f/0", "f/1", "f/2", "f/3", "f/4", "f/5") as $f | builtins | any(. == $f)`, ) if err != nil { t.Fatal(err) } code, err = gojq.Compile(query, options...) if err != nil { t.Fatal(err) } iter = code.Run(nil) n = 0 for { v, ok := iter.Next() if !ok { break } if expected := n < 5; v != expected { t.Errorf("expected: %v, got: %v (n = %d)", expected, v, n) } n++ } if expected := 6; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } } type valueError struct { v interface{} } func (err *valueError) Error() string { return "error: " + fmt.Sprint(err.v) } func (err *valueError) Value() interface{} { return err.v } func TestWithFunctionValueError(t *testing.T) { expected := map[string]interface{}{"foo": 42} query, err := gojq.Parse("try f catch .") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query, gojq.WithFunction("f", 0, 0, func(x interface{}, _ []interface{}) interface{} { return &valueError{expected} }), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) for { v, ok := iter.Next() if !ok { break } if !reflect.DeepEqual(v, expected) { t.Errorf("expected: %#v, got: %#v", expected, v) } } } func TestWithFunctionCompileArgsError(t *testing.T) { query, err := gojq.Parse("f(g)") if err != nil { t.Fatal(err) } _, err = gojq.Compile(query, gojq.WithFunction("f", 0, 1, func(interface{}, []interface{}) interface{} { return 0 }), ) if got, expected := err.Error(), "function not defined: g/0"; got != expected { t.Errorf("expected: %v, got: %v", expected, got) } } func TestWithFunctionArityError(t *testing.T) { query, err := gojq.Parse("f") if err != nil { t.Fatal(err) } for _, tc := range []struct{ min, max int }{{3, 2}, {-1, 3}, {0, 31}, {-1, 31}} { func() { defer func() { expected := fmt.Sprintf(`invalid arity for "f": %d, %d`, tc.min, tc.max) if got := recover(); got != expected { t.Errorf("expected: %v, got: %v", expected, got) } }() t.Fatal(gojq.Compile(query, gojq.WithFunction("f", tc.min, tc.max, func(interface{}, []interface{}) interface{} { return 0 }), )) }() } } func TestWithIterFunction(t *testing.T) { query, err := gojq.Parse("f * g(5; 10), h, 10") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query, gojq.WithIterFunction("f", 0, 0, func(interface{}, []interface{}) gojq.Iter { return gojq.NewIter(1, 2, 3) }), gojq.WithIterFunction("g", 2, 2, func(_ interface{}, xs []interface{}) gojq.Iter { if x, ok := xs[0].(int); ok { if y, ok := xs[1].(int); ok { return &rangeIter{x, y} } } return gojq.NewIter(fmt.Errorf("g cannot be applied to: %v", xs)) }), gojq.WithIterFunction("h", 0, 0, func(interface{}, []interface{}) gojq.Iter { return gojq.NewIter() }), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) n := 0 for { v, ok := iter.Next() if !ok { break } if expected := (1 + n%3) * (5 + n/3); v != expected { t.Errorf("expected: %v, got: %v", expected, v) } n++ } if expected := 16; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } } func TestWithIterFunctionError(t *testing.T) { query, err := gojq.Parse("range(3) * (f, 0), f") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query, gojq.WithIterFunction("f", 0, 0, func(interface{}, []interface{}) gojq.Iter { return gojq.NewIter(1, errors.New("error"), 3) }), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) n := 0 for { v, ok := iter.Next() if !ok { break } switch n { case 0, 1, 2: if expected := n; v != expected { t.Errorf("expected: %v, got: %v", expected, v) } case 3, 5: if expected := "error"; v.(error).Error() != expected { t.Errorf("expected: %v, got: %v", expected, err) } default: if expected := n - 3; v != expected { t.Errorf("expected: %v, got: %v", expected, v) } } n++ } if expected := 7; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } } func TestWithIterFunctionPath(t *testing.T) { query, err := gojq.Parse(".[f] = 1") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query, gojq.WithIterFunction("f", 0, 0, func(interface{}, []interface{}) gojq.Iter { return gojq.NewIter(0, 1, 2) }), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) for { v, ok := iter.Next() if !ok { break } if expected := []interface{}{1, 1, 1}; !reflect.DeepEqual(v, expected) { t.Errorf("expected: %v, got: %v", expected, v) } } } func TestWithIterFunctionPathError(t *testing.T) { query, err := gojq.Parse("{x: 0} | (f|.x) = 1") if err != nil { t.Fatal(err) } code, err := gojq.Compile(query, gojq.WithIterFunction("f", 0, 0, func(interface{}, []interface{}) gojq.Iter { return gojq.NewIter(map[string]interface{}{"x": 0}) }), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) v, ok := iter.Next() if !ok { t.Fatal("should emit an error but got no output") } err, expected := v.(error), "invalid path on iterating against: gojq.Iter" if got := err.Error(); got != expected { t.Errorf("expected: %v, got: %v", expected, got) } } func TestWithIterFunctionDefineError(t *testing.T) { query, err := gojq.Parse("f") if err != nil { t.Fatal(err) } defer func() { expected := `cannot define both iterator and non-iterator functions for "f"` if got := recover(); got != expected { t.Errorf("expected: %v, got: %v", expected, got) } }() t.Fatal(gojq.Compile(query, gojq.WithFunction("f", 0, 0, func(interface{}, []interface{}) interface{} { return 0 }), gojq.WithIterFunction("f", 0, 0, func(interface{}, []interface{}) gojq.Iter { return gojq.NewIter() }), )) } type moduleLoader2 struct{} func (*moduleLoader2) LoadModule(name string) (*gojq.Query, error) { switch name { case "module1": return gojq.Parse(` def g: def h: f * 3; h * 4; `) } return nil, fmt.Errorf("module not found: %q", name) } func TestWithFunctionWithModuleLoader(t *testing.T) { query, err := gojq.Parse(`include "module1"; .[] | g`) if err != nil { t.Fatal(err) } code, err := gojq.Compile(query, gojq.WithFunction("f", 0, 0, func(x interface{}, _ []interface{}) interface{} { if x, ok := x.(int); ok { return x * 2 } return fmt.Errorf("f cannot be applied to: %v", x) }), gojq.WithModuleLoader(&moduleLoader2{}), ) if err != nil { t.Fatal(err) } iter := code.Run([]interface{}{0, 1, 2, 3, 4}) n := 0 for { v, ok := iter.Next() if !ok { break } if expected := n * 2 * 3 * 4; v != expected { t.Errorf("expected: %v, got: %v", expected, v) } n++ } if expected := 5; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } } func TestWithInputIter(t *testing.T) { query, err := gojq.Parse("range(10) | input") if err != nil { t.Fatal(err) } code, err := gojq.Compile( query, gojq.WithInputIter(gojq.NewIter(0, 1, 2, 3, 4)), ) if err != nil { t.Fatal(err) } iter := code.Run(nil) n := 0 for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { if expected := "break"; err.Error() != expected { t.Errorf("expected: %v, got: %v", expected, err) } } else if v != n { t.Errorf("expected: %v, got: %v", n, v) } n++ } if expected := 10; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/option_variables_test.go000066400000000000000000000010221416642156300256550ustar00rootroot00000000000000package gojq_test import ( "fmt" "log" "github.com/wader/gojq" ) func ExampleWithVariables() { query, err := gojq.Parse("$x * 100 + $y, $z") if err != nil { log.Fatalln(err) } code, err := gojq.Compile( query, gojq.WithVariables([]string{ "$x", "$y", "$z", }), ) if err != nil { log.Fatalln(err) } iter := code.Run(nil, 12, 42, 128) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } // Output: // 1242 // 128 } golang-github-wader-gojq-0.0~git20220108.6a05b6c/parser.go000066400000000000000000001243211416642156300225620ustar00rootroot00000000000000// Code generated by goyacc -o parser.go parser.go.y. DO NOT EDIT. //line parser.go.y:2 package gojq import __yyfmt__ "fmt" //line parser.go.y:2 // Parse parses a query. func Parse(src string) (*Query, error) { l := newLexer(src) if yyParse(l) > 0 { return nil, l.err } return l.result, nil } func reverseFuncDef(xs []*FuncDef) []*FuncDef { for i, j := 0, len(xs)-1; i < j; i, j = i+1, j-1 { xs[i], xs[j] = xs[j], xs[i] } return xs } func prependFuncDef(xs []*FuncDef, x *FuncDef) []*FuncDef { xs = append(xs, nil) copy(xs[1:], xs) xs[0] = x return xs } //line parser.go.y:28 type yySymType struct { yys int value interface{} token string operator Operator } const tokAltOp = 57346 const tokUpdateOp = 57347 const tokDestAltOp = 57348 const tokOrOp = 57349 const tokAndOp = 57350 const tokCompareOp = 57351 const tokModule = 57352 const tokImport = 57353 const tokInclude = 57354 const tokDef = 57355 const tokAs = 57356 const tokLabel = 57357 const tokBreak = 57358 const tokNull = 57359 const tokTrue = 57360 const tokFalse = 57361 const tokIdent = 57362 const tokVariable = 57363 const tokModuleIdent = 57364 const tokModuleVariable = 57365 const tokIndex = 57366 const tokNumber = 57367 const tokFormat = 57368 const tokInvalid = 57369 const tokString = 57370 const tokStringStart = 57371 const tokStringQuery = 57372 const tokStringEnd = 57373 const tokIf = 57374 const tokThen = 57375 const tokElif = 57376 const tokElse = 57377 const tokEnd = 57378 const tokTry = 57379 const tokCatch = 57380 const tokReduce = 57381 const tokForeach = 57382 const tokRecurse = 57383 const tokFuncDefPost = 57384 const tokTermPost = 57385 const tokEmptyCatch = 57386 var yyToknames = [...]string{ "$end", "error", "$unk", "tokAltOp", "tokUpdateOp", "tokDestAltOp", "tokOrOp", "tokAndOp", "tokCompareOp", "tokModule", "tokImport", "tokInclude", "tokDef", "tokAs", "tokLabel", "tokBreak", "tokNull", "tokTrue", "tokFalse", "tokIdent", "tokVariable", "tokModuleIdent", "tokModuleVariable", "tokIndex", "tokNumber", "tokFormat", "tokInvalid", "tokString", "tokStringStart", "tokStringQuery", "tokStringEnd", "tokIf", "tokThen", "tokElif", "tokElse", "tokEnd", "tokTry", "tokCatch", "tokReduce", "tokForeach", "tokRecurse", "tokFuncDefPost", "tokTermPost", "tokEmptyCatch", "'|'", "','", "'+'", "'-'", "'*'", "'/'", "'%'", "'.'", "'?'", "'['", "';'", "':'", "'('", "')'", "']'", "'{'", "'}'", } var yyStatenames = [...]string{} const yyEofCode = 1 const yyErrCode = 2 const yyInitialStackSize = 16 //line parser.go.y:687 //line yacctab:1 var yyExca = [...]int{ -1, 1, 1, -1, -2, 0, -1, 130, 5, 0, -2, 32, -1, 133, 9, 0, -2, 35, -1, 194, 56, 114, -2, 54, } const yyPrivate = 57344 const yyLast = 1126 var yyAct = [...]int{ 86, 214, 174, 112, 12, 203, 9, 175, 111, 31, 190, 6, 156, 140, 117, 47, 95, 97, 93, 94, 89, 227, 49, 75, 76, 7, 77, 78, 79, 240, 235, 103, 239, 106, 164, 123, 226, 119, 107, 108, 105, 234, 102, 75, 76, 113, 77, 78, 79, 163, 122, 104, 211, 75, 76, 210, 77, 78, 79, 158, 159, 264, 259, 243, 72, 74, 80, 81, 82, 83, 84, 229, 73, 127, 275, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 80, 81, 82, 83, 84, 228, 73, 147, 72, 74, 80, 81, 82, 83, 84, 145, 73, 141, 278, 161, 246, 277, 157, 225, 166, 165, 144, 126, 125, 167, 88, 42, 43, 245, 124, 258, 224, 206, 179, 180, 181, 44, 183, 184, 73, 242, 177, 154, 267, 178, 142, 186, 49, 173, 256, 257, 143, 92, 91, 90, 92, 191, 100, 197, 150, 99, 200, 192, 201, 202, 188, 42, 43, 207, 88, 182, 120, 198, 199, 209, 219, 7, 216, 98, 215, 215, 218, 213, 113, 101, 185, 75, 76, 155, 77, 78, 79, 204, 205, 221, 222, 3, 91, 90, 92, 179, 180, 181, 230, 28, 8, 232, 27, 177, 223, 220, 178, 80, 81, 82, 83, 84, 85, 73, 176, 157, 241, 46, 110, 149, 237, 152, 72, 74, 80, 81, 82, 83, 84, 88, 73, 255, 182, 196, 79, 191, 195, 236, 7, 253, 254, 192, 248, 247, 160, 121, 249, 250, 96, 262, 260, 261, 215, 263, 11, 189, 187, 91, 90, 92, 11, 268, 269, 139, 270, 82, 83, 84, 208, 73, 272, 273, 80, 81, 82, 83, 84, 10, 73, 279, 5, 4, 271, 280, 51, 52, 2, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 115, 116, 170, 1, 171, 169, 0, 0, 42, 43, 0, 0, 63, 64, 65, 66, 67, 68, 69, 70, 71, 0, 0, 20, 0, 17, 37, 24, 25, 26, 38, 40, 39, 41, 23, 29, 30, 114, 42, 43, 0, 212, 15, 0, 0, 0, 0, 16, 0, 13, 14, 22, 0, 0, 0, 0, 0, 33, 34, 0, 0, 0, 21, 0, 36, 0, 148, 32, 0, 146, 35, 51, 52, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 115, 116, 0, 0, 0, 0, 0, 0, 42, 43, 0, 0, 63, 64, 65, 66, 67, 68, 69, 70, 71, 18, 19, 20, 0, 17, 37, 24, 25, 26, 38, 40, 39, 41, 23, 29, 30, 114, 42, 43, 0, 109, 15, 0, 0, 0, 0, 16, 0, 13, 14, 22, 0, 0, 0, 0, 0, 33, 34, 0, 0, 0, 21, 0, 36, 0, 0, 32, 0, 20, 35, 17, 37, 24, 25, 26, 38, 40, 39, 41, 23, 29, 30, 0, 42, 43, 0, 0, 15, 0, 0, 0, 0, 16, 0, 13, 14, 22, 0, 87, 0, 0, 0, 33, 34, 0, 0, 0, 21, 88, 36, 0, 0, 32, 0, 231, 35, 20, 0, 17, 37, 24, 25, 26, 38, 40, 39, 41, 23, 29, 30, 0, 42, 43, 0, 0, 15, 91, 90, 92, 0, 16, 0, 13, 14, 22, 0, 0, 0, 0, 0, 33, 34, 0, 0, 0, 21, 0, 36, 0, 0, 32, 0, 118, 35, 20, 0, 17, 37, 24, 25, 26, 38, 40, 39, 41, 23, 29, 30, 0, 42, 43, 0, 0, 15, 0, 77, 78, 79, 16, 0, 13, 14, 22, 0, 0, 0, 0, 0, 33, 34, 0, 0, 0, 21, 0, 36, 0, 0, 32, 51, 52, 35, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 48, 0, 0, 80, 81, 82, 83, 84, 50, 73, 0, 0, 63, 64, 65, 66, 67, 68, 69, 70, 71, 51, 52, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 48, 0, 0, 0, 0, 0, 0, 172, 50, 0, 0, 0, 63, 64, 65, 66, 67, 68, 69, 70, 71, 51, 52, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 115, 194, 78, 79, 0, 0, 0, 45, 42, 43, 0, 0, 63, 64, 65, 66, 67, 68, 69, 70, 71, 37, 24, 25, 26, 38, 40, 39, 41, 23, 29, 30, 0, 42, 43, 75, 76, 193, 77, 78, 79, 80, 81, 82, 83, 84, 22, 73, 0, 0, 0, 0, 33, 34, 0, 0, 0, 21, 0, 36, 0, 0, 32, 75, 76, 35, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 0, 75, 76, 252, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 0, 75, 76, 233, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 0, 75, 76, 168, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 75, 76, 281, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 75, 76, 276, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 75, 76, 251, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 75, 76, 244, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 75, 76, 217, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 75, 76, 162, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 266, 75, 76, 0, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 265, 75, 76, 0, 77, 78, 79, 0, 0, 0, 75, 76, 0, 77, 78, 79, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 238, 75, 76, 274, 77, 78, 79, 0, 0, 0, 0, 0, 151, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 72, 74, 80, 81, 82, 83, 84, 0, 73, 75, 76, 153, 77, 78, 79, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 74, 80, 81, 82, 83, 84, 0, 73, } var yyPact = [...]int{ 177, -1000, -1000, -35, -1000, 387, 72, 614, -1000, 1072, -1000, 529, 462, 673, 673, 529, 529, 148, 123, 120, 155, 89, -1000, -1000, -1000, -1000, -1000, -6, -1000, -1000, 129, -1000, 529, 673, 673, 357, 481, 141, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -11, -1000, 64, 58, 57, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 529, -1000, 529, 529, 529, 529, 529, 529, 529, 529, 529, 529, 529, -1000, 1072, 82, -1000, -1000, -1000, 89, 303, 201, 136, 1022, 529, 1040, 88, 165, -35, 3, -1000, -1000, 529, -1000, 909, 92, 92, -1000, -12, -1000, 55, 54, 529, -1000, -1000, -1000, -1000, 752, -1000, 267, -1000, 580, 174, 174, 174, 1072, 39, 39, 556, 662, 221, 156, 212, 212, 77, 77, 77, 131, -1000, -1000, 82, 648, -1000, -1000, -1000, 173, 529, 82, 82, 529, -1000, 529, 529, 163, 68, -1000, 529, 163, -3, 1072, -1000, -1000, 273, 673, 673, 884, -1000, -1000, -1000, 529, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 107, -1000, -1000, 529, 82, 63, -1000, -25, -1000, 35, 15, 529, -1000, -1000, 433, 727, -16, -27, 1072, -1000, 1072, -35, -1000, -1000, -1000, 988, -26, -1000, -1000, 529, -1000, -1000, 86, 92, 86, 7, 857, -1000, 60, -1000, 1072, -1000, -1000, 82, -1000, 648, 82, 82, 832, -1000, 699, -1000, 529, 529, 106, 66, -1000, 6, 163, 1072, 673, 673, -1000, -1000, 174, -1000, -1000, -1000, -1000, 5, -1000, 961, 936, 98, 529, 529, -1000, 529, -1000, 92, 86, -1000, 82, 529, 529, -1000, 1013, 1072, 19, -1000, 805, 49, 529, -1000, -1000, -1000, 529, 1072, 780, -1000, } var yyPgo = [...]int{ 0, 296, 282, 277, 276, 273, 12, 196, 244, 264, 0, 259, 13, 252, 251, 10, 4, 9, 241, 20, 240, 233, 227, 217, 214, 8, 1, 2, 7, 213, 15, 210, 201, 5, 198, 195, 14, 3, } var yyR1 = [...]int{ 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 33, 33, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 18, 18, 18, 34, 34, 35, 35, 19, 19, 19, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 25, 25, 37, 37, 37, 26, 26, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 29, 29, 30, 30, 30, 31, 31, 32, 32, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, } var yyR2 = [...]int{ 0, 2, 0, 3, 2, 2, 0, 2, 6, 4, 0, 1, 0, 2, 5, 8, 1, 3, 1, 1, 2, 3, 5, 9, 9, 11, 7, 3, 4, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 3, 1, 3, 3, 1, 3, 1, 3, 3, 3, 5, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 4, 1, 1, 1, 2, 1, 3, 2, 2, 2, 3, 4, 2, 3, 2, 2, 2, 2, 3, 3, 1, 3, 0, 2, 4, 1, 1, 1, 1, 2, 3, 4, 4, 5, 1, 3, 0, 5, 0, 2, 0, 2, 1, 3, 3, 3, 5, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 1, 3, 3, 3, 3, 2, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, } var yyChk = [...]int{ -1000, -1, -2, 10, -3, -4, -28, 60, -7, -10, -5, -8, -16, 39, 40, 32, 37, 15, 11, 12, 13, 52, 41, 24, 17, 18, 19, -34, -35, 25, 26, -17, 57, 47, 48, 60, 54, 16, 20, 22, 21, 23, 28, 29, 55, 61, -29, -30, 20, -36, 28, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 32, 33, 34, 35, 36, 37, 38, 39, 40, 45, 53, 46, 4, 5, 7, 8, 9, 47, 48, 49, 50, 51, -7, -10, 14, 24, -19, 53, 52, 54, -16, -16, -10, -8, -10, 21, 28, 28, 20, -19, -17, 57, -17, -10, -16, -16, 61, -24, -25, -37, -17, 57, 20, 21, -36, 59, -10, 21, -18, 61, 46, 56, 56, 56, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -11, -12, 21, 54, 60, -19, -17, 59, -10, 56, 14, 14, 33, -23, 38, 45, 14, -6, -28, 56, 57, -20, -10, 58, 61, 46, 56, 56, -10, 59, 31, 28, 30, 61, -30, -27, -28, -31, 25, 28, 17, 18, 19, 54, -27, -27, 45, 6, -13, -12, -14, -15, -37, -17, 57, 21, 59, 56, -10, -12, -12, -10, -10, -10, -33, 20, 21, 55, -10, -9, -33, 58, 55, 61, -25, -26, -16, -26, 58, -10, 59, -32, -27, -10, -12, 59, 46, 61, 46, 56, 56, -10, 59, -10, 59, 57, 57, -21, -6, 55, 58, 55, -10, 45, 56, 58, 59, 46, -12, -15, -12, -12, 58, 59, -10, -10, -22, 34, 35, 55, 56, -33, -16, -26, -27, 56, 55, 55, 36, -10, -10, -10, -12, -10, -10, 33, 55, 58, 58, 55, -10, -10, 58, } var yyDef = [...]int{ 2, -2, 6, 0, 1, 12, 0, 0, 4, 5, 7, 12, 41, 0, 0, 0, 0, 0, 0, 0, 0, 55, 56, 57, 60, 61, 62, 63, 65, 66, 67, 69, 0, 0, 0, 0, 0, 0, 89, 90, 91, 92, 84, 86, 3, 125, 0, 128, 0, 0, 0, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 20, 0, 79, 80, 81, 0, 0, 0, 0, 0, 0, 104, 0, 0, 10, 0, 58, 59, 0, 68, 0, 71, 72, 73, 0, 106, 111, 112, 0, 113, 114, 115, 76, 0, 78, 0, 126, 0, 0, 0, 0, 21, 30, 31, -2, 33, 34, -2, 36, 37, 38, 39, 40, 0, 42, 44, 0, 0, 82, 83, 93, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 11, 0, 0, 0, 98, 70, 74, 0, 0, 0, 0, 77, 85, 87, 0, 127, 129, 130, 118, 119, 120, 121, 122, 123, 124, 0, 131, 132, 0, 0, 0, 47, 0, 49, 0, 0, 0, -2, 94, 0, 0, 0, 0, 100, 105, 28, 10, 18, 19, 9, 0, 0, 16, 64, 0, 75, 107, 108, 116, 109, 0, 0, 133, 0, 135, 22, 43, 45, 0, 46, 0, 0, 0, 0, 95, 0, 96, 0, 0, 102, 0, 14, 0, 0, 99, 0, 0, 88, 134, 0, 48, 50, 51, 52, 0, 97, 0, 0, 0, 0, 0, 8, 0, 17, 117, 110, 136, 0, 0, 0, 26, 0, 103, 0, 53, 0, 0, 0, 15, 23, 24, 0, 101, 0, 25, } var yyTok1 = [...]int{ 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 51, 3, 3, 57, 58, 49, 47, 46, 48, 52, 50, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 56, 55, 3, 3, 3, 53, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 54, 3, 59, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 60, 45, 61, } var yyTok2 = [...]int{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, } var yyTok3 = [...]int{ 0, } var yyErrorMessages = [...]struct { state int token int msg string }{} //line yaccpar:1 /* parser for yacc output */ var ( yyDebug = 0 yyErrorVerbose = false ) type yyLexer interface { Lex(lval *yySymType) int Error(s string) } type yyParser interface { Parse(yyLexer) int Lookahead() int } type yyParserImpl struct { lval yySymType stack [yyInitialStackSize]yySymType char int } func (p *yyParserImpl) Lookahead() int { return p.char } func yyNewParser() yyParser { return &yyParserImpl{} } const yyFlag = -1000 func yyTokname(c int) string { if c >= 1 && c-1 < len(yyToknames) { if yyToknames[c-1] != "" { return yyToknames[c-1] } } return __yyfmt__.Sprintf("tok-%v", c) } func yyStatname(s int) string { if s >= 0 && s < len(yyStatenames) { if yyStatenames[s] != "" { return yyStatenames[s] } } return __yyfmt__.Sprintf("state-%v", s) } func yyErrorMessage(state, lookAhead int) string { const TOKSTART = 4 if !yyErrorVerbose { return "syntax error" } for _, e := range yyErrorMessages { if e.state == state && e.token == lookAhead { return "syntax error: " + e.msg } } res := "syntax error: unexpected " + yyTokname(lookAhead) // To match Bison, suggest at most four expected tokens. expected := make([]int, 0, 4) // Look for shiftable tokens. base := yyPact[state] for tok := TOKSTART; tok-1 < len(yyToknames); tok++ { if n := base + tok; n >= 0 && n < yyLast && yyChk[yyAct[n]] == tok { if len(expected) == cap(expected) { return res } expected = append(expected, tok) } } if yyDef[state] == -2 { i := 0 for yyExca[i] != -1 || yyExca[i+1] != state { i += 2 } // Look for tokens that we accept or reduce. for i += 2; yyExca[i] >= 0; i += 2 { tok := yyExca[i] if tok < TOKSTART || yyExca[i+1] == 0 { continue } if len(expected) == cap(expected) { return res } expected = append(expected, tok) } // If the default action is to accept or reduce, give up. if yyExca[i+1] != 0 { return res } } for i, tok := range expected { if i == 0 { res += ", expecting " } else { res += " or " } res += yyTokname(tok) } return res } func yylex1(lex yyLexer, lval *yySymType) (char, token int) { token = 0 char = lex.Lex(lval) if char <= 0 { token = yyTok1[0] goto out } if char < len(yyTok1) { token = yyTok1[char] goto out } if char >= yyPrivate { if char < yyPrivate+len(yyTok2) { token = yyTok2[char-yyPrivate] goto out } } for i := 0; i < len(yyTok3); i += 2 { token = yyTok3[i+0] if token == char { token = yyTok3[i+1] goto out } } out: if token == 0 { token = yyTok2[1] /* unknown char */ } if yyDebug >= 3 { __yyfmt__.Printf("lex %s(%d)\n", yyTokname(token), uint(char)) } return char, token } func yyParse(yylex yyLexer) int { return yyNewParser().Parse(yylex) } func (yyrcvr *yyParserImpl) Parse(yylex yyLexer) int { var yyn int var yyVAL yySymType var yyDollar []yySymType _ = yyDollar // silence set and not used yyS := yyrcvr.stack[:] Nerrs := 0 /* number of errors */ Errflag := 0 /* error recovery flag */ yystate := 0 yyrcvr.char = -1 yytoken := -1 // yyrcvr.char translated into internal numbering defer func() { // Make sure we report no lookahead when not parsing. yystate = -1 yyrcvr.char = -1 yytoken = -1 }() yyp := -1 goto yystack ret0: return 0 ret1: return 1 yystack: /* put a state and value onto the stack */ if yyDebug >= 4 { __yyfmt__.Printf("char %v in %v\n", yyTokname(yytoken), yyStatname(yystate)) } yyp++ if yyp >= len(yyS) { nyys := make([]yySymType, len(yyS)*2) copy(nyys, yyS) yyS = nyys } yyS[yyp] = yyVAL yyS[yyp].yys = yystate yynewstate: yyn = yyPact[yystate] if yyn <= yyFlag { goto yydefault /* simple state */ } if yyrcvr.char < 0 { yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval) } yyn += yytoken if yyn < 0 || yyn >= yyLast { goto yydefault } yyn = yyAct[yyn] if yyChk[yyn] == yytoken { /* valid shift */ yyrcvr.char = -1 yytoken = -1 yyVAL = yyrcvr.lval yystate = yyn if Errflag > 0 { Errflag-- } goto yystack } yydefault: /* default state action */ yyn = yyDef[yystate] if yyn == -2 { if yyrcvr.char < 0 { yyrcvr.char, yytoken = yylex1(yylex, &yyrcvr.lval) } /* look through exception table */ xi := 0 for { if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate { break } xi += 2 } for xi += 2; ; xi += 2 { yyn = yyExca[xi+0] if yyn < 0 || yyn == yytoken { break } } yyn = yyExca[xi+1] if yyn < 0 { goto ret0 } } if yyn == 0 { /* error ... attempt to resume parsing */ switch Errflag { case 0: /* brand new error */ yylex.Error(yyErrorMessage(yystate, yytoken)) Nerrs++ if yyDebug >= 1 { __yyfmt__.Printf("%s", yyStatname(yystate)) __yyfmt__.Printf(" saw %s\n", yyTokname(yytoken)) } fallthrough case 1, 2: /* incompletely recovered error ... try again */ Errflag = 3 /* find a state where "error" is a legal shift action */ for yyp >= 0 { yyn = yyPact[yyS[yyp].yys] + yyErrCode if yyn >= 0 && yyn < yyLast { yystate = yyAct[yyn] /* simulate a shift of "error" */ if yyChk[yystate] == yyErrCode { goto yystack } } /* the current p has no shift on "error", pop stack */ if yyDebug >= 2 { __yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) } yyp-- } /* there is no state on the stack with an error shift ... abort */ goto ret1 case 3: /* no shift yet; clobber input char */ if yyDebug >= 2 { __yyfmt__.Printf("error recovery discards %s\n", yyTokname(yytoken)) } if yytoken == yyEofCode { goto ret1 } yyrcvr.char = -1 yytoken = -1 goto yynewstate /* try again in the same state */ } } /* reduction by production yyn */ if yyDebug >= 2 { __yyfmt__.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate)) } yynt := yyn yypt := yyp _ = yypt // guard against "declared and not used" yyp -= yyR2[yyn] // yyp is now the index of $0. Perform the default action. Iff the // reduced production is ε, $1 is possibly out of range. if yyp+1 >= len(yyS) { nyys := make([]yySymType, len(yyS)*2) copy(nyys, yyS) yyS = nyys } yyVAL = yyS[yyp+1] /* consult goto table to find next state */ yyn = yyR1[yyn] yyg := yyPgo[yyn] yyj := yyg + yyS[yyp].yys + 1 if yyj >= yyLast { yystate = yyAct[yyg] } else { yystate = yyAct[yyj] if yyChk[yystate] != -yyn { yystate = yyAct[yyg] } } // dummy call; replaced with literal code switch yynt { case 1: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:67 { if yyDollar[1].value != nil { yyDollar[2].value.(*Query).Meta = yyDollar[1].value.(*ConstObject) } yylex.(*lexer).result = yyDollar[2].value.(*Query) } case 2: yyDollar = yyS[yypt-0 : yypt+1] //line parser.go.y:74 { yyVAL.value = nil } case 3: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:78 { yyVAL.value = yyDollar[2].value } case 4: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:84 { yyVAL.value = &Query{Imports: yyDollar[1].value.([]*Import), FuncDefs: reverseFuncDef(yyDollar[2].value.([]*FuncDef)), Term: &Term{Type: TermTypeIdentity}} } case 5: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:88 { if yyDollar[1].value != nil { yyDollar[2].value.(*Query).Imports = yyDollar[1].value.([]*Import) } yyVAL.value = yyDollar[2].value } case 6: yyDollar = yyS[yypt-0 : yypt+1] //line parser.go.y:95 { yyVAL.value = []*Import(nil) } case 7: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:99 { yyVAL.value = append(yyDollar[1].value.([]*Import), yyDollar[2].value.(*Import)) } case 8: yyDollar = yyS[yypt-6 : yypt+1] //line parser.go.y:105 { yyVAL.value = &Import{ImportPath: yyDollar[2].token, ImportAlias: yyDollar[4].token, Meta: yyDollar[5].value.(*ConstObject)} } case 9: yyDollar = yyS[yypt-4 : yypt+1] //line parser.go.y:109 { yyVAL.value = &Import{IncludePath: yyDollar[2].token, Meta: yyDollar[3].value.(*ConstObject)} } case 10: yyDollar = yyS[yypt-0 : yypt+1] //line parser.go.y:115 { yyVAL.value = (*ConstObject)(nil) } case 11: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:118 { } case 12: yyDollar = yyS[yypt-0 : yypt+1] //line parser.go.y:122 { yyVAL.value = []*FuncDef(nil) } case 13: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:126 { yyVAL.value = append(yyDollar[2].value.([]*FuncDef), yyDollar[1].value.(*FuncDef)) } case 14: yyDollar = yyS[yypt-5 : yypt+1] //line parser.go.y:132 { yyVAL.value = &FuncDef{Name: yyDollar[2].token, Body: yyDollar[4].value.(*Query)} } case 15: yyDollar = yyS[yypt-8 : yypt+1] //line parser.go.y:136 { yyVAL.value = &FuncDef{yyDollar[2].token, yyDollar[4].value.([]string), yyDollar[7].value.(*Query)} } case 16: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:142 { yyVAL.value = []string{yyDollar[1].token} } case 17: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:146 { yyVAL.value = append(yyDollar[1].value.([]string), yyDollar[3].token) } case 18: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:151 { } case 19: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:152 { } case 20: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:156 { yyDollar[2].value.(*Query).FuncDefs = prependFuncDef(yyDollar[2].value.(*Query).FuncDefs, yyDollar[1].value.(*FuncDef)) yyVAL.value = yyDollar[2].value } case 21: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:161 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpPipe, Right: yyDollar[3].value.(*Query)} } case 22: yyDollar = yyS[yypt-5 : yypt+1] //line parser.go.y:165 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, &Suffix{Bind: &Bind{yyDollar[3].value.([]*Pattern), yyDollar[5].value.(*Query)}}) yyVAL.value = &Query{Term: yyDollar[1].value.(*Term)} } case 23: yyDollar = yyS[yypt-9 : yypt+1] //line parser.go.y:170 { yyVAL.value = &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{yyDollar[2].value.(*Term), yyDollar[4].value.(*Pattern), yyDollar[6].value.(*Query), yyDollar[8].value.(*Query)}}} } case 24: yyDollar = yyS[yypt-9 : yypt+1] //line parser.go.y:174 { yyVAL.value = &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{yyDollar[2].value.(*Term), yyDollar[4].value.(*Pattern), yyDollar[6].value.(*Query), yyDollar[8].value.(*Query), nil}}} } case 25: yyDollar = yyS[yypt-11 : yypt+1] //line parser.go.y:178 { yyVAL.value = &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{yyDollar[2].value.(*Term), yyDollar[4].value.(*Pattern), yyDollar[6].value.(*Query), yyDollar[8].value.(*Query), yyDollar[10].value.(*Query)}}} } case 26: yyDollar = yyS[yypt-7 : yypt+1] //line parser.go.y:182 { yyVAL.value = &Query{Term: &Term{Type: TermTypeIf, If: &If{yyDollar[2].value.(*Query), yyDollar[4].value.(*Query), yyDollar[5].value.([]*IfElif), yyDollar[6].value.(*Query)}}} } case 27: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:186 { yyVAL.value = &Query{Term: &Term{Type: TermTypeTry, Try: &Try{yyDollar[2].value.(*Query), yyDollar[3].value.(*Query)}}} } case 28: yyDollar = yyS[yypt-4 : yypt+1] //line parser.go.y:190 { yyVAL.value = &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{yyDollar[2].token, yyDollar[4].value.(*Query)}}} } case 29: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:194 { if t := yyDollar[1].value.(*Query).Term; t != nil { t.SuffixList = append(t.SuffixList, &Suffix{Optional: true}) } else { yyVAL.value = &Query{Term: &Term{Type: TermTypeQuery, Query: yyDollar[1].value.(*Query), SuffixList: []*Suffix{{Optional: true}}}} } } case 30: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:202 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpComma, Right: yyDollar[3].value.(*Query)} } case 31: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:206 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: yyDollar[2].operator, Right: yyDollar[3].value.(*Query)} } case 32: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:210 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: yyDollar[2].operator, Right: yyDollar[3].value.(*Query)} } case 33: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:214 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpOr, Right: yyDollar[3].value.(*Query)} } case 34: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:218 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpAnd, Right: yyDollar[3].value.(*Query)} } case 35: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:222 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: yyDollar[2].operator, Right: yyDollar[3].value.(*Query)} } case 36: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:226 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpAdd, Right: yyDollar[3].value.(*Query)} } case 37: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:230 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpSub, Right: yyDollar[3].value.(*Query)} } case 38: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:234 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpMul, Right: yyDollar[3].value.(*Query)} } case 39: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:238 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpDiv, Right: yyDollar[3].value.(*Query)} } case 40: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:242 { yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpMod, Right: yyDollar[3].value.(*Query)} } case 41: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:246 { yyVAL.value = &Query{Term: yyDollar[1].value.(*Term)} } case 42: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:252 { yyVAL.value = []*Pattern{yyDollar[1].value.(*Pattern)} } case 43: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:256 { yyVAL.value = append(yyDollar[1].value.([]*Pattern), yyDollar[3].value.(*Pattern)) } case 44: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:262 { yyVAL.value = &Pattern{Name: yyDollar[1].token} } case 45: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:266 { yyVAL.value = &Pattern{Array: yyDollar[2].value.([]*Pattern)} } case 46: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:270 { yyVAL.value = &Pattern{Object: yyDollar[2].value.([]*PatternObject)} } case 47: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:276 { yyVAL.value = []*Pattern{yyDollar[1].value.(*Pattern)} } case 48: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:280 { yyVAL.value = append(yyDollar[1].value.([]*Pattern), yyDollar[3].value.(*Pattern)) } case 49: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:286 { yyVAL.value = []*PatternObject{yyDollar[1].value.(*PatternObject)} } case 50: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:290 { yyVAL.value = append(yyDollar[1].value.([]*PatternObject), yyDollar[3].value.(*PatternObject)) } case 51: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:296 { yyVAL.value = &PatternObject{Key: yyDollar[1].token, Val: yyDollar[3].value.(*Pattern)} } case 52: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:300 { yyVAL.value = &PatternObject{KeyString: yyDollar[1].value.(*String), Val: yyDollar[3].value.(*Pattern)} } case 53: yyDollar = yyS[yypt-5 : yypt+1] //line parser.go.y:304 { yyVAL.value = &PatternObject{KeyQuery: yyDollar[2].value.(*Query), Val: yyDollar[5].value.(*Pattern)} } case 54: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:308 { yyVAL.value = &PatternObject{KeyOnly: yyDollar[1].token} } case 55: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:314 { yyVAL.value = &Term{Type: TermTypeIdentity} } case 56: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:318 { yyVAL.value = &Term{Type: TermTypeRecurse} } case 57: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:322 { yyVAL.value = &Term{Type: TermTypeIndex, Index: &Index{Name: yyDollar[1].token}} } case 58: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:326 { if yyDollar[2].value.(*Suffix).Iter { yyVAL.value = &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{yyDollar[2].value.(*Suffix)}} } else { yyVAL.value = &Term{Type: TermTypeIndex, Index: yyDollar[2].value.(*Suffix).Index} } } case 59: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:334 { yyVAL.value = &Term{Type: TermTypeIndex, Index: &Index{Str: yyDollar[2].value.(*String)}} } case 60: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:338 { yyVAL.value = &Term{Type: TermTypeNull} } case 61: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:342 { yyVAL.value = &Term{Type: TermTypeTrue} } case 62: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:346 { yyVAL.value = &Term{Type: TermTypeFalse} } case 63: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:350 { yyVAL.value = &Term{Type: TermTypeFunc, Func: &Func{Name: yyDollar[1].token}} } case 64: yyDollar = yyS[yypt-4 : yypt+1] //line parser.go.y:354 { yyVAL.value = &Term{Type: TermTypeFunc, Func: &Func{Name: yyDollar[1].token, Args: yyDollar[3].value.([]*Query)}} } case 65: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:358 { yyVAL.value = &Term{Type: TermTypeFunc, Func: &Func{Name: yyDollar[1].token}} } case 66: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:362 { yyVAL.value = &Term{Type: TermTypeNumber, Number: yyDollar[1].token} } case 67: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:366 { yyVAL.value = &Term{Type: TermTypeFormat, Format: yyDollar[1].token} } case 68: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:370 { yyVAL.value = &Term{Type: TermTypeFormat, Format: yyDollar[1].token, Str: yyDollar[2].value.(*String)} } case 69: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:374 { yyVAL.value = &Term{Type: TermTypeString, Str: yyDollar[1].value.(*String)} } case 70: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:378 { yyVAL.value = &Term{Type: TermTypeQuery, Query: yyDollar[2].value.(*Query)} } case 71: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:382 { yyVAL.value = &Term{Type: TermTypeUnary, Unary: &Unary{OpAdd, yyDollar[2].value.(*Term)}} } case 72: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:386 { yyVAL.value = &Term{Type: TermTypeUnary, Unary: &Unary{OpSub, yyDollar[2].value.(*Term)}} } case 73: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:390 { yyVAL.value = &Term{Type: TermTypeObject, Object: &Object{}} } case 74: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:394 { yyVAL.value = &Term{Type: TermTypeObject, Object: &Object{yyDollar[2].value.([]*ObjectKeyVal)}} } case 75: yyDollar = yyS[yypt-4 : yypt+1] //line parser.go.y:398 { yyVAL.value = &Term{Type: TermTypeObject, Object: &Object{yyDollar[2].value.([]*ObjectKeyVal)}} } case 76: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:402 { yyVAL.value = &Term{Type: TermTypeArray, Array: &Array{}} } case 77: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:406 { yyVAL.value = &Term{Type: TermTypeArray, Array: &Array{yyDollar[2].value.(*Query)}} } case 78: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:410 { yyVAL.value = &Term{Type: TermTypeBreak, Break: yyDollar[2].token} } case 79: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:414 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, &Suffix{Index: &Index{Name: yyDollar[2].token}}) } case 80: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:418 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, yyDollar[2].value.(*Suffix)) } case 81: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:422 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, &Suffix{Optional: true}) } case 82: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:426 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, yyDollar[3].value.(*Suffix)) } case 83: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:430 { yyDollar[1].value.(*Term).SuffixList = append(yyDollar[1].value.(*Term).SuffixList, &Suffix{Index: &Index{Str: yyDollar[3].value.(*String)}}) } case 84: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:436 { yyVAL.value = &String{Str: yyDollar[1].token} } case 85: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:440 { yyVAL.value = &String{Queries: yyDollar[2].value.([]*Query)} } case 86: yyDollar = yyS[yypt-0 : yypt+1] //line parser.go.y:446 { yyVAL.value = []*Query{} } case 87: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:450 { yyVAL.value = append(yyDollar[1].value.([]*Query), &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: yyDollar[2].token}}}) } case 88: yyDollar = yyS[yypt-4 : yypt+1] //line parser.go.y:454 { yylex.(*lexer).inString = true yyVAL.value = append(yyDollar[1].value.([]*Query), &Query{Term: &Term{Type: TermTypeQuery, Query: yyDollar[3].value.(*Query)}}) } case 89: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:460 { } case 90: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:461 { } case 91: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:464 { } case 92: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:465 { } case 93: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:469 { yyVAL.value = &Suffix{Iter: true} } case 94: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:473 { yyVAL.value = &Suffix{Index: &Index{Start: yyDollar[2].value.(*Query)}} } case 95: yyDollar = yyS[yypt-4 : yypt+1] //line parser.go.y:477 { yyVAL.value = &Suffix{Index: &Index{Start: yyDollar[2].value.(*Query), IsSlice: true}} } case 96: yyDollar = yyS[yypt-4 : yypt+1] //line parser.go.y:481 { yyVAL.value = &Suffix{Index: &Index{End: yyDollar[3].value.(*Query), IsSlice: true}} } case 97: yyDollar = yyS[yypt-5 : yypt+1] //line parser.go.y:485 { yyVAL.value = &Suffix{Index: &Index{Start: yyDollar[2].value.(*Query), End: yyDollar[4].value.(*Query), IsSlice: true}} } case 98: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:491 { yyVAL.value = []*Query{yyDollar[1].value.(*Query)} } case 99: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:495 { yyVAL.value = append(yyDollar[1].value.([]*Query), yyDollar[3].value.(*Query)) } case 100: yyDollar = yyS[yypt-0 : yypt+1] //line parser.go.y:501 { yyVAL.value = []*IfElif(nil) } case 101: yyDollar = yyS[yypt-5 : yypt+1] //line parser.go.y:505 { yyVAL.value = append(yyDollar[1].value.([]*IfElif), &IfElif{yyDollar[3].value.(*Query), yyDollar[5].value.(*Query)}) } case 102: yyDollar = yyS[yypt-0 : yypt+1] //line parser.go.y:511 { yyVAL.value = (*Query)(nil) } case 103: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:515 { yyVAL.value = yyDollar[2].value } case 104: yyDollar = yyS[yypt-0 : yypt+1] //line parser.go.y:521 { yyVAL.value = (*Query)(nil) } case 105: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:525 { yyVAL.value = yyDollar[2].value } case 106: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:531 { yyVAL.value = []*ObjectKeyVal{yyDollar[1].value.(*ObjectKeyVal)} } case 107: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:535 { yyVAL.value = append(yyDollar[1].value.([]*ObjectKeyVal), yyDollar[3].value.(*ObjectKeyVal)) } case 108: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:541 { yyVAL.value = &ObjectKeyVal{Key: yyDollar[1].token, Val: yyDollar[3].value.(*ObjectVal)} } case 109: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:545 { yyVAL.value = &ObjectKeyVal{KeyString: yyDollar[1].value.(*String), Val: yyDollar[3].value.(*ObjectVal)} } case 110: yyDollar = yyS[yypt-5 : yypt+1] //line parser.go.y:549 { yyVAL.value = &ObjectKeyVal{KeyQuery: yyDollar[2].value.(*Query), Val: yyDollar[5].value.(*ObjectVal)} } case 111: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:553 { yyVAL.value = &ObjectKeyVal{KeyOnly: yyDollar[1].token} } case 112: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:557 { yyVAL.value = &ObjectKeyVal{KeyOnlyString: yyDollar[1].value.(*String)} } case 113: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:562 { } case 114: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:563 { } case 115: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:564 { } case 116: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:568 { yyVAL.value = &ObjectVal{[]*Query{{Term: yyDollar[1].value.(*Term)}}} } case 117: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:572 { yyVAL.value = &ObjectVal{append(yyDollar[1].value.(*ObjectVal).Queries, &Query{Term: yyDollar[3].value.(*Term)})} } case 118: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:578 { yyVAL.value = &ConstTerm{Object: yyDollar[1].value.(*ConstObject)} } case 119: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:582 { yyVAL.value = &ConstTerm{Array: yyDollar[1].value.(*ConstArray)} } case 120: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:586 { yyVAL.value = &ConstTerm{Number: yyDollar[1].token} } case 121: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:590 { yyVAL.value = &ConstTerm{Str: yyDollar[1].token} } case 122: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:594 { yyVAL.value = &ConstTerm{Null: true} } case 123: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:598 { yyVAL.value = &ConstTerm{True: true} } case 124: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:602 { yyVAL.value = &ConstTerm{False: true} } case 125: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:608 { yyVAL.value = &ConstObject{} } case 126: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:612 { yyVAL.value = &ConstObject{yyDollar[2].value.([]*ConstObjectKeyVal)} } case 127: yyDollar = yyS[yypt-4 : yypt+1] //line parser.go.y:616 { yyVAL.value = &ConstObject{yyDollar[2].value.([]*ConstObjectKeyVal)} } case 128: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:622 { yyVAL.value = []*ConstObjectKeyVal{yyDollar[1].value.(*ConstObjectKeyVal)} } case 129: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:626 { yyVAL.value = append(yyDollar[1].value.([]*ConstObjectKeyVal), yyDollar[3].value.(*ConstObjectKeyVal)) } case 130: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:632 { yyVAL.value = &ConstObjectKeyVal{Key: yyDollar[1].token, Val: yyDollar[3].value.(*ConstTerm)} } case 131: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:636 { yyVAL.value = &ConstObjectKeyVal{Key: yyDollar[1].token, Val: yyDollar[3].value.(*ConstTerm)} } case 132: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:640 { yyVAL.value = &ConstObjectKeyVal{KeyString: yyDollar[1].token, Val: yyDollar[3].value.(*ConstTerm)} } case 133: yyDollar = yyS[yypt-2 : yypt+1] //line parser.go.y:646 { yyVAL.value = &ConstArray{} } case 134: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:650 { yyVAL.value = &ConstArray{yyDollar[2].value.([]*ConstTerm)} } case 135: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:656 { yyVAL.value = []*ConstTerm{yyDollar[1].value.(*ConstTerm)} } case 136: yyDollar = yyS[yypt-3 : yypt+1] //line parser.go.y:660 { yyVAL.value = append(yyDollar[1].value.([]*ConstTerm), yyDollar[3].value.(*ConstTerm)) } case 137: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:665 { } case 138: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:666 { } case 139: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:667 { } case 140: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:668 { } case 141: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:669 { } case 142: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:670 { } case 143: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:671 { } case 144: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:672 { } case 145: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:673 { } case 146: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:674 { } case 147: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:675 { } case 148: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:676 { } case 149: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:677 { } case 150: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:678 { } case 151: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:679 { } case 152: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:680 { } case 153: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:681 { } case 154: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:682 { } case 155: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:683 { } case 156: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:684 { } case 157: yyDollar = yyS[yypt-1 : yypt+1] //line parser.go.y:685 { } } goto yystack /* stack new state and value */ } golang-github-wader-gojq-0.0~git20220108.6a05b6c/parser.go.y000066400000000000000000000362421416642156300230350ustar00rootroot00000000000000%{ package gojq // Parse parses a query. func Parse(src string) (*Query, error) { l := newLexer(src) if yyParse(l) > 0 { return nil, l.err } return l.result, nil } func reverseFuncDef(xs []*FuncDef) []*FuncDef { for i, j := 0, len(xs)-1; i < j; i, j = i+1, j-1 { xs[i], xs[j] = xs[j], xs[i] } return xs } func prependFuncDef(xs []*FuncDef, x *FuncDef) []*FuncDef { xs = append(xs, nil) copy(xs[1:], xs) xs[0] = x return xs } %} %union { value interface{} token string operator Operator } %type program moduleheader programbody imports import metaopt funcdefs funcdef funcdefargs query %type bindpatterns pattern arraypatterns objectpatterns objectpattern %type term string stringparts suffix args ifelifs ifelse trycatch %type objectkeyvals objectkeyval objectval %type constterm constobject constobjectkeyvals constobjectkeyval constarray constarrayelems %type tokIdentVariable tokIdentModuleIdent tokVariableModuleVariable tokKeyword objectkey %token tokAltOp tokUpdateOp tokDestAltOp tokOrOp tokAndOp tokCompareOp %token tokModule tokImport tokInclude tokDef tokAs tokLabel tokBreak %token tokNull tokTrue tokFalse %token tokIdent tokVariable tokModuleIdent tokModuleVariable %token tokIndex tokNumber tokFormat tokInvalid %token tokString tokStringStart tokStringQuery tokStringEnd %token tokIf tokThen tokElif tokElse tokEnd %token tokTry tokCatch tokReduce tokForeach %token tokRecurse tokFuncDefPost tokTermPost tokEmptyCatch %nonassoc tokFuncDefPost tokTermPost tokEmptyCatch %right '|' %left ',' %right tokAltOp %nonassoc tokUpdateOp %left tokOrOp %left tokAndOp %nonassoc tokCompareOp %left '+' '-' %left '*' '/' '%' %nonassoc tokAs tokIndex '.' '?' %nonassoc '[' tokTry tokCatch %% program : moduleheader programbody { if $1 != nil { $2.(*Query).Meta = $1.(*ConstObject) } yylex.(*lexer).result = $2.(*Query) } moduleheader : { $$ = nil } | tokModule constobject ';' { $$ = $2; } programbody : imports funcdefs { $$ = &Query{Imports: $1.([]*Import), FuncDefs: reverseFuncDef($2.([]*FuncDef)), Term: &Term{Type: TermTypeIdentity}} } | imports query { if $1 != nil { $2.(*Query).Imports = $1.([]*Import) } $$ = $2 } imports : { $$ = []*Import(nil) } | imports import { $$ = append($1.([]*Import), $2.(*Import)) } import : tokImport tokString tokAs tokIdentVariable metaopt ';' { $$ = &Import{ImportPath: $2, ImportAlias: $4, Meta: $5.(*ConstObject)} } | tokInclude tokString metaopt ';' { $$ = &Import{IncludePath: $2, Meta: $3.(*ConstObject)} } metaopt : { $$ = (*ConstObject)(nil) } | constobject {} funcdefs : { $$ = []*FuncDef(nil) } | funcdef funcdefs { $$ = append($2.([]*FuncDef), $1.(*FuncDef)) } funcdef : tokDef tokIdent ':' query ';' { $$ = &FuncDef{Name: $2, Body: $4.(*Query)} } | tokDef tokIdent '(' funcdefargs ')' ':' query ';' { $$ = &FuncDef{$2, $4.([]string), $7.(*Query)} } funcdefargs : tokIdentVariable { $$ = []string{$1} } | funcdefargs ';' tokIdentVariable { $$ = append($1.([]string), $3) } tokIdentVariable : tokIdent {} | tokVariable {} query : funcdef query %prec tokFuncDefPost { $2.(*Query).FuncDefs = prependFuncDef($2.(*Query).FuncDefs, $1.(*FuncDef)) $$ = $2 } | query '|' query { $$ = &Query{Left: $1.(*Query), Op: OpPipe, Right: $3.(*Query)} } | term tokAs bindpatterns '|' query { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, &Suffix{Bind: &Bind{$3.([]*Pattern), $5.(*Query)}}) $$ = &Query{Term: $1.(*Term)} } | tokReduce term tokAs pattern '(' query ';' query ')' { $$ = &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{$2.(*Term), $4.(*Pattern), $6.(*Query), $8.(*Query)}}} } | tokForeach term tokAs pattern '(' query ';' query ')' { $$ = &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{$2.(*Term), $4.(*Pattern), $6.(*Query), $8.(*Query), nil}}} } | tokForeach term tokAs pattern '(' query ';' query ';' query ')' { $$ = &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{$2.(*Term), $4.(*Pattern), $6.(*Query), $8.(*Query), $10.(*Query)}}} } | tokIf query tokThen query ifelifs ifelse tokEnd { $$ = &Query{Term: &Term{Type: TermTypeIf, If: &If{$2.(*Query), $4.(*Query), $5.([]*IfElif), $6.(*Query)}}} } | tokTry query trycatch { $$ = &Query{Term: &Term{Type: TermTypeTry, Try: &Try{$2.(*Query), $3.(*Query)}}} } | tokLabel tokVariable '|' query { $$ = &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{$2, $4.(*Query)}}} } | query '?' { if t := $1.(*Query).Term; t != nil { t.SuffixList = append(t.SuffixList, &Suffix{Optional: true}) } else { $$ = &Query{Term: &Term{Type: TermTypeQuery, Query: $1.(*Query), SuffixList: []*Suffix{{Optional: true}}}} } } | query ',' query { $$ = &Query{Left: $1.(*Query), Op: OpComma, Right: $3.(*Query)} } | query tokAltOp query { $$ = &Query{Left: $1.(*Query), Op: $2, Right: $3.(*Query)} } | query tokUpdateOp query { $$ = &Query{Left: $1.(*Query), Op: $2, Right: $3.(*Query)} } | query tokOrOp query { $$ = &Query{Left: $1.(*Query), Op: OpOr, Right: $3.(*Query)} } | query tokAndOp query { $$ = &Query{Left: $1.(*Query), Op: OpAnd, Right: $3.(*Query)} } | query tokCompareOp query { $$ = &Query{Left: $1.(*Query), Op: $2, Right: $3.(*Query)} } | query '+' query { $$ = &Query{Left: $1.(*Query), Op: OpAdd, Right: $3.(*Query)} } | query '-' query { $$ = &Query{Left: $1.(*Query), Op: OpSub, Right: $3.(*Query)} } | query '*' query { $$ = &Query{Left: $1.(*Query), Op: OpMul, Right: $3.(*Query)} } | query '/' query { $$ = &Query{Left: $1.(*Query), Op: OpDiv, Right: $3.(*Query)} } | query '%' query { $$ = &Query{Left: $1.(*Query), Op: OpMod, Right: $3.(*Query)} } | term %prec tokTermPost { $$ = &Query{Term: $1.(*Term)} } bindpatterns : pattern { $$ = []*Pattern{$1.(*Pattern)} } | bindpatterns tokDestAltOp pattern { $$ = append($1.([]*Pattern), $3.(*Pattern)) } pattern : tokVariable { $$ = &Pattern{Name: $1} } | '[' arraypatterns ']' { $$ = &Pattern{Array: $2.([]*Pattern)} } | '{' objectpatterns '}' { $$ = &Pattern{Object: $2.([]*PatternObject)} } arraypatterns : pattern { $$ = []*Pattern{$1.(*Pattern)} } | arraypatterns ',' pattern { $$ = append($1.([]*Pattern), $3.(*Pattern)) } objectpatterns : objectpattern { $$ = []*PatternObject{$1.(*PatternObject)} } | objectpatterns ',' objectpattern { $$ = append($1.([]*PatternObject), $3.(*PatternObject)) } objectpattern : objectkey ':' pattern { $$ = &PatternObject{Key: $1, Val: $3.(*Pattern)} } | string ':' pattern { $$ = &PatternObject{KeyString: $1.(*String), Val: $3.(*Pattern)} } | '(' query ')' ':' pattern { $$ = &PatternObject{KeyQuery: $2.(*Query), Val: $5.(*Pattern)} } | tokVariable { $$ = &PatternObject{KeyOnly: $1} } term : '.' { $$ = &Term{Type: TermTypeIdentity} } | tokRecurse { $$ = &Term{Type: TermTypeRecurse} } | tokIndex { $$ = &Term{Type: TermTypeIndex, Index: &Index{Name: $1}} } | '.' suffix { if $2.(*Suffix).Iter { $$ = &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{$2.(*Suffix)}} } else { $$ = &Term{Type: TermTypeIndex, Index: $2.(*Suffix).Index} } } | '.' string { $$ = &Term{Type: TermTypeIndex, Index: &Index{Str: $2.(*String)}} } | tokNull { $$ = &Term{Type: TermTypeNull} } | tokTrue { $$ = &Term{Type: TermTypeTrue} } | tokFalse { $$ = &Term{Type: TermTypeFalse} } | tokIdentModuleIdent { $$ = &Term{Type: TermTypeFunc, Func: &Func{Name: $1}} } | tokIdentModuleIdent '(' args ')' { $$ = &Term{Type: TermTypeFunc, Func: &Func{Name: $1, Args: $3.([]*Query)}} } | tokVariableModuleVariable { $$ = &Term{Type: TermTypeFunc, Func: &Func{Name: $1}} } | tokNumber { $$ = &Term{Type: TermTypeNumber, Number: $1} } | tokFormat { $$ = &Term{Type: TermTypeFormat, Format: $1} } | tokFormat string { $$ = &Term{Type: TermTypeFormat, Format: $1, Str: $2.(*String)} } | string { $$ = &Term{Type: TermTypeString, Str: $1.(*String)} } | '(' query ')' { $$ = &Term{Type: TermTypeQuery, Query: $2.(*Query)} } | '+' term { $$ = &Term{Type: TermTypeUnary, Unary: &Unary{OpAdd, $2.(*Term)}} } | '-' term { $$ = &Term{Type: TermTypeUnary, Unary: &Unary{OpSub, $2.(*Term)}} } | '{' '}' { $$ = &Term{Type: TermTypeObject, Object: &Object{}} } | '{' objectkeyvals '}' { $$ = &Term{Type: TermTypeObject, Object: &Object{$2.([]*ObjectKeyVal)}} } | '{' objectkeyvals ',' '}' { $$ = &Term{Type: TermTypeObject, Object: &Object{$2.([]*ObjectKeyVal)}} } | '[' ']' { $$ = &Term{Type: TermTypeArray, Array: &Array{}} } | '[' query ']' { $$ = &Term{Type: TermTypeArray, Array: &Array{$2.(*Query)}} } | tokBreak tokVariable { $$ = &Term{Type: TermTypeBreak, Break: $2} } | term tokIndex { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, &Suffix{Index: &Index{Name: $2}}) } | term suffix { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, $2.(*Suffix)) } | term '?' { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, &Suffix{Optional: true}) } | term '.' suffix { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, $3.(*Suffix)) } | term '.' string { $1.(*Term).SuffixList = append($1.(*Term).SuffixList, &Suffix{Index: &Index{Str: $3.(*String)}}) } string : tokString { $$ = &String{Str: $1} } | tokStringStart stringparts tokStringEnd { $$ = &String{Queries: $2.([]*Query)} } stringparts : { $$ = []*Query{} } | stringparts tokString { $$ = append($1.([]*Query), &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: $2}}}) } | stringparts tokStringQuery query ')' { yylex.(*lexer).inString = true $$ = append($1.([]*Query), &Query{Term: &Term{Type: TermTypeQuery, Query: $3.(*Query)}}) } tokIdentModuleIdent : tokIdent {} | tokModuleIdent {} tokVariableModuleVariable : tokVariable {} | tokModuleVariable {} suffix : '[' ']' { $$ = &Suffix{Iter: true} } | '[' query ']' { $$ = &Suffix{Index: &Index{Start: $2.(*Query)}} } | '[' query ':' ']' { $$ = &Suffix{Index: &Index{Start: $2.(*Query), IsSlice: true}} } | '[' ':' query ']' { $$ = &Suffix{Index: &Index{End: $3.(*Query), IsSlice: true}} } | '[' query ':' query ']' { $$ = &Suffix{Index: &Index{Start: $2.(*Query), End: $4.(*Query), IsSlice: true}} } args : query { $$ = []*Query{$1.(*Query)} } | args ';' query { $$ = append($1.([]*Query), $3.(*Query)) } ifelifs : { $$ = []*IfElif(nil) } | ifelifs tokElif query tokThen query { $$ = append($1.([]*IfElif), &IfElif{$3.(*Query), $5.(*Query)}) } ifelse : { $$ = (*Query)(nil) } | tokElse query { $$ = $2 } trycatch : %prec tokEmptyCatch { $$ = (*Query)(nil) } | tokCatch query { $$ = $2 } objectkeyvals : objectkeyval { $$ = []*ObjectKeyVal{$1.(*ObjectKeyVal)} } | objectkeyvals ',' objectkeyval { $$ = append($1.([]*ObjectKeyVal), $3.(*ObjectKeyVal)) } objectkeyval : objectkey ':' objectval { $$ = &ObjectKeyVal{Key: $1, Val: $3.(*ObjectVal)} } | string ':' objectval { $$ = &ObjectKeyVal{KeyString: $1.(*String), Val: $3.(*ObjectVal)} } | '(' query ')' ':' objectval { $$ = &ObjectKeyVal{KeyQuery: $2.(*Query), Val: $5.(*ObjectVal)} } | objectkey { $$ = &ObjectKeyVal{KeyOnly: $1} } | string { $$ = &ObjectKeyVal{KeyOnlyString: $1.(*String)} } objectkey : tokIdent {} | tokVariable {} | tokKeyword {} objectval : term { $$ = &ObjectVal{[]*Query{{Term: $1.(*Term)}}} } | objectval '|' term { $$ = &ObjectVal{append($1.(*ObjectVal).Queries, &Query{Term: $3.(*Term)})} } constterm : constobject { $$ = &ConstTerm{Object: $1.(*ConstObject)} } | constarray { $$ = &ConstTerm{Array: $1.(*ConstArray)} } | tokNumber { $$ = &ConstTerm{Number: $1} } | tokString { $$ = &ConstTerm{Str: $1} } | tokNull { $$ = &ConstTerm{Null: true} } | tokTrue { $$ = &ConstTerm{True: true} } | tokFalse { $$ = &ConstTerm{False: true} } constobject : '{' '}' { $$ = &ConstObject{} } | '{' constobjectkeyvals '}' { $$ = &ConstObject{$2.([]*ConstObjectKeyVal)} } | '{' constobjectkeyvals ',' '}' { $$ = &ConstObject{$2.([]*ConstObjectKeyVal)} } constobjectkeyvals : constobjectkeyval { $$ = []*ConstObjectKeyVal{$1.(*ConstObjectKeyVal)} } | constobjectkeyvals ',' constobjectkeyval { $$ = append($1.([]*ConstObjectKeyVal), $3.(*ConstObjectKeyVal)) } constobjectkeyval : tokIdent ':' constterm { $$ = &ConstObjectKeyVal{Key: $1, Val: $3.(*ConstTerm)} } | tokKeyword ':' constterm { $$ = &ConstObjectKeyVal{Key: $1, Val: $3.(*ConstTerm)} } | tokString ':' constterm { $$ = &ConstObjectKeyVal{KeyString: $1, Val: $3.(*ConstTerm)} } constarray : '[' ']' { $$ = &ConstArray{} } | '[' constarrayelems ']' { $$ = &ConstArray{$2.([]*ConstTerm)} } constarrayelems : constterm { $$ = []*ConstTerm{$1.(*ConstTerm)} } | constarrayelems ',' constterm { $$ = append($1.([]*ConstTerm), $3.(*ConstTerm)) } tokKeyword : tokOrOp {} | tokAndOp {} | tokModule {} | tokImport {} | tokInclude {} | tokDef {} | tokAs {} | tokLabel {} | tokBreak {} | tokNull {} | tokTrue {} | tokFalse {} | tokIf {} | tokThen {} | tokElif {} | tokElse {} | tokEnd {} | tokTry {} | tokCatch {} | tokReduce {} | tokForeach {} %% golang-github-wader-gojq-0.0~git20220108.6a05b6c/query.go000066400000000000000000000522271416642156300224400ustar00rootroot00000000000000package gojq import ( "context" "encoding/json" "strconv" "strings" ) // Query represents the abstract syntax tree of a jq query. type Query struct { Meta *ConstObject `json:"meta,omitempty"` Imports []*Import `json:"imports,omitempty"` FuncDefs []*FuncDef `json:"func_defs,omitempty"` Term *Term `json:"term,omitempty"` Left *Query `json:"left,omitempty"` Op Operator `json:"op,omitempty"` Right *Query `json:"right,omitempty"` Func string `json:"func,omitempty"` } // Run the query. // // It is safe to call this method of a *Query in multiple goroutines. func (e *Query) Run(v interface{}) Iter { return e.RunWithContext(context.Background(), v) } // RunWithContext runs the query with context. func (e *Query) RunWithContext(ctx context.Context, v interface{}) Iter { code, err := Compile(e) if err != nil { return NewIter(err) } return code.RunWithContext(ctx, v) } func (e *Query) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Query) writeTo(s *strings.Builder) { if e.Meta != nil { s.WriteString("module ") e.Meta.writeTo(s) s.WriteString(";\n") } for _, im := range e.Imports { im.writeTo(s) } for i, fd := range e.FuncDefs { if i > 0 { s.WriteByte(' ') } fd.writeTo(s) } if len(e.FuncDefs) > 0 { s.WriteByte(' ') } if e.Func != "" { s.WriteString(e.Func) } else if e.Term != nil { e.Term.writeTo(s) } else if e.Right != nil { e.Left.writeTo(s) if e.Op == OpComma { s.WriteString(", ") } else { s.WriteByte(' ') s.WriteString(e.Op.String()) s.WriteByte(' ') } e.Right.writeTo(s) } } func (e *Query) minify() { for _, e := range e.FuncDefs { e.Minify() } if e.Term != nil { if name := e.Term.toFunc(); name != "" { e.Term = nil e.Func = name } else { e.Term.minify() } } else if e.Right != nil { e.Left.minify() e.Right.minify() } } func (e *Query) toIndices() []interface{} { if e.FuncDefs != nil || e.Right != nil || e.Term == nil { return nil } return e.Term.toIndices() } // Import ... type Import struct { ImportPath string `json:"import_path,omitempty"` ImportAlias string `json:"import_alias,omitempty"` IncludePath string `json:"include_path,omitempty"` Meta *ConstObject `json:"meta,omitempty"` } func (e *Import) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Import) writeTo(s *strings.Builder) { if e.ImportPath != "" { s.WriteString("import ") s.WriteString(strconv.Quote(e.ImportPath)) s.WriteString(" as ") s.WriteString(e.ImportAlias) } else { s.WriteString("include ") s.WriteString(strconv.Quote(e.IncludePath)) } if e.Meta != nil { s.WriteByte(' ') e.Meta.writeTo(s) } s.WriteString(";\n") } // FuncDef ... type FuncDef struct { Name string `json:"name,omitempty"` Args []string `json:"args,omitempty"` Body *Query `json:"body,omitempty"` } func (e *FuncDef) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *FuncDef) writeTo(s *strings.Builder) { s.WriteString("def ") s.WriteString(e.Name) if len(e.Args) > 0 { s.WriteByte('(') for i, e := range e.Args { if i > 0 { s.WriteString("; ") } s.WriteString(e) } s.WriteByte(')') } s.WriteString(": ") e.Body.writeTo(s) s.WriteByte(';') } // Minify ... func (e *FuncDef) Minify() { e.Body.minify() } // Term ... type Term struct { Type TermType `json:"type,omitempty"` Index *Index `json:"index,omitempty"` Func *Func `json:"func,omitempty"` Object *Object `json:"object,omitempty"` Array *Array `json:"array,omitempty"` Number string `json:"number,omitempty"` Unary *Unary `json:"unary,omitempty"` Format string `json:"format,omitempty"` Str *String `json:"str,omitempty"` If *If `json:"if,omitempty"` Try *Try `json:"try,omitempty"` Reduce *Reduce `json:"reduce,omitempty"` Foreach *Foreach `json:"foreach,omitempty"` Label *Label `json:"label,omitempty"` Break string `json:"break,omitempty"` Query *Query `json:"query,omitempty"` SuffixList []*Suffix `json:"suffix_list,omitempty"` } func (e *Term) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Term) writeTo(s *strings.Builder) { switch e.Type { case TermTypeIdentity: s.WriteByte('.') case TermTypeRecurse: s.WriteString("..") case TermTypeNull: s.WriteString("null") case TermTypeTrue: s.WriteString("true") case TermTypeFalse: s.WriteString("false") case TermTypeIndex: e.Index.writeTo(s) case TermTypeFunc: e.Func.writeTo(s) case TermTypeObject: e.Object.writeTo(s) case TermTypeArray: e.Array.writeTo(s) case TermTypeNumber: s.WriteString(e.Number) case TermTypeUnary: e.Unary.writeTo(s) case TermTypeFormat: s.WriteString(e.Format) if e.Str != nil { s.WriteByte(' ') e.Str.writeTo(s) } case TermTypeString: e.Str.writeTo(s) case TermTypeIf: e.If.writeTo(s) case TermTypeTry: e.Try.writeTo(s) case TermTypeReduce: e.Reduce.writeTo(s) case TermTypeForeach: e.Foreach.writeTo(s) case TermTypeLabel: e.Label.writeTo(s) case TermTypeBreak: s.WriteString("break ") s.WriteString(e.Break) case TermTypeQuery: s.WriteByte('(') e.Query.writeTo(s) s.WriteByte(')') } for _, e := range e.SuffixList { e.writeTo(s) } } func (e *Term) minify() { switch e.Type { case TermTypeIndex: e.Index.minify() case TermTypeFunc: e.Func.minify() case TermTypeObject: e.Object.minify() case TermTypeArray: e.Array.minify() case TermTypeUnary: e.Unary.minify() case TermTypeFormat: if e.Str != nil { e.Str.minify() } case TermTypeString: e.Str.minify() case TermTypeIf: e.If.minify() case TermTypeTry: e.Try.minify() case TermTypeReduce: e.Reduce.minify() case TermTypeForeach: e.Foreach.minify() case TermTypeLabel: e.Label.minify() case TermTypeQuery: e.Query.minify() } for _, e := range e.SuffixList { e.minify() } } func (e *Term) toFunc() string { if len(e.SuffixList) != 0 { return "" } // ref: compiler#compileQuery switch e.Type { case TermTypeIdentity: return "." case TermTypeRecurse: return ".." case TermTypeNull: return "null" case TermTypeTrue: return "true" case TermTypeFalse: return "false" case TermTypeFunc: return e.Func.toFunc() default: return "" } } func (e *Term) toIndices() []interface{} { if e.Index != nil { xs := e.Index.toIndices() if xs == nil { return nil } for _, s := range e.SuffixList { x := s.toIndices() if x == nil { return nil } xs = append(xs, x...) } return xs } else if e.Query != nil && len(e.SuffixList) == 0 { return e.Query.toIndices() } else { return nil } } // Unary ... type Unary struct { Op Operator `json:"op,omitempty"` Term *Term `json:"term,omitempty"` } func (e *Unary) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Unary) writeTo(s *strings.Builder) { s.WriteString(e.Op.String()) e.Term.writeTo(s) } func (e *Unary) minify() { e.Term.minify() } // Pattern ... type Pattern struct { Name string `json:"name,omitempty"` Array []*Pattern `json:"array,omitempty"` Object []*PatternObject `json:"object,omitempty"` } func (e *Pattern) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Pattern) writeTo(s *strings.Builder) { if e.Name != "" { s.WriteString(e.Name) } else if len(e.Array) > 0 { s.WriteByte('[') for i, e := range e.Array { if i > 0 { s.WriteString(", ") } e.writeTo(s) } s.WriteByte(']') } else if len(e.Object) > 0 { s.WriteByte('{') for i, e := range e.Object { if i > 0 { s.WriteString(", ") } e.writeTo(s) } s.WriteByte('}') } } // PatternObject ... type PatternObject struct { Key string `json:"key,omitempty"` KeyString *String `json:"key_string,omitempty"` KeyQuery *Query `json:"key_query,omitempty"` Val *Pattern `json:"val,omitempty"` KeyOnly string `json:"key_only,omitempty"` } func (e *PatternObject) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *PatternObject) writeTo(s *strings.Builder) { if e.Key != "" { s.WriteString(e.Key) } else if e.KeyString != nil { e.KeyString.writeTo(s) } else if e.KeyQuery != nil { s.WriteByte('(') e.KeyQuery.writeTo(s) s.WriteByte(')') } if e.Val != nil { s.WriteString(": ") e.Val.writeTo(s) } if e.KeyOnly != "" { s.WriteString(e.KeyOnly) } } // Index ... type Index struct { Name string `json:"name,omitempty"` Str *String `json:"str,omitempty"` Start *Query `json:"start,omitempty"` End *Query `json:"end,omitempty"` IsSlice bool `json:"is_slice,omitempty"` } func (e *Index) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Index) writeTo(s *strings.Builder) { if l := s.Len(); l > 0 { // ". .x" != "..x" and "0 .x" != "0.x" if c := s.String()[l-1]; c == '.' || '0' <= c && c <= '9' { s.WriteByte(' ') } } s.WriteByte('.') e.writeSuffixTo(s) } func (e *Index) writeSuffixTo(s *strings.Builder) { if e.Name != "" { s.WriteString(e.Name) } else { if e.Str != nil { e.Str.writeTo(s) } else { s.WriteByte('[') if e.IsSlice { if e.Start != nil { e.Start.writeTo(s) } s.WriteByte(':') if e.End != nil { e.End.writeTo(s) } } else { e.Start.writeTo(s) } s.WriteByte(']') } } } func (e *Index) minify() { if e.Str != nil { e.Str.minify() } if e.Start != nil { e.Start.minify() } if e.End != nil { e.End.minify() } } func (e *Index) toIndices() []interface{} { if e.Name == "" { return nil } return []interface{}{e.Name} } // Func ... type Func struct { Name string `json:"name,omitempty"` Args []*Query `json:"args,omitempty"` } func (e *Func) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Func) writeTo(s *strings.Builder) { s.WriteString(e.Name) if len(e.Args) > 0 { s.WriteByte('(') for i, e := range e.Args { if i > 0 { s.WriteString("; ") } e.writeTo(s) } s.WriteByte(')') } } func (e *Func) minify() { for _, x := range e.Args { x.minify() } } func (e *Func) toFunc() string { if len(e.Args) != 0 { return "" } return e.Name } // String ... type String struct { Str string `json:"str,omitempty"` Queries []*Query `json:"queries,omitempty"` } func (e *String) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *String) writeTo(s *strings.Builder) { if e.Queries == nil { s.WriteString(strconv.Quote(e.Str)) return } s.WriteByte('"') for _, e := range e.Queries { if e.Term.Str == nil { s.WriteString(`\`) e.writeTo(s) } else { es := e.String() s.WriteString(es[1 : len(es)-1]) } } s.WriteByte('"') } func (e *String) minify() { for _, e := range e.Queries { e.minify() } } // Object ... type Object struct { KeyVals []*ObjectKeyVal `json:"key_vals,omitempty"` } func (e *Object) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Object) writeTo(s *strings.Builder) { if len(e.KeyVals) == 0 { s.WriteString("{}") return } s.WriteString("{ ") for i, kv := range e.KeyVals { if i > 0 { s.WriteString(", ") } kv.writeTo(s) } s.WriteString(" }") } func (e *Object) minify() { for _, e := range e.KeyVals { e.minify() } } // ObjectKeyVal ... type ObjectKeyVal struct { Key string `json:"key,omitempty"` KeyString *String `json:"key_string,omitempty"` KeyQuery *Query `json:"key_query,omitempty"` Val *ObjectVal `json:"val,omitempty"` KeyOnly string `json:"key_only,omitempty"` KeyOnlyString *String `json:"key_only_string,omitempty"` } func (e *ObjectKeyVal) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *ObjectKeyVal) writeTo(s *strings.Builder) { if e.Key != "" { s.WriteString(e.Key) } else if e.KeyString != nil { e.KeyString.writeTo(s) } else if e.KeyQuery != nil { s.WriteByte('(') e.KeyQuery.writeTo(s) s.WriteByte(')') } if e.Val != nil { s.WriteString(": ") e.Val.writeTo(s) } if e.KeyOnly != "" { s.WriteString(e.KeyOnly) } else if e.KeyOnlyString != nil { e.KeyOnlyString.writeTo(s) } } func (e *ObjectKeyVal) minify() { if e.KeyString != nil { e.KeyString.minify() } else if e.KeyQuery != nil { e.KeyQuery.minify() } if e.Val != nil { e.Val.minify() } if e.KeyOnlyString != nil { e.KeyOnlyString.minify() } } // ObjectVal ... type ObjectVal struct { Queries []*Query `json:"queries,omitempty"` } func (e *ObjectVal) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *ObjectVal) writeTo(s *strings.Builder) { for i, e := range e.Queries { if i > 0 { s.WriteString(" | ") } e.writeTo(s) } } func (e *ObjectVal) minify() { for _, e := range e.Queries { e.minify() } } // Array ... type Array struct { Query *Query `json:"query,omitempty"` } func (e *Array) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Array) writeTo(s *strings.Builder) { s.WriteByte('[') if e.Query != nil { e.Query.writeTo(s) } s.WriteByte(']') } func (e *Array) minify() { if e.Query != nil { e.Query.minify() } } // Suffix ... type Suffix struct { Index *Index `json:"index,omitempty"` Iter bool `json:"iter,omitempty"` Optional bool `json:"optional,omitempty"` Bind *Bind `json:"bind,omitempty"` } func (e *Suffix) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Suffix) writeTo(s *strings.Builder) { if e.Index != nil { if e.Index.Name != "" || e.Index.Str != nil { e.Index.writeTo(s) } else { e.Index.writeSuffixTo(s) } } else if e.Iter { s.WriteString("[]") } else if e.Optional { s.WriteByte('?') } else if e.Bind != nil { e.Bind.writeTo(s) } } func (e *Suffix) minify() { if e.Index != nil { e.Index.minify() } else if e.Bind != nil { e.Bind.minify() } } func (e *Suffix) toTerm() (*Term, bool) { if e.Index != nil { return &Term{Type: TermTypeIndex, Index: e.Index}, true } else if e.Iter { return &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}, true } else { return nil, false } } func (e *Suffix) toIndices() []interface{} { if e.Index == nil { return nil } return e.Index.toIndices() } // Bind ... type Bind struct { Patterns []*Pattern `json:"patterns,omitempty"` Body *Query `json:"body,omitempty"` } func (e *Bind) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Bind) writeTo(s *strings.Builder) { for i, p := range e.Patterns { if i == 0 { s.WriteString(" as ") p.writeTo(s) s.WriteByte(' ') } else { s.WriteString("?// ") p.writeTo(s) s.WriteByte(' ') } } s.WriteString("| ") e.Body.writeTo(s) } func (e *Bind) minify() { e.Body.minify() } // If ... type If struct { Cond *Query `json:"cond,omitempty"` Then *Query `json:"then,omitempty"` Elif []*IfElif `json:"elif,omitempty"` Else *Query `json:"else,omitempty"` } func (e *If) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *If) writeTo(s *strings.Builder) { s.WriteString("if ") e.Cond.writeTo(s) s.WriteString(" then ") e.Then.writeTo(s) for _, e := range e.Elif { s.WriteByte(' ') e.writeTo(s) } if e.Else != nil { s.WriteString(" else ") e.Else.writeTo(s) } s.WriteString(" end") } func (e *If) minify() { e.Cond.minify() e.Then.minify() for _, x := range e.Elif { x.minify() } if e.Else != nil { e.Else.minify() } } // IfElif ... type IfElif struct { Cond *Query `json:"cond,omitempty"` Then *Query `json:"then,omitempty"` } func (e *IfElif) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *IfElif) writeTo(s *strings.Builder) { s.WriteString("elif ") e.Cond.writeTo(s) s.WriteString(" then ") e.Then.writeTo(s) } func (e *IfElif) minify() { e.Cond.minify() e.Then.minify() } // Try ... type Try struct { Body *Query `json:"body,omitempty"` Catch *Query `json:"catch,omitempty"` } func (e *Try) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Try) writeTo(s *strings.Builder) { s.WriteString("try ") e.Body.writeTo(s) if e.Catch != nil { s.WriteString(" catch ") e.Catch.writeTo(s) } } func (e *Try) minify() { e.Body.minify() if e.Catch != nil { e.Catch.minify() } } // Reduce ... type Reduce struct { Term *Term `json:"term,omitempty"` Pattern *Pattern `json:"pattern,omitempty"` Start *Query `json:"start,omitempty"` Update *Query `json:"update,omitempty"` } func (e *Reduce) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Reduce) writeTo(s *strings.Builder) { s.WriteString("reduce ") e.Term.writeTo(s) s.WriteString(" as ") e.Pattern.writeTo(s) s.WriteString(" (") e.Start.writeTo(s) s.WriteString("; ") e.Update.writeTo(s) s.WriteByte(')') } func (e *Reduce) minify() { e.Term.minify() e.Start.minify() e.Update.minify() } // Foreach ... type Foreach struct { Term *Term `json:"term,omitempty"` Pattern *Pattern `json:"pattern,omitempty"` Start *Query `json:"start,omitempty"` Update *Query `json:"update,omitempty"` Extract *Query `json:"extract,omitempty"` } func (e *Foreach) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Foreach) writeTo(s *strings.Builder) { s.WriteString("foreach ") e.Term.writeTo(s) s.WriteString(" as ") e.Pattern.writeTo(s) s.WriteString(" (") e.Start.writeTo(s) s.WriteString("; ") e.Update.writeTo(s) if e.Extract != nil { s.WriteString("; ") e.Extract.writeTo(s) } s.WriteByte(')') } func (e *Foreach) minify() { e.Term.minify() e.Start.minify() e.Update.minify() if e.Extract != nil { e.Extract.minify() } } // Label ... type Label struct { Ident string `json:"ident,omitempty"` Body *Query `json:"body,omitempty"` } func (e *Label) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *Label) writeTo(s *strings.Builder) { s.WriteString("label ") s.WriteString(e.Ident) s.WriteString(" | ") e.Body.writeTo(s) } func (e *Label) minify() { e.Body.minify() } // ConstTerm ... type ConstTerm struct { Object *ConstObject `json:"object,omitempty"` Array *ConstArray `json:"array,omitempty"` Number string `json:"number,omitempty"` Str string `json:"str,omitempty"` Null bool `json:"null,omitempty"` True bool `json:"true,omitempty"` False bool `json:"false,omitempty"` } func (e *ConstTerm) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *ConstTerm) writeTo(s *strings.Builder) { if e.Object != nil { e.Object.writeTo(s) } else if e.Array != nil { e.Array.writeTo(s) } else if e.Number != "" { s.WriteString(e.Number) } else if e.Str != "" { s.WriteString(strconv.Quote(e.Str)) } else if e.Null { s.WriteString("null") } else if e.True { s.WriteString("true") } else if e.False { s.WriteString("false") } } func (e *ConstTerm) toValue() interface{} { if e.Object != nil { return e.Object.ToValue() } else if e.Array != nil { return e.Array.toValue() } else if e.Number != "" { return normalizeNumbers(json.Number(e.Number)) } else if e.Null { return nil } else if e.True { return true } else if e.False { return false } else { return e.Str } } // ConstObject ... type ConstObject struct { KeyVals []*ConstObjectKeyVal `json:"keyvals,omitempty"` } func (e *ConstObject) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *ConstObject) writeTo(s *strings.Builder) { if len(e.KeyVals) == 0 { s.WriteString("{}") return } s.WriteString("{ ") for i, kv := range e.KeyVals { if i > 0 { s.WriteString(", ") } kv.writeTo(s) } s.WriteString(" }") } // ToValue converts the object to map[string]interface{}. func (e *ConstObject) ToValue() map[string]interface{} { if e == nil { return nil } v := make(map[string]interface{}, len(e.KeyVals)) for _, e := range e.KeyVals { key := e.Key if key == "" { key = e.KeyString } v[key] = e.Val.toValue() } return v } // ConstObjectKeyVal ... type ConstObjectKeyVal struct { Key string `json:"key,omitempty"` KeyString string `json:"key_string,omitempty"` Val *ConstTerm `json:"val,omitempty"` } func (e *ConstObjectKeyVal) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *ConstObjectKeyVal) writeTo(s *strings.Builder) { if e.Key != "" { s.WriteString(e.Key) } else { s.WriteString(e.KeyString) } s.WriteString(": ") e.Val.writeTo(s) } // ConstArray ... type ConstArray struct { Elems []*ConstTerm `json:"elems,omitempty"` } func (e *ConstArray) String() string { var s strings.Builder e.writeTo(&s) return s.String() } func (e *ConstArray) writeTo(s *strings.Builder) { s.WriteByte('[') for i, e := range e.Elems { if i > 0 { s.WriteString(", ") } e.writeTo(s) } s.WriteByte(']') } func (e *ConstArray) toValue() []interface{} { v := make([]interface{}, len(e.Elems)) for i, e := range e.Elems { v[i] = e.toValue() } return v } golang-github-wader-gojq-0.0~git20220108.6a05b6c/query_test.go000066400000000000000000000117101416642156300234670ustar00rootroot00000000000000package gojq_test import ( "context" "encoding/json" "fmt" "log" "math" "math/big" "os" "reflect" "strconv" "strings" "sync" "testing" "time" "github.com/wader/gojq" ) func ExampleQuery_Run() { query, err := gojq.Parse(".foo | ..") if err != nil { log.Fatalln(err) } input := map[string]interface{}{"foo": []interface{}{1, 2, 3}} iter := query.Run(input) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { log.Fatalln(err) } fmt.Printf("%#v\n", v) } // Output: // []interface {}{1, 2, 3} // 1 // 2 // 3 } func ExampleQuery_RunWithContext() { query, err := gojq.Parse("def f: f; f, f") if err != nil { log.Fatalln(err) } ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() iter := query.RunWithContext(ctx, nil) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { fmt.Println(err) continue } _ = v } // Output: // context deadline exceeded } func TestQueryRun_Errors(t *testing.T) { query, err := gojq.Parse(".[] | error") if err != nil { t.Fatal(err) } iter := query.Run([]interface{}{0, 1, 2, 3, 4}) n := 0 for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { if expected := "error: " + strconv.Itoa(n); err.Error() != expected { t.Errorf("expected: %v, got: %v", expected, err) } } else { t.Errorf("errors should occur") } n++ } if expected := 5; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } } func TestQueryRun_ObjectError(t *testing.T) { query, err := gojq.Parse(".[] | {(.): 1}") if err != nil { t.Fatal(err) } iter := query.Run([]interface{}{0, "x", []interface{}{}}) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { if expected := "expected a string for object key but got"; !strings.Contains(err.Error(), expected) { t.Errorf("expected: %v, got: %v", expected, err) } } else if expected := map[string]interface{}{"x": 1}; !reflect.DeepEqual(v, expected) { t.Errorf("expected: %v, got: %v", expected, v) } } } func TestQueryRun_InvalidPathError(t *testing.T) { query, err := gojq.Parse(". + 1, path(. + 1)") if err != nil { t.Fatal(err) } iter := query.Run(0) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { if expected := "invalid path against: number (1)"; err.Error() != expected { t.Errorf("expected: %v, got: %v", expected, err) } } else if expected := 1; !reflect.DeepEqual(v, expected) { t.Errorf("expected: %v, got: %v", expected, v) } } } func TestQueryRun_NumericTypes(t *testing.T) { query, err := gojq.Parse(".[] != 0") if err != nil { t.Fatal(err) } iter := query.Run([]interface{}{ int64(1), int32(1), int16(1), int8(1), uint64(1), uint32(1), uint16(1), uint8(1), ^uint(0), int64(math.MaxInt64), int64(math.MinInt64), uint64(math.MaxUint64), uint32(math.MaxUint32), new(big.Int).SetUint64(math.MaxUint64), new(big.Int).SetUint64(math.MaxUint32), json.Number(fmt.Sprint(uint64(math.MaxInt64))), json.Number(fmt.Sprint(uint64(math.MaxInt32))), float64(1.0), float32(1.0), }) for { v, ok := iter.Next() if !ok { break } if err, ok := v.(error); ok { t.Fatal(err) } if expected := true; expected != v { t.Errorf("expected: %v, got: %v", expected, v) } } } func TestQueryRun_Input(t *testing.T) { query, err := gojq.Parse("input") if err != nil { t.Fatal(err) } iter := query.Run(nil) v, ok := iter.Next() if !ok { t.Fatal("should emit an error but got no output") } err, expected := v.(error), "input(s)/0 is not allowed" if got := err.Error(); got != expected { t.Errorf("expected: %v, got: %v", expected, got) } } func TestQueryRun_Race(t *testing.T) { query, err := gojq.Parse("range(10)") if err != nil { t.Fatal(err) } var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() iter := query.Run(nil) n := 0 for { got, ok := iter.Next() if !ok { break } if got != n { t.Errorf("expected: %v, got: %v", n, got) } n++ } if expected := 10; n != expected { t.Errorf("expected: %v, got: %v", expected, n) } }() } wg.Wait() } func TestQueryString(t *testing.T) { cnt, err := os.ReadFile("builtin.jq") if err != nil { t.Fatal(err) } q, err := gojq.Parse(string(cnt)) if err != nil { t.Fatal(err) } r, err := gojq.Parse(q.String()) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(q, r) { t.Errorf("\n%v\n%v", q, r) } } func BenchmarkRun(b *testing.B) { query, err := gojq.Parse("range(1000)") if err != nil { b.Fatal(err) } for i := 0; i < b.N; i++ { iter := query.Run(nil) for { _, ok := iter.Next() if !ok { break } } } } func BenchmarkParse(b *testing.B) { cnt, err := os.ReadFile("builtin.jq") if err != nil { b.Fatal(err) } src := string(cnt) for i := 0; i < b.N; i++ { _, err := gojq.Parse(src) if err != nil { b.Fatal(err) } } } golang-github-wader-gojq-0.0~git20220108.6a05b6c/release.go000066400000000000000000000004411416642156300227020ustar00rootroot00000000000000//go:build !debug // +build !debug package gojq type codeinfo struct{} func (c *compiler) appendCodeInfo(interface{}) {} func (c *compiler) deleteCodeInfo(string) {} func (env *env) debugCodes() {} func (env *env) debugState(int, bool) {} func (env *env) debugForks(int, string) {} golang-github-wader-gojq-0.0~git20220108.6a05b6c/scope_stack.go000066400000000000000000000014451416642156300235650ustar00rootroot00000000000000package gojq type scopeStack struct { data []scopeBlock index int limit int } type scopeBlock struct { value scope next int } func newScopeStack() *scopeStack { return &scopeStack{index: -1, limit: -1} } func (s *scopeStack) push(v scope) { b := scopeBlock{v, s.index} i := s.index + 1 if i <= s.limit { i = s.limit + 1 } s.index = i if i < len(s.data) { s.data[i] = b } else { s.data = append(s.data, b) } } func (s *scopeStack) pop() scope { b := s.data[s.index] s.index = b.next return b.value } func (s *scopeStack) empty() bool { return s.index < 0 } func (s *scopeStack) save(index, limit *int) { *index, *limit = s.index, s.limit if s.index > s.limit { s.limit = s.index } } func (s *scopeStack) restore(index, limit int) { s.index, s.limit = index, limit } golang-github-wader-gojq-0.0~git20220108.6a05b6c/stack.go000066400000000000000000000015001416642156300223640ustar00rootroot00000000000000package gojq type stack struct { data []block index int limit int } type block struct { value interface{} next int } func newStack() *stack { return &stack{index: -1, limit: -1} } func (s *stack) push(v interface{}) { b := block{v, s.index} i := s.index + 1 if i <= s.limit { i = s.limit + 1 } s.index = i if i < len(s.data) { s.data[i] = b } else { s.data = append(s.data, b) } } func (s *stack) pop() interface{} { b := s.data[s.index] s.index = b.next return b.value } func (s *stack) top() interface{} { return s.data[s.index].value } func (s *stack) empty() bool { return s.index < 0 } func (s *stack) save(index, limit *int) { *index, *limit = s.index, s.limit if s.index > s.limit { s.limit = s.index } } func (s *stack) restore(index, limit int) { s.index, s.limit = index, limit } golang-github-wader-gojq-0.0~git20220108.6a05b6c/term_type.go000066400000000000000000000057621416642156300233050ustar00rootroot00000000000000package gojq import ( "encoding/json" "fmt" ) // TermType represents the type of Term. type TermType int // TermType list. const ( TermTypeIdentity TermType = iota + 1 TermTypeRecurse TermTypeNull TermTypeTrue TermTypeFalse TermTypeIndex TermTypeFunc TermTypeObject TermTypeArray TermTypeNumber TermTypeUnary TermTypeFormat TermTypeString TermTypeIf TermTypeTry TermTypeReduce TermTypeForeach TermTypeLabel TermTypeBreak TermTypeQuery ) // GoString implements GoStringer. func TermTypeFromString(s string) TermType { switch s { case "TermTypeIdentity": return TermTypeIdentity case "TermTypeRecurse": return TermTypeRecurse case "TermTypeNull": return TermTypeNull case "TermTypeTrue": return TermTypeTrue case "TermTypeFalse": return TermTypeFalse case "TermTypeIndex": return TermTypeIndex case "TermTypeFunc": return TermTypeFunc case "TermTypeObject": return TermTypeObject case "TermTypeArray": return TermTypeArray case "TermTypeNumber": return TermTypeNumber case "TermTypeUnary": return TermTypeUnary case "TermTypeFormat": return TermTypeFormat case "TermTypeString": return TermTypeString case "TermTypeIf": return TermTypeIf case "TermTypeTry": return TermTypeTry case "TermTypeReduce": return TermTypeReduce case "TermTypeForeach": return TermTypeForeach case "TermTypeLabel": return TermTypeLabel case "TermTypeBreak": return TermTypeBreak case "TermTypeQuery": return TermTypeQuery default: return 0 } } // GoString implements GoStringer. func (termType TermType) GoString() (str string) { defer func() { str = "gojq." + str }() switch termType { case TermTypeIdentity: return "TermTypeIdentity" case TermTypeRecurse: return "TermTypeRecurse" case TermTypeNull: return "TermTypeNull" case TermTypeTrue: return "TermTypeTrue" case TermTypeFalse: return "TermTypeFalse" case TermTypeIndex: return "TermTypeIndex" case TermTypeFunc: return "TermTypeFunc" case TermTypeObject: return "TermTypeObject" case TermTypeArray: return "TermTypeArray" case TermTypeNumber: return "TermTypeNumber" case TermTypeUnary: return "TermTypeUnary" case TermTypeFormat: return "TermTypeFormat" case TermTypeString: return "TermTypeString" case TermTypeIf: return "TermTypeIf" case TermTypeTry: return "TermTypeTry" case TermTypeReduce: return "TermTypeReduce" case TermTypeForeach: return "TermTypeForeach" case TermTypeLabel: return "TermTypeLabel" case TermTypeBreak: return "TermTypeBreak" case TermTypeQuery: return "TermTypeQuery" default: panic(termType) } } func (termType TermType) MarshalJSON() ([]byte, error) { if termType == 0 { return json.Marshal(nil) } // TODO: gojq. skips prefix return json.Marshal(termType.GoString()[5:]) } func (termType *TermType) UnmarshalJSON(text []byte) error { var s string err := json.Unmarshal(text, &s) if err != nil { return err } *termType = TermTypeFromString(s) if *termType == 0 { return fmt.Errorf("unknown term %v", s) } return nil }