From b4c8746f48a51150313788c46b1f77082774d6d1 Mon Sep 17 00:00:00 2001 From: abhay1999 Date: Thu, 9 Apr 2026 20:33:27 +0530 Subject: [PATCH] driver/kubernetes: add manifest-patch driver option Add a generic "manifest-patch" driver option that accepts a jq expression, applied to the generated Deployment or StatefulSet manifest after creation. This allows users to set arbitrary manifest fields that are not exposed as dedicated driver options, such as ownerReferences: --driver-opt manifest-patch='.metadata.ownerReferences=[{"apiVersion":"actions.github.com/v1alpha1","kind":"EphemeralRunner","name":"runner-xyz","uid":"..."}]' The jq expression is evaluated using github.com/itchyny/gojq, a pure-Go jq implementation. The expression is validated at driver initialization time, and an error is returned if it is invalid or produces no output. Fixes: https://github.com/docker/buildx/issues/2626 Signed-off-by: abhay1999 --- driver/kubernetes/factory.go | 94 +- driver/kubernetes/factory_test.go | 75 +- go.mod | 5 +- go.sum | 10 +- .../clipperhouse/stringish/.gitignore | 2 + .../github.com/clipperhouse/stringish/LICENSE | 21 + .../clipperhouse/stringish/README.md | 64 + .../clipperhouse/stringish/interface.go | 5 + .../clipperhouse/uax29/v2/graphemes/README.md | 24 +- .../uax29/v2/graphemes/iterator.go | 7 +- .../uax29/v2/graphemes/splitfunc.go | 4 +- .../clipperhouse/uax29/v2/graphemes/trie.go | 6 +- .../uax29/v2/internal/iterators/iterator.go | 27 +- vendor/github.com/itchyny/gojq/.dockerignore | 16 + vendor/github.com/itchyny/gojq/.gitattributes | 3 + vendor/github.com/itchyny/gojq/.gitignore | 7 + vendor/github.com/itchyny/gojq/CHANGELOG.md | 418 ++++ vendor/github.com/itchyny/gojq/Dockerfile | 14 + vendor/github.com/itchyny/gojq/LICENSE | 21 + vendor/github.com/itchyny/gojq/Makefile | 102 + vendor/github.com/itchyny/gojq/README.md | 164 ++ vendor/github.com/itchyny/gojq/_gojq | 43 + vendor/github.com/itchyny/gojq/builtin.go | 73 + vendor/github.com/itchyny/gojq/builtin.jq | 188 ++ vendor/github.com/itchyny/gojq/code.go | 108 + vendor/github.com/itchyny/gojq/compare.go | 76 + vendor/github.com/itchyny/gojq/compiler.go | 1715 +++++++++++++ vendor/github.com/itchyny/gojq/debug.go | 208 ++ vendor/github.com/itchyny/gojq/encoder.go | 192 ++ vendor/github.com/itchyny/gojq/env.go | 49 + vendor/github.com/itchyny/gojq/error.go | 383 +++ vendor/github.com/itchyny/gojq/execute.go | 443 ++++ vendor/github.com/itchyny/gojq/func.go | 2190 +++++++++++++++++ vendor/github.com/itchyny/gojq/go.dev.mod | 8 + vendor/github.com/itchyny/gojq/go.dev.sum | 4 + vendor/github.com/itchyny/gojq/gojq.go | 5 + vendor/github.com/itchyny/gojq/iter.go | 49 + vendor/github.com/itchyny/gojq/lexer.go | 578 +++++ .../github.com/itchyny/gojq/module_loader.go | 184 ++ vendor/github.com/itchyny/gojq/operator.go | 557 +++++ vendor/github.com/itchyny/gojq/option.go | 96 + vendor/github.com/itchyny/gojq/parser.go | 1506 ++++++++++++ vendor/github.com/itchyny/gojq/parser.go.y | 669 +++++ vendor/github.com/itchyny/gojq/preview.go | 77 + vendor/github.com/itchyny/gojq/query.go | 942 +++++++ vendor/github.com/itchyny/gojq/release.go | 15 + vendor/github.com/itchyny/gojq/scope_stack.go | 48 + vendor/github.com/itchyny/gojq/stack.go | 52 + vendor/github.com/itchyny/gojq/term_type.go | 77 + vendor/github.com/itchyny/gojq/type.go | 31 + .../itchyny/timefmt-go/CHANGELOG.md | 37 + vendor/github.com/itchyny/timefmt-go/LICENSE | 21 + vendor/github.com/itchyny/timefmt-go/Makefile | 20 + .../github.com/itchyny/timefmt-go/README.md | 69 + .../github.com/itchyny/timefmt-go/format.go | 462 ++++ vendor/github.com/itchyny/timefmt-go/parse.go | 474 ++++ .../github.com/itchyny/timefmt-go/timefmt.go | 2 + vendor/modules.txt | 11 +- 58 files changed, 12699 insertions(+), 52 deletions(-) create mode 100644 vendor/github.com/clipperhouse/stringish/.gitignore create mode 100644 vendor/github.com/clipperhouse/stringish/LICENSE create mode 100644 vendor/github.com/clipperhouse/stringish/README.md create mode 100644 vendor/github.com/clipperhouse/stringish/interface.go create mode 100644 vendor/github.com/itchyny/gojq/.dockerignore create mode 100644 vendor/github.com/itchyny/gojq/.gitattributes create mode 100644 vendor/github.com/itchyny/gojq/.gitignore create mode 100644 vendor/github.com/itchyny/gojq/CHANGELOG.md create mode 100644 vendor/github.com/itchyny/gojq/Dockerfile create mode 100644 vendor/github.com/itchyny/gojq/LICENSE create mode 100644 vendor/github.com/itchyny/gojq/Makefile create mode 100644 vendor/github.com/itchyny/gojq/README.md create mode 100644 vendor/github.com/itchyny/gojq/_gojq create mode 100644 vendor/github.com/itchyny/gojq/builtin.go create mode 100644 vendor/github.com/itchyny/gojq/builtin.jq create mode 100644 vendor/github.com/itchyny/gojq/code.go create mode 100644 vendor/github.com/itchyny/gojq/compare.go create mode 100644 vendor/github.com/itchyny/gojq/compiler.go create mode 100644 vendor/github.com/itchyny/gojq/debug.go create mode 100644 vendor/github.com/itchyny/gojq/encoder.go create mode 100644 vendor/github.com/itchyny/gojq/env.go create mode 100644 vendor/github.com/itchyny/gojq/error.go create mode 100644 vendor/github.com/itchyny/gojq/execute.go create mode 100644 vendor/github.com/itchyny/gojq/func.go create mode 100644 vendor/github.com/itchyny/gojq/go.dev.mod create mode 100644 vendor/github.com/itchyny/gojq/go.dev.sum create mode 100644 vendor/github.com/itchyny/gojq/gojq.go create mode 100644 vendor/github.com/itchyny/gojq/iter.go create mode 100644 vendor/github.com/itchyny/gojq/lexer.go create mode 100644 vendor/github.com/itchyny/gojq/module_loader.go create mode 100644 vendor/github.com/itchyny/gojq/operator.go create mode 100644 vendor/github.com/itchyny/gojq/option.go create mode 100644 vendor/github.com/itchyny/gojq/parser.go create mode 100644 vendor/github.com/itchyny/gojq/parser.go.y create mode 100644 vendor/github.com/itchyny/gojq/preview.go create mode 100644 vendor/github.com/itchyny/gojq/query.go create mode 100644 vendor/github.com/itchyny/gojq/release.go create mode 100644 vendor/github.com/itchyny/gojq/scope_stack.go create mode 100644 vendor/github.com/itchyny/gojq/stack.go create mode 100644 vendor/github.com/itchyny/gojq/term_type.go create mode 100644 vendor/github.com/itchyny/gojq/type.go create mode 100644 vendor/github.com/itchyny/timefmt-go/CHANGELOG.md create mode 100644 vendor/github.com/itchyny/timefmt-go/LICENSE create mode 100644 vendor/github.com/itchyny/timefmt-go/Makefile create mode 100644 vendor/github.com/itchyny/timefmt-go/README.md create mode 100644 vendor/github.com/itchyny/timefmt-go/format.go create mode 100644 vendor/github.com/itchyny/timefmt-go/parse.go create mode 100644 vendor/github.com/itchyny/timefmt-go/timefmt.go diff --git a/driver/kubernetes/factory.go b/driver/kubernetes/factory.go index 284e6ab3946e..0cd2fdb93c45 100644 --- a/driver/kubernetes/factory.go +++ b/driver/kubernetes/factory.go @@ -2,6 +2,7 @@ package kubernetes import ( "context" + "encoding/json" "os" "strconv" "strings" @@ -13,6 +14,7 @@ import ( "github.com/docker/buildx/driver/kubernetes/kubeclient" "github.com/docker/buildx/driver/kubernetes/manifest" "github.com/docker/buildx/driver/kubernetes/podchooser" + "github.com/itchyny/gojq" dockerclient "github.com/moby/moby/client" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -123,7 +125,7 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver InitConfig: cfg, } - deploymentOpt, loadbalance, namespace, defaultLoad, timeout, err := f.processDriverOpts(deploymentName, namespace, cfg) + deploymentOpt, loadbalance, namespace, defaultLoad, timeout, manifestPatch, err := f.processDriverOpts(deploymentName, namespace, cfg) if nil != err { return nil, err } @@ -136,6 +138,19 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver return nil, err } + if manifestPatch != "" { + if d.deployment != nil { + if d.deployment, err = applyManifestPatch(d.deployment, manifestPatch); err != nil { + return nil, errors.Wrap(err, "failed to apply manifest-patch to Deployment") + } + } + if d.statefulSet != nil { + if d.statefulSet, err = applyManifestPatch(d.statefulSet, manifestPatch); err != nil { + return nil, errors.Wrap(err, "failed to apply manifest-patch to StatefulSet") + } + } + } + d.minReplicas = int(deploymentOpt.Replicas) clients, err := kubeclient.New(restClientConfig, namespace) @@ -165,7 +180,7 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver return d, nil } -func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg driver.InitConfig) (*manifest.DeploymentOpt, string, string, bool, time.Duration, error) { +func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg driver.InitConfig) (*manifest.DeploymentOpt, string, string, bool, time.Duration, string, error) { deploymentOpt := &manifest.DeploymentOpt{ Name: deploymentName, Image: bkimage.DefaultImage, @@ -180,6 +195,7 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg timeout := defaultTimeout deploymentOpt.Qemu.Image = bkimage.QemuImage loadbalance := LoadbalanceSticky + manifestPatch := "" var err error for k, v := range cfg.DriverOpts { @@ -193,7 +209,7 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg case k == "replicas": r, err := strconv.ParseInt(v, 10, 32) if err != nil { - return nil, "", "", false, 0, err + return nil, "", "", false, 0, "", err } deploymentOpt.Replicas = int32(r) case k == "requests.cpu": @@ -213,7 +229,7 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg case k == "rootless": deploymentOpt.Rootless, err = strconv.ParseBool(v) if err != nil { - return nil, "", "", false, 0, err + return nil, "", "", false, 0, "", err } if _, isImage := cfg.DriverOpts["image"]; !isImage { deploymentOpt.Image = bkimage.DefaultRootlessImage @@ -225,17 +241,17 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg case k == "nodeselector": deploymentOpt.NodeSelector, err = splitMultiValues(v, ",", "=") if err != nil { - return nil, "", "", false, 0, errors.Wrap(err, "cannot parse node selector") + return nil, "", "", false, 0, "", errors.Wrap(err, "cannot parse node selector") } case k == "annotations": deploymentOpt.CustomAnnotations, err = splitMultiValues(v, ",", "=") if err != nil { - return nil, "", "", false, 0, errors.Wrap(err, "cannot parse annotations") + return nil, "", "", false, 0, "", errors.Wrap(err, "cannot parse annotations") } case k == "labels": deploymentOpt.CustomLabels, err = splitMultiValues(v, ",", "=") if err != nil { - return nil, "", "", false, 0, errors.Wrap(err, "cannot parse labels") + return nil, "", "", false, 0, "", errors.Wrap(err, "cannot parse labels") } case k == "tolerations": ts := strings.Split(v, ";") @@ -260,29 +276,34 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg case "tolerationSeconds": c, err := strconv.Atoi(kv[1]) if nil != err { - return nil, "", "", false, 0, err + return nil, "", "", false, 0, "", err } c64 := int64(c) t.TolerationSeconds = &c64 default: - return nil, "", "", false, 0, errors.Errorf("invalid tolaration %q", v) + return nil, "", "", false, 0, "", errors.Errorf("invalid tolaration %q", v) } } } deploymentOpt.Tolerations = append(deploymentOpt.Tolerations, t) } + case k == "manifest-patch": + if _, err := gojq.Parse(v); err != nil { + return nil, "", "", false, 0, "", errors.Wrap(err, "invalid manifest-patch jq expression") + } + manifestPatch = v case k == "loadbalance": switch v { case LoadbalanceSticky, LoadbalanceRandom: loadbalance = v default: - return nil, "", "", false, 0, errors.Errorf("invalid loadbalance %q", v) + return nil, "", "", false, 0, "", errors.Errorf("invalid loadbalance %q", v) } case k == "qemu.install": deploymentOpt.Qemu.Install, err = strconv.ParseBool(v) if err != nil { - return nil, "", "", false, 0, err + return nil, "", "", false, 0, "", err } case k == "qemu.image": if v != "" { @@ -295,24 +316,65 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg case k == "default-load": defaultLoad, err = strconv.ParseBool(v) if err != nil { - return nil, "", "", false, 0, err + return nil, "", "", false, 0, "", err } case k == "timeout": timeout, err = time.ParseDuration(v) if err != nil { - return nil, "", "", false, 0, errors.Wrap(err, "cannot parse timeout") + return nil, "", "", false, 0, "", errors.Wrap(err, "cannot parse timeout") } case strings.HasPrefix(k, "env."): envName := strings.TrimPrefix(k, "env.") if envName == "" { - return nil, "", "", false, 0, errors.Errorf("invalid env option %q, expecting env.FOO=bar", k) + return nil, "", "", false, 0, "", errors.Errorf("invalid env option %q, expecting env.FOO=bar", k) } deploymentOpt.Env = append(deploymentOpt.Env, corev1.EnvVar{Name: envName, Value: v}) default: - return nil, "", "", false, 0, errors.Errorf("invalid driver option %s for driver %s", k, DriverName) + return nil, "", "", false, 0, "", errors.Errorf("invalid driver option %s for driver %s", k, DriverName) } } - return deploymentOpt, loadbalance, namespace, defaultLoad, timeout, nil + return deploymentOpt, loadbalance, namespace, defaultLoad, timeout, manifestPatch, nil +} + +// applyManifestPatch applies a jq expression to patch a Kubernetes manifest object. +// The expression receives the manifest as input and must produce a single object as output. +func applyManifestPatch[T any](obj *T, patch string) (*T, error) { + query, err := gojq.Parse(patch) + if err != nil { + return nil, errors.Wrap(err, "failed to parse manifest-patch expression") + } + code, err := gojq.Compile(query) + if err != nil { + return nil, errors.Wrap(err, "failed to compile manifest-patch expression") + } + + b, err := json.Marshal(obj) + if err != nil { + return nil, err + } + var input interface{} + if err := json.Unmarshal(b, &input); err != nil { + return nil, err + } + + iter := code.Run(input) + v, ok := iter.Next() + if !ok { + return nil, errors.New("manifest-patch expression produced no output") + } + if err, ok := v.(error); ok { + return nil, errors.Wrap(err, "manifest-patch expression failed") + } + + b, err = json.Marshal(v) + if err != nil { + return nil, err + } + var result T + if err := json.Unmarshal(b, &result); err != nil { + return nil, err + } + return &result, nil } func splitMultiValues(in string, itemsep string, kvsep string) (map[string]string, error) { diff --git a/driver/kubernetes/factory_test.go b/driver/kubernetes/factory_test.go index 9219b3893422..52c6fed994b4 100644 --- a/driver/kubernetes/factory_test.go +++ b/driver/kubernetes/factory_test.go @@ -7,7 +7,10 @@ import ( "github.com/docker/buildx/driver" "github.com/docker/buildx/driver/bkimage" "github.com/stretchr/testify/require" + appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" ) @@ -55,7 +58,7 @@ func TestFactory_processDriverOpts(t *testing.T) { "qemu.image": "qemu:latest", "default-load": "true", } - r, loadbalance, ns, defaultLoad, timeout, err := f.processDriverOpts(cfg.Name, "test", cfg) + r, loadbalance, ns, defaultLoad, timeout, _, err := f.processDriverOpts(cfg.Name, "test", cfg) nodeSelectors := map[string]string{ "selector1": "value1", @@ -113,7 +116,7 @@ func TestFactory_processDriverOpts(t *testing.T) { "NoOptions", func(t *testing.T) { cfg.DriverOpts = map[string]string{} - r, loadbalance, ns, defaultLoad, timeout, err := f.processDriverOpts(cfg.Name, "test", cfg) + r, loadbalance, ns, defaultLoad, timeout, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.NoError(t, err) @@ -144,7 +147,7 @@ func TestFactory_processDriverOpts(t *testing.T) { "loadbalance": "sticky", } - r, loadbalance, ns, defaultLoad, timeout, err := f.processDriverOpts(cfg.Name, "test", cfg) + r, loadbalance, ns, defaultLoad, timeout, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.NoError(t, err) @@ -173,7 +176,7 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "replicas": "invalid", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) @@ -183,7 +186,7 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "rootless": "invalid", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) @@ -193,7 +196,7 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "tolerations": "key=foo,value=bar,invalid=foo2", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) @@ -203,7 +206,7 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "tolerations": "key=foo,value=bar,tolerationSeconds=invalid", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) @@ -213,7 +216,7 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "annotations": "key,value", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) @@ -223,7 +226,7 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "labels": "key=value=foo", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) @@ -233,7 +236,7 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "loadbalance": "invalid", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) @@ -243,7 +246,7 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "qemu.install": "invalid", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) @@ -253,7 +256,7 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "invalid": "foo", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) @@ -263,8 +266,54 @@ func TestFactory_processDriverOpts(t *testing.T) { cfg.DriverOpts = map[string]string{ "timeout": "invalid", } - _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) require.Error(t, err) }, ) + + t.Run( + "ManifestPatch", func(t *testing.T) { + cfg.DriverOpts = map[string]string{ + "manifest-patch": `.metadata.ownerReferences=[{"apiVersion":"actions.github.com/v1alpha1","kind":"EphemeralRunner","name":"runner-xyz","uid":"b636330d-26b7-417a-8464-c2641438feed"}]`, + } + _, _, _, _, _, patch, err := f.processDriverOpts(cfg.Name, "test", cfg) + require.NoError(t, err) + require.NotEmpty(t, patch) + }, + ) + + t.Run( + "InvalidManifestPatch", func(t *testing.T) { + cfg.DriverOpts = map[string]string{ + "manifest-patch": "invalid jq [[[", + } + _, _, _, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg) + require.Error(t, err) + }, + ) +} + +func TestApplyManifestPatch(t *testing.T) { + t.Run("SetOwnerReferences", func(t *testing.T) { + d := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + } + patch := `.metadata.ownerReferences=[{"apiVersion":"actions.github.com/v1alpha1","kind":"EphemeralRunner","name":"runner-xyz","uid":"b636330d-26b7-417a-8464-c2641438feed"}]` + result, err := applyManifestPatch(d, patch) + require.NoError(t, err) + require.Len(t, result.OwnerReferences, 1) + require.Equal(t, "actions.github.com/v1alpha1", result.OwnerReferences[0].APIVersion) + require.Equal(t, "EphemeralRunner", result.OwnerReferences[0].Kind) + require.Equal(t, "runner-xyz", result.OwnerReferences[0].Name) + require.Equal(t, types.UID("b636330d-26b7-417a-8464-c2641438feed"), result.OwnerReferences[0].UID) + }) + + t.Run("InvalidExpression", func(t *testing.T) { + d := &appsv1.Deployment{} + _, err := applyManifestPatch(d, "invalid [[[") + require.Error(t, err) + }) } diff --git a/go.mod b/go.mod index 0347e1ab357d..189e51403f3c 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928 github.com/hashicorp/hcl/v2 v2.24.0 github.com/in-toto/in-toto-golang v0.10.0 + github.com/itchyny/gojq v0.12.19 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/moby/buildkit v0.29.0 github.com/moby/go-archive v0.2.0 @@ -101,7 +102,8 @@ require ( github.com/blang/semver v3.5.1+incompatible // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/clipperhouse/uax29/v2 v2.2.0 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/containerd/containerd/api v1.10.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -155,6 +157,7 @@ require ( github.com/hiddeco/sshsig v0.2.0 // indirect github.com/in-toto/attestation v1.1.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/itchyny/timefmt-go v0.1.8 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.5 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect diff --git a/go.sum b/go.sum index a66b821d6b1f..fcd9a8a47a87 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,10 @@ github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1x github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= -github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= @@ -362,6 +364,10 @@ github.com/in-toto/in-toto-golang v0.10.0 h1:+s2eZQSK3WmWfYV85qXVSBfqgawi/5L02Ma github.com/in-toto/in-toto-golang v0.10.0/go.mod h1:wjT4RiyFlLWCmLUJjwB8oZcjaq7HA390aMJcD3xXgmg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/itchyny/gojq v0.12.19 h1:ttXA0XCLEMoaLOz5lSeFOZ6u6Q3QxmG46vfgI4O0DEs= +github.com/itchyny/gojq v0.12.19/go.mod h1:5galtVPDywX8SPSOrqjGxkBeDhSxEW1gSxoy7tn1iZY= +github.com/itchyny/timefmt-go v0.1.8 h1:1YEo1JvfXeAHKdjelbYr/uCuhkybaHCeTkH8Bo791OI= +github.com/itchyny/timefmt-go v0.1.8/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= diff --git a/vendor/github.com/clipperhouse/stringish/.gitignore b/vendor/github.com/clipperhouse/stringish/.gitignore new file mode 100644 index 000000000000..12fbfb739bee --- /dev/null +++ b/vendor/github.com/clipperhouse/stringish/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +*.test diff --git a/vendor/github.com/clipperhouse/stringish/LICENSE b/vendor/github.com/clipperhouse/stringish/LICENSE new file mode 100644 index 000000000000..4b8064eb37bf --- /dev/null +++ b/vendor/github.com/clipperhouse/stringish/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Matt Sherman + +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. diff --git a/vendor/github.com/clipperhouse/stringish/README.md b/vendor/github.com/clipperhouse/stringish/README.md new file mode 100644 index 000000000000..fa1f7cc6723a --- /dev/null +++ b/vendor/github.com/clipperhouse/stringish/README.md @@ -0,0 +1,64 @@ +# stringish + +A small Go module that provides a generic type constraint for “string-like” +data, and a utf8 package that works with both strings and byte slices +without conversions. + +```go +type Interface interface { + ~[]byte | ~string +} +``` + +[![Go Reference](https://pkg.go.dev/badge/github.com/clipperhouse/stringish/utf8.svg)](https://pkg.go.dev/github.com/clipperhouse/stringish/utf8) +[![Test Status](https://github.com/clipperhouse/stringish/actions/workflows/gotest.yml/badge.svg)](https://github.com/clipperhouse/stringish/actions/workflows/gotest.yml) + +## Install + +``` +go get github.com/clipperhouse/stringish +``` + +## Examples + +```go +import ( + "github.com/clipperhouse/stringish" + "github.com/clipperhouse/stringish/utf8" +) + +s := "Hello, 世界" +r, size := utf8.DecodeRune(s) // not DecodeRuneInString 🎉 + +b := []byte("Hello, 世界") +r, size = utf8.DecodeRune(b) // same API! + +func MyFoo[T stringish.Interface](s T) T { + // pass a string or a []byte + // iterate, slice, transform, whatever +} +``` + +## Motivation + +Sometimes we want APIs to accept `string` or `[]byte` without having to convert +between those types. That conversion usually allocates! + +By implementing with `stringish.Interface`, we can have a single API, and +single implementation for both types: one `Foo` instead of `Foo` and +`FooString`. + +We have converted the +[`unicode/utf8` package](https://github.com/clipperhouse/stringish/blob/main/utf8/utf8.go) +as an example -- note the absence of`*InString` funcs. We might look at `x/text` +next. + +## Used by + +- clipperhouse/uax29: [stringish trie](https://github.com/clipperhouse/uax29/blob/master/graphemes/trie.go#L27), [stringish iterator](https://github.com/clipperhouse/uax29/blob/master/internal/iterators/iterator.go#L9), [stringish SplitFunc](https://github.com/clipperhouse/uax29/blob/master/graphemes/splitfunc.go#L21) + +- [clipperhouse/displaywidth](https://github.com/clipperhouse/displaywidth) + +## Prior discussion + +- [Consideration of similar by the Go team](https://github.com/golang/go/issues/48643) diff --git a/vendor/github.com/clipperhouse/stringish/interface.go b/vendor/github.com/clipperhouse/stringish/interface.go new file mode 100644 index 000000000000..adfeab61eb6d --- /dev/null +++ b/vendor/github.com/clipperhouse/stringish/interface.go @@ -0,0 +1,5 @@ +package stringish + +type Interface interface { + ~[]byte | ~string +} diff --git a/vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md b/vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md index 4d9a6d717b82..dc14d11e2f71 100644 --- a/vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md +++ b/vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md @@ -1,5 +1,9 @@ An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode version 15.0.0. +[![Documentation](https://pkg.go.dev/badge/github.com/clipperhouse/uax29/v2/graphemes.svg)](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes) +![Tests](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg) +![Fuzz](https://github.com/clipperhouse/uax29/actions/workflows/gofuzz.yml/badge.svg) + ## Quick start ``` @@ -18,15 +22,14 @@ for tokens.Next() { // Next() returns true until end of data } ``` -[![Documentation](https://pkg.go.dev/badge/github.com/clipperhouse/uax29/v2/graphemes.svg)](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes) - _A grapheme is a “single visible character”, which might be a simple as a single letter, or a complex emoji that consists of several Unicode code points._ ## Conformance -We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29). Status: +We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29). -![Go](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg) +![Tests](https://github.com/clipperhouse/uax29/actions/workflows/gotest.yml/badge.svg) +![Fuzz](https://github.com/clipperhouse/uax29/actions/workflows/gofuzz.yml/badge.svg) ## APIs @@ -71,9 +74,18 @@ for tokens.Next() { // Next() returns true until end of data } ``` -### Performance +### Benchmarks -On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second. You should see ~constant memory, and no allocations. +On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second, and no allocations. + +``` +goos: darwin +goarch: arm64 +pkg: github.com/clipperhouse/uax29/graphemes/comparative +cpu: Apple M2 +BenchmarkGraphemes/clipperhouse/uax29-8 173805 ns/op 201.16 MB/s 0 B/op 0 allocs/op +BenchmarkGraphemes/rivo/uniseg-8 2045128 ns/op 17.10 MB/s 0 B/op 0 allocs/op +``` ### Invalid inputs diff --git a/vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go b/vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go index 14b4ea2c4618..1eaaa534ce31 100644 --- a/vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go +++ b/vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go @@ -1,8 +1,11 @@ package graphemes -import "github.com/clipperhouse/uax29/v2/internal/iterators" +import ( + "github.com/clipperhouse/stringish" + "github.com/clipperhouse/uax29/v2/internal/iterators" +) -type Iterator[T iterators.Stringish] struct { +type Iterator[T stringish.Interface] struct { *iterators.Iterator[T] } diff --git a/vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go b/vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go index 08987f549fc0..cbe1ec9ef14e 100644 --- a/vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go +++ b/vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go @@ -3,7 +3,7 @@ package graphemes import ( "bufio" - "github.com/clipperhouse/uax29/v2/internal/iterators" + "github.com/clipperhouse/stringish" ) // is determines if lookup intersects propert(ies) @@ -18,7 +18,7 @@ const _Ignore = _Extend // See https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries. var SplitFunc bufio.SplitFunc = splitFunc[[]byte] -func splitFunc[T iterators.Stringish](data T, atEOF bool) (advance int, token T, err error) { +func splitFunc[T stringish.Interface](data T, atEOF bool) (advance int, token T, err error) { var empty T if len(data) == 0 { return 0, empty, nil diff --git a/vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go b/vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go index c8c6c33bcd02..8aaabfacf00b 100644 --- a/vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go +++ b/vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go @@ -1,10 +1,10 @@ package graphemes +import "github.com/clipperhouse/stringish" + // generated by github.com/clipperhouse/uax29/v2 // from https://www.unicode.org/Public/15.0.0/ucd/auxiliary/GraphemeBreakProperty.txt -import "github.com/clipperhouse/uax29/v2/internal/iterators" - type property uint16 const ( @@ -27,7 +27,7 @@ const ( // lookup returns the trie value for the first UTF-8 encoding in s and // the width in bytes of this encoding. The size will be 0 if s does not // hold enough bytes to complete the encoding. len(s) must be greater than 0. -func lookup[T iterators.Stringish](s T) (v property, sz int) { +func lookup[T stringish.Interface](s T) (v property, sz int) { c0 := s[0] switch { case c0 < 0x80: // is ASCII diff --git a/vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go b/vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go index 17e9b550b555..e21348638011 100644 --- a/vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go +++ b/vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go @@ -1,14 +1,12 @@ package iterators -type Stringish interface { - []byte | string -} +import "github.com/clipperhouse/stringish" -type SplitFunc[T Stringish] func(T, bool) (int, T, error) +type SplitFunc[T stringish.Interface] func(T, bool) (int, T, error) // Iterator is a generic iterator for words that are either []byte or string. // Iterate while Next() is true, and access the word via Value(). -type Iterator[T Stringish] struct { +type Iterator[T stringish.Interface] struct { split SplitFunc[T] data T start int @@ -16,7 +14,7 @@ type Iterator[T Stringish] struct { } // New creates a new Iterator for the given data and SplitFunc. -func New[T Stringish](split SplitFunc[T], data T) *Iterator[T] { +func New[T stringish.Interface](split SplitFunc[T], data T) *Iterator[T] { return &Iterator[T]{ split: split, data: data, @@ -83,3 +81,20 @@ func (iter *Iterator[T]) Reset() { iter.start = 0 iter.pos = 0 } + +func (iter *Iterator[T]) First() T { + if len(iter.data) == 0 { + return iter.data + } + advance, _, err := iter.split(iter.data, true) + if err != nil { + panic(err) + } + if advance <= 0 { + panic("SplitFunc returned a zero or negative advance") + } + if advance > len(iter.data) { + panic("SplitFunc advanced beyond the end of the data") + } + return iter.data[:advance] +} diff --git a/vendor/github.com/itchyny/gojq/.dockerignore b/vendor/github.com/itchyny/gojq/.dockerignore new file mode 100644 index 000000000000..54095155ccfa --- /dev/null +++ b/vendor/github.com/itchyny/gojq/.dockerignore @@ -0,0 +1,16 @@ +/gojq +/goxz +/CREDITS +/y.output +*.exe +*.test +*.out +*.md +*.y +**/*.jq +**/*.json +**/*.yaml +**/*_test.go +.github +_gojq +_tools diff --git a/vendor/github.com/itchyny/gojq/.gitattributes b/vendor/github.com/itchyny/gojq/.gitattributes new file mode 100644 index 000000000000..797f39593968 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/.gitattributes @@ -0,0 +1,3 @@ +**/testdata/** binary +/builtin.go eol=lf linguist-generated=true +/parser.go eol=lf linguist-generated=true diff --git a/vendor/github.com/itchyny/gojq/.gitignore b/vendor/github.com/itchyny/gojq/.gitignore new file mode 100644 index 000000000000..bbeb991f8c3b --- /dev/null +++ b/vendor/github.com/itchyny/gojq/.gitignore @@ -0,0 +1,7 @@ +/gojq +/goxz +/CREDITS +/y.output +*.exe +*.test +*.out diff --git a/vendor/github.com/itchyny/gojq/CHANGELOG.md b/vendor/github.com/itchyny/gojq/CHANGELOG.md new file mode 100644 index 000000000000..04399180b620 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/CHANGELOG.md @@ -0,0 +1,418 @@ +# Changelog +## [v0.12.19](https://github.com/itchyny/gojq/compare/v0.12.18..v0.12.19) (2026-04-01) +* fix `gsub` and `sub` when the replacement emits multiple values +* fix `fmax`, `fmin`, `modf` functions against NaN and infinities +* fix `join/1` to use `add/0` implementation and handle null separator +* fix `del` and `delpaths` on null to emit null +* fix arithmetic operations on the minimum integer +* fix `significand` function against subnormal numbers +* fix handling of `--` in cli flag parsing for jq compatibility +* fix `flatten/1` to emit error when the depth is NaN +* fix array slice update to validate index types +* fix string repetition boundary check to match jq behavior +* implement `splits/2` using `match/2` for better jq compatibility +* implement `to_entries` and `from_entries` in jq for simplicity +* improve performance of regexp functions by caching compiled regexps + +## [v0.12.18](https://github.com/itchyny/gojq/compare/v0.12.17..v0.12.18) (2025-12-02) +* implement `trimstr/1`, `toboolean/0` function +* fix `last/1` to be included in `builtins/0` +* fix `--indent 0` to preserve newlines +* fix string repetition to emit error when the result is too large +* increase the array index limit to 536870912 (`2^29`) +* stop numeric normalization for concurrent execution (see 1ace748d08df) +* support binding expressions with binary operators (`1 + 2 as $x | -$x`) +* improve `gojq.NewIter` to be a generic function +* improve logic for getting file contents on JSON parse error +* improve JSON parsing to preserve the precision of floating-point numbers +* improve YAML parsing performance and preserve the precision of large integers +* improve performance and reduce memory allocation of long-running queries + +## [v0.12.17](https://github.com/itchyny/gojq/compare/v0.12.16..v0.12.17) (2024-12-01) +* implement `add/1`, `skip/2` functions +* implement `--library-path` option as the alias of `-L` option +* fix `reduce` syntax to emit results for each initial value +* fix `last/1` to yield no values when the argument yields no values +* fix `limit/2` to emit an error on negative count +* fix `@uri` and `@urid` formats not to convert space between plus sign +* fix resolving search paths of import statements in the query +* improve time functions to accept fewer element arrays + +## [v0.12.16](https://github.com/itchyny/gojq/compare/v0.12.15..v0.12.16) (2024-06-01) +* fix offset of query parsing error on multi-byte characters +* fix tests of `exp10` and `atan2` failing on some platforms +* fix `debug/1` to be available only when `debug/0` is defined +* improve parser to allow binary operators as object values +* improve compiler to emit error if query is missing + +## [v0.12.15](https://github.com/itchyny/gojq/compare/v0.12.14..v0.12.15) (2024-04-01) +* implement `ltrim`, `rtrim`, and `trim` functions +* implement `gojq.ParseError` for getting the offset and token of query parsing error +* implement `gojq.HaltError` for detecting halt errors and stopping outer iteration +* fix object construction with duplicate keys (`{x:0,y:1} | {a:.x,a:.y}`) +* fix `halt` and `halt_error` functions to stop the command execution immediately +* fix variable scope of binding syntax (`"a" as $v | def f: $v; "b" as $v | f`) +* fix pre-defined variables to be available in initial modules (`$ARGS` in `~/.jq`) +* fix `ltrimstr` and `rtrimstr` functions to emit error on non-string input +* fix `nearbyint` and `rint` functions to round ties to even +* improve parser to allow `reduce`, `foreach`, `if`, `try`-`catch` syntax as object values +* remove `pow10` in favor of `exp10`, define `scalbn` and `scalbln` by `ldexp` + +## [v0.12.14](https://github.com/itchyny/gojq/compare/v0.12.13..v0.12.14) (2023-12-01) +* implement `abs`, `pick`, and `debug/1` functions +* implement `--raw-output0` option, and remove `--nul-output` (`-0`) option +* fix string multiplication by zero to emit an empty string +* fix zero divided by zero to emit an error, not `nan` +* fix modulo operator to emit `nan` if either side is `nan` +* fix `implode` function to emit replacement characters on invalid code points +* fix `stderr` function to output strings in raw format +* fix `error` function to throw an error even for `null` +* fix `walk` function on multiple outputs arguments +* fix `--from-file` option to work with `--args` and `--jsonargs` options +* fix the default module search path `../lib` relative to the executable +* improve query parser to support comment continuation with backslash +* improve `modulemeta` function to include defined function names in the module +* improve search path of `import` and `include` directives to support `$ORIGIN` expansion +* remove deprecated `leaf_paths` function + +## [v0.12.13](https://github.com/itchyny/gojq/compare/v0.12.12..v0.12.13) (2023-06-01) +* implement `@urid` format string to decode URI values +* fix functions returning arrays not to emit nil slices (`flatten`, `group_by`, + `unique`, `unique_by`, `nth`, `indices`, `path`, and `modulemeta.deps`) + +## [v0.12.12](https://github.com/itchyny/gojq/compare/v0.12.11..v0.12.12) (2023-03-01) +* fix assignment operator (`=`) with overlapping paths and multiple values (`[[]] | .. = ..`) +* fix crash on multiplying large numbers to an empty string (`9223372036854775807 * ""`) +* improve zsh completion file + +## [v0.12.11](https://github.com/itchyny/gojq/compare/v0.12.10..v0.12.11) (2022-12-24) +* fix crash on assignment operator (`=`) with multiple values (`. = (0,0)`) +* fix `isnormal` and `normals` functions against subnormal numbers + +## [v0.12.10](https://github.com/itchyny/gojq/compare/v0.12.9..v0.12.10) (2022-12-01) +* fix `break` in `try`-`catch` query (`label $x | try break $x catch .`) +* fix path value validation for `getpath` function (`path(getpath([[0]][0]))`) +* fix path value validation for custom iterator functions +* fix `walk` function with argument emitting multiple values (`[1],{x:1} | walk(.,0)`) +* fix `@csv`, `@tsv`, `@sh` to escape the null character (`["\u0000"] | @csv,@tsv,@sh`) +* improve performance of assignment operator (`=`), update-assignment operator (`|=`), + `map_values`, `del`, `delpaths`, `walk`, `ascii_downcase`, and `ascii_upcase` functions + +## [v0.12.9](https://github.com/itchyny/gojq/compare/v0.12.8..v0.12.9) (2022-09-01) +* fix `fromjson` to emit error on unexpected trailing string +* fix path analyzer on variable argument evaluation (`def f($x): .y; path(f(.x))`) +* fix raw input option `--raw-input` (`-R`) to keep carriage returns and support 64KiB+ lines + +## [v0.12.8](https://github.com/itchyny/gojq/compare/v0.12.7..v0.12.8) (2022-06-01) +* implement `gojq.Compare` for comparing values in custom internal functions +* implement `gojq.TypeOf` for obtaining type name of values in custom internal functions +* implement `gojq.Preview` for previewing values for error messages of custom internal functions +* fix query lexer to parse string literals as JSON to support surrogate pairs (`"\ud83d\ude04"`) +* fix priority bug of declared and builtin functions (`def empty: .; null | select(.)`) +* fix string indexing by index out of bounds to emit `null` (`"abc" | .[3]`) +* fix array binding pattern not to match against strings (`"abc" as [$a] ?// $a | $a`) +* fix `sub` and `gsub` functions to emit results in the same order of jq +* fix `fromjson` to keep integer precision (`"10000000000000000" | fromjson + 1`) +* fix stream option to raise error against incomplete JSON input +* improve array updating index and string repetition to increase limitations +* improve `mktime` to support nanoseconds, just like `gmtime` and `now` +* improve query lexer to report unterminated string literals +* improve performance of string indexing and slicing by reducing allocations +* improve performance of object and array indexing, slicing, and iteration, + by validating path values by comparing data addresses. This change improves jq + compatibility of path value validation (`{} | {}.x = 0`, `[0] | [.[]][] = 1`). + Also optimize constant indexing and slicing by specialized instruction +* improve performance of `add` (on array of strings), `flatten`, `min`, `max`, + `sort`, `unique`, `join`, `to_entries`, `from_entries`, `indices`, `index`, + `rindex`, `startswith`, `endswith`, `ltrimstr`, `rtrimstr`, `explode`, + `capture`, `sub`, and `gsub` functions + +## [v0.12.7](https://github.com/itchyny/gojq/compare/v0.12.6..v0.12.7) (2022-03-01) +* fix precedence of try expression against operators (`try 0 * error(0)`) +* fix iterator suffix with optional operator (`0 | .x[]?`) +* fix stream option with slurp option or `input`, `inputs` functions +* fix the command flag parser to support equal sign in short options with argument +* fix string conversion of query including empty strings in module and import metadata +* improve performance of `isempty` function + +## [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 diff --git a/vendor/github.com/itchyny/gojq/Dockerfile b/vendor/github.com/itchyny/gojq/Dockerfile new file mode 100644 index 000000000000..4b469f49ab1f --- /dev/null +++ b/vendor/github.com/itchyny/gojq/Dockerfile @@ -0,0 +1,14 @@ +FROM golang:1.26 AS builder + +WORKDIR /app +COPY go.* ./ +RUN go mod download +COPY . . +ENV CGO_ENABLED=0 +RUN make build + +FROM gcr.io/distroless/static:debug + +COPY --from=builder /app/gojq / +ENTRYPOINT ["/gojq"] +CMD ["--help"] diff --git a/vendor/github.com/itchyny/gojq/LICENSE b/vendor/github.com/itchyny/gojq/LICENSE new file mode 100644 index 000000000000..6b4faf3b270e --- /dev/null +++ b/vendor/github.com/itchyny/gojq/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019-2026 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. diff --git a/vendor/github.com/itchyny/gojq/Makefile b/vendor/github.com/itchyny/gojq/Makefile new file mode 100644 index 000000000000..c92c0dbd81df --- /dev/null +++ b/vendor/github.com/itchyny/gojq/Makefile @@ -0,0 +1,102 @@ +BIN := 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 gojq_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) ./cmd/$(BIN) + +.PHONY: install-dev +install-dev: parser.go builtin.go + go install -ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) + +.PHONY: install-debug +install-debug: parser.go builtin.go + go install -tags gojq_debug -ldflags=$(BUILD_LDFLAGS) ./cmd/$(BIN) + +.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) \ + -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 -checks all -tags gojq_debug ./... + +$(GOBIN)/staticcheck: + go install honnef.co/go/tools/cmd/staticcheck@v0.6.1 + +.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: + go get -u ./... && go get github.com/itchyny/go-yaml@gojq && go mod tidy + rm go.dev.sum && go get -u -modfile=go.dev.mod github.com/itchyny/{astgen,timefmt}-go && go generate + +.PHONY: bump +bump: $(GOBIN)/gobump + test -z "$$(git status --porcelain || echo .)" + test "$$(git branch --show-current)" = "main" + @gobump up -w "$(VERSION_PATH)" + git commit -am "bump up version to $(VERSION)" + git tag "v$(VERSION)" + git push --atomic origin main tag "v$(VERSION)" diff --git a/vendor/github.com/itchyny/gojq/README.md b/vendor/github.com/itchyny/gojq/README.md new file mode 100644 index 000000000000..6f6aa161291a --- /dev/null +++ b/vendor/github.com/itchyny/gojq/README.md @@ -0,0 +1,164 @@ +# gojq +[![CI Status](https://github.com/itchyny/gojq/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/itchyny/gojq/actions?query=branch:main) +[![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/jqlang/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 +``` + +### mise +```sh +mise use -g gojq@latest +``` + +### 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 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. +- gojq supports arbitrary-precision integer calculation while jq does not; jq loses the precision of large integers when calculation is involved. Note that even with gojq, all mathematical functions, including `floor` and `round`, convert integers to floating-point numbers; only addition, subtraction, multiplication, modulo, and division operators (when divisible) keep the integer precision. To calculate floor division of integers without losing the precision, use `def idivide($n): (. - . % $n) / $n;`. To round down floating-point numbers to integers, use `def ifloor: floor | tostring | tonumber;`, but note that this function does not work with large floating-point numbers and also loses the precision of large integers. +- gojq behaves differently than jq in some features, expecting jq to fix its behavior in the future. gojq supports string indexing; `"abcde"[2]` ([jq#1520](https://github.com/jqlang/jq/issues/1520)). gojq fixes handling files with no newline characters at the end ([jq#2374](https://github.com/jqlang/jq/issues/2374)). gojq fixes `@base64d` to allow binary string as the decoded string ([jq#1931](https://github.com/jqlang/jq/issues/1931)). gojq improves time formatting and parsing; deals with `%f` in `strftime` and `strptime` ([jq#1409](https://github.com/jqlang/jq/issues/1409)), parses timezone offsets with `fromdate` and `fromdateiso8601` ([jq#1053](https://github.com/jqlang/jq/issues/1053)), supports timezone name/offset with `%Z`/`%z` in `strptime` ([jq#929](https://github.com/jqlang/jq/issues/929), [jq#2195](https://github.com/jqlang/jq/issues/2195)). gojq supports nanoseconds in date and time functions. +- 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). gojq does not support some flags; `--ascii-output, -a` (performance issue), `--seq` (not used commonly), `--sort-keys, -S` (sorts by default because `map[string]any` does not keep the order), `--unbuffered` (unbuffered by default). gojq does not parse JSON extensions supported by jq; `NaN`, `Infinity`, and `[000]`. gojq does not support some regular expression metacharacters, backreferences, look-around assertions, and some flags (regular expression engine differences). gojq does not support BOM (`encoding/json` does not support this). gojq disallows using keywords for function names (`def true: .; true` is a confusing query), and module name prefixes in function declarations (using module prefixes like `def m::f: .;` is undocumented). +- 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/itchyny/gojq" +) + +func main() { + query, err := gojq.Parse(".foo | ..") + if err != nil { + log.Fatalln(err) + } + input := map[string]any{"foo": []any{1, 2, 3}} + iter := query.Run(input) // or query.RunWithContext + for { + v, ok := iter.Next() + if !ok { + break + } + if err, ok := v.(error); ok { + if err, ok := err.(*gojq.HaltError); ok && err.Value() == nil { + break + } + 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. + - Use [`gojq.ParseError`](https://pkg.go.dev/github.com/itchyny/gojq#ParseError) to get the error position and token of the parsing error. +- 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 `[]any` for an array and `map[string]any` for a map (just like decoded to an `any` 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 `any` and use it as the query input. +- Thirdly, iterate through the results using [`iter.Next() (any, 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 `(any, error)` because the iterator may emit multiple errors. The `jq` and `gojq` commands stop the iteration on the first error, but the library user can choose to stop the iteration on errors, or to continue until it terminates. + - In any case, it is recommended to stop the iteration on [`gojq.HaltError`](https://pkg.go.dev/github.com/itchyny/gojq#HaltError), which is emitted by `halt` and `halt_error` functions, although these functions are rarely used. + The error implements [`gojq.ValueError`](https://pkg.go.dev/github.com/itchyny/gojq#ValueError), and if the error value is `nil`, stop the iteration without handling the error. + Technically speaking, we can fix the iterator to terminate on the halting error, but it does not terminate at the moment. + The `halt` function in jq not only stops the iteration, but also terminates the command execution, even if there are still input values. + So, gojq leaves it up to the library user how to handle the halting error. + - 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 () + +## License +This software is released under the MIT License, see LICENSE. diff --git a/vendor/github.com/itchyny/gojq/_gojq b/vendor/github.com/itchyny/gojq/_gojq new file mode 100644 index 000000000000..2dc00336952a --- /dev/null +++ b/vendor/github.com/itchyny/gojq/_gojq @@ -0,0 +1,43 @@ +#compdef gojq + +_gojq() +{ + _arguments -s -S \ + '(-r --raw-output --raw-output0 -j --join-output)'{-r,--raw-output}'[output raw strings]' \ + '(-r --raw-output -j --join-output)--raw-output0[implies -r with NUL character delimiter]' \ + '(-r --raw-output --raw-output0 -j --join-output)'{-j,--join-output}'[implies -r with no newline delimiter]' \ + '(-c --compact-output --indent --tab --yaml-output)'{-c,--compact-output}'[output without pretty-printing]' \ + '(-c --compact-output --tab --yaml-output)--indent[number of spaces for indentation]:indentation count:(2 4 8)' \ + '(-c --compact-output --indent --yaml-output)--tab[use tabs for indentation]' \ + '(-c --compact-output --indent --tab )--yaml-output[output in YAML format]' \ + '(-C --color-output -M --monochrome-output)'{-C,--color-output}'[output with colors even if piped]' \ + '(-C --color-output -M --monochrome-output)'{-M,--monochrome-output}'[output without colors]' \ + '(-n --null-input)'{-n,--null-input}'[use null as input value]' \ + '(-R --raw-input --stream --yaml-input)'{-R,--raw-input}'[read input as raw strings]' \ + '(-R --raw-input --yaml-input)--stream[parse input in stream fashion]' \ + '(-R --raw-input --stream )--yaml-input[read input as YAML format]' \ + '(-s --slurp)'{-s,--slurp}'[read all inputs into an array]' \ + '(-f --from-file 1)'{-f,--from-file}'[load query from file]:filename of jq query:_files' \ + '*'{-L,--library-path}'[directory to search modules from]:module directory:_directories' \ + '*--arg[set a string value to a variable]:variable name: :string value' \ + '*--argjson[set a JSON value to a variable]:variable name: :JSON value' \ + '*--slurpfile[set the JSON contents of a file to a variable]:variable name: :JSON file:_files' \ + '*--rawfile[set the contents of a file to a variable]:variable name: :file:_files' \ + '*--args[consume remaining arguments as positional string values]' \ + '*--jsonargs[consume remaining arguments as positional JSON values]' \ + '(-e --exit-status)'{-e,--exit-status}'[exit 1 when the last value is false or null]' \ + '(- 1 *)'{-v,--version}'[display version information]' \ + '(- 1 *)'{-h,--help}'[display help information]' \ + '1: :_guard "^-([[:alpha:]0]#|-*)" "jq query"' \ + '*: :_gojq_args' +} + +_gojq_args() { + if (($words[(I)--args] > $words[(I)--jsonargs])); then + _message 'string value' + elif (($words[(I)--args] < $words[(I)--jsonargs])); then + _message 'JSON value' + else + _arguments '*:input file:_files' + fi +} diff --git a/vendor/github.com/itchyny/gojq/builtin.go b/vendor/github.com/itchyny/gojq/builtin.go new file mode 100644 index 000000000000..c518fe8f6835 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/builtin.go @@ -0,0 +1,73 @@ +// Code generated by _tools/gen_builtin.go; DO NOT EDIT. + +package gojq + +func init() { + builtinFuncDefs = map[string][]*FuncDef{ + "IN": {{Name: "IN", Args: []string{"s"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "s"}}}, Right: &Query{Term: &Term{Type: TermTypeIdentity}}, Op: OpEq}, {Term: &Term{Type: TermTypeIdentity}}}}}}}, {Name: "IN", Args: []string{"src", "s"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "src"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "s"}}}, Op: OpEq}, {Term: &Term{Type: TermTypeIdentity}}}}}}}}, + "INDEX": {{Name: "INDEX", Args: []string{"stream", "idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Query: &Query{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{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$row"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "idx_expr"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "tostring"}}}, Op: OpPipe}, Op: OpPipe}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$row"}}}, Op: OpAssign}}}}}, {Name: "INDEX", Args: []string{"idx_expr"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "INDEX", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "idx_expr"}}}}}}}}}, + "JOIN": {{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{{Iter: true}}}}, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "idx_expr"}}}}}}}}, Op: OpComma}}}}, Op: OpPipe}}}}}, {Name: "JOIN", Args: []string{"$idx", "stream", "idx_expr"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "stream"}}}, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "idx_expr"}}}}}}}}, Op: OpComma}}}}, Op: OpPipe}}, {Name: "JOIN", Args: []string{"$idx", "stream", "idx_expr", "join_expr"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "stream"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$idx"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "idx_expr"}}}}}}}}, Op: OpComma}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "join_expr"}}}, Op: OpPipe}, Op: OpPipe}}}, + "_assign": {}, + "_last": {}, + "_modify": {}, + "add": {{Name: "add", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "add"}}}, Op: OpPipe}}}, + "all": {{Name: "all", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "all", Args: []*Query{{Term: &Term{Type: TermTypeIdentity}}}}}}}, {Name: "all", Args: []string{"y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "all", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "y"}}}}}}}}, {Name: "all", Args: []string{"g", "y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isempty", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "y"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "not"}}}, Op: OpPipe}}}}}, Op: OpPipe}}}}}}}, + "any": {{Name: "any", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Term: &Term{Type: TermTypeIdentity}}}}}}}, {Name: "any", Args: []string{"y"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "any", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "y"}}}}}}}}, {Name: "any", Args: []string{"g", "y"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isempty", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "y"}}}}}}}, Op: OpPipe}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "not"}}}, Op: OpPipe}}}, + "arrays": {{Name: "arrays", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}, Op: OpEq}}}}}}}, + "booleans": {{Name: "booleans", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "boolean"}}}, Op: OpEq}}}}}}}, + "capture": {{Name: "capture", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "capture", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "capture", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_captures"}}}, Op: OpPipe}, Op: OpPipe}}}, + "combinations": {{Name: "combinations", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "length"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}, Else: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}, SuffixList: []*Suffix{{Iter: true}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}}}}}}, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, IsSlice: true}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "combinations"}}}, Op: OpPipe}}}, Op: OpAdd}, Patterns: []*Pattern{{Name: "$x"}}, Op: OpPipe}}}}}, {Name: "combinations", Args: []string{"n"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "limit", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "n"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "repeat", Args: []*Query{{Term: &Term{Type: TermTypeIdentity}}}}}}}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "combinations"}}}, Op: OpPipe}}}, + "del": {{Name: "del", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "delpaths", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}}}}}}}}}}, + "finites": {{Name: "finites", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isfinite"}}}}}}}}}, + "first": {{Name: "first", Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}}, {Name: "first", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}, Op: OpComma}, Op: OpPipe}}}}}}, + "from_entries": {{Name: "from_entries", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{KeyQuery: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "key"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Key"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "name"}}}, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "Name"}}}, Op: OpAlt}, Op: OpAlt}, Op: OpAlt}, Val: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "has", Args: []*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"}}}}}}}}}}}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "add"}}}, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{}}}, Op: OpAlt}, Op: OpPipe}}}, + "fromdate": {{Name: "fromdate", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "fromdateiso8601"}}}}}, + "fromdateiso8601": {{Name: "fromdateiso8601", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strptime", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%S%z"}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "mktime"}}}, Op: OpPipe}}}, + "fromstream": {{Name: "fromstream", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Pattern: &Pattern{Name: "$pv"}, Start: &Query{Term: &Term{Type: TermTypeNull}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "e"}}}, Then: &Query{Term: &Term{Type: TermTypeNull}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$pv"}}}, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$pv"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "length"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "2"}}, Op: OpEq}, Op: OpPipe}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "v"}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Op: OpAdd}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$v"}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "length"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpEq}, Op: OpPipe}}}}}, Op: OpPipe}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "e"}}}}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "length"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, Op: OpEq}, Op: OpPipe}}}}}}}}, Patterns: []*Pattern{{Array: []*Pattern{{Name: "$p"}, {Name: "$v"}}}}, Op: OpPipe}, Op: OpPipe}, 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: "v"}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}}}}}}}, + "group_by": {{Name: "group_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_group_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}}}}}}}}}}, + "gsub": {{Name: "gsub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, {Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}}}}}}, {Name: "gsub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}, Op: OpAdd}}}}}}}, + "in": {{Name: "in", Args: []string{"xs"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "xs"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "has", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$x"}}}}}}}, Op: OpPipe}, Patterns: []*Pattern{{Name: "$x"}}, Op: OpPipe}}}, + "inputs": {{Name: "inputs", Body: &Query{Term: &Term{Type: TermTypeTry, Try: &Try{Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "repeat", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "input"}}}}}}}, Catch: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "break"}}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error"}}}}}}}}}}}, + "isempty": {{Name: "isempty", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFalse}}, Right: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}, Op: OpComma}, Op: OpPipe}}}, Right: &Query{Term: &Term{Type: TermTypeTrue}}, Op: OpComma}}}}}}, + "iterables": {{Name: "iterables", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}, Op: OpEq}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}, Op: OpEq}, Op: OpOr}, Op: OpPipe}}}}}}}, + "last": {{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"}}}}}}}}, {Name: "last", Args: []string{"g"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_last", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}}}}}}}, + "limit": {{Name: "limit", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpGt}, Then: &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{Ident: "$out", Body: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, Op: OpSub}, Extract: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$item"}}}, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpLe}, Then: &Query{Term: &Term{Type: TermTypeBreak, Break: "$out"}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}}, Op: OpComma}}}}}}}, Elif: []*IfElif{{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "limit doesn't support negative count"}}}}}}}}}}}}, + "map": {{Name: "map", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Op: OpPipe}}}}}}, + "map_values": {{Name: "map_values", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Op: OpModify}}}, + "match": {{Name: "match", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "match", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}, {Term: &Term{Type: TermTypeFalse}}}}, SuffixList: []*Suffix{{Iter: true}}}}}}, + "max_by": {{Name: "max_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_max_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}}}}}}}}}}, + "min_by": {{Name: "min_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_min_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}}}}}}}}}}, + "normals": {{Name: "normals", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "isnormal"}}}}}}}}}, + "not": {{Name: "not", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeIdentity}}, Then: &Query{Term: &Term{Type: TermTypeFalse}}, Else: &Query{Term: &Term{Type: TermTypeTrue}}}}}}}, + "nth": {{Name: "nth", Args: []string{"$n"}, Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}}}}}, {Name: "nth", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpGe}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "first", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "skip", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}}}}}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "nth doesn't support negative index"}}}}}}}}}}}}, + "nulls": {{Name: "nulls", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeNull}}, Op: OpEq}}}}}}}, + "numbers": {{Name: "numbers", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "number"}}}, Op: OpEq}}}}}}}, + "objects": {{Name: "objects", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}, Op: OpEq}}}}}}}, + "paths": {{Name: "paths", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Term: &Term{Type: TermTypeRecurse}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}, Op: OpNe}}}}}, Op: OpPipe}}, {Name: "paths", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeRecurse}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}, Op: OpPipe}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}, Op: OpNe}}}}}, Op: OpPipe}}}, + "pick": {{Name: "pick", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}, Pattern: &Pattern{Name: "$p"}, Start: &Query{Term: &Term{Type: TermTypeNull}}, Update: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "setpath", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$v"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}}}}}, Op: OpPipe}}}}}}}}, Patterns: []*Pattern{{Name: "$v"}}, Op: OpPipe}}}, + "range": {{Name: "range", Args: []string{"$end"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{{Term: &Term{Type: TermTypeNumber, Number: "0"}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$end"}}}, {Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, {Name: "range", Args: []string{"$start", "$end"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$start"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$end"}}}, {Term: &Term{Type: TermTypeNumber, Number: "1"}}}}}}}, {Name: "range", Args: []string{"$start", "$end", "$step"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_range", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$start"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$end"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$step"}}}}}}}}}, + "recurse": {{Name: "recurse", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "recurse", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}, {Optional: true}}}}}}}}}, {Name: "recurse", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "r", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "r"}}}, Op: OpPipe}}}, Op: OpComma}}}, Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "r"}}}}, {Name: "recurse", Args: []string{"f", "cond"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "r", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "cond"}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "r"}}}, Op: OpPipe}, Op: OpPipe}}}, Op: OpComma}}}, Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "r"}}}}}, + "repeat": {{Name: "repeat", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "_repeat", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_repeat"}}}, Op: OpComma}}}, Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_repeat"}}}}}, + "scalars": {{Name: "scalars", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}, Op: OpNe}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}, Op: OpNe}, Op: OpAnd}, Op: OpPipe}}}}}}}, + "scan": {{Name: "scan", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "scan", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "scan", Args: []string{"$re", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}, Op: OpAdd}}}}}, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}}}, Right: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "string"}}}, Else: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "captures"}, SuffixList: []*Suffix{{Iter: true}, {Index: &Index{Name: "string"}}}}}}}}}}}, Op: OpPipe}}}, + "select": {{Name: "select", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Then: &Query{Term: &Term{Type: TermTypeIdentity}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}}}}, + "skip": {{Name: "skip", Args: []string{"$n", "g"}, Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpGt}, Then: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}, Pattern: &Pattern{Name: "$item"}, Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, Op: OpSub}, Extract: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpLt}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$item"}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}}}}}, Elif: []*IfElif{{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "g"}}}}}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "error", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "skip doesn't support negative count"}}}}}}}}}}}}, + "sort_by": {{Name: "sort_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_sort_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}}}}}}}}}}, + "split": {{Name: "split", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "splits", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}}}}}}}}}}, + "splits": {{Name: "splits", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeForeach, Foreach: &Foreach{Query: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "g"}}}, Op: OpAdd}}}}}, Right: &Query{Term: &Term{Type: TermTypeNull}}, Op: OpComma}}}, Pattern: &Pattern{Object: []*PatternObject{{Key: "$offset"}, {Key: "$length"}}}, Start: &Query{Term: &Term{Type: TermTypeNull}}, Update: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "start", Val: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "next"}}}}, {Key: "end", Val: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$offset"}}}}, {Key: "next", Val: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$offset"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$length"}}}, Op: OpAdd}}}}}}}}}}}}}, {Name: "splits", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "splits", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}}, + "strings": {{Name: "strings", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "string"}}}, Op: OpEq}}}}}}}, + "sub": {{Name: "sub", Args: []string{"$re", "str"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "sub", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "sub", Args: []string{"$re", "str", "$flags"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}}}}}, Pattern: &Pattern{Object: []*PatternObject{{Key: "$offset"}, {Key: "$length"}, {Key: "$captures"}}}, Start: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "s", Val: &Query{Term: &Term{Type: TermTypeIdentity}}}, {Key: "r", Val: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{}}}}}}}}, Update: &Query{Left: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Query: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$captures"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_captures"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "str"}}}, Op: OpPipe}, Op: OpPipe}}}, Pattern: &Pattern{Name: "$s"}, Start: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "i"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}, Op: OpAssign}, Update: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "r"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "i"}}}}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "s"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "next"}}}, End: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$offset"}}}, IsSlice: true}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$s"}}}, Op: OpAdd}, Op: OpUpdateAdd}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "i"}}}, Right: &Query{Term: &Term{Type: TermTypeNumber, Number: "1"}}, Op: OpUpdateAdd}, Op: OpPipe}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "next"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$offset"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$length"}}}, Op: OpAdd}, Op: OpAssign}, Op: OpPipe}}}}, Right: &Query{Left: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "r"}, SuffixList: []*Suffix{{Iter: true}}}}, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "s"}, SuffixList: []*Suffix{{Index: &Index{Start: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "next"}}}, IsSlice: true}}}}}, Op: OpAdd}, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Name: "s"}}}, Op: OpAlt}, Op: OpPipe}}}, + "test": {{Name: "test", Args: []string{"$re"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "test", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeNull}}}}}}}, {Name: "test", Args: []string{"$re", "$flags"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_match", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$re"}}}, {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$flags"}}}, {Term: &Term{Type: TermTypeTrue}}}}}}}}, + "to_entries": {{Name: "to_entries", Body: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "keys"}, SuffixList: []*Suffix{{Iter: true}}}}, Right: &Query{Term: &Term{Type: TermTypeObject, Object: &Object{KeyVals: []*ObjectKeyVal{{Key: "key", Val: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$k"}}}}, {Key: "value", Val: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$k"}}}}}}}}}}}, Patterns: []*Pattern{{Name: "$k"}}, Op: OpPipe}}}}}}, + "todate": {{Name: "todate", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "todateiso8601"}}}}}, + "todateiso8601": {{Name: "todateiso8601", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "strftime", Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: "%Y-%m-%dT%H:%M:%SZ"}}}}}}}}}, + "tostream": {{Name: "tostream", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{FuncDefs: []*FuncDef{{Name: "r", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}, {Optional: true}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "r"}}}, Op: OpPipe}}}, Right: &Query{Term: &Term{Type: TermTypeIdentity}}, Op: OpComma}}}, Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "r"}}}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "getpath", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeReduce, Reduce: &Reduce{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "path", Args: []*Query{{Term: &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}, {Optional: true}}}}}}}}, Pattern: &Pattern{Name: "$q"}, Start: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Right: &Query{Term: &Term{Type: TermTypeIdentity}}, Op: OpComma}}}}, Update: &Query{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$p"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$q"}}}, Op: OpAdd}}}}}}}, Op: OpPipe}, Patterns: []*Pattern{{Name: "$p"}}, Op: OpPipe}}}, + "truncate_stream": {{Name: "truncate_stream", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeNull}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Right: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "length"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, Op: OpGt}, Op: OpPipe}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeNumber, Number: "0"}}}}}, Right: &Query{Term: &Term{Type: TermTypeIndex, Index: &Index{Start: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "$n"}}}, IsSlice: true}}}, Op: OpModify}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}}, Op: OpPipe}, Op: OpPipe}, Patterns: []*Pattern{{Name: "$n"}}, Op: OpPipe}}}, + "unique_by": {{Name: "unique_by", Args: []string{"f"}, Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_unique_by", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeArray, Array: &Array{Query: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}}}}}}}}}}, + "until": {{Name: "until", Args: []string{"cond", "next"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "_until", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "cond"}}}, Then: &Query{Term: &Term{Type: TermTypeIdentity}}, Else: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "next"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_until"}}}, Op: OpPipe}}}}}}, Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_until"}}}}}, + "values": {{Name: "values", Body: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "select", Args: []*Query{{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeNull}}, Op: OpNe}}}}}}}, + "walk": {{Name: "walk", Args: []string{"f"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "_walk", Body: &Query{Left: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "array"}}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_walk"}}}}}}}, Elif: []*IfElif{{Cond: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "type"}}}, Right: &Query{Term: &Term{Type: TermTypeString, Str: &String{Str: "object"}}}, Op: OpEq}, Then: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map_values", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_walk"}}}}}}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}, Op: OpPipe}}}, Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_walk"}}}}}, + "while": {{Name: "while", Args: []string{"cond", "update"}, Body: &Query{FuncDefs: []*FuncDef{{Name: "_while", Body: &Query{Term: &Term{Type: TermTypeIf, If: &If{Cond: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "cond"}}}, Then: &Query{Left: &Query{Term: &Term{Type: TermTypeIdentity}}, Right: &Query{Term: &Term{Type: TermTypeQuery, Query: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "update"}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_while"}}}, Op: OpPipe}}}, Op: OpComma}, Else: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "empty"}}}}}}}}, Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "_while"}}}}}, + "with_entries": {{Name: "with_entries", Args: []string{"f"}, Body: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "to_entries"}}}, Right: &Query{Left: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "map", Args: []*Query{{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "f"}}}}}}}, Right: &Query{Term: &Term{Type: TermTypeFunc, Func: &Func{Name: "from_entries"}}}, Op: OpPipe}, Op: OpPipe}}}, + } +} diff --git a/vendor/github.com/itchyny/gojq/builtin.jq b/vendor/github.com/itchyny/gojq/builtin.jq new file mode 100644 index 000000000000..5feb25ccfdc1 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/builtin.jq @@ -0,0 +1,188 @@ +def map(f): [.[] | f]; +def add(f): [f] | add; +def map_values(f): .[] |= f; +def in(xs): . as $x | xs | has($x); +def not: if . then false else true end; +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 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 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 min_by(f): _min_by(map([f])); +def max_by(f): _max_by(map([f])); +def sort_by(f): _sort_by(map([f])); +def group_by(f): _group_by(map([f])); +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 combinations: + if length == 0 then + [] + else + .[0][] as $x | [$x] + (.[1:] | combinations) + end; +def combinations(n): [limit(n; repeat(.))] | combinations; + +def walk(f): + def _walk: + if type == "array" then + map(_walk) + elif type == "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): _last(g); +def isempty(g): label $out | (g | false, break $out), true; +def all: all(.); +def all(y): all(.[]; y); +def all(g; y): isempty(g | select(y | not)); +def any: any(.); +def any(y): any(.[]; y); +def any(g; y): isempty(g | select(y)) | 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 + error("limit doesn't support negative count") + end; +def skip($n; g): + if $n > 0 then + foreach g as $item ( + $n; + . - 1; + if . < 0 then $item else empty end + ) + elif $n == 0 then + g + else + error("skip doesn't support negative count") + end; +def nth($n): .[$n]; +def nth($n; g): + if $n >= 0 then + first(skip($n; g)) + else + error("nth doesn't support negative index") + end; + +def truncate_stream(f): + . as $n | null | f | + if .[0] | length > $n then .[0] |= .[$n:] else empty end; +def fromstream(f): + foreach f as $pv ( + null; + if .e then null end | + $pv as [$p, $v] | + if $pv | length == 2 then + setpath(["v"] + $p; $v) | + setpath(["e"]; $p | length == 0) + else + setpath(["e"]; $p | length == 1) + end; + if .e then .v else empty end + ); +def tostream: + path(def r: (.[]? | r), .; r) as $p | + getpath($p) | + reduce path(.[]?) as $q ([$p, .]; [$p + $q]); + +def del(f): delpaths([path(f)]); +def paths: path(..) | select(. != []); +def paths(f): path(.. | select(f)) | select(. != []); +def pick(f): . as $v | + reduce path(f) as $p (null; setpath($p; $v | getpath($p))); + +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 | _captures; +def scan($re): scan($re; null); +def scan($re; $flags): + match($re; $flags + "g") | + if .captures == [] then + .string + else + [.captures[].string] + end; +def splits($re; $flags): + .[foreach (match($re; $flags + "g"), null) as {$offset, $length} + (null; {start: .next, end: $offset, next: $offset + $length})]; +def splits($re): splits($re; null); +def split($re; $flags): [splits($re; $flags)]; +def sub($re; str): sub($re; str; null); +def sub($re; str; $flags): + reduce match($re; $flags) as {$offset, $length, $captures} + ({s: ., r: []}; + reduce ($captures | _captures | str) as $s + (.i = 0; .r[.i] += .s[.next:$offset] + $s | .i += 1) | + .next = $offset + $length) | .r[] + .s[.next:] // .s; +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; .); diff --git a/vendor/github.com/itchyny/gojq/code.go b/vendor/github.com/itchyny/gojq/code.go new file mode 100644 index 000000000000..33505bde0544 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/code.go @@ -0,0 +1,108 @@ +package gojq + +type code struct { + v any + 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 + opindex + opindexarray + opcall + opcallrec + oppushpc + opcallpc + opscope + opret + opiter + 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 opindex: + return "index" + case opindexarray: + return "indexarray" + 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 opiter: + return "iter" + case opexpbegin: + return "expbegin" + case opexpend: + return "expend" + case oppathbegin: + return "pathbegin" + case oppathend: + return "pathend" + default: + panic(op) + } +} diff --git a/vendor/github.com/itchyny/gojq/compare.go b/vendor/github.com/itchyny/gojq/compare.go new file mode 100644 index 000000000000..d62399277e28 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/compare.go @@ -0,0 +1,76 @@ +package gojq + +import ( + "cmp" + "encoding/json" + "math" + "math/big" +) + +// Compare l and r, and returns jq-flavored comparison value. +// The result will be 0 if l == r, -1 if l < r, and +1 if l > r. +// This comparison is used by built-in operators and functions. +func Compare(l, r any) int { + return binopTypeSwitch(l, r, + cmp.Compare, + func(l, r float64) int { + switch { + case lt(l, r): + return -1 + case l == r: + return 0 + default: + return 1 + } + }, + (*big.Int).Cmp, + cmp.Compare, + func(l, r []any) int { + for i := range min(len(l), len(r)) { + if cmp := Compare(l[i], r[i]); cmp != 0 { + return cmp + } + } + return cmp.Compare(len(l), len(r)) + }, + func(l, r map[string]any) int { + lk, rk := funcKeys(l), funcKeys(r) + if cmp := Compare(lk, rk); cmp != 0 { + return cmp + } + for _, k := range lk.([]any) { + if cmp := Compare(l[k.(string)], r[k.(string)]); cmp != 0 { + return cmp + } + } + return 0 + }, + func(l, r any) int { + return cmp.Compare(typeIndex(l), typeIndex(r)) + }, + ) +} + +func lt(l, r float64) bool { + return l < r || math.IsNaN(l) +} + +func typeIndex(v any) int { + switch v := v.(type) { + default: + return 0 + case bool: + if !v { + return 1 + } + return 2 + case int, float64, *big.Int, json.Number: + return 3 + case string: + return 4 + case []any: + return 5 + case map[string]any: + return 6 + } +} diff --git a/vendor/github.com/itchyny/gojq/compiler.go b/vendor/github.com/itchyny/gojq/compiler.go new file mode 100644 index 000000000000..a86ea7aab5b3 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/compiler.go @@ -0,0 +1,1715 @@ +package gojq + +import ( + "context" + "errors" + "fmt" + "sort" + "strconv" + "strings" + "sync" +) + +type compiler struct { + moduleLoader ModuleLoader + environLoader func() []string + variables []string + customFuncs map[string]function + inputIter Iter + codes []*code + codeinfos []codeinfo + builtinScope *scopeinfo + scopes []*scopeinfo + scopecnt int + regexpCache sync.Map +} + +// 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 in goroutines, to reuse a compiled [*Code]. +func (c *Code) Run(v any, values ...any) Iter { + return c.RunWithContext(context.Background(), v, values...) +} + +// RunWithContext runs the code with context. +func (c *Code) RunWithContext(ctx context.Context, v any, values ...any) Iter { + if len(values) > len(c.variables) { + return NewIter(&tooManyVariableValuesError{}) + } else if len(values) < len(c.variables) { + return NewIter(&expectedVariableError{c.variables[len(values)]}) + } + return newEnv(ctx).execute(c, v, values...) +} + +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) + } + c.builtinScope = c.newScope() + scope := c.newScope() + c.scopes = []*scopeinfo{scope} + setscope := c.lazy(func() *code { + return &code{op: opscope, v: [3]int{scope.id, scope.variablecnt, 0}} + }) + for _, name := range c.variables { + if !newLexer(name).validVarName() { + return nil, &variableNameError{name} + } + c.appendCodeInfo(name) + c.append(&code{op: opstore, v: c.pushVariable(name)}) + } + 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 _, 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 any + if moduleLoader, ok := c.moduleLoader.(interface { + LoadJSONWithMeta(string, map[string]any) (any, error) + }); ok { + if vals, err = moduleLoader.LoadJSONWithMeta(path, i.Meta.ToValue()); err != nil { + return err + } + } else if moduleLoader, ok := c.moduleLoader.(interface { + LoadJSON(string) (any, error) + }); ok { + if vals, err = moduleLoader.LoadJSON(path); err != nil { + return err + } + } else { + return fmt.Errorf("module not found: %q", path) + } + 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]any) (*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) + if err = c.compileModule(q, alias); err != nil { + return err + } + c.appendCodeInfo("end of module " + path) + return nil +} + +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) lookupBuiltin(name string, argcnt int) *funcinfo { + s := c.builtinScope + for i := len(s.funcs) - 1; i >= 0; i-- { + if f := s.funcs[i]; f.name == name && f.argcnt == argcnt { + return f + } + } + return nil +} + +func (c *compiler) appendBuiltin(name string, argcnt int) func() { + setjump := c.lazy(func() *code { + return &code{op: opjump, v: len(c.codes)} + }) + c.appendCodeInfo(name) + c.builtinScope.funcs = append( + c.builtinScope.funcs, + &funcinfo{name, len(c.codes), argcnt}, + ) + return func() { + setjump() + c.appendCodeInfo("end of " + name) + } +} + +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.builtinScope + } else { + scope = c.scopes[len(c.scopes)-1] + } + defer c.lazy(func() *code { + return &code{op: opjump, v: len(c.codes)} + })() + c.appendCodeInfo(e.Name) + scope.funcs = append(scope.funcs, &funcinfo{e.Name, len(c.codes), 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.builtinScope, 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: opexpbegin}) + 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: opexpend}) + } + c.append(&code{op: opload, v: v}) + } + if err := c.compile(e.Body); err != nil { + return err + } + c.appendCodeInfo("end of " + e.Name) + return nil +} + +func (c *compiler) compileQuery(e *Query) error { + for _, fd := range e.FuncDefs { + if err := c.compileFuncDef(fd, false); err != nil { + return err + } + } + if e.Term != nil { + return c.compileTerm(e.Term) + } + switch e.Op { + case Operator(0): + return errors.New(`missing query (try ".")`) + case OpPipe: + if len(e.Patterns) > 0 { + return c.compileBind(e.Left, e.Right, e.Patterns) + } + 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: len(c.codes)} + }) + if err := c.compileQuery(l); err != nil { + return err + } + defer c.lazy(func() *code { + return &code{op: opjump, v: len(c.codes)} + })() + setfork() + 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: len(c.codes)} // opload found + }) + if err := c.compileQuery(l); err != nil { + return err + } + c.append(&code{op: opdup}) + c.append(&code{op: opjumpifnot, v: len(c.codes) + 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: len(c.codes)} + })() + c.append(&code{op: oppop}) + c.append(&code{op: opbacktrack}) + setfork() + c.append(&code{op: opload, v: found}) + c.append(&code{op: opjumpifnot, v: len(c.codes) + 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: + // optimize assignment operator with constant indexing and slicing + // .foo.[0].[1:2] = f => setpath(["foo",0,{"start":1,"end":2}]; f) + if xs := l.toIndices(nil); 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]any{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}}, + {Term: &Term{Type: TermTypeFunc, Func: &Func{Name: name}}}, + }, + }, + }}, + }, + }, + ) + } +} + +func (c *compiler) compileBind(l, r *Query, patterns []*Pattern) error { + defer c.newScopeDepth()() + c.append(&code{op: opdup}) + c.append(&code{op: opexpbegin}) + if err := c.compileQuery(l); err != nil { + return err + } + var pc int + var vs [][2]int + for i, p := range patterns { + var pcc int + var err error + if i < len(patterns)-1 { + defer c.lazy(func() *code { + return &code{op: opforkalt, v: pcc} + })() + } + if 0 < i { + for _, v := range vs { + c.append(&code{op: oppush, v: nil}) + c.append(&code{op: opstore, v: v}) + } + } + if vs, err = c.compilePattern(vs[:0], p); err != nil { + return err + } + if i < len(patterns)-1 { + defer c.lazy(func() *code { + return &code{op: opjump, v: pc} + })() + pcc = len(c.codes) + } + } + if len(patterns) > 1 { + pc = len(c.codes) + } + if len(patterns) == 1 && c.codes[len(c.codes)-2].op == opexpbegin { + c.codes[len(c.codes)-2].op = opnop + } else { + c.append(&code{op: opexpend}) + } + return c.compileQuery(r) +} + +func (c *compiler) compilePattern(vs [][2]int, p *Pattern) ([][2]int, error) { + var err error + c.appendCodeInfo(p) + if p.Name != "" { + v := c.pushVariable(p.Name) + c.append(&code{op: opstore, v: v}) + return append(vs, v), nil + } else if len(p.Array) > 0 { + v := c.newVariable() + c.append(&code{op: opstore, v: v}) + for i, p := range p.Array { + c.append(&code{op: opload, v: v}) + c.append(&code{op: opindexarray, v: i}) + if vs, err = c.compilePattern(vs, p); err != nil { + return nil, err + } + } + return vs, nil + } else if len(p.Object) > 0 { + v := c.newVariable() + c.append(&code{op: opstore, v: v}) + for _, kv := range p.Object { + var key, name string + c.append(&code{op: opload, v: v}) + if key = kv.Key; key != "" { + if key[0] == '$' { + key, name = key[1:], key + } + } else if kv.KeyString != nil { + if key = kv.KeyString.Str; key == "" { + if err := c.compileString(kv.KeyString, nil); err != nil { + return nil, err + } + } + } else if kv.KeyQuery != nil { + if err := c.compileQuery(kv.KeyQuery); err != nil { + return nil, err + } + } + if key != "" { + c.append(&code{op: opindex, v: key}) + } else { + c.append(&code{op: opload, v: v}) + c.append(&code{op: oppush, v: nil}) + // ref: compileCall + c.append(&code{op: opcall, v: [3]any{internalFuncs["_index"].callback, 2, "_index"}}) + } + if name != "" { + if kv.Val != nil { + c.append(&code{op: opdup}) + } + if vs, err = c.compilePattern(vs, &Pattern{Name: name}); err != nil { + return nil, err + } + } + if kv.Val != nil { + if vs, err = c.compilePattern(vs, kv.Val); err != nil { + return nil, err + } + } + } + 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: len(c.codes)} // skip then clause + }) + f = c.newScopeDepth() + if err := c.compileQuery(e.Then); err != nil { + return err + } + f() + defer c.lazy(func() *code { + return &code{op: opjump, v: len(c.codes)} + })() + setjumpifnot() + 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: len(c.codes)} + }) + 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: len(c.codes)} + })() + 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()() + 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}) + setfork := c.lazy(func() *code { + return &code{op: opfork, v: len(c.codes)} + }) + if err := c.compileQuery(e.Query); err != nil { + return err + } + if _, err := c.compilePattern(nil, 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}) + setfork() + 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.compileQuery(e.Query); err != nil { + return err + } + if _, err := c.compilePattern(nil, 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:]) + c.append(&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]any{funcBreak(label), 0, "_break"}}) + return nil +} + +func funcBreak(label string) func(any, []any) any { + return func(v any, _ []any) any { + return &breakError{label, v} + } +} + +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: + c.append(&code{op: opconst, v: toNumber(e.Number)}) + 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 { + if k := x.toIndexKey(); k != nil { + if err := c.compileTerm(e); err != nil { + return err + } + c.appendCodeInfo(x) + c.append(&code{op: opindex, v: k}) + return nil + } + c.appendCodeInfo(x) + 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 { + if len(e.Args) == 0 { + if f, v := c.lookupFuncOrVariable(e.Name); f != nil { + return c.compileCallPc(f, e.Args) + } else if v != nil { + if e.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 e.Name == "$ENV" || e.Name == "env" { + env := make(map[string]any) + if c.environLoader != nil { + for _, kv := range c.environLoader() { + if k, v, ok := strings.Cut(kv, "="); ok && k != "" { + env[k] = v + } + } + } + c.append(&code{op: opconst, v: env}) + return nil + } else if e.Name[0] == '$' { + return &variableNotFoundError{e.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 == e.Name && f.argcnt == len(e.Args) { + return c.compileCallPc(f, e.Args) + } + } + } + } + if f := c.lookupBuiltin(e.Name, len(e.Args)); f != nil { + return c.compileCallPc(f, e.Args) + } + if fds, ok := builtinFuncDefs[e.Name]; ok { + var compiled bool + for _, fd := range fds { + if len(fd.Args) == len(e.Args) { + if err := c.compileFuncDef(fd, true); err != nil { + return err + } + compiled = true + break + } + } + if !compiled { + switch e.Name { + case "_assign": + c.compileAssign() + case "_modify": + c.compileModify() + case "_last": + c.compileLast() + } + } + if f := c.lookupBuiltin(e.Name, len(e.Args)); f != nil { + 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]any{c.funcBuiltins, 0, e.Name}, + e.Args, + true, + -1, + ) + case "input": + if c.inputIter == nil { + return &inputNotAllowedError{} + } + return c.compileCallInternal( + [3]any{c.funcInput, 0, e.Name}, + e.Args, + true, + -1, + ) + case "modulemeta": + return c.compileCallInternal( + [3]any{c.funcModulemeta, 0, e.Name}, + e.Args, + true, + -1, + ) + case "debug": + setfork := c.lazy(func() *code { + return &code{op: opfork, v: len(c.codes)} + }) + if err := c.compileQuery(e.Args[0]); err != nil { + return err + } + if err := c.compileFunc(&Func{Name: "debug"}); err != nil { + if _, ok := err.(*funcNotFoundError); ok { + err = &funcNotFoundError{e} + } + return err + } + c.append(&code{op: opbacktrack}) + setfork() + return nil + case "_match": + return c.compileCallInternal( + [3]any{c.funcMatch, len(e.Args), e.Name}, + e.Args, + true, + -1, + ) + 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]any{fn.callback, len(e.Args), e.Name}, + e.Args, + true, + -1, + ); err != nil { + return err + } + if fn.iter { + c.append(&code{op: opiter}) + } + return nil + } + return &funcNotFoundError{e} +} + +// Appends the compiled code for the assignment operator (`=`) to maximize +// performance. Originally the operator was implemented as follows. +// +// def _assign(p; $x): reduce path(p) as $q (.; setpath($q; $x)); +// +// To overcome the difficulty of reducing allocations on `setpath`, we use the +// `allocator` type and track the allocated addresses during the reduction. +func (c *compiler) compileAssign() { + defer c.appendBuiltin("_assign", 2)() + scope := c.newScope() + v, p := [2]int{scope.id, 0}, [2]int{scope.id, 1} + x, a := [2]int{scope.id, 2}, [2]int{scope.id, 3} + // Cannot reuse v, p due to backtracking in x. + w, q := [2]int{scope.id, 4}, [2]int{scope.id, 5} + c.appends( + &code{op: opscope, v: [3]int{scope.id, 6, 2}}, + &code{op: opstore, v: v}, // def _assign(p; $x): + &code{op: opstore, v: p}, + &code{op: opstore, v: x}, + &code{op: opload, v: v}, + &code{op: opexpbegin}, + &code{op: opload, v: x}, + &code{op: opcallpc}, + &code{op: opstore, v: x}, + &code{op: opexpend}, + &code{op: oppush, v: nil}, + &code{op: opcall, v: [3]any{funcAllocator, 0, "_allocator"}}, + &code{op: opstore, v: a}, + &code{op: opload, v: v}, + &code{op: opfork, v: len(c.codes) + 30}, // reduce [L1] + &code{op: opdup}, + &code{op: opstore, v: w}, + &code{op: oppathbegin}, // path(p) + &code{op: opload, v: p}, + &code{op: opcallpc}, + &code{op: opload, v: w}, + &code{op: oppathend}, + &code{op: opstore, v: q}, // as $q (.; + &code{op: opload, v: a}, // setpath($q; $x) + &code{op: opload, v: x}, + &code{op: opload, v: q}, + &code{op: opload, v: w}, + &code{op: opcall, v: [3]any{funcSetpathWithAllocator, 3, "_setpath"}}, + &code{op: opstore, v: w}, + &code{op: opbacktrack}, // ); + &code{op: oppop}, // [L1] + &code{op: opload, v: w}, + &code{op: opret}, + ) +} + +// Appends the compiled code for the update-assignment operator (`|=`) to +// maximize performance. We use the `allocator` type, just like `_assign/2`. +func (c *compiler) compileModify() { + defer c.appendBuiltin("_modify", 2)() + scope := c.newScope() + v, p := [2]int{scope.id, 0}, [2]int{scope.id, 1} + f, d := [2]int{scope.id, 2}, [2]int{scope.id, 3} + a, l := [2]int{scope.id, 4}, [2]int{scope.id, 5} + c.appends( + &code{op: opscope, v: [3]int{scope.id, 6, 2}}, + &code{op: opstore, v: v}, // def _modify(p; f): + &code{op: opstore, v: p}, + &code{op: opstore, v: f}, + &code{op: oppush, v: []any{}}, + &code{op: opstore, v: d}, + &code{op: oppush, v: nil}, + &code{op: opcall, v: [3]any{funcAllocator, 0, "_allocator"}}, + &code{op: opstore, v: a}, + &code{op: opload, v: v}, + &code{op: opfork, v: len(c.codes) + 39}, // reduce [L1] + &code{op: oppathbegin}, // path(p) + &code{op: opload, v: p}, + &code{op: opcallpc}, + &code{op: opload, v: v}, + &code{op: oppathend}, + &code{op: opstore, v: p}, // as $p (.; + &code{op: opforklabel, v: l}, // label $l | + &code{op: opload, v: v}, // + &code{op: opfork, v: len(c.codes) + 36}, // [L2] + &code{op: oppop}, // (getpath($p) | + &code{op: opload, v: a}, + &code{op: opload, v: p}, + &code{op: opload, v: v}, + &code{op: opcall, v: [3]any{internalFuncs["getpath"].callback, 1, "getpath"}}, + &code{op: opload, v: f}, // f) + &code{op: opcallpc}, + &code{op: opload, v: p}, // setpath($p; ...) + &code{op: opload, v: v}, + &code{op: opcall, v: [3]any{funcSetpathWithAllocator, 3, "_setpath"}}, + &code{op: opstore, v: v}, + &code{op: opload, v: v}, // ., break $l + &code{op: opfork, v: len(c.codes) + 34}, // [L4] + &code{op: opjump, v: len(c.codes) + 38}, // [L3] + &code{op: opload, v: l}, // [L4] + &code{op: opcall, v: [3]any{funcBreak(""), 0, "_break"}}, + &code{op: opload, v: p}, // append $p to $d [L2] + &code{op: opappend, v: d}, // + &code{op: opbacktrack}, // ) | [L3] + &code{op: oppop}, // delpaths($d); [L1] + &code{op: opload, v: a}, + &code{op: opload, v: d}, + &code{op: opload, v: v}, + &code{op: opcall, v: [3]any{funcDelpathsWithAllocator, 2, "_delpaths"}}, + &code{op: opret}, + ) +} + +// Appends the compiled code for the `last/1` function to +// maximize performance avoiding unnecessary boxing. +func (c *compiler) compileLast() { + defer c.appendBuiltin("_last", 1)() + scope := c.newScope() + v, g, x := [2]int{scope.id, 0}, [2]int{scope.id, 1}, [2]int{scope.id, 2} + c.appends( + &code{op: opscope, v: [3]int{scope.id, 3, 1}}, + &code{op: opstore, v: v}, + &code{op: opstore, v: g}, + &code{op: oppush, v: true}, // $x = true + &code{op: opstore, v: x}, + &code{op: opload, v: v}, + &code{op: opfork, v: len(c.codes) + 13}, // reduce [L1] + &code{op: opload, v: g}, // g + &code{op: opcallpc}, + &code{op: opstore, v: v}, // as $v ( + &code{op: oppush, v: false}, // $x = false + &code{op: opstore, v: x}, + &code{op: opbacktrack}, // ); + &code{op: oppop}, // [L1] + &code{op: opload, v: x}, // if $x then $v else empty end + &code{op: opjumpifnot, v: len(c.codes) + 17}, + &code{op: opbacktrack}, + &code{op: opload, v: v}, + &code{op: opret}, + ) +} + +func (c *compiler) funcBuiltins(any, []any) any { + 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([]any, len(xs)) + for i, x := range xs { + ys[i] = x.name + "/" + strconv.Itoa(x.arity) + } + return ys +} + +func (c *compiler) funcInput(any, []any) any { + v, ok := c.inputIter.Next() + if !ok { + return errors.New("break") + } + return v +} + +func (c *compiler) funcModulemeta(v any, _ []any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"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]any) (*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]any) + } + meta["defs"] = listModuleDefs(q) + meta["deps"] = listModuleDeps(q) + return meta +} + +func listModuleDefs(q *Query) []any { + type funcNameArity struct { + name string + arity int + } + var xs []*funcNameArity + for _, fd := range q.FuncDefs { + if fd.Name[0] != '_' { + xs = append(xs, &funcNameArity{fd.Name, len(fd.Args)}) + } + } + 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 + }) + defs := make([]any, len(xs)) + for i, x := range xs { + defs[i] = x.name + "/" + strconv.Itoa(x.arity) + } + return defs +} + +func listModuleDeps(q *Query) []any { + deps := make([]any, len(q.Imports)) + for j, i := range q.Imports { + v := i.Meta.ToValue() + if v == nil { + v = make(map[string]any) + } + relpath := i.ImportPath + if relpath == "" { + relpath = i.IncludePath + } + v["relpath"] = relpath + if i.ImportAlias != "" { + v["as"] = strings.TrimPrefix(i.ImportAlias, "$") + } + v["is_data"] = strings.HasPrefix(i.ImportAlias, "$") + deps[j] = v + } + return deps +} + +func (c *compiler) funcMatch(v any, args []any) any { + return funcMatch(v, args[0], args[1], args[2], &c.regexpCache) +} + +func (c *compiler) compileObject(e *Object) error { + c.appendCodeInfo(e) + if len(e.KeyVals) == 0 { + c.append(&code{op: opconst, v: map[string]any{}}) + 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 := range l { + 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]any, l) + for i := range l { + 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 key := kv.Key; key != "" { + if key[0] == '$' { + if kv.Val == nil { // {$foo} == {foo:$foo} + c.append(&code{op: oppush, v: key[1:]}) + } + c.append(&code{op: opload, v: v}) + if err := c.compileFunc(&Func{Name: key}); err != nil { + return err + } + } else { + c.append(&code{op: oppush, v: key}) + if kv.Val == nil { // {foo} == {foo:.foo} + c.append(&code{op: opload, v: v}) + c.append(&code{op: opindex, v: key}) + } + } + } else if key := kv.KeyString; key != nil { + if key.Queries == nil { + c.append(&code{op: oppush, v: key.Str}) + if kv.Val == nil { // {"foo"} == {"foo":.["foo"]} + c.append(&code{op: opload, v: v}) + c.append(&code{op: opindex, v: key.Str}) + } + } else { + c.append(&code{op: opload, v: v}) + if err := c.compileString(key, nil); err != nil { + return err + } + if kv.Val == nil { + c.append(&code{op: opdup}) + c.append(&code{op: opload, v: v}) + c.append(&code{op: oppush, v: nil}) + // ref: compileCall + c.append(&code{op: opcall, v: [3]any{internalFuncs["_index"].callback, 2, "_index"}}) + } + } + } 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() + } + if kv.Val != nil { + c.append(&code{op: opload, v: v}) + if err := c.compileQuery(kv.Val); 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: []any{}}) + return nil + } + c.append(&code{op: oppush, v: []any{}}) + arr := c.newVariable() + c.append(&code{op: opstore, v: arr}) + pc := len(c.codes) + setfork := c.lazy(func() *code { + return &code{op: opfork, v: len(c.codes)} + }) + 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}) + setfork() + 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 := range l { + 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([]any, l) + for i := range l { + 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 v := e.toNumber(); v != nil { + c.append(&code{op: opconst, v: v}) + return nil + } + 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(format string, str *String) error { + f := formatToFunc(format) + if f == nil { + f = &Func{ + Name: "format", + Args: []*Query{{Term: &Term{Type: TermTypeString, Str: &String{Str: format[1:]}}}}, + } + } + if str == nil { + return c.compileFunc(f) + } + return c.compileString(str, f) +} + +func formatToFunc(format string) *Func { + switch format { + 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 "@urid": + return &Func{Name: "_tourid"} + 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: opiter}) + return nil + } else if s.Optional { + if len(e.SuffixList) > 0 { + if u := e.SuffixList[len(e.SuffixList)-1].toTerm(); u != nil { + // no need to clone (ref: compileTerm) + e.SuffixList = e.SuffixList[:len(e.SuffixList)-1] + if err := c.compileTerm(e); err != nil { + return err + } + e = u + } + } + return c.compileTry(&Try{Body: &Query{Term: e}}) + } else { + return fmt.Errorf("invalid suffix: %s", s) + } +} + +func (c *compiler) compileCall(name string, args []*Query) error { + fn := internalFuncs[name] + var indexing int + switch name { + case "_index", "_slice": + indexing = 1 + case "getpath": + indexing = 0 + default: + indexing = -1 + } + if err := c.compileCallInternal( + [3]any{fn.callback, len(args), name}, + args, + true, + indexing, + ); err != nil { + return err + } + if fn.iter { + c.append(&code{op: opiter}) + } + return nil +} + +func (c *compiler) compileCallPc(fn *funcinfo, args []*Query) error { + return c.compileCallInternal(fn.pc, args, false, -1) +} + +func (c *compiler) compileCallInternal( + fn any, args []*Query, internal bool, indexing int, +) error { + if len(args) == 0 { + c.append(&code{op: opcall, v: fn}) + return nil + } + v := c.newVariable() + c.append(&code{op: opstore, v: v}) + if indexing >= 0 { + c.append(&code{op: opexpbegin}) + } + for i := len(args) - 1; i >= 0; i-- { + pc := len(c.codes) + 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 len(c.codes) - pc { + case 2: // optimize identity argument (opscope, opret) + j := len(c.codes) - 3 + c.codes[j] = &code{op: opload, v: v} + 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: v} + 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: v}) + c.append(&code{op: oppushpc, v: pc}) + c.append(&code{op: opcallpc}) + } + } else { + c.append(&code{op: oppushpc, v: pc}) + } + if i == indexing { + 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}) + } + } + } + if indexing > 0 { + c.append(&code{op: oppush, v: nil}) + } else { + c.append(&code{op: opload, v: v}) + } + c.append(&code{op: opcall, v: fn}) + return nil +} + +func (c *compiler) append(code *code) { + c.codes = append(c.codes, code) +} + +func (c *compiler) appends(codes ...*code) { + c.codes = append(c.codes, 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, code := range c.codes { + switch code.op { + case opscope: + pcs = append(pcs, i) + if v := code.v.([3]int); v[2] == 0 { + scopes[i] = v[1] == 0 + } + case opcall: + var canjump bool + if j, ok := code.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 < len(c.codes); { + switch c.codes[j].op { + case opjump: + j = c.codes[j].v.(int) + case opret: + if canjump { + code.op = opjump + code.v = pcs[len(pcs)-1] + 1 + } else { + code.op = opcallrec + } + fallthrough + 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 + } +} diff --git a/vendor/github.com/itchyny/gojq/debug.go b/vendor/github.com/itchyny/gojq/debug.go new file mode 100644 index 000000000000..709914b71764 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/debug.go @@ -0,0 +1,208 @@ +//go:build gojq_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 any) { + 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, len(c.codes) + 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, 25, c.op, 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] + op := c.op.String() + if backtrack { + op += " " + } + fmt.Fprintf(&sb, "\t%d\t%-*s%s\t|", pc, 25, op, 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 (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%d\t|\t%s\n", 25, op, 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]any: + return fmt.Sprintf("%s/%d", v[2], v[1]) + default: + panic(c) + } + default: + return debugValue(c.v) + } +} + +func debugValue(v any) string { + switch v := v.(type) { + case Iter: + return fmt.Sprintf("gojq.Iter(%#v)", v) + case []pathValue: + return fmt.Sprintf("[]gojq.pathValue(%v)", 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]any: + return fmt.Sprintf("[%v,%v,%v]", v[0], v[1], v[2]) + case allocator: + return fmt.Sprintf("%v", v) + default: + return Preview(v) + } +} diff --git a/vendor/github.com/itchyny/gojq/encoder.go b/vendor/github.com/itchyny/gojq/encoder.go new file mode 100644 index 000000000000..cc44c95b435e --- /dev/null +++ b/vendor/github.com/itchyny/gojq/encoder.go @@ -0,0 +1,192 @@ +package gojq + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "math" + "math/big" + "sort" + "strconv" + "strings" + "unicode/utf8" +) + +// Marshal returns the jq-flavored JSON encoding of v. +// +// This method accepts only limited types (nil, bool, int, float64, *big.Int, +// json.Number, string, []any, and map[string]any) 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 '<', '>', '&', '\u2028', and '\u2029'. These behaviors +// are based on the marshaler of jq command, and different from json.Marshal in +// the Go standard library. Note that the result is not safe to embed in HTML. +func Marshal(v any) ([]byte, error) { + var b bytes.Buffer + (&encoder{w: &b}).encode(v) + return b.Bytes(), nil +} + +func jsonMarshal(v any) string { + var sb strings.Builder + (&encoder{w: &sb}).encode(v) + return sb.String() +} + +func jsonEncodeString(sb *strings.Builder, v string) { + (&encoder{w: sb}).encodeString(v) +} + +type encoder struct { + w interface { + io.Writer + io.ByteWriter + io.StringWriter + } + buf [64]byte +} + +func (e *encoder) encode(v any) { + 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 json.Number: + e.w.WriteString(v.String()) + case string: + e.encodeString(v) + case []any: + e.encodeArray(v) + case map[string]any: + e.encodeObject(v) + default: + panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v)) + } +} + +// ref: floatEncoder in encoding/json +func (e *encoder) encodeFloat64(f float64) { + if math.IsNaN(f) { + e.w.WriteString("null") + return + } + f = min(max(f, -math.MaxFloat64), math.MaxFloat64) + format := byte('f') + if x := math.Abs(f); x != 0 && x < 1e-6 || x >= 1e21 { + format = 'e' + } + buf := strconv.AppendFloat(e.buf[:0], f, format, -1, 64) + if format == '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 != '\\' { + i++ + continue + } + if start < i { + e.w.WriteString(s[start:i]) + } + switch b { + case '"': + e.w.WriteString(`\"`) + case '\\': + e.w.WriteString(`\\`) + case '\b': + e.w.WriteString(`\b`) + case '\f': + e.w.WriteString(`\f`) + case '\n': + e.w.WriteString(`\n`) + case '\r': + e.w.WriteString(`\r`) + case '\t': + e.w.WriteString(`\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 []any) { + e.w.WriteByte('[') + for i, v := range vs { + if i > 0 { + e.w.WriteByte(',') + } + e.encode(v) + } + e.w.WriteByte(']') +} + +func (e *encoder) encodeObject(vs map[string]any) { + e.w.WriteByte('{') + type keyVal struct { + key string + val any + } + 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('}') +} diff --git a/vendor/github.com/itchyny/gojq/env.go b/vendor/github.com/itchyny/gojq/env.go new file mode 100644 index 000000000000..3ba44ad1d5b2 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/env.go @@ -0,0 +1,49 @@ +package gojq + +import "context" + +type env struct { + pc int + stack *stack + paths *stack + scopes *scopeStack + values []any + codes []*code + codeinfos []codeinfo + forks []fork + backtrack bool + offset int + expdepth int + label int + args [32]any // 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 + offset int + expdepth int +} diff --git a/vendor/github.com/itchyny/gojq/error.go b/vendor/github.com/itchyny/gojq/error.go new file mode 100644 index 000000000000..213086ffbd83 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/error.go @@ -0,0 +1,383 @@ +package gojq + +import "strconv" + +// 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() any +} + +type expectedObjectError struct { + v any +} + +func (err *expectedObjectError) Error() string { + return "expected an object but got: " + typeErrorPreview(err.v) +} + +type expectedArrayError struct { + v any +} + +func (err *expectedArrayError) Error() string { + return "expected an array but got: " + typeErrorPreview(err.v) +} + +type iteratorError struct { + v any +} + +func (err *iteratorError) Error() string { + return "cannot iterate over: " + typeErrorPreview(err.v) +} + +type arrayIndexNegativeError struct { + v int +} + +func (err *arrayIndexNegativeError) Error() string { + return "array index should not be negative: " + Preview(err.v) +} + +type arrayIndexTooLargeError struct { + v any +} + +func (err *arrayIndexTooLargeError) Error() string { + return "array index too large: " + Preview(err.v) +} + +type repeatStringTooLargeError struct { + s string + n float64 +} + +func (err *repeatStringTooLargeError) Error() string { + return "repeat string result too large: " + Preview(err.s) + " * " + Preview(err.n) +} + +type objectKeyNotStringError struct { + v any +} + +func (err *objectKeyNotStringError) Error() string { + return "expected a string for object key but got: " + typeErrorPreview(err.v) +} + +type arrayIndexNotNumberError struct { + v any +} + +func (err *arrayIndexNotNumberError) Error() string { + return "expected a number for indexing an array but got: " + typeErrorPreview(err.v) +} + +type stringIndexNotNumberError struct { + v any +} + +func (err *stringIndexNotNumberError) Error() string { + return "expected a number for indexing a string but got: " + typeErrorPreview(err.v) +} + +type expectedStartEndError struct { + v any +} + +func (err *expectedStartEndError) Error() string { + return `expected "start" and "end" for slicing but got: ` + typeErrorPreview(err.v) +} + +type lengthMismatchError struct{} + +func (*lengthMismatchError) Error() string { + return "length mismatch" +} + +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 func0TypeError struct { + name string + v any +} + +func (err *func0TypeError) Error() string { + return err.name + " cannot be applied to: " + typeErrorPreview(err.v) +} + +type func1TypeError struct { + name string + v, w any +} + +func (err *func1TypeError) Error() string { + return err.name + "(" + Preview(err.w) + ") cannot be applied to: " + typeErrorPreview(err.v) +} + +type func2TypeError struct { + name string + v, w, x any +} + +func (err *func2TypeError) Error() string { + return err.name + "(" + Preview(err.w) + "; " + Preview(err.x) + ") cannot be applied to: " + typeErrorPreview(err.v) +} + +type func0WrapError struct { + name string + v any + err error +} + +func (err *func0WrapError) Error() string { + return err.name + " cannot be applied to " + Preview(err.v) + ": " + err.err.Error() +} + +type func1WrapError struct { + name string + v, w any + err error +} + +func (err *func1WrapError) Error() string { + return err.name + "(" + Preview(err.w) + ") cannot be applied to " + Preview(err.v) + ": " + err.err.Error() +} + +type func2WrapError struct { + name string + v, w, x any + err error +} + +func (err *func2WrapError) Error() string { + return err.name + "(" + Preview(err.w) + "; " + Preview(err.x) + ") cannot be applied to " + Preview(err.v) + ": " + err.err.Error() +} + +type exitCodeError struct { + value any + code int +} + +func (err *exitCodeError) Error() string { + if s, ok := err.value.(string); ok { + return "error: " + s + } + return "error: " + jsonMarshal(err.value) +} + +func (err *exitCodeError) Value() any { + return err.value +} + +func (err *exitCodeError) ExitCode() int { + return err.code +} + +// HaltError is an error emitted by halt and halt_error functions. +// It implements [ValueError], and if the value is nil, discard the error +// and stop the iteration. Consider a query like "1, halt, 2"; +// the first value is 1, and the second value is a HaltError with nil value. +// You might think the iterator should not emit an error this case, but it +// should so that we can recognize the halt error to stop the outer loop +// of iterating input values; echo 1 2 3 | gojq "., halt". +type HaltError exitCodeError + +func (err *HaltError) Error() string { + return "halt " + (*exitCodeError)(err).Error() +} + +// Value returns the value of the error. This implements [ValueError], +// but halt error is not catchable by try-catch. +func (err *HaltError) Value() any { + return (*exitCodeError)(err).Value() +} + +// ExitCode returns the exit code of the error. +func (err *HaltError) ExitCode() int { + return (*exitCodeError)(err).ExitCode() +} + +type flattenDepthError struct { + v float64 +} + +func (err *flattenDepthError) Error() string { + return "flatten depth should not be negative: " + Preview(err.v) +} + +type timeArrayError struct{} + +func (*timeArrayError) Error() string { + return "expected an array of 8 numbers" +} + +type unaryTypeError struct { + name string + v any +} + +func (err *unaryTypeError) Error() string { + return "cannot " + err.name + ": " + typeErrorPreview(err.v) +} + +type binopTypeError struct { + name string + l, r any +} + +func (err *binopTypeError) Error() string { + return "cannot " + err.name + ": " + typeErrorPreview(err.l) + " and " + typeErrorPreview(err.r) +} + +type zeroDivisionError struct { + l, r any +} + +func (err *zeroDivisionError) Error() string { + return "cannot divide " + typeErrorPreview(err.l) + " by: " + typeErrorPreview(err.r) +} + +type zeroModuloError struct { + l, r any +} + +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 formatRowError struct { + typ string + v any +} + +func (err *formatRowError) Error() string { + return "@" + err.typ + " cannot format an array including: " + typeErrorPreview(err.v) +} + +type tooManyVariableValuesError struct{} + +func (*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 any +} + +func (err *breakError) Error() string { + return "label not defined: " + err.n +} + +func (*breakError) ExitCode() int { + return 3 +} + +type tryEndError struct { + err error +} + +func (err *tryEndError) Error() string { + return err.err.Error() +} + +type invalidPathError struct { + v any +} + +func (err *invalidPathError) Error() string { + return "invalid path against: " + typeErrorPreview(err.v) +} + +type invalidPathIterError struct { + v any +} + +func (err *invalidPathIterError) Error() string { + return "invalid path on iterating against: " + typeErrorPreview(err.v) +} + +type queryParseError struct { + fname, contents string + err error +} + +func (err *queryParseError) QueryParseError() (string, string, error) { + return err.fname, err.contents, err.err +} + +func (err *queryParseError) Error() string { + return "invalid query: " + 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 any) string { + switch v.(type) { + case nil: + return "null" + case Iter: + return "gojq.Iter" + default: + return TypeOf(v) + " (" + Preview(v) + ")" + } +} diff --git a/vendor/github.com/itchyny/gojq/execute.go b/vendor/github.com/itchyny/gojq/execute.go new file mode 100644 index 000000000000..3b7eab2b784d --- /dev/null +++ b/vendor/github.com/itchyny/gojq/execute.go @@ -0,0 +1,443 @@ +package gojq + +import ( + "context" + "math" + "reflect" + "sort" +) + +func (env *env) execute(bc *Code, v any, vars ...any) 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() (any, 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: + v := env.pop() + env.push(v) + env.push(v) + 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]any, n) + for range n { + v, k := env.pop(), env.pop() + s, ok := k.(string) + if !ok { + err = &objectKeyNotStringError{k} + break loop + } + if _, ok := m[s]; !ok { + m[s] = v + } + } + env.push(m) + case opappend: + i := env.index(code.v.([2]int)) + env.values[i] = append(env.values[i].([]any), env.pop()) + case opfork: + if backtrack { + if err != nil { + break loop + } + pc, backtrack = code.v.(int), false + goto loop + } + env.pushfork(pc) + case opforktrybegin: + if backtrack { + if err == nil { + break loop + } + switch e := err.(type) { + case *tryEndError: + err = e.err + break loop + case *breakError, *HaltError: + break loop + case ValueError: + env.pop() + env.push(e.Value()) + default: + env.pop() + env.push(err.Error()) + } + pc, backtrack, err = code.v.(int), false, nil + goto loop + } + env.pushfork(pc) + case opforktryend: + if backtrack { + if err != nil { + err = &tryEndError{err} + } + break loop + } + env.pushfork(pc) + case opforkalt: + if backtrack { + if err == nil { + break loop + } + pc, backtrack, err = code.v.(int), false, nil + goto loop + } + env.pushfork(pc) + case opforklabel: + if backtrack { + label := env.pop() + if e, ok := err.(*breakError); ok && e.v == label { + err = nil + } + break loop + } + 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: + if v := env.pop(); v == nil || v == false { + pc = code.v.(int) + goto loop + } + case opindex, opindexarray: + if backtrack { + break loop + } + p, v := code.v, env.pop() + if code.op == opindexarray && v != nil { + if _, ok := v.([]any); !ok { + err = &expectedArrayError{v} + break loop + } + } + w := funcIndex2(nil, v, p) + if e, ok := w.(error); ok { + err = e + break loop + } + env.push(w) + if !env.paths.empty() && env.expdepth == 0 { + if !env.pathIntact(v) { + err = &invalidPathError{v} + break loop + } + env.paths.push(pathValue{path: p, value: w}) + } + 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]any: + argcnt := v[1].(int) + x, args := env.pop(), env.args[:argcnt] + for i := range argcnt { + args[i] = env.pop() + } + w := v[0].(func(any, []any) any)(x, args) + if e, ok := w.(error); ok { + err = e + break loop + } + env.push(w) + if !env.paths.empty() && env.expdepth == 0 { + switch v[2].(string) { + case "_index": + if x = args[0]; !env.pathIntact(x) { + err = &invalidPathError{x} + break loop + } + env.paths.push(pathValue{path: args[1], value: w}) + case "_slice": + if x = args[0]; !env.pathIntact(x) { + err = &invalidPathError{x} + break loop + } + env.paths.push(pathValue{ + path: map[string]any{"start": args[2], "end": args[1]}, + value: w, + }) + case "getpath": + if !env.pathIntact(x) { + err = &invalidPathError{x} + break loop + } + for _, p := range args[0].([]any) { + 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 int + if index == env.scopes.index { + if callpc >= 0 { + saveindex = index + } else { + callpc, saveindex = env.popscope() + } + } else { + saveindex = env.scopes.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([]any, 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 opiter: + if err != nil { + break loop + } + backtrack = false + var xs []pathValue + switch v := env.pop().(type) { + case []pathValue: + xs = v + case []any: + if !env.paths.empty() && env.expdepth == 0 && !env.pathIntact(v) { + 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]any: + if !env.paths.empty() && env.expdepth == 0 && !env.pathIntact(v) { + 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 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 + default: + err = &iteratorError{v} + env.push(emptyIter{}) + 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 + } + env.pop() + if v := env.pop(); !env.pathIntact(v) { + err = &invalidPathError{v} + break loop + } + env.push(env.poppaths()) + env.expdepth = env.paths.pop().(int) + 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 any) { + env.stack.push(v) +} + +func (env *env) pop() any { + 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, offset: env.offset, expdepth: env.expdepth} + f.stackindex, f.stacklimit = env.stack.save() + f.scopeindex, f.scopelimit = env.scopes.save() + f.pathindex, f.pathlimit = env.paths.save() + 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.offset, env.expdepth = + env.forks[:len(env.forks)-1], f.offset, 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") +} + +type pathValue struct { + path, value any +} + +func (env *env) pathIntact(v any) bool { + w := env.paths.top().(pathValue).value + switch v := v.(type) { + case []any, map[string]any: + switch w.(type) { + case []any, map[string]any: + v, w := reflect.ValueOf(v), reflect.ValueOf(w) + return v.Pointer() == w.Pointer() && v.Len() == w.Len() + } + case float64: + if w, ok := w.(float64); ok { + return v == w || math.IsNaN(v) && math.IsNaN(w) + } + } + return v == w +} + +func (env *env) poppaths() []any { + xs := []any{} + 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 +} diff --git a/vendor/github.com/itchyny/gojq/func.go b/vendor/github.com/itchyny/gojq/func.go new file mode 100644 index 000000000000..7014fbc9e70d --- /dev/null +++ b/vendor/github.com/itchyny/gojq/func.go @@ -0,0 +1,2190 @@ +package gojq + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "iter" + "maps" + "math" + "math/big" + "net/url" + "reflect" + "regexp" + "slices" + "sort" + "strconv" + "strings" + "sync" + "time" + "unicode" + "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(any, []any) any +} + +func (fn function) accept(cnt int) bool { + return fn.argcount&(1<= 0 { + return v + } + return negate(v) + case float64: + return math.Abs(v) + case *big.Int: + if v.Sign() >= 0 { + return v + } + return new(big.Int).Abs(v) + case json.Number: + if !strings.HasPrefix(v.String(), "-") { + return v + } + return v[1:] + default: + return &func0TypeError{"abs", v} + } +} + +func funcLength(v any) any { + switch v := v.(type) { + case nil: + return 0 + case int: + if v >= 0 { + return v + } + return negate(v) + case float64: + return math.Abs(v) + case *big.Int: + if v.Sign() >= 0 { + return v + } + return new(big.Int).Abs(v) + case json.Number: + if !strings.HasPrefix(v.String(), "-") { + return v + } + return v[1:] + case string: + return len([]rune(v)) + case []any: + return len(v) + case map[string]any: + return len(v) + default: + return &func0TypeError{"length", v} + } +} + +func funcUtf8ByteLength(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"utf8bytelength", v} + } + return len(s) +} + +func funcKeys(v any) any { + switch v := v.(type) { + case []any: + w := make([]any, len(v)) + for i := range v { + w[i] = i + } + return w + case map[string]any: + w := make([]any, len(v)) + for i, k := range keys(v) { + w[i] = k + } + return w + default: + return &func0TypeError{"keys", v} + } +} + +func keys(v map[string]any) []string { + w := make([]string, len(v)) + var i int + for k := range v { + w[i] = k + i++ + } + sort.Strings(w) + return w +} + +func values(v any) ([]any, bool) { + switch v := v.(type) { + case []any: + return v, true + case map[string]any: + vs := make([]any, len(v)) + for i, k := range keys(v) { + vs[i] = v[k] + } + return vs, true + default: + return nil, false + } +} + +func funcHas(v, x any) any { + switch v := v.(type) { + case []any: + if x, ok := toInt(x); ok { + return 0 <= x && x < len(v) + } + case map[string]any: + if x, ok := x.(string); ok { + _, ok := v[x] + return ok + } + case nil: + return false + } + return &func1TypeError{"has", v, x} +} + +func funcAdd(v any) any { + vs, ok := values(v) + if !ok { + return &func0TypeError{"add", v} + } + return add(slices.Values(vs)) +} + +func add(xs iter.Seq[any]) any { + var v any + for x := range xs { + switch x := x.(type) { + case nil: + continue + case string: + switch w := v.(type) { + case nil: + var sb strings.Builder + sb.WriteString(x) + v = &sb + continue + case *strings.Builder: + w.WriteString(x) + continue + } + case []any: + switch w := v.(type) { + case nil: + s := make([]any, len(x)) + copy(s, x) + v = s + continue + case []any: + v = append(w, x...) + continue + } + case map[string]any: + switch w := v.(type) { + case nil: + v = maps.Clone(x) + continue + case map[string]any: + maps.Copy(w, x) + continue + } + } + if sb, ok := v.(*strings.Builder); ok { + v = sb.String() + } + v = funcOpAdd(nil, v, x) + if err, ok := v.(error); ok { + return err + } + } + if sb, ok := v.(*strings.Builder); ok { + v = sb.String() + } + return v +} + +func funcToBoolean(v any) any { + switch v := v.(type) { + case bool: + return v + case string: + switch v { + case "true": + return true + case "false": + return false + default: + return &func0WrapError{"toboolean", v, errors.New("invalid boolean")} + } + default: + return &func0TypeError{"toboolean", v} + } +} + +func funcToNumber(v any) any { + switch v := v.(type) { + case int, float64, *big.Int, json.Number: + return v + case string: + if !newLexer(v).validNumber() { + return &func0WrapError{"tonumber", v, errors.New("invalid number")} + } + return toNumber(v) + default: + return &func0TypeError{"tonumber", v} + } +} + +func toNumber(v string) any { + return parseNumber(json.Number(v)) +} + +func funcToString(v any) any { + if s, ok := v.(string); ok { + return s + } + return funcToJSON(v) +} + +func funcType(v any) any { + return TypeOf(v) +} + +func funcReverse(v any) any { + vs, ok := v.([]any) + if !ok { + return &func0TypeError{"reverse", v} + } + ws := make([]any, len(vs)) + for i, v := range vs { + ws[len(ws)-i-1] = v + } + return ws +} + +func funcContains(v, x any) any { + return binopTypeSwitch(v, x, + func(l, r int) any { return l == r }, + func(l, r float64) any { return l == r }, + func(l, r *big.Int) any { return l.Cmp(r) == 0 }, + func(l, r string) any { return strings.Contains(l, r) }, + func(l, r []any) any { + R: + for _, r := range r { + for _, l := range l { + if funcContains(l, r) == true { + continue R + } + } + return false + } + return true + }, + func(l, r map[string]any) any { + if len(l) < len(r) { + return false + } + for k, r := range r { + if l, ok := l[k]; !ok || funcContains(l, r) != true { + return false + } + } + return true + }, + func(l, r any) any { + if l == r { + return true + } + return &func1TypeError{"contains", l, r} + }, + ) +} + +func funcInside(v, x any) any { + return funcContains(x, v) +} + +func funcIndices(v, x any) any { + return indexFunc("indices", v, x, indices) +} + +func indices(vs, xs []any) any { + rs := []any{} + if len(xs) == 0 { + return rs + } + for i := range len(vs) - len(xs) + 1 { + if Compare(vs[i:i+len(xs)], xs) == 0 { + rs = append(rs, i) + } + } + return rs +} + +func funcIndex(v, x any) any { + return indexFunc("index", v, x, func(vs, xs []any) any { + if len(xs) == 0 { + return nil + } + for i := range len(vs) - len(xs) + 1 { + if Compare(vs[i:i+len(xs)], xs) == 0 { + return i + } + } + return nil + }) +} + +func funcRindex(v, x any) any { + return indexFunc("rindex", v, x, func(vs, xs []any) any { + if len(xs) == 0 { + return nil + } + for i := len(vs) - len(xs); i >= 0; i-- { + if Compare(vs[i:i+len(xs)], xs) == 0 { + return i + } + } + return nil + }) +} + +func indexFunc(name string, v, x any, f func(_, _ []any) any) any { + switch v := v.(type) { + case nil: + return nil + case []any: + switch x := x.(type) { + case []any: + return f(v, x) + default: + return f(v, []any{x}) + } + case string: + if x, ok := x.(string); ok { + return f(explode(v), explode(x)) + } + return &func1TypeError{name, v, x} + default: + return &func1TypeError{name, v, x} + } +} + +func funcStartsWith(v, x any) any { + s, ok := v.(string) + if !ok { + return &func1TypeError{"startswith", v, x} + } + t, ok := x.(string) + if !ok { + return &func1TypeError{"startswith", v, x} + } + return strings.HasPrefix(s, t) +} + +func funcEndsWith(v, x any) any { + s, ok := v.(string) + if !ok { + return &func1TypeError{"endswith", v, x} + } + t, ok := x.(string) + if !ok { + return &func1TypeError{"endswith", v, x} + } + return strings.HasSuffix(s, t) +} + +func funcLtrimstr(v, x any) any { + s, ok := v.(string) + if !ok { + return &func1TypeError{"ltrimstr", v, x} + } + t, ok := x.(string) + if !ok { + return &func1TypeError{"ltrimstr", v, x} + } + return strings.TrimPrefix(s, t) +} + +func funcRtrimstr(v, x any) any { + s, ok := v.(string) + if !ok { + return &func1TypeError{"rtrimstr", v, x} + } + t, ok := x.(string) + if !ok { + return &func1TypeError{"rtrimstr", v, x} + } + return strings.TrimSuffix(s, t) +} + +func funcTrimstr(v, x any) any { + s, ok := v.(string) + if !ok { + return &func1TypeError{"trimstr", v, x} + } + t, ok := x.(string) + if !ok { + return &func1TypeError{"trimstr", v, x} + } + return strings.TrimSuffix(strings.TrimPrefix(s, t), t) +} + +func funcLtrim(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"ltrim", v} + } + return strings.TrimLeftFunc(s, unicode.IsSpace) +} + +func funcRtrim(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"rtrim", v} + } + return strings.TrimRightFunc(s, unicode.IsSpace) +} + +func funcTrim(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"trim", v} + } + return strings.TrimSpace(s) +} + +func funcExplode(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"explode", v} + } + return explode(s) +} + +func explode(s string) []any { + xs := make([]any, len([]rune(s))) + var i int + for _, r := range s { + xs[i] = int(r) + i++ + } + return xs +} + +func funcImplode(v any) any { + vs, ok := v.([]any) + if !ok { + return &func0TypeError{"implode", v} + } + var sb strings.Builder + sb.Grow(len(vs)) + for _, v := range vs { + if r, ok := toInt(v); ok { + if 0 <= r && r <= utf8.MaxRune { + sb.WriteRune(rune(r)) + } else { + sb.WriteRune(utf8.RuneError) + } + } else { + return &func0TypeError{"implode", vs} + } + } + return sb.String() +} + +func funcSplit(v, x any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"split", v} + } + t, ok := x.(string) + if !ok { + return &func0TypeError{"split", x} + } + ss := strings.Split(s, t) + xs := make([]any, len(ss)) + for i, s := range ss { + xs[i] = s + } + return xs +} + +func funcJoin(v, x any) any { + vs, ok := values(v) + if !ok { + return &func1TypeError{"join", v, x} + } + if len(vs) == 0 { + return "" + } + return add(func(yield func(any) bool) { + for i, v := range vs { + s := x + if i == 0 { + s = "" + } + if !yield(s) { + return + } + switch w := v.(type) { + case bool, int, float64, *big.Int, json.Number: + v = jsonMarshal(w) + } + if !yield(v) { + return + } + } + }) +} + +func funcASCIIDowncase(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"ascii_downcase", v} + } + return strings.Map(func(r rune) rune { + if 'A' <= r && r <= 'Z' { + return r + ('a' - 'A') + } + return r + }, s) +} + +func funcASCIIUpcase(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"ascii_upcase", v} + } + return strings.Map(func(r rune) rune { + if 'a' <= r && r <= 'z' { + return r - ('a' - 'A') + } + return r + }, s) +} + +func funcToJSON(v any) any { + return jsonMarshal(v) +} + +func funcFromJSON(v any) any { + s, ok := v.(string) + if !ok { + return &func0TypeError{"fromjson", v} + } + var w any + dec := json.NewDecoder(strings.NewReader(s)) + dec.UseNumber() + if err := dec.Decode(&w); err != nil { + return &func0WrapError{"fromjson", v, err} + } + if _, err := dec.Token(); err != io.EOF { + return &func0TypeError{"fromjson", v} + } + return w +} + +func funcFormat(v, x any) any { + s, ok := x.(string) + if !ok { + return &func0TypeError{"format", x} + } + format := "@" + s + f := formatToFunc(format) + if f == nil { + return &formatNotFoundError{format} + } + return internalFuncs[f.Name].callback(v, nil) +} + +var htmlEscaper = strings.NewReplacer( + `<`, "<", + `>`, ">", + `&`, "&", + `'`, "'", + `"`, """, +) + +func funcToHTML(v any) any { + switch x := funcToString(v).(type) { + case string: + return htmlEscaper.Replace(x) + default: + return x + } +} + +func funcToURI(v any) any { + switch x := funcToString(v).(type) { + case string: + return strings.ReplaceAll(url.QueryEscape(x), "+", "%20") + default: + return x + } +} + +func funcToURId(v any) any { + switch x := funcToString(v).(type) { + case string: + x, err := url.QueryUnescape(strings.ReplaceAll(x, "+", "%2B")) + if err != nil { + return &func0WrapError{"@urid", v, err} + } + return x + default: + return x + } +} + +var csvEscaper = strings.NewReplacer( + `"`, `""`, + "\x00", `\0`, +) + +func funcToCSV(v any) any { + return formatJoin("csv", v, ",", func(s string) string { + return `"` + csvEscaper.Replace(s) + `"` + }) +} + +var tsvEscaper = strings.NewReplacer( + "\t", `\t`, + "\r", `\r`, + "\n", `\n`, + "\\", `\\`, + "\x00", `\0`, +) + +func funcToTSV(v any) any { + return formatJoin("tsv", v, "\t", tsvEscaper.Replace) +} + +var shEscaper = strings.NewReplacer( + "'", `'\''`, + "\x00", `\0`, +) + +func funcToSh(v any) any { + if _, ok := v.([]any); !ok { + v = []any{v} + } + return formatJoin("sh", v, " ", func(s string) string { + return "'" + shEscaper.Replace(s) + "'" + }) +} + +func formatJoin(typ string, v any, sep string, escape func(string) string) any { + vs, ok := v.([]any) + if !ok { + return &func0TypeError{"@" + typ, v} + } + ss := make([]string, len(vs)) + for i, v := range vs { + switch v := v.(type) { + case []any, map[string]any: + return &formatRowError{typ, v} + case string: + ss[i] = escape(v) + default: + if s := jsonMarshal(v); s != "null" || typ == "sh" { + ss[i] = s + } + } + } + return strings.Join(ss, sep) +} + +func funcToBase64(v any) any { + switch x := funcToString(v).(type) { + case string: + return base64.StdEncoding.EncodeToString([]byte(x)) + default: + return x + } +} + +func funcToBase64d(v any) any { + 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 &func0WrapError{"@base64d", v, err} + } + return string(y) + default: + return x + } +} + +func funcIndex2(_, v, x any) any { + switch x := x.(type) { + case string: + switch v := v.(type) { + case nil: + return nil + case map[string]any: + return v[x] + default: + return &expectedObjectError{v} + } + case int, float64, *big.Int, json.Number: + i, _ := toInt(x) + switch v := v.(type) { + case nil: + return nil + case []any: + return index(v, i) + case string: + return indexString(v, i) + default: + return &expectedArrayError{v} + } + case []any: + switch v := v.(type) { + case nil: + return nil + case []any: + return indices(v, x) + default: + return &expectedArrayError{v} + } + case map[string]any: + 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: + switch v.(type) { + case []any: + return &arrayIndexNotNumberError{x} + case string: + return &stringIndexNotNumberError{x} + default: + return &objectKeyNotStringError{x} + } + } +} + +func index(vs []any, i int) any { + i = clampIndex(i, -1, len(vs)) + if 0 <= i && i < len(vs) { + return vs[i] + } + return nil +} + +func indexString(s string, i int) any { + l := len([]rune(s)) + i = clampIndex(i, -1, l) + if 0 <= i && i < l { + for _, r := range s { + if i--; i < 0 { + return string(r) + } + } + } + return nil +} + +func funcSlice(_, v, e, s any) (r any) { + switch v := v.(type) { + case nil: + return nil + case []any: + return slice(v, e, s) + case string: + return sliceString(v, e, s) + default: + return &expectedArrayError{v} + } +} + +func slice(vs []any, e, s any) any { + var start, end int + if s != nil { + if i, ok := toInt(s); ok { + start = clampIndex(i, 0, len(vs)) + } else { + return &arrayIndexNotNumberError{s} + } + } + if e != nil { + if i, ok := toIntCeil(e); ok { + end = clampIndex(i, start, len(vs)) + } else { + return &arrayIndexNotNumberError{e} + } + } else { + end = len(vs) + } + return vs[start:end] +} + +func sliceString(v string, e, s any) any { + var start, end int + l := len([]rune(v)) + if s != nil { + if i, ok := toInt(s); ok { + start = clampIndex(i, 0, l) + } else { + return &stringIndexNotNumberError{s} + } + } + if e != nil { + if i, ok := toIntCeil(e); ok { + end = clampIndex(i, start, l) + } else { + return &stringIndexNotNumberError{e} + } + } else { + end = l + } + if start < l { + for i := range v { + if start--; start < 0 { + start = i + break + } + } + } else { + start = len(v) + } + if end < l { + for i := range v { + if end--; end < 0 { + end = i + break + } + } + } else { + end = len(v) + } + return v[start:end] +} + +func clampIndex(i, minimum, maximum int) int { + if i < 0 { + i += maximum + } + if i < minimum { + return minimum + } else if i < maximum { + return i + } else { + return maximum + } +} + +func funcFlatten(v any, args []any) any { + vs, ok := values(v) + if !ok { + return &func0TypeError{"flatten", v} + } + var depth float64 + if len(args) == 0 { + depth = -1 + } else { + depth, ok = toFloat(args[0]) + if !ok { + return &func0TypeError{"flatten", args[0]} + } + if lt(depth, 0) { + return &flattenDepthError{depth} + } + } + return flatten([]any{}, vs, depth) +} + +func flatten(xs, vs []any, depth float64) []any { + for _, v := range vs { + if vs, ok := v.([]any); ok && depth != 0 { + xs = flatten(xs, vs, depth-1) + } else { + xs = append(xs, v) + } + } + return xs +} + +type rangeIter struct { + value, end, step any +} + +func (iter *rangeIter) Next() (any, 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(_ any, xs []any) any { + for _, x := range xs { + switch x.(type) { + case int, float64, *big.Int, json.Number: + default: + return &func0TypeError{"range", x} + } + } + return &rangeIter{xs[0], xs[1], xs[2]} +} + +func funcMin(v any) any { + vs, ok := v.([]any) + if !ok { + return &func0TypeError{"min", v} + } + return minMaxBy(vs, vs, true) +} + +func funcMinBy(v, x any) any { + vs, ok := v.([]any) + if !ok { + return &func1TypeError{"min_by", v, x} + } + xs, ok := x.([]any) + if !ok { + return &func1TypeError{"min_by", v, x} + } + if len(vs) != len(xs) { + return &func1WrapError{"min_by", v, x, &lengthMismatchError{}} + } + return minMaxBy(vs, xs, true) +} + +func funcMax(v any) any { + vs, ok := v.([]any) + if !ok { + return &func0TypeError{"max", v} + } + return minMaxBy(vs, vs, false) +} + +func funcMaxBy(v, x any) any { + vs, ok := v.([]any) + if !ok { + return &func1TypeError{"max_by", v, x} + } + xs, ok := x.([]any) + if !ok { + return &func1TypeError{"max_by", v, x} + } + if len(vs) != len(xs) { + return &func1WrapError{"max_by", v, x, &lengthMismatchError{}} + } + return minMaxBy(vs, xs, false) +} + +func minMaxBy(vs, xs []any, isMin bool) any { + 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 any +} + +func sortItems(name string, v, x any) ([]*sortItem, error) { + vs, ok := v.([]any) + if !ok { + if strings.HasSuffix(name, "_by") { + return nil, &func1TypeError{name, v, x} + } + return nil, &func0TypeError{name, v} + } + xs, ok := x.([]any) + if !ok { + return nil, &func1TypeError{name, v, x} + } + if len(vs) != len(xs) { + return nil, &func1WrapError{name, v, x, &lengthMismatchError{}} + } + 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 funcSort(v any) any { + return sortBy("sort", v, v) +} + +func funcSortBy(v, x any) any { + return sortBy("sort_by", v, x) +} + +func sortBy(name string, v, x any) any { + items, err := sortItems(name, v, x) + if err != nil { + return err + } + rs := make([]any, len(items)) + for i, x := range items { + rs[i] = x.value + } + return rs +} + +func funcGroupBy(v, x any) any { + items, err := sortItems("group_by", v, x) + if err != nil { + return err + } + rs := []any{} + var last any + for i, r := range items { + if i == 0 || Compare(last, r.key) != 0 { + rs, last = append(rs, []any{r.value}), r.key + } else { + rs[len(rs)-1] = append(rs[len(rs)-1].([]any), r.value) + } + } + return rs +} + +func funcUnique(v any) any { + return uniqueBy("unique", v, v) +} + +func funcUniqueBy(v, x any) any { + return uniqueBy("unique_by", v, x) +} + +func uniqueBy(name string, v, x any) any { + items, err := sortItems(name, v, x) + if err != nil { + return err + } + rs := []any{} + var last any + for i, r := range items { + if i == 0 || Compare(last, r.key) != 0 { + rs, last = append(rs, r.value), r.key + } + } + return rs +} + +func funcSignificand(v float64) float64 { + frac, _ := math.Frexp(v) + return frac * 2 +} + +func funcExp10(v float64) float64 { + return math.Pow(10, v) +} + +func funcFrexp(v any) any { + x, ok := toFloat(v) + if !ok { + return &func0TypeError{"frexp", v} + } + f, e := math.Frexp(x) + return []any{f, e} +} + +func funcModf(v any) any { + x, ok := toFloat(v) + if !ok { + return &func0TypeError{"modf", v} + } + if math.IsInf(x, 0) { + return []any{math.Copysign(0, x), x} + } + i, f := math.Modf(x) + return []any{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 funcFmax(l, r float64) float64 { + if math.IsNaN(l) { + return r + } + if math.IsNaN(r) { + return l + } + return max(l, r) +} + +func funcFmin(l, r float64) float64 { + if math.IsNaN(l) { + return r + } + if math.IsNaN(r) { + return l + } + return min(l, r) +} + +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 funcYn(l, r float64) float64 { + return math.Yn(int(l), r) +} + +func funcInfinite(any) any { + return math.Inf(1) +} + +func funcIsfinite(v any) any { + x, ok := toFloat(v) + return ok && !math.IsInf(x, 0) +} + +func funcIsinfinite(v any) any { + x, ok := toFloat(v) + return ok && math.IsInf(x, 0) +} + +func funcNan(any) any { + return math.NaN() +} + +func funcIsnan(v any) any { + x, ok := toFloat(v) + if !ok { + if v == nil { + return false + } + return &func0TypeError{"isnan", v} + } + return math.IsNaN(x) +} + +func funcIsnormal(v any) any { + if v, ok := toFloat(v); ok { + e := (math.Float64bits(v) & 0x7ff0000000000000) >> 52 + return 0 < e && e < 0x7ff + } + return false +} + +// An `allocator` creates new maps and slices, stores the allocated addresses. +// This allocator is used to reduce allocations on assignment operator (`=`), +// update-assignment operator (`|=`), and the `map_values`, `del`, `delpaths` +// functions. +type allocator map[uintptr]struct{} + +func funcAllocator(any, []any) any { + return allocator{} +} + +func (a allocator) allocated(v any) bool { + _, ok := a[reflect.ValueOf(v).Pointer()] + return ok +} + +func (a allocator) makeObject(l int) map[string]any { + v := make(map[string]any, l) + if a != nil { + a[reflect.ValueOf(v).Pointer()] = struct{}{} + } + return v +} + +func (a allocator) makeArray(l, c int) []any { + v := make([]any, l, max(l, c)) + if a != nil { + a[reflect.ValueOf(v).Pointer()] = struct{}{} + } + return v +} + +func funcSetpath(v, p, n any) any { + // There is no need to use an allocator on a single update. + return setpath(v, p, n, nil) +} + +// Used in compiler#compileAssign and compiler#compileModify. +func funcSetpathWithAllocator(v any, args []any) any { + return setpath(v, args[0], args[1], args[2].(allocator)) +} + +func setpath(v, p, n any, a allocator) any { + path, ok := p.([]any) + if !ok { + return &func1TypeError{"setpath", v, p} + } + u, err := update(v, path, n, a) + if err != nil { + return &func2WrapError{"setpath", v, p, n, err} + } + return u +} + +func funcDelpaths(v, p any) any { + return delpaths(v, p, allocator{}) +} + +// Used in compiler#compileAssign and compiler#compileModify. +func funcDelpathsWithAllocator(v any, args []any) any { + return delpaths(v, args[0], args[1].(allocator)) +} + +func delpaths(v, p any, a allocator) any { + paths, ok := p.([]any) + if !ok { + return &func1TypeError{"delpaths", v, p} + } + if len(paths) == 0 { + return v + } + // 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 + u := v + for _, q := range paths { + path, ok := q.([]any) + if !ok { + return &func1WrapError{"delpaths", v, p, &expectedArrayError{q}} + } + u, err = update(u, path, empty, a) + if err != nil { + return &func1WrapError{"delpaths", v, p, err} + } + } + return deleteEmpty(u) +} + +func update(v any, path []any, n any, a allocator) (any, error) { + if len(path) == 0 { + return n, nil + } + switch p := path[0].(type) { + case string: + switch v := v.(type) { + case nil: + return updateObject(nil, p, path[1:], n, a) + case map[string]any: + return updateObject(v, p, path[1:], n, a) + case struct{}: + return v, nil + default: + return nil, &expectedObjectError{v} + } + case int, float64, *big.Int, json.Number: + i, _ := toInt(p) + switch v := v.(type) { + case nil: + return updateArrayIndex(nil, i, path[1:], n, a) + case []any: + return updateArrayIndex(v, i, path[1:], n, a) + case struct{}: + return v, nil + default: + return nil, &expectedArrayError{v} + } + case map[string]any: + switch v := v.(type) { + case nil: + return updateArraySlice(nil, p, path[1:], n, a) + case []any: + return updateArraySlice(v, p, path[1:], n, a) + case struct{}: + return v, nil + default: + return nil, &expectedArrayError{v} + } + default: + switch v.(type) { + case []any: + return nil, &arrayIndexNotNumberError{p} + default: + return nil, &objectKeyNotStringError{p} + } + } +} + +func updateObject(v map[string]any, k string, path []any, n any, a allocator) (any, error) { + x, ok := v[k] + if !ok && n == struct{}{} { + if v == nil { + return nil, nil + } + return v, nil + } + u, err := update(x, path, n, a) + if err != nil { + return nil, err + } + if a.allocated(v) { + v[k] = u + return v, nil + } + w := a.makeObject(len(v) + 1) + maps.Copy(w, v) + w[k] = u + return w, nil +} + +func updateArrayIndex(v []any, i int, path []any, n any, a allocator) (any, error) { + var x any + if j := clampIndex(i, -1, len(v)); j < 0 { + if n == struct{}{} { + if v == nil { + return nil, nil + } + return v, nil + } + return nil, &arrayIndexNegativeError{i} + } else if j < len(v) { + i = j + x = v[i] + } else { + if n == struct{}{} { + if v == nil { + return nil, nil + } + return v, nil + } + if i >= 0x20000000 { + return nil, &arrayIndexTooLargeError{i} + } + } + u, err := update(x, path, n, a) + if err != nil { + return nil, err + } + l, c := len(v), cap(v) + if a.allocated(v) { + if i < c { + if i >= l { + v = v[:i+1] + } + v[i] = u + return v, nil + } + c *= 2 + } + if i >= l { + l = i + 1 + } + w := a.makeArray(l, c) + copy(w, v) + w[i] = u + return w, nil +} + +func updateArraySlice(v []any, m map[string]any, path []any, n any, a allocator) (any, error) { + s, ok := m["start"] + if !ok { + return nil, &expectedStartEndError{m} + } + e, ok := m["end"] + if !ok { + return nil, &expectedStartEndError{m} + } + var start, end int + if s != nil { + if i, ok := toInt(s); ok { + start = clampIndex(i, 0, len(v)) + } else { + return nil, &arrayIndexNotNumberError{s} + } + } + if e != nil { + if i, ok := toIntCeil(e); ok { + end = clampIndex(i, start, len(v)) + } else { + return nil, &arrayIndexNotNumberError{e} + } + } else { + end = len(v) + } + if start == end && n == struct{}{} { + if v == nil { + return nil, nil + } + return v, nil + } + u, err := update(v[start:end], path, n, a) + if err != nil { + return nil, err + } + switch u := u.(type) { + case []any: + var w []any + if len(u) == end-start && a.allocated(v) { + w = v + } else { + w = a.makeArray(len(v)-(end-start)+len(u), 0) + copy(w, v[:start]) + copy(w[start+len(u):], v[end:]) + } + copy(w[start:], u) + return w, nil + case struct{}: + var w []any + if a.allocated(v) { + w = v + } else { + w = a.makeArray(len(v), 0) + copy(w, v) + } + for i := start; i < end; i++ { + w[i] = u + } + return w, nil + default: + return nil, &expectedArrayError{u} + } +} + +func deleteEmpty(v any) any { + switch v := v.(type) { + case struct{}: + return nil + case map[string]any: + for k, w := range v { + if w == struct{}{} { + delete(v, k) + } else { + v[k] = deleteEmpty(w) + } + } + return v + case []any: + 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 + } +} + +func funcGetpath(v, p any) any { + path, ok := p.([]any) + if !ok { + return &func1TypeError{"getpath", v, p} + } + u := v + for _, x := range path { + switch v.(type) { + case nil, []any, map[string]any: + v = funcIndex2(nil, v, x) + if err, ok := v.(error); ok { + return &func1WrapError{"getpath", u, p, err} + } + default: + return &func1TypeError{"getpath", u, p} + } + } + return v +} + +func funcTranspose(v any) any { + vss, ok := v.([]any) + if !ok { + return &func0TypeError{"transpose", v} + } + if len(vss) == 0 { + return []any{} + } + var l int + for _, vs := range vss { + vs, ok := vs.([]any) + if !ok { + return &func0TypeError{"transpose", v} + } + if k := len(vs); l < k { + l = k + } + } + wss := make([][]any, l) + xs := make([]any, l) + for i, k := 0, len(vss); i < l; i++ { + s := make([]any, k) + wss[i] = s + xs[i] = s + } + for i, vs := range vss { + for j, v := range vs.([]any) { + wss[j][i] = v + } + } + return xs +} + +func funcBsearch(v, t any) any { + vs, ok := v.([]any) + if !ok { + return &func1TypeError{"bsearch", v, t} + } + 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 any) any { + if v, ok := toFloat(v); ok { + return epochToArray(v, time.UTC) + } + return &func0TypeError{"gmtime", v} +} + +func funcLocaltime(v any) any { + if v, ok := toFloat(v); ok { + return epochToArray(v, time.Local) + } + return &func0TypeError{"localtime", v} +} + +func epochToArray(v float64, loc *time.Location) []any { + t := time.Unix(int64(v), int64((v-math.Floor(v))*1e9)).In(loc) + return []any{ + 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 any) any { + a, ok := v.([]any) + if !ok { + return &func0TypeError{"mktime", v} + } + t, err := arrayToTime(a, time.UTC) + if err != nil { + return &func0WrapError{"mktime", v, err} + } + return timeToEpoch(t) +} + +func timeToEpoch(t time.Time) float64 { + return float64(t.Unix()) + float64(t.Nanosecond())/1e9 +} + +func funcStrftime(v, x any) any { + if w, ok := toFloat(v); ok { + v = epochToArray(w, time.UTC) + } + a, ok := v.([]any) + if !ok { + return &func1TypeError{"strftime", v, x} + } + format, ok := x.(string) + if !ok { + return &func1TypeError{"strftime", v, x} + } + t, err := arrayToTime(a, time.UTC) + if err != nil { + return &func1WrapError{"strftime", v, x, err} + } + return timefmt.Format(t, format) +} + +func funcStrflocaltime(v, x any) any { + if w, ok := toFloat(v); ok { + v = epochToArray(w, time.Local) + } + a, ok := v.([]any) + if !ok { + return &func1TypeError{"strflocaltime", v, x} + } + format, ok := x.(string) + if !ok { + return &func1TypeError{"strflocaltime", v, x} + } + t, err := arrayToTime(a, time.Local) + if err != nil { + return &func1WrapError{"strflocaltime", v, x, err} + } + return timefmt.Format(t, format) +} + +func funcStrptime(v, x any) any { + s, ok := v.(string) + if !ok { + return &func1TypeError{"strptime", v, x} + } + format, ok := x.(string) + if !ok { + return &func1TypeError{"strptime", v, x} + } + t, err := timefmt.Parse(s, format) + if err != nil { + return &func1WrapError{"strptime", v, x, err} + } + if t.Equal(time.Time{}) { + return &func1TypeError{"strptime", v, x} + } + return epochToArray(timeToEpoch(t), time.UTC) +} + +func arrayToTime(a []any, loc *time.Location) (time.Time, error) { + var t time.Time + var year, month, day, hour, minute, + second, nanosecond, weekday, yearday int + for i, p := range []*int{ + &year, &month, &day, &hour, &minute, + &second, &weekday, &yearday, + } { + if i >= len(a) { + break + } + if i == 5 { + if v, ok := toFloat(a[i]); ok { + *p = int(v) + nanosecond = int((v - math.Floor(v)) * 1e9) + } else { + return t, &timeArrayError{} + } + } else if v, ok := toInt(a[i]); ok { + *p = v + } else { + return t, &timeArrayError{} + } + } + return time.Date(year, time.Month(month+1), day, + hour, minute, second, nanosecond, loc), nil +} + +func funcNow(any) any { + return timeToEpoch(time.Now()) +} + +func funcMatch(v, re, fs, testing any, cache *sync.Map) any { + var name string + if testing == true { + name = "test" + } else { + name = "match" + } + var flags string + if fs != nil { + var ok bool + flags, ok = fs.(string) + if !ok { + return &func2TypeError{name, v, re, fs} + } + } + s, ok := v.(string) + if !ok { + return &func2TypeError{name, v, re, fs} + } + restr, ok := re.(string) + if !ok { + return &func2TypeError{name, v, re, fs} + } + r, err := compileRegexp(restr, flags, cache) + if err != nil { + return err + } + if testing == true { + return r.MatchString(s) + } + var n int + if strings.ContainsRune(flags, 'g') { + n = -1 + } else { + n = 1 + } + xs := r.FindAllStringSubmatchIndex(s, n) + res, names := make([]any, len(xs)), r.SubexpNames() + for i, x := range xs { + captures := make([]any, (len(x)-2)/2) + for j := 1; j < len(x)/2; j++ { + var name any + if n := names[j]; n != "" { + name = n + } + if x[j*2] < 0 { + captures[j-1] = map[string]any{ + "name": name, + "offset": -1, + "length": 0, + "string": nil, + } + continue + } + captures[j-1] = map[string]any{ + "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]any{ + "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, cache *sync.Map) (*regexp.Regexp, error) { + key := [2]string{re, flags} + if r, ok := cache.Load(key); ok { + return r.(*regexp.Regexp), nil + } + 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) + } + 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) + } + cache.Store(key, r) + return r, nil +} + +func funcCaptures(v any) any { + captures, ok := v.([]any) + if !ok { + return &expectedArrayError{v} + } + w := make(map[string]any, len(captures)) + for _, capture := range captures { + if capture, ok := capture.(map[string]any); ok { + if name, ok := capture["name"].(string); ok { + w[name] = capture["string"] + } + } + } + return w +} + +func funcError(v any, args []any) any { + if len(args) > 0 { + v = args[0] + } + return &exitCodeError{v, 5} +} + +func funcHalt(any) any { + return &HaltError{nil, 0} +} + +func funcHaltError(v any, args []any) any { + code := 5 + if len(args) > 0 { + var ok bool + if code, ok = toInt(args[0]); !ok { + return &func0TypeError{"halt_error", args[0]} + } + } + return &HaltError{v, code} +} + +func toInt(x any) (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(); math.MinInt <= i && i <= math.MaxInt { + return int(i), true + } + } + if x.Sign() > 0 { + return math.MaxInt, true + } + return math.MinInt, true + case json.Number: + return toInt(parseNumber(x)) + default: + return 0, false + } +} + +func toIntCeil(x any) (int, bool) { + if f, ok := x.(float64); ok { + x = math.Ceil(f) + } + return toInt(x) +} + +func floatToInt(x float64) int { + if math.MinInt <= x && x < math.MaxInt { + return int(x) + } + if x > 0 { + return math.MaxInt + } + return math.MinInt +} + +func toFloat(x any) (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 json.Number: + v, err := x.Float64() + return v, err == nil + 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()) +} + +func parseNumber(v json.Number) any { + if i, err := v.Int64(); err == nil && math.MinInt <= i && i <= math.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(), 10); ok { + return bi + } + if strings.HasPrefix(v.String(), "-") { + return math.Inf(-1) + } + return math.Inf(1) +} diff --git a/vendor/github.com/itchyny/gojq/go.dev.mod b/vendor/github.com/itchyny/gojq/go.dev.mod new file mode 100644 index 000000000000..dc9f4cc5f292 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/go.dev.mod @@ -0,0 +1,8 @@ +module github.com/itchyny/gojq + +go 1.24.0 + +require ( + github.com/itchyny/astgen-go v0.0.0-20250520171007-4331c963041e // indirect + github.com/itchyny/timefmt-go v0.1.8 // indirect +) diff --git a/vendor/github.com/itchyny/gojq/go.dev.sum b/vendor/github.com/itchyny/gojq/go.dev.sum new file mode 100644 index 000000000000..873ec047e0ca --- /dev/null +++ b/vendor/github.com/itchyny/gojq/go.dev.sum @@ -0,0 +1,4 @@ +github.com/itchyny/astgen-go v0.0.0-20250520171007-4331c963041e h1:Pu7Ev38D+75aWdsHzULyVZfMP3WrgIHNUaAUH81CTqE= +github.com/itchyny/astgen-go v0.0.0-20250520171007-4331c963041e/go.mod h1:8EXgnGrgW1p0qVnOF1A/4ORQEUAi8TvxQdIR+zJfKxM= +github.com/itchyny/timefmt-go v0.1.8 h1:1YEo1JvfXeAHKdjelbYr/uCuhkybaHCeTkH8Bo791OI= +github.com/itchyny/timefmt-go v0.1.8/go.mod h1:5E46Q+zj7vbTgWY8o5YkMeYb4I6GeWLFnetPy5oBrAI= diff --git a/vendor/github.com/itchyny/gojq/gojq.go b/vendor/github.com/itchyny/gojq/gojq.go new file mode 100644 index 000000000000..e078c80908be --- /dev/null +++ b/vendor/github.com/itchyny/gojq/gojq.go @@ -0,0 +1,5 @@ +// Package gojq provides the parser and the interpreter of gojq. +// Please refer to [Usage as a library] for introduction. +// +// [Usage as a library]: https://github.com/itchyny/gojq#usage-as-a-library +package gojq diff --git a/vendor/github.com/itchyny/gojq/iter.go b/vendor/github.com/itchyny/gojq/iter.go new file mode 100644 index 000000000000..fb8a8ffe83b3 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/iter.go @@ -0,0 +1,49 @@ +package gojq + +// Iter is an interface for an iterator. +type Iter interface { + Next() (any, bool) +} + +// NewIter creates a new [Iter] from values. +func NewIter[T any](values ...T) Iter { + switch len(values) { + case 0: + return emptyIter{} + case 1: + return &unitIter{value: values[0]} + default: + iter := sliceIter[T](values) + return &iter + } +} + +type emptyIter struct{} + +func (emptyIter) Next() (any, bool) { + return nil, false +} + +type unitIter struct { + value any + done bool +} + +func (iter *unitIter) Next() (any, bool) { + if iter.done { + return nil, false + } + iter.done = true + return iter.value, true +} + +type sliceIter[T any] []T + +func (iter *sliceIter[T]) Next() (any, bool) { + if len(*iter) == 0 { + return nil, false + } + value := (*iter)[0] + *iter = (*iter)[1:] + return value, true +} diff --git a/vendor/github.com/itchyny/gojq/lexer.go b/vendor/github.com/itchyny/gojq/lexer.go new file mode 100644 index 000000000000..82aeef31aaa7 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/lexer.go @@ -0,0 +1,578 @@ +package gojq + +import ( + "encoding/json" + "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): + 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, size := utf8.DecodeRuneInString(l.source[l.offset-1:]) + // -1 to adjust for first byte consumed by next() + l.offset += size - 1 + l.token = string(r) + } + } + return int(ch) +} + +func (l *lexer) next() (byte, bool) { + for { + ch := l.source[l.offset] + l.offset++ + if ch == '#' { + if l.skipComment() { + return 0, true + } + } else if !isWhite(ch) { + return ch, false + } else if len(l.source) == l.offset { + return 0, true + } + } +} + +func (l *lexer) skipComment() bool { + for { + switch l.peek() { + case 0: + return true + case '\\': + switch l.offset++; l.peek() { + case '\\', '\n': + l.offset++ + case '\r': + if l.offset++; l.peek() == '\n' { + l.offset++ + } + } + case '\n', '\r': + return false + default: + l.offset++ + } + } +} + +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 decode bool + var controls int + unquote := func(src string, quote bool) (string, error) { + if !decode { + if quote { + return src, nil + } + return src[1 : len(src)-1], nil + } + var buf []byte + if !quote && controls == 0 { + buf = []byte(src) + } else { + buf = quoteAndEscape(src, quote, controls) + } + if err := json.Unmarshal(buf, &src); err != nil { + return "", err + } + return src, nil + } + for i := l.offset; i < len(l.source); i++ { + ch := l.source[i] + switch ch { + case '\\': + if i++; i >= len(l.source) { + break + } + switch l.source[i] { + case 'u': + for j := 1; j <= 4; j++ { + if i+j >= len(l.source) || !isHex(l.source[i+j]) { + l.offset = i + j + l.token = l.source[i-1 : l.offset] + return tokInvalidEscapeSequence, "" + } + } + i += 4 + fallthrough + case '"', '/', '\\', 'b', 'f', 'n', 'r', 't': + decode = true + case '(': + if !l.inString { + l.inString = true + return tokStringStart, "" + } + if i == l.offset+1 { + l.offset += 2 + l.inString = false + return tokStringQuery, "" + } + 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 + default: + l.offset = i + 1 + l.token = l.source[l.offset-2 : l.offset] + return tokInvalidEscapeSequence, "" + } + case '"': + 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, "" + default: + if !decode { + decode = ch > '~' + } + if ch < ' ' { // ref: unquoteBytes in encoding/json + controls++ + } + } + } + l.offset = len(l.source) + l.token = "" + return tokUnterminatedString, "" +} + +func quoteAndEscape(src string, quote bool, controls int) []byte { + size := len(src) + controls*5 + if quote { + size += 2 + } + buf := make([]byte, size) + var j int + if quote { + buf[0] = '"' + buf[len(buf)-1] = '"' + j++ + } + for i := range len(src) { + if ch := src[i]; ch < ' ' { + const hex = "0123456789abcdef" + copy(buf[j:], `\u00`) + buf[j+4] = hex[ch>>4] + buf[j+5] = hex[ch&0xF] + j += 6 + } else { + buf[j] = ch + j++ + } + } + return buf +} + +// ParseError represents a description of a query parsing error. +type ParseError struct { + Offset int // the error occurred after reading Offset bytes + Token string // the Token that caused the error (may be empty) + tokenType int +} + +func (err *ParseError) Error() string { + switch err.tokenType { + case eof: + return "unexpected EOF" + case tokInvalid: + return "invalid token " + jsonMarshal(err.Token) + case tokInvalidEscapeSequence: + return `invalid escape sequence "` + err.Token + `" in string literal` + case tokUnterminatedString: + return "unterminated string literal" + default: + return "unexpected token " + jsonMarshal(err.Token) + } +} + +func (l *lexer) Error(string) { + offset, token := l.offset, l.token + if l.tokenType != eof && l.tokenType < utf8.RuneSelf { + 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 isHex(ch byte) bool { + return 'a' <= ch && ch <= 'f' || + 'A' <= ch && ch <= 'F' || + isNumber(ch) +} + +func isNumber(ch byte) bool { + return '0' <= ch && ch <= '9' +} diff --git a/vendor/github.com/itchyny/gojq/module_loader.go b/vendor/github.com/itchyny/gojq/module_loader.go new file mode 100644 index 000000000000..0e3d1af034ee --- /dev/null +++ b/vendor/github.com/itchyny/gojq/module_loader.go @@ -0,0 +1,184 @@ +package gojq + +import ( + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "strings" +) + +// ModuleLoader is the interface for loading modules. +// +// Implement following optional methods. Use [NewModuleLoader] to load local modules. +// +// LoadInitModules() ([]*Query, error) +// LoadModule(string) (*Query, error) +// LoadModuleWithMeta(string, map[string]any) (*Query, error) +// LoadJSON(string) (any, error) +// LoadJSONWithMeta(string, map[string]any) (any, error) +type ModuleLoader any + +// NewModuleLoader creates a new [ModuleLoader] loading local modules in the paths. +// Note that user can load modules outside the paths using "search" path of metadata. +// Empty paths are ignored, so specify "." for the current working directory. +func NewModuleLoader(paths []string) ModuleLoader { + ps := make([]string, 0, len(paths)) + for _, path := range paths { + if path = resolvePath(path, ""); path != "" { + ps = append(ps, path) + } + } + return &moduleLoader{ps} +} + +type moduleLoader struct { + paths []string +} + +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(string(cnt), filepath.Dir(path)) + if err != nil { + return nil, &queryParseError{path, string(cnt), err} + } + qs = append(qs, q) + } + return qs, nil +} + +func (l *moduleLoader) LoadModuleWithMeta(name string, meta map[string]any) (*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(string(cnt), filepath.Dir(path)) + if err != nil { + return nil, &queryParseError{path, string(cnt), err} + } + return q, nil +} + +func (l *moduleLoader) LoadJSONWithMeta(name string, meta map[string]any) (any, 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() + vals := []any{} + dec := json.NewDecoder(f) + dec.UseNumber() + for { + var val any + 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]any) (string, error) { + paths := l.paths + if path, ok := meta["search"].(string); ok { + if path = resolvePath(path, ""); path != "" { + paths = append([]string{path}, paths...) + } + } + for _, base := range paths { + path := filepath.Join(base, name+extension) + if _, err := os.Stat(path); err == nil { + return path, err + } + path = 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) +} + +func parseModule(cnt, dir string) (*Query, error) { + q, err := Parse(cnt) + if err != nil { + return nil, err + } + for _, i := range q.Imports { + if i.Meta != nil { + for _, e := range i.Meta.KeyVals { + if e.Key == "search" || e.KeyString == "search" { + if path, ok := e.Val.toString(); ok { + if path = resolvePath(path, dir); path != "" { + e.Val.Str = path + } else { + e.Val.Null = true + } + } + } + } + } + } + return q, nil +} + +func resolvePath(path, dir string) string { + switch { + case filepath.IsAbs(path): + return path + case strings.HasPrefix(path, "~/"): + dir, err := os.UserHomeDir() + if err != nil { + return "" + } + return filepath.Join(dir, path[2:]) + case strings.HasPrefix(path, "$ORIGIN/"): + exe, err := os.Executable() + if err != nil { + return "" + } + exe, err = filepath.EvalSymlinks(exe) + if err != nil { + return "" + } + return filepath.Join(filepath.Dir(exe), path[8:]) + default: + return filepath.Join(dir, path) + } +} diff --git a/vendor/github.com/itchyny/gojq/operator.go b/vendor/github.com/itchyny/gojq/operator.go new file mode 100644 index 000000000000..b1bf66702586 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/operator.go @@ -0,0 +1,557 @@ +package gojq + +import ( + "encoding/json" + "maps" + "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 [fmt.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: + panic(op) + } +} + +// GoString implements [fmt.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 OpAdd, OpUpdateAdd: + return "_add" + case OpSub, OpUpdateSub: + return "_subtract" + case OpMul, OpUpdateMul: + return "_multiply" + case OpDiv, OpUpdateDiv: + return "_divide" + case OpMod, OpUpdateMod: + 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 OpAssign: + return "_assign" + case OpModify: + return "_modify" + case OpUpdateAlt: + return "_alternative" + default: + panic(op) + } +} + +func binopTypeSwitch[T any]( + l, r any, + callbackInts func(_, _ int) T, + callbackFloats func(_, _ float64) T, + callbackBigInts func(_, _ *big.Int) T, + callbackStrings func(_, _ string) T, + callbackArrays func(_, _ []any) T, + callbackMaps func(_, _ map[string]any) T, + fallback func(_, _ any) T) T { + if n, ok := l.(json.Number); ok { + l = parseNumber(n) + } + if n, ok := r.(json.Number); ok { + r = parseNumber(n) + } + switch l := l.(type) { + case int: + switch r := r.(type) { + case int: + return callbackInts(l, 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 []any: + switch r := r.(type) { + case []any: + return callbackArrays(l, r) + default: + return fallback(l, r) + } + case map[string]any: + switch r := r.(type) { + case map[string]any: + return callbackMaps(l, r) + default: + return fallback(l, r) + } + default: + return fallback(l, r) + } +} + +func funcOpPlus(v any) any { + switch v := v.(type) { + case int: + return v + case float64: + return v + case *big.Int: + return v + case json.Number: + return v + default: + return &unaryTypeError{"plus", v} + } +} + +func negate(v int) any { + if v == math.MinInt { + return new(big.Int).Neg(big.NewInt(int64(v))) + } + return -v +} + +func funcOpNegate(v any) any { + switch v := v.(type) { + case int: + return negate(v) + case float64: + return -v + case *big.Int: + return new(big.Int).Neg(v) + case json.Number: + if strings.HasPrefix(v.String(), "-") { + return v[1:] + } + return "-" + v + default: + return &unaryTypeError{"negate", v} + } +} + +func funcOpAdd(_, l, r any) any { + return binopTypeSwitch(l, r, + func(l, r int) any { + if v := l + r; (v >= l) == (r >= 0) { + return v + } + x, y := big.NewInt(int64(l)), big.NewInt(int64(r)) + return x.Add(x, y) + }, + func(l, r float64) any { return l + r }, + func(l, r *big.Int) any { return new(big.Int).Add(l, r) }, + func(l, r string) any { return l + r }, + func(l, r []any) any { + if len(l) == 0 { + return r + } + if len(r) == 0 { + return l + } + v := make([]any, len(l)+len(r)) + copy(v, l) + copy(v[len(l):], r) + return v + }, + func(l, r map[string]any) any { + if len(l) == 0 { + return r + } + if len(r) == 0 { + return l + } + m := make(map[string]any, len(l)+len(r)) + maps.Copy(m, l) + maps.Copy(m, r) + return m + }, + func(l, r any) any { + if l == nil { + return r + } + if r == nil { + return l + } + return &binopTypeError{"add", l, r} + }, + ) +} + +func funcOpSub(_, l, r any) any { + return binopTypeSwitch(l, r, + func(l, r int) any { + if v := l - r; (v <= l) == (r >= 0) { + return v + } + x, y := big.NewInt(int64(l)), big.NewInt(int64(r)) + return x.Sub(x, y) + }, + func(l, r float64) any { return l - r }, + func(l, r *big.Int) any { return new(big.Int).Sub(l, r) }, + func(l, r string) any { return &binopTypeError{"subtract", l, r} }, + func(l, r []any) any { + v := make([]any, 0, len(l)) + L: + for _, l := range l { + for _, r := range r { + if Compare(l, r) == 0 { + continue L + } + } + v = append(v, l) + } + return v + }, + func(l, r map[string]any) any { return &binopTypeError{"subtract", l, r} }, + func(l, r any) any { return &binopTypeError{"subtract", l, r} }, + ) +} + +func funcOpMul(_, l, r any) any { + return binopTypeSwitch(l, r, + func(l, r int) any { + if r == -1 { + return negate(l) + } + if v := l * r; r == 0 || v/r == l { + return v + } + x, y := big.NewInt(int64(l)), big.NewInt(int64(r)) + return x.Mul(x, y) + }, + func(l, r float64) any { return l * r }, + func(l, r *big.Int) any { return new(big.Int).Mul(l, r) }, + func(l, r string) any { return &binopTypeError{"multiply", l, r} }, + func(l, r []any) any { return &binopTypeError{"multiply", l, r} }, + deepMergeObjects, + func(l, r any) any { + if l, ok := l.(string); ok { + if r, ok := toFloat(r); ok { + return repeatString(l, r) + } + } + if r, ok := r.(string); ok { + if l, ok := toFloat(l); ok { + return repeatString(r, l) + } + } + return &binopTypeError{"multiply", l, r} + }, + ) +} + +func deepMergeObjects(l, r map[string]any) any { + m := make(map[string]any, len(l)+len(r)) + maps.Copy(m, l) + for k, v := range r { + if mk, ok := m[k]; ok { + if mk, ok := mk.(map[string]any); ok { + if w, ok := v.(map[string]any); ok { + v = deepMergeObjects(mk, w) + } + } + } + m[k] = v + } + return m +} + +func repeatString(s string, n float64) any { + if lt(n, 0) { + return nil + } + c := int(min(n, math.MaxInt32)) + if uint64(len(s))*uint64(c) >= math.MaxInt32 { + return &repeatStringTooLargeError{s, n} + } + return strings.Repeat(s, c) +} + +func funcOpDiv(_, l, r any) any { + return binopTypeSwitch(l, r, + func(l, r int) any { + switch r { + case 0: + return &zeroDivisionError{l, r} + case -1: + return negate(l) + default: + if l%r == 0 { + return l / r + } + return float64(l) / float64(r) + } + }, + func(l, r float64) any { + if r == 0.0 { + return &zeroDivisionError{l, r} + } + return l / r + }, + func(l, r *big.Int) any { + if r.Sign() == 0 { + 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) any { + if l == "" { + return []any{} + } + xs := strings.Split(l, r) + vs := make([]any, len(xs)) + for i, x := range xs { + vs[i] = x + } + return vs + }, + func(l, r []any) any { return &binopTypeError{"divide", l, r} }, + func(l, r map[string]any) any { return &binopTypeError{"divide", l, r} }, + func(l, r any) any { return &binopTypeError{"divide", l, r} }, + ) +} + +func funcOpMod(_, l, r any) any { + return binopTypeSwitch(l, r, + func(l, r int) any { + switch r { + case 0: + return &zeroModuloError{l, r} + case -1: + return 0 + default: + return l % r + } + }, + func(l, r float64) any { + if math.IsNaN(l) || math.IsNaN(r) { + return math.NaN() + } + ri := floatToInt(r) + if ri == 0 { + return &zeroModuloError{l, r} + } + return floatToInt(l) % ri + }, + func(l, r *big.Int) any { + if r.Sign() == 0 { + return &zeroModuloError{l, r} + } + return new(big.Int).Rem(l, r) + }, + func(l, r string) any { return &binopTypeError{"modulo", l, r} }, + func(l, r []any) any { return &binopTypeError{"modulo", l, r} }, + func(l, r map[string]any) any { return &binopTypeError{"modulo", l, r} }, + func(l, r any) any { return &binopTypeError{"modulo", l, r} }, + ) +} + +func funcOpAlt(_, l, r any) any { + if l == nil || l == false { + return r + } + return l +} + +func funcOpEq(_, l, r any) any { + return Compare(l, r) == 0 +} + +func funcOpNe(_, l, r any) any { + return Compare(l, r) != 0 +} + +func funcOpGt(_, l, r any) any { + return Compare(l, r) > 0 +} + +func funcOpLt(_, l, r any) any { + return Compare(l, r) < 0 +} + +func funcOpGe(_, l, r any) any { + return Compare(l, r) >= 0 +} + +func funcOpLe(_, l, r any) any { + return Compare(l, r) <= 0 +} diff --git a/vendor/github.com/itchyny/gojq/option.go b/vendor/github.com/itchyny/gojq/option.go new file mode 100644 index 000000000000..801e9a999a80 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/option.go @@ -0,0 +1,96 @@ +package 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 +// reasons. You can specify [os.Environ] as argument if you allow to access. +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, take account of int, float64, *big.Int, and json.Number. +// 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(any, []any) any) 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(any, []any) Iter) CompilerOption { + return withFunction(name, minarity, maxarity, true, + func(v any, args []any) any { + return f(v, args) + }, + ) +} + +func withFunction(name string, minarity, maxarity int, iter bool, f func(any, []any) any) 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<= 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 := int(yyPact[state]) + for tok := TOKSTART; tok-1 < len(yyToknames); tok++ { + if n := base + tok; n >= 0 && n < yyLast && int(yyChk[int(yyAct[n])]) == tok { + if len(expected) == cap(expected) { + return res + } + expected = append(expected, tok) + } + } + + if yyDef[state] == -2 { + i := 0 + for yyExca[i] != -1 || int(yyExca[i+1]) != state { + i += 2 + } + + // Look for tokens that we accept or reduce. + for i += 2; yyExca[i] >= 0; i += 2 { + tok := int(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 = int(yyTok1[0]) + goto out + } + if char < len(yyTok1) { + token = int(yyTok1[char]) + goto out + } + if char >= yyPrivate { + if char < yyPrivate+len(yyTok2) { + token = int(yyTok2[char-yyPrivate]) + goto out + } + } + for i := 0; i < len(yyTok3); i += 2 { + token = int(yyTok3[i+0]) + if token == char { + token = int(yyTok3[i+1]) + goto out + } + } + +out: + if token == 0 { + token = int(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 = int(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 = int(yyAct[yyn]) + if int(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 = int(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 && int(yyExca[xi+1]) == yystate { + break + } + xi += 2 + } + for xi += 2; ; xi += 2 { + yyn = int(yyExca[xi+0]) + if yyn < 0 || yyn == yytoken { + break + } + } + yyn = int(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 = int(yyPact[yyS[yyp].yys]) + yyErrCode + if yyn >= 0 && yyn < yyLast { + yystate = int(yyAct[yyn]) /* simulate a shift of "error" */ + if int(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 -= int(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 = int(yyR1[yyn]) + yyg := int(yyPgo[yyn]) + yyj := yyg + yyS[yyp].yys + 1 + + if yyj >= yyLast { + yystate = int(yyAct[yyg]) + } else { + yystate = int(yyAct[yyj]) + if int(yyChk[yystate]) != -yyn { + yystate = int(yyAct[yyg]) + } + } + // dummy call; replaced with literal code + switch yynt { + + case 1: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:58 + { + query := yyDollar[3].value.(*Query) + query.Meta = yyDollar[1].value.(*ConstObject) + query.Imports = yyDollar[2].value.([]*Import) + yylex.(*lexer).result = query + } + case 2: + yyDollar = yyS[yypt-0 : yypt+1] +//line parser.go.y:67 + { + yyVAL.value = (*ConstObject)(nil) + } + case 3: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:71 + { + yyVAL.value = yyDollar[2].value + } + case 4: + yyDollar = yyS[yypt-0 : yypt+1] +//line parser.go.y:77 + { + yyVAL.value = []*Import(nil) + } + case 5: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:81 + { + yyVAL.value = append(yyDollar[1].value.([]*Import), yyDollar[2].value.(*Import)) + } + case 6: + yyDollar = yyS[yypt-6 : yypt+1] +//line parser.go.y:87 + { + yyVAL.value = &Import{ImportPath: yyDollar[2].token, ImportAlias: yyDollar[4].token, Meta: yyDollar[5].value.(*ConstObject)} + } + case 7: + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:91 + { + yyVAL.value = &Import{IncludePath: yyDollar[2].token, Meta: yyDollar[3].value.(*ConstObject)} + } + case 8: + yyDollar = yyS[yypt-0 : yypt+1] +//line parser.go.y:97 + { + yyVAL.value = (*ConstObject)(nil) + } + case 10: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:104 + { + yyVAL.value = &Query{FuncDefs: reverseFuncDef(yyDollar[1].value.([]*FuncDef))} + } + case 12: + yyDollar = yyS[yypt-0 : yypt+1] +//line parser.go.y:111 + { + yyVAL.value = []*FuncDef(nil) + } + case 13: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:115 + { + yyVAL.value = append(yyDollar[2].value.([]*FuncDef), yyDollar[1].value.(*FuncDef)) + } + case 14: + yyDollar = yyS[yypt-5 : yypt+1] +//line parser.go.y:121 + { + yyVAL.value = &FuncDef{Name: yyDollar[2].token, Body: yyDollar[4].value.(*Query)} + } + case 15: + yyDollar = yyS[yypt-8 : yypt+1] +//line parser.go.y:125 + { + 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:131 + { + yyVAL.value = []string{yyDollar[1].token} + } + case 17: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:135 + { + yyVAL.value = append(yyDollar[1].value.([]string), yyDollar[3].token) + } + case 20: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:145 + { + query := yyDollar[2].value.(*Query) + query.FuncDefs = prependFuncDef(query.FuncDefs, yyDollar[1].value.(*FuncDef)) + yyVAL.value = query + } + case 21: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:151 + { + 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:155 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpPipe, Right: yyDollar[5].value.(*Query), Patterns: yyDollar[3].value.([]*Pattern)} + } + case 23: + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:159 + { + yyVAL.value = &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{yyDollar[2].token, yyDollar[4].value.(*Query)}}} + } + case 24: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:163 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpComma, Right: yyDollar[3].value.(*Query)} + } + case 26: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:170 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: yyDollar[2].operator, Right: yyDollar[3].value.(*Query)} + } + case 27: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:174 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: yyDollar[2].operator, Right: yyDollar[3].value.(*Query)} + } + case 28: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:178 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpOr, Right: yyDollar[3].value.(*Query)} + } + case 29: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:182 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpAnd, Right: yyDollar[3].value.(*Query)} + } + case 30: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:186 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: yyDollar[2].operator, Right: yyDollar[3].value.(*Query)} + } + case 31: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:190 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpAdd, Right: yyDollar[3].value.(*Query)} + } + case 32: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:194 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpSub, Right: yyDollar[3].value.(*Query)} + } + case 33: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:198 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpMul, Right: yyDollar[3].value.(*Query)} + } + case 34: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:202 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpDiv, Right: yyDollar[3].value.(*Query)} + } + case 35: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:206 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpMod, Right: yyDollar[3].value.(*Query)} + } + case 36: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:210 + { + yyVAL.value = &Query{Term: yyDollar[1].value.(*Term)} + } + case 37: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:216 + { + yyVAL.value = []*Pattern{yyDollar[1].value.(*Pattern)} + } + case 38: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:220 + { + yyVAL.value = append(yyDollar[1].value.([]*Pattern), yyDollar[3].value.(*Pattern)) + } + case 39: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:226 + { + yyVAL.value = &Pattern{Name: yyDollar[1].token} + } + case 40: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:230 + { + yyVAL.value = &Pattern{Array: yyDollar[2].value.([]*Pattern)} + } + case 41: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:234 + { + yyVAL.value = &Pattern{Object: yyDollar[2].value.([]*PatternObject)} + } + case 42: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:240 + { + yyVAL.value = []*Pattern{yyDollar[1].value.(*Pattern)} + } + case 43: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:244 + { + yyVAL.value = append(yyDollar[1].value.([]*Pattern), yyDollar[3].value.(*Pattern)) + } + case 44: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:250 + { + yyVAL.value = []*PatternObject{yyDollar[1].value.(*PatternObject)} + } + case 45: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:254 + { + yyVAL.value = append(yyDollar[1].value.([]*PatternObject), yyDollar[3].value.(*PatternObject)) + } + case 46: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:260 + { + yyVAL.value = &PatternObject{Key: yyDollar[1].token, Val: yyDollar[3].value.(*Pattern)} + } + case 47: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:264 + { + yyVAL.value = &PatternObject{KeyString: yyDollar[1].value.(*String), Val: yyDollar[3].value.(*Pattern)} + } + case 48: + yyDollar = yyS[yypt-5 : yypt+1] +//line parser.go.y:268 + { + yyVAL.value = &PatternObject{KeyQuery: yyDollar[2].value.(*Query), Val: yyDollar[5].value.(*Pattern)} + } + case 49: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:272 + { + yyVAL.value = &PatternObject{Key: yyDollar[1].token} + } + case 50: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:278 + { + yyVAL.value = &Term{Type: TermTypeIdentity} + } + case 51: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:282 + { + yyVAL.value = &Term{Type: TermTypeRecurse} + } + case 52: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:286 + { + yyVAL.value = &Term{Type: TermTypeIndex, Index: &Index{Name: yyDollar[1].token}} + } + case 53: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:290 + { + suffix := yyDollar[2].value.(*Suffix) + if suffix.Iter { + yyVAL.value = &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{suffix}} + } else { + yyVAL.value = &Term{Type: TermTypeIndex, Index: suffix.Index} + } + } + case 54: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:299 + { + yyVAL.value = &Term{Type: TermTypeIndex, Index: &Index{Str: yyDollar[2].value.(*String)}} + } + case 55: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:303 + { + yyVAL.value = &Term{Type: TermTypeNull} + } + case 56: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:307 + { + yyVAL.value = &Term{Type: TermTypeTrue} + } + case 57: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:311 + { + yyVAL.value = &Term{Type: TermTypeFalse} + } + case 58: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:315 + { + yyVAL.value = &Term{Type: TermTypeFunc, Func: &Func{Name: yyDollar[1].token}} + } + case 59: + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:319 + { + yyVAL.value = &Term{Type: TermTypeFunc, Func: &Func{Name: yyDollar[1].token, Args: yyDollar[3].value.([]*Query)}} + } + case 60: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:323 + { + yyVAL.value = &Term{Type: TermTypeFunc, Func: &Func{Name: yyDollar[1].token}} + } + case 61: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:327 + { + yyVAL.value = &Term{Type: TermTypeObject, Object: &Object{}} + } + case 62: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:331 + { + yyVAL.value = &Term{Type: TermTypeObject, Object: &Object{yyDollar[2].value.([]*ObjectKeyVal)}} + } + case 63: + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:335 + { + yyVAL.value = &Term{Type: TermTypeObject, Object: &Object{yyDollar[2].value.([]*ObjectKeyVal)}} + } + case 64: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:339 + { + yyVAL.value = &Term{Type: TermTypeArray, Array: &Array{}} + } + case 65: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:343 + { + yyVAL.value = &Term{Type: TermTypeArray, Array: &Array{yyDollar[2].value.(*Query)}} + } + case 66: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:347 + { + yyVAL.value = &Term{Type: TermTypeNumber, Number: yyDollar[1].token} + } + case 67: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:351 + { + yyVAL.value = &Term{Type: TermTypeUnary, Unary: &Unary{OpAdd, yyDollar[2].value.(*Term)}} + } + case 68: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:355 + { + yyVAL.value = &Term{Type: TermTypeUnary, Unary: &Unary{OpSub, yyDollar[2].value.(*Term)}} + } + case 69: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:359 + { + yyVAL.value = &Term{Type: TermTypeFormat, Format: yyDollar[1].token} + } + case 70: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:363 + { + yyVAL.value = &Term{Type: TermTypeFormat, Format: yyDollar[1].token, Str: yyDollar[2].value.(*String)} + } + case 71: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:367 + { + yyVAL.value = &Term{Type: TermTypeString, Str: yyDollar[1].value.(*String)} + } + case 72: + yyDollar = yyS[yypt-7 : yypt+1] +//line parser.go.y:371 + { + yyVAL.value = &Term{Type: TermTypeIf, If: &If{yyDollar[2].value.(*Query), yyDollar[4].value.(*Query), yyDollar[5].value.([]*IfElif), yyDollar[6].value.(*Query)}} + } + case 73: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:375 + { + yyVAL.value = &Term{Type: TermTypeTry, Try: &Try{yyDollar[2].value.(*Query), yyDollar[3].value.(*Query)}} + } + case 74: + yyDollar = yyS[yypt-9 : yypt+1] +//line parser.go.y:379 + { + yyVAL.value = &Term{Type: TermTypeReduce, Reduce: &Reduce{yyDollar[2].value.(*Query), yyDollar[4].value.(*Pattern), yyDollar[6].value.(*Query), yyDollar[8].value.(*Query)}} + } + case 75: + yyDollar = yyS[yypt-9 : yypt+1] +//line parser.go.y:383 + { + yyVAL.value = &Term{Type: TermTypeForeach, Foreach: &Foreach{yyDollar[2].value.(*Query), yyDollar[4].value.(*Pattern), yyDollar[6].value.(*Query), yyDollar[8].value.(*Query), nil}} + } + case 76: + yyDollar = yyS[yypt-11 : yypt+1] +//line parser.go.y:387 + { + yyVAL.value = &Term{Type: TermTypeForeach, Foreach: &Foreach{yyDollar[2].value.(*Query), yyDollar[4].value.(*Pattern), yyDollar[6].value.(*Query), yyDollar[8].value.(*Query), yyDollar[10].value.(*Query)}} + } + case 77: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:391 + { + yyVAL.value = &Term{Type: TermTypeBreak, Break: yyDollar[2].token} + } + case 78: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:395 + { + yyVAL.value = &Term{Type: TermTypeQuery, Query: yyDollar[2].value.(*Query)} + } + case 79: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:399 + { + 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:403 + { + 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:407 + { + 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:411 + { + 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:415 + { + 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:421 + { + yyVAL.value = &String{Str: yyDollar[1].token} + } + case 85: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:425 + { + yyVAL.value = &String{Queries: yyDollar[2].value.([]*Query)} + } + case 86: + yyDollar = yyS[yypt-0 : yypt+1] +//line parser.go.y:431 + { + yyVAL.value = []*Query{} + } + case 87: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:435 + { + 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:439 + { + yylex.(*lexer).inString = true + yyVAL.value = append(yyDollar[1].value.([]*Query), &Query{Term: &Term{Type: TermTypeQuery, Query: yyDollar[3].value.(*Query)}}) + } + case 93: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:454 + { + yyVAL.value = &Suffix{Iter: true} + } + case 94: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:458 + { + yyVAL.value = &Suffix{Index: &Index{Start: yyDollar[2].value.(*Query)}} + } + case 95: + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:462 + { + yyVAL.value = &Suffix{Index: &Index{Start: yyDollar[2].value.(*Query), IsSlice: true}} + } + case 96: + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:466 + { + yyVAL.value = &Suffix{Index: &Index{End: yyDollar[3].value.(*Query), IsSlice: true}} + } + case 97: + yyDollar = yyS[yypt-5 : yypt+1] +//line parser.go.y:470 + { + 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:476 + { + yyVAL.value = []*Query{yyDollar[1].value.(*Query)} + } + case 99: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:480 + { + yyVAL.value = append(yyDollar[1].value.([]*Query), yyDollar[3].value.(*Query)) + } + case 100: + yyDollar = yyS[yypt-0 : yypt+1] +//line parser.go.y:486 + { + yyVAL.value = []*IfElif(nil) + } + case 101: + yyDollar = yyS[yypt-5 : yypt+1] +//line parser.go.y:490 + { + 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:496 + { + yyVAL.value = (*Query)(nil) + } + case 103: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:500 + { + yyVAL.value = yyDollar[2].value + } + case 104: + yyDollar = yyS[yypt-0 : yypt+1] +//line parser.go.y:506 + { + yyVAL.value = (*Query)(nil) + } + case 105: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:510 + { + yyVAL.value = yyDollar[2].value + } + case 106: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:516 + { + yyVAL.value = []*ObjectKeyVal{yyDollar[1].value.(*ObjectKeyVal)} + } + case 107: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:520 + { + yyVAL.value = append(yyDollar[1].value.([]*ObjectKeyVal), yyDollar[3].value.(*ObjectKeyVal)) + } + case 108: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:526 + { + yyVAL.value = &ObjectKeyVal{Key: yyDollar[1].token, Val: yyDollar[3].value.(*Query)} + } + case 109: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:530 + { + yyVAL.value = &ObjectKeyVal{KeyString: yyDollar[1].value.(*String), Val: yyDollar[3].value.(*Query)} + } + case 110: + yyDollar = yyS[yypt-5 : yypt+1] +//line parser.go.y:534 + { + yyVAL.value = &ObjectKeyVal{KeyQuery: yyDollar[2].value.(*Query), Val: yyDollar[5].value.(*Query)} + } + case 111: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:538 + { + yyVAL.value = &ObjectKeyVal{Key: yyDollar[1].token} + } + case 112: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:542 + { + yyVAL.value = &ObjectKeyVal{KeyString: yyDollar[1].value.(*String)} + } + case 116: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:553 + { + yyVAL.value = &Query{Left: yyDollar[1].value.(*Query), Op: OpPipe, Right: yyDollar[3].value.(*Query)} + } + case 118: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:560 + { + yyVAL.value = &ConstTerm{Object: yyDollar[1].value.(*ConstObject)} + } + case 119: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:564 + { + yyVAL.value = &ConstTerm{Array: yyDollar[1].value.(*ConstArray)} + } + case 120: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:568 + { + yyVAL.value = &ConstTerm{Number: yyDollar[1].token} + } + case 121: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:572 + { + yyVAL.value = &ConstTerm{Str: yyDollar[1].token} + } + case 122: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:576 + { + yyVAL.value = &ConstTerm{Null: true} + } + case 123: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:580 + { + yyVAL.value = &ConstTerm{True: true} + } + case 124: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:584 + { + yyVAL.value = &ConstTerm{False: true} + } + case 125: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:590 + { + yyVAL.value = &ConstObject{} + } + case 126: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:594 + { + yyVAL.value = &ConstObject{yyDollar[2].value.([]*ConstObjectKeyVal)} + } + case 127: + yyDollar = yyS[yypt-4 : yypt+1] +//line parser.go.y:598 + { + yyVAL.value = &ConstObject{yyDollar[2].value.([]*ConstObjectKeyVal)} + } + case 128: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:604 + { + yyVAL.value = []*ConstObjectKeyVal{yyDollar[1].value.(*ConstObjectKeyVal)} + } + case 129: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:608 + { + yyVAL.value = append(yyDollar[1].value.([]*ConstObjectKeyVal), yyDollar[3].value.(*ConstObjectKeyVal)) + } + case 130: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:614 + { + yyVAL.value = &ConstObjectKeyVal{Key: yyDollar[1].token, Val: yyDollar[3].value.(*ConstTerm)} + } + case 131: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:618 + { + yyVAL.value = &ConstObjectKeyVal{Key: yyDollar[1].token, Val: yyDollar[3].value.(*ConstTerm)} + } + case 132: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:622 + { + yyVAL.value = &ConstObjectKeyVal{KeyString: yyDollar[1].token, Val: yyDollar[3].value.(*ConstTerm)} + } + case 133: + yyDollar = yyS[yypt-2 : yypt+1] +//line parser.go.y:628 + { + yyVAL.value = &ConstArray{} + } + case 134: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:632 + { + yyVAL.value = &ConstArray{yyDollar[2].value.([]*ConstTerm)} + } + case 135: + yyDollar = yyS[yypt-1 : yypt+1] +//line parser.go.y:638 + { + yyVAL.value = []*ConstTerm{yyDollar[1].value.(*ConstTerm)} + } + case 136: + yyDollar = yyS[yypt-3 : yypt+1] +//line parser.go.y:642 + { + yyVAL.value = append(yyDollar[1].value.([]*ConstTerm), yyDollar[3].value.(*ConstTerm)) + } + } + goto yystack /* stack new state and value */ +} diff --git a/vendor/github.com/itchyny/gojq/parser.go.y b/vendor/github.com/itchyny/gojq/parser.go.y new file mode 100644 index 000000000000..5f9df778e8ed --- /dev/null +++ b/vendor/github.com/itchyny/gojq/parser.go.y @@ -0,0 +1,669 @@ +%{ +package gojq + +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 any + token string + operator Operator +} + +%type program header imports import meta body funcdefs funcdef funcargs query +%type bindpatterns pattern arraypatterns objectpatterns objectpattern +%type expr 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 tokCompareOp +%token tokOrOp tokAndOp tokModule tokImport tokInclude tokDef tokAs tokLabel tokBreak +%token tokNull tokTrue tokFalse +%token tokIf tokThen tokElif tokElse tokEnd +%token tokTry tokCatch tokReduce tokForeach +%token tokIdent tokVariable tokModuleIdent tokModuleVariable +%token tokRecurse tokIndex tokNumber tokFormat +%token tokString tokStringStart tokStringQuery tokStringEnd +%token tokInvalid tokInvalidEscapeSequence tokUnterminatedString + +%nonassoc tokFuncDefQuery tokExpr tokTerm +%right '|' +%left ',' +%right tokAltOp +%nonassoc tokUpdateOp +%left tokOrOp +%left tokAndOp +%nonassoc tokCompareOp +%left '+' '-' +%left '*' '/' '%' +%nonassoc tokAs tokIndex '.' '?' tokEmptyCatch +%nonassoc '[' tokTry tokCatch + +%% + +program + : header imports body + { + query := $3.(*Query) + query.Meta = $1.(*ConstObject) + query.Imports = $2.([]*Import) + yylex.(*lexer).result = query + } + +header + : + { + $$ = (*ConstObject)(nil) + } + | tokModule constobject ';' + { + $$ = $2; + } + +imports + : + { + $$ = []*Import(nil) + } + | imports import + { + $$ = append($1.([]*Import), $2.(*Import)) + } + +import + : tokImport tokString tokAs tokIdentVariable meta ';' + { + $$ = &Import{ImportPath: $2, ImportAlias: $4, Meta: $5.(*ConstObject)} + } + | tokInclude tokString meta ';' + { + $$ = &Import{IncludePath: $2, Meta: $3.(*ConstObject)} + } + +meta + : + { + $$ = (*ConstObject)(nil) + } + | constobject + +body + : funcdefs + { + $$ = &Query{FuncDefs: reverseFuncDef($1.([]*FuncDef))} + } + | query + +funcdefs + : + { + $$ = []*FuncDef(nil) + } + | funcdef funcdefs + { + $$ = append($2.([]*FuncDef), $1.(*FuncDef)) + } + +funcdef + : tokDef tokIdent ':' query ';' + { + $$ = &FuncDef{Name: $2, Body: $4.(*Query)} + } + | tokDef tokIdent '(' funcargs ')' ':' query ';' + { + $$ = &FuncDef{$2, $4.([]string), $7.(*Query)} + } + +funcargs + : tokIdentVariable + { + $$ = []string{$1} + } + | funcargs ';' tokIdentVariable + { + $$ = append($1.([]string), $3) + } + +tokIdentVariable + : tokIdent + | tokVariable + +query + : funcdef query %prec tokFuncDefQuery + { + query := $2.(*Query) + query.FuncDefs = prependFuncDef(query.FuncDefs, $1.(*FuncDef)) + $$ = query + } + | query '|' query + { + $$ = &Query{Left: $1.(*Query), Op: OpPipe, Right: $3.(*Query)} + } + | query tokAs bindpatterns '|' query + { + $$ = &Query{Left: $1.(*Query), Op: OpPipe, Right: $5.(*Query), Patterns: $3.([]*Pattern)} + } + | tokLabel tokVariable '|' query + { + $$ = &Query{Term: &Term{Type: TermTypeLabel, Label: &Label{$2, $4.(*Query)}}} + } + | query ',' query + { + $$ = &Query{Left: $1.(*Query), Op: OpComma, Right: $3.(*Query)} + } + | expr %prec tokExpr + +expr + : expr tokAltOp expr + { + $$ = &Query{Left: $1.(*Query), Op: $2, Right: $3.(*Query)} + } + | expr tokUpdateOp expr + { + $$ = &Query{Left: $1.(*Query), Op: $2, Right: $3.(*Query)} + } + | expr tokOrOp expr + { + $$ = &Query{Left: $1.(*Query), Op: OpOr, Right: $3.(*Query)} + } + | expr tokAndOp expr + { + $$ = &Query{Left: $1.(*Query), Op: OpAnd, Right: $3.(*Query)} + } + | expr tokCompareOp expr + { + $$ = &Query{Left: $1.(*Query), Op: $2, Right: $3.(*Query)} + } + | expr '+' expr + { + $$ = &Query{Left: $1.(*Query), Op: OpAdd, Right: $3.(*Query)} + } + | expr '-' expr + { + $$ = &Query{Left: $1.(*Query), Op: OpSub, Right: $3.(*Query)} + } + | expr '*' expr + { + $$ = &Query{Left: $1.(*Query), Op: OpMul, Right: $3.(*Query)} + } + | expr '/' expr + { + $$ = &Query{Left: $1.(*Query), Op: OpDiv, Right: $3.(*Query)} + } + | expr '%' expr + { + $$ = &Query{Left: $1.(*Query), Op: OpMod, Right: $3.(*Query)} + } + | term %prec tokTerm + { + $$ = &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{Key: $1} + } + +term + : '.' + { + $$ = &Term{Type: TermTypeIdentity} + } + | tokRecurse + { + $$ = &Term{Type: TermTypeRecurse} + } + | tokIndex + { + $$ = &Term{Type: TermTypeIndex, Index: &Index{Name: $1}} + } + | '.' suffix + { + suffix := $2.(*Suffix) + if suffix.Iter { + $$ = &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{suffix}} + } else { + $$ = &Term{Type: TermTypeIndex, Index: 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}} + } + | '{' '}' + { + $$ = &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)}} + } + | tokNumber + { + $$ = &Term{Type: TermTypeNumber, Number: $1} + } + | '+' term + { + $$ = &Term{Type: TermTypeUnary, Unary: &Unary{OpAdd, $2.(*Term)}} + } + | '-' term + { + $$ = &Term{Type: TermTypeUnary, Unary: &Unary{OpSub, $2.(*Term)}} + } + | tokFormat + { + $$ = &Term{Type: TermTypeFormat, Format: $1} + } + | tokFormat string + { + $$ = &Term{Type: TermTypeFormat, Format: $1, Str: $2.(*String)} + } + | string + { + $$ = &Term{Type: TermTypeString, Str: $1.(*String)} + } + | tokIf query tokThen query ifelifs ifelse tokEnd + { + $$ = &Term{Type: TermTypeIf, If: &If{$2.(*Query), $4.(*Query), $5.([]*IfElif), $6.(*Query)}} + } + | tokTry expr trycatch + { + $$ = &Term{Type: TermTypeTry, Try: &Try{$2.(*Query), $3.(*Query)}} + } + | tokReduce expr tokAs pattern '(' query ';' query ')' + { + $$ = &Term{Type: TermTypeReduce, Reduce: &Reduce{$2.(*Query), $4.(*Pattern), $6.(*Query), $8.(*Query)}} + } + | tokForeach expr tokAs pattern '(' query ';' query ')' + { + $$ = &Term{Type: TermTypeForeach, Foreach: &Foreach{$2.(*Query), $4.(*Pattern), $6.(*Query), $8.(*Query), nil}} + } + | tokForeach expr tokAs pattern '(' query ';' query ';' query ')' + { + $$ = &Term{Type: TermTypeForeach, Foreach: &Foreach{$2.(*Query), $4.(*Pattern), $6.(*Query), $8.(*Query), $10.(*Query)}} + } + | tokBreak tokVariable + { + $$ = &Term{Type: TermTypeBreak, Break: $2} + } + | '(' query ')' + { + $$ = &Term{Type: TermTypeQuery, Query: $2.(*Query)} + } + | 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 expr + { + $$ = $2 + } + +objectkeyvals + : objectkeyval + { + $$ = []*ObjectKeyVal{$1.(*ObjectKeyVal)} + } + | objectkeyvals ',' objectkeyval + { + $$ = append($1.([]*ObjectKeyVal), $3.(*ObjectKeyVal)) + } + +objectkeyval + : objectkey ':' objectval + { + $$ = &ObjectKeyVal{Key: $1, Val: $3.(*Query)} + } + | string ':' objectval + { + $$ = &ObjectKeyVal{KeyString: $1.(*String), Val: $3.(*Query)} + } + | '(' query ')' ':' objectval + { + $$ = &ObjectKeyVal{KeyQuery: $2.(*Query), Val: $5.(*Query)} + } + | objectkey + { + $$ = &ObjectKeyVal{Key: $1} + } + | string + { + $$ = &ObjectKeyVal{KeyString: $1.(*String)} + } + +objectkey + : tokIdent + | tokVariable + | tokKeyword + +objectval + : objectval '|' objectval + { + $$ = &Query{Left: $1.(*Query), Op: OpPipe, Right: $3.(*Query)} + } + | expr + +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 + +%% diff --git a/vendor/github.com/itchyny/gojq/preview.go b/vendor/github.com/itchyny/gojq/preview.go new file mode 100644 index 000000000000..92f1a54a3d6a --- /dev/null +++ b/vendor/github.com/itchyny/gojq/preview.go @@ -0,0 +1,77 @@ +package gojq + +import "unicode/utf8" + +// Preview returns the preview string of v. The preview string is basically the +// same as the jq-flavored JSON encoding returned by [Marshal], but is truncated +// by 30 bytes, and more efficient than truncating the result of [Marshal]. +// +// This method is used by error messages of built-in operators and functions, +// and accepts only limited types (nil, bool, int, float64, *big.Int, +// json.Number, string, []any, and map[string]any). Note that the maximum width +// and trailing strings on truncation may be changed in the future. +func Preview(v any) string { + bs := jsonLimitedMarshal(v, 32) + if l := 30; len(bs) > l { + var trailing string + switch v.(type) { + case string: + trailing = ` ..."` + case []any: + trailing = " ...]" + case map[string]any: + trailing = " ...}" + default: + trailing = " ..." + } + for len(bs) > l-len(trailing) { + _, size := utf8.DecodeLastRune(bs) + bs = bs[:len(bs)-size] + } + bs = append(bs, trailing...) + } + return string(bs) +} + +func jsonLimitedMarshal(v any, n int) (bs []byte) { + w := &limitedWriter{buf: make([]byte, n)} + defer func() { + _ = recover() + bs = w.Bytes() + }() + (&encoder{w: w}).encode(v) + return +} + +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(struct{}{}) + } + return n, nil +} + +func (w *limitedWriter) WriteByte(b byte) error { + w.buf[w.off] = b + if w.off++; w.off == len(w.buf) { + panic(struct{}{}) + } + 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(struct{}{}) + } + return n, nil +} + +func (w *limitedWriter) Bytes() []byte { + return w.buf[:w.off] +} diff --git a/vendor/github.com/itchyny/gojq/query.go b/vendor/github.com/itchyny/gojq/query.go new file mode 100644 index 000000000000..512fa80dd638 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/query.go @@ -0,0 +1,942 @@ +package gojq + +import ( + "context" + "strings" +) + +// Parse a query string, and returns the query struct. +// +// If parsing failed, it returns an error of type [*ParseError], which has +// the byte offset and the invalid token. The byte offset is the scanned bytes +// when the error occurred. The token is empty if the error occurred after +// scanning the entire query string. +func Parse(src string) (*Query, error) { + l := newLexer(src) + if yyParse(l) > 0 { + return nil, l.err + } + return l.result, nil +} + +// Query represents the abstract syntax tree of a jq query. +type Query struct { + Meta *ConstObject + Imports []*Import + FuncDefs []*FuncDef + Term *Term + Left *Query + Right *Query + Patterns []*Pattern + Op Operator +} + +// Run the query. +// +// It is safe to call this method in goroutines, to reuse a parsed [*Query]. +func (e *Query) Run(v any) Iter { + return e.RunWithContext(context.Background(), v) +} + +// RunWithContext runs the query with context. +func (e *Query) RunWithContext(ctx context.Context, v any) 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 _, fd := range e.FuncDefs { + fd.writeTo(s) + s.WriteByte(' ') + } + if e.Term != nil { + e.Term.writeTo(s) + } else if e.Right != nil { + e.Left.writeTo(s) + if e.Op != OpComma { + s.WriteByte(' ') + } + for i, p := range e.Patterns { + if i == 0 { + s.WriteString("as ") + } else { + s.WriteString("?// ") + } + p.writeTo(s) + s.WriteByte(' ') + } + s.WriteString(e.Op.String()) + s.WriteByte(' ') + e.Right.writeTo(s) + } +} + +func (e *Query) toIndexKey() any { + if e.Term == nil { + return nil + } + return e.Term.toIndexKey() +} + +func (e *Query) toIndices(xs []any) []any { + if e.Term == nil { + return nil + } + return e.Term.toIndices(xs) +} + +// Import ... +type Import struct { + ImportPath string + ImportAlias string + IncludePath string + Meta *ConstObject +} + +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 ") + jsonEncodeString(s, e.ImportPath) + s.WriteString(" as ") + s.WriteString(e.ImportAlias) + } else { + s.WriteString("include ") + jsonEncodeString(s, e.IncludePath) + } + if e.Meta != nil { + s.WriteByte(' ') + e.Meta.writeTo(s) + } + s.WriteString(";\n") +} + +// FuncDef ... +type FuncDef struct { + Name string + Args []string + Body *Query +} + +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(';') +} + +// Term ... +type Term struct { + Type TermType + Index *Index + Func *Func + Object *Object + Array *Array + Number string + Unary *Unary + Format string + Str *String + If *If + Try *Try + Reduce *Reduce + Foreach *Foreach + Label *Label + Break string + Query *Query + SuffixList []*Suffix +} + +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) toIndexKey() any { + switch e.Type { + case TermTypeNumber: + return toNumber(e.Number) + case TermTypeUnary: + return e.Unary.toNumber() + case TermTypeString: + if e.Str.Queries == nil { + return e.Str.Str + } + return nil + default: + return nil + } +} + +func (e *Term) toIndices(xs []any) []any { + switch e.Type { + case TermTypeIndex: + if xs = e.Index.toIndices(xs); xs == nil { + return nil + } + case TermTypeQuery: + if xs = e.Query.toIndices(xs); xs == nil { + return nil + } + default: + return nil + } + for _, s := range e.SuffixList { + if xs = s.toIndices(xs); xs == nil { + return nil + } + } + return xs +} + +func (e *Term) toNumber() any { + if e.Type == TermTypeNumber { + return toNumber(e.Number) + } + return nil +} + +// Unary ... +type Unary struct { + Op Operator + Term *Term +} + +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) toNumber() any { + v := e.Term.toNumber() + if v != nil && e.Op == OpSub { + v = funcOpNegate(v) + } + return v +} + +// Pattern ... +type Pattern struct { + Name string + Array []*Pattern + Object []*PatternObject +} + +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 + KeyString *String + KeyQuery *Query + Val *Pattern +} + +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) + } +} + +// Index ... +type Index struct { + Name string + Str *String + Start *Query + End *Query + IsSlice bool +} + +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) toIndexKey() any { + if e.Name != "" { + return e.Name + } else if e.Str != nil { + if e.Str.Queries == nil { + return e.Str.Str + } + } else if !e.IsSlice { + return e.Start.toIndexKey() + } else { + var start, end any + ok := true + if e.Start != nil { + start = e.Start.toIndexKey() + ok = start != nil + } + if e.End != nil && ok { + end = e.End.toIndexKey() + ok = end != nil + } + if ok { + return map[string]any{"start": start, "end": end} + } + } + return nil +} + +func (e *Index) toIndices(xs []any) []any { + if k := e.toIndexKey(); k != nil { + return append(xs, k) + } + return nil +} + +// Func ... +type Func struct { + Name string + Args []*Query +} + +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(')') + } +} + +// String ... +type String struct { + Str string + Queries []*Query +} + +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 { + jsonEncodeString(s, 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('"') +} + +// Object ... +type Object struct { + KeyVals []*ObjectKeyVal +} + +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(" }") +} + +// ObjectKeyVal ... +type ObjectKeyVal struct { + Key string + KeyString *String + KeyQuery *Query + Val *Query +} + +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) + } +} + +// Array ... +type Array struct { + Query *Query +} + +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(']') +} + +// Suffix ... +type Suffix struct { + Index *Index + Iter bool + Optional bool +} + +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('?') + } +} + +func (e *Suffix) toTerm() *Term { + if e.Index != nil { + return &Term{Type: TermTypeIndex, Index: e.Index} + } else if e.Iter { + return &Term{Type: TermTypeIdentity, SuffixList: []*Suffix{{Iter: true}}} + } else { + return nil + } +} + +func (e *Suffix) toIndices(xs []any) []any { + if e.Index == nil { + return nil + } + return e.Index.toIndices(xs) +} + +// If ... +type If struct { + Cond *Query + Then *Query + Elif []*IfElif + Else *Query +} + +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") +} + +// IfElif ... +type IfElif struct { + Cond *Query + Then *Query +} + +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) +} + +// Try ... +type Try struct { + Body *Query + Catch *Query +} + +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) + } +} + +// Reduce ... +type Reduce struct { + Query *Query + Pattern *Pattern + Start *Query + Update *Query +} + +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.Query.writeTo(s) + s.WriteString(" as ") + e.Pattern.writeTo(s) + s.WriteString(" (") + e.Start.writeTo(s) + s.WriteString("; ") + e.Update.writeTo(s) + s.WriteByte(')') +} + +// Foreach ... +type Foreach struct { + Query *Query + Pattern *Pattern + Start *Query + Update *Query + Extract *Query +} + +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.Query.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(')') +} + +// Label ... +type Label struct { + Ident string + Body *Query +} + +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) +} + +// ConstTerm ... +type ConstTerm struct { + Object *ConstObject + Array *ConstArray + Number string + Str string + Null bool + True bool + False bool +} + +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.Null { + s.WriteString("null") + } else if e.True { + s.WriteString("true") + } else if e.False { + s.WriteString("false") + } else { + jsonEncodeString(s, e.Str) + } +} + +func (e *ConstTerm) toValue() any { + if e.Object != nil { + return e.Object.ToValue() + } else if e.Array != nil { + return e.Array.toValue() + } else if e.Number != "" { + return toNumber(e.Number) + } else if e.Null { + return nil + } else if e.True { + return true + } else if e.False { + return false + } else { + return e.Str + } +} + +func (e *ConstTerm) toString() (string, bool) { + if e.Object != nil || e.Array != nil || + e.Number != "" || e.Null || e.True || e.False { + return "", false + } + return e.Str, true +} + +// ConstObject ... +type ConstObject struct { + KeyVals []*ConstObjectKeyVal +} + +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]any. +func (e *ConstObject) ToValue() map[string]any { + if e == nil { + return nil + } + v := make(map[string]any, 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 + KeyString string + Val *ConstTerm +} + +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 { + jsonEncodeString(s, e.KeyString) + } + s.WriteString(": ") + e.Val.writeTo(s) +} + +// ConstArray ... +type ConstArray struct { + Elems []*ConstTerm +} + +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() []any { + v := make([]any, len(e.Elems)) + for i, e := range e.Elems { + v[i] = e.toValue() + } + return v +} diff --git a/vendor/github.com/itchyny/gojq/release.go b/vendor/github.com/itchyny/gojq/release.go new file mode 100644 index 000000000000..7bbf77e61aaa --- /dev/null +++ b/vendor/github.com/itchyny/gojq/release.go @@ -0,0 +1,15 @@ +//go:build !gojq_debug + +package gojq + +type codeinfo struct{} + +func (*compiler) appendCodeInfo(any) {} + +func (*compiler) deleteCodeInfo(string) {} + +func (*env) debugCodes() {} + +func (*env) debugState(int, bool) {} + +func (*env) debugForks(int, string) {} diff --git a/vendor/github.com/itchyny/gojq/scope_stack.go b/vendor/github.com/itchyny/gojq/scope_stack.go new file mode 100644 index 000000000000..f652e2997cfc --- /dev/null +++ b/vendor/github.com/itchyny/gojq/scope_stack.go @@ -0,0 +1,48 @@ +package 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} + s.index = max(s.index, s.limit) + 1 + if s.index < len(s.data) { + s.data[s.index] = 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 + } + return +} + +func (s *scopeStack) restore(index, limit int) { + s.index, s.limit = index, limit +} diff --git a/vendor/github.com/itchyny/gojq/stack.go b/vendor/github.com/itchyny/gojq/stack.go new file mode 100644 index 000000000000..0983ed26a2c0 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/stack.go @@ -0,0 +1,52 @@ +package gojq + +type stack struct { + data []block + index int + limit int +} + +type block struct { + value any + next int +} + +func newStack() *stack { + return &stack{index: -1, limit: -1} +} + +func (s *stack) push(v any) { + b := block{v, s.index} + s.index = max(s.index, s.limit) + 1 + if s.index < len(s.data) { + s.data[s.index] = b + } else { + s.data = append(s.data, b) + } +} + +func (s *stack) pop() any { + b := s.data[s.index] + s.index = b.next + return b.value +} + +func (s *stack) top() any { + 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 + } + return +} + +func (s *stack) restore(index, limit int) { + s.index, s.limit = index, limit +} diff --git a/vendor/github.com/itchyny/gojq/term_type.go b/vendor/github.com/itchyny/gojq/term_type.go new file mode 100644 index 000000000000..941e7ba9c0c7 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/term_type.go @@ -0,0 +1,77 @@ +package gojq + +// 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 [fmt.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) + } +} diff --git a/vendor/github.com/itchyny/gojq/type.go b/vendor/github.com/itchyny/gojq/type.go new file mode 100644 index 000000000000..89ccda0e3659 --- /dev/null +++ b/vendor/github.com/itchyny/gojq/type.go @@ -0,0 +1,31 @@ +package gojq + +import ( + "encoding/json" + "fmt" + "math/big" +) + +// TypeOf returns the jq-flavored type name of v. +// +// This method is used by built-in type/0 function, and accepts only limited +// types (nil, bool, int, float64, *big.Int, json.Number, string, []any, and +// map[string]any). +func TypeOf(v any) string { + switch v.(type) { + case nil: + return "null" + case bool: + return "boolean" + case int, float64, *big.Int, json.Number: + return "number" + case string: + return "string" + case []any: + return "array" + case map[string]any: + return "object" + default: + panic(fmt.Sprintf("invalid type: %[1]T (%[1]v)", v)) + } +} diff --git a/vendor/github.com/itchyny/timefmt-go/CHANGELOG.md b/vendor/github.com/itchyny/timefmt-go/CHANGELOG.md new file mode 100644 index 000000000000..43b26213c4dc --- /dev/null +++ b/vendor/github.com/itchyny/timefmt-go/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog +## [v0.1.8](https://github.com/itchyny/timefmt-go/compare/v0.1.7..v0.1.8) (2026-04-01) +* fix parsing negative year and Unix time (`%Y`, `%G`, `%s`) +* fix formatting negative year, century, Unix time (`%Y`, `%G`, `%C`, `%y`, `%g`, `%s`) +* fix `%g` parsing to use the same two-digit year threshold 69 as `%y` +* fix `%s` formatting and parsing on 32-bit platforms +* support parsing time zone offset with `%:::z` +* improve performance of parsing/formatting compound directives + +## [v0.1.7](https://github.com/itchyny/timefmt-go/compare/v0.1.6..v0.1.7) (2025-10-01) +* refactor code using built-in `min` and `max` functions + +## [v0.1.6](https://github.com/itchyny/timefmt-go/compare/v0.1.5..v0.1.6) (2024-06-01) +* support parsing week directives (`%A`, `%a`, `%w`, `%u`, `%V`, `%U`, `%W`) +* validate range of values on parsing directives +* fix formatting `%l` to show `12` at midnight + +## [v0.1.5](https://github.com/itchyny/timefmt-go/compare/v0.1.4..v0.1.5) (2022-12-01) +* support parsing time zone offset with name using both `%z` and `%Z` + +## [v0.1.4](https://github.com/itchyny/timefmt-go/compare/v0.1.3..v0.1.4) (2022-09-01) +* improve documents +* drop support for Go 1.16 + +## [v0.1.3](https://github.com/itchyny/timefmt-go/compare/v0.1.2..v0.1.3) (2021-04-14) +* implement `ParseInLocation` for configuring the default location + +## [v0.1.2](https://github.com/itchyny/timefmt-go/compare/v0.1.1..v0.1.2) (2021-02-22) +* implement parsing/formatting time zone offset with colons (`%:z`, `%::z`, `%:::z`) +* recognize `Z` as UTC on parsing time zone offset (`%z`) +* fix padding on formatting time zone offset (`%z`) + +## [v0.1.1](https://github.com/itchyny/timefmt-go/compare/v0.1.0..v0.1.1) (2020-09-01) +* fix overflow check in 32-bit architecture + +## [v0.1.0](https://github.com/itchyny/timefmt-go/compare/2c02364..v0.1.0) (2020-08-16) +* initial implementation diff --git a/vendor/github.com/itchyny/timefmt-go/LICENSE b/vendor/github.com/itchyny/timefmt-go/LICENSE new file mode 100644 index 000000000000..de520bb31a98 --- /dev/null +++ b/vendor/github.com/itchyny/timefmt-go/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020-2025 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. diff --git a/vendor/github.com/itchyny/timefmt-go/Makefile b/vendor/github.com/itchyny/timefmt-go/Makefile new file mode 100644 index 000000000000..a87cb2864414 --- /dev/null +++ b/vendor/github.com/itchyny/timefmt-go/Makefile @@ -0,0 +1,20 @@ +GOBIN ?= $(shell go env GOPATH)/bin + +.PHONY: all +all: test + +.PHONY: test +test: + go test -v -race ./... + +.PHONY: lint +lint: $(GOBIN)/staticcheck + go vet ./... + staticcheck -checks all,-ST1000 ./... + +$(GOBIN)/staticcheck: + go install honnef.co/go/tools/cmd/staticcheck@latest + +.PHONY: clean +clean: + go clean diff --git a/vendor/github.com/itchyny/timefmt-go/README.md b/vendor/github.com/itchyny/timefmt-go/README.md new file mode 100644 index 000000000000..9c028c746a5c --- /dev/null +++ b/vendor/github.com/itchyny/timefmt-go/README.md @@ -0,0 +1,69 @@ +# timefmt-go +[![CI Status](https://github.com/itchyny/timefmt-go/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/itchyny/timefmt-go/actions?query=branch:main) +[![Go Report Card](https://goreportcard.com/badge/github.com/itchyny/timefmt-go)](https://goreportcard.com/report/github.com/itchyny/timefmt-go) +[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/itchyny/timefmt-go/blob/main/LICENSE) +[![release](https://img.shields.io/github/release/itchyny/timefmt-go/all.svg)](https://github.com/itchyny/timefmt-go/releases) +[![pkg.go.dev](https://pkg.go.dev/badge/github.com/itchyny/timefmt-go)](https://pkg.go.dev/github.com/itchyny/timefmt-go) + +### Efficient time formatting library (strftime, strptime) for Golang +This is a Go language package for formatting and parsing date time strings. + +```go +package main + +import ( + "fmt" + "log" + + "github.com/itchyny/timefmt-go" +) + +func main() { + t, err := timefmt.Parse("2020/07/24 09:07:29", "%Y/%m/%d %H:%M:%S") + if err != nil { + log.Fatal(err) + } + fmt.Println(t) // 2020-07-24 09:07:29 +0000 UTC + + str := timefmt.Format(t, "%Y/%m/%d %H:%M:%S") + fmt.Println(str) // 2020/07/24 09:07:29 + + str = timefmt.Format(t, "%a, %d %b %Y %T %z") + fmt.Println(str) // Fri, 24 Jul 2020 09:07:29 +0000 +} +``` + +Please refer to [`man 3 strftime`](https://linux.die.net/man/3/strftime) and +[`man 3 strptime`](https://linux.die.net/man/3/strptime) for formatters. +As an extension, `%f` directive is supported for zero-padded microseconds, which originates from Python. +Note that `E` and `O` modifier characters are not supported. + +## Comparison to other libraries +- This library + - provides both formatting and parsing functions in pure Go language, + - depends only on the Go standard libraries not to grow up dependency. +- `Format` (`strftime`) implements glibc extensions including + - width specifier like `%6Y %10B %4Z` (limited to 1024 bytes), + - omitting padding modifier like `%-y-%-m-%-d`, + - space padding modifier like `%_y-%_m-%_d`, + - upper case modifier like `%^a %^b`, + - swapping case modifier like `%#Z`, + - time zone offset modifier like `%:z %::z %:::z`, + - and its performance is very good. +- `AppendFormat` is provided for reducing allocations. +- `Parse` (`strptime`) allows to parse + - composed directives like `%F %T`, + - century years like `%C %y`, + - week directives like `%W %a` and `%G-W%V-%u`. +- `ParseInLocation` is provided for configuring the default location. + +![](https://user-images.githubusercontent.com/375258/88606920-de475c80-d0b8-11ea-8d40-cbfee9e35c2e.jpg) + +## Bug Tracker +Report bug at [Issues・itchyny/timefmt-go - GitHub](https://github.com/itchyny/timefmt-go/issues). + +## Author +itchyny () + +## License +This software is released under the MIT License, see LICENSE. diff --git a/vendor/github.com/itchyny/timefmt-go/format.go b/vendor/github.com/itchyny/timefmt-go/format.go new file mode 100644 index 000000000000..24ea7b2c5263 --- /dev/null +++ b/vendor/github.com/itchyny/timefmt-go/format.go @@ -0,0 +1,462 @@ +package timefmt + +import ( + "strconv" + "time" +) + +// Format time to string using the format. +func Format(t time.Time, format string) string { + return string(AppendFormat(make([]byte, 0, 64), t, format)) +} + +// AppendFormat appends formatted time string to the buffer. +func AppendFormat(buf []byte, t time.Time, format string) []byte { + year, month, day := t.Date() + hour, minute, second := t.Clock() + var width, colons int + var padding byte + var pending string + var upper, swap bool + for i := 0; i < len(format); i++ { + if b := format[i]; b == '%' { + if i++; i == len(format) { + buf = append(buf, '%') + break + } + b, width, padding, upper, swap = format[i], 0, '0', false, false + L: + switch b { + case '-': + if pending != "" { + buf = append(buf, '-') + break + } + if i++; i == len(format) { + goto K + } + padding = ^paddingMask + b = format[i] + goto L + case '_': + if i++; i == len(format) { + goto K + } + padding = ' ' | ^paddingMask + b = format[i] + goto L + case '^': + if i++; i == len(format) { + goto K + } + upper = true + b = format[i] + goto L + case '#': + if i++; i == len(format) { + goto K + } + swap = true + b = format[i] + goto L + case '0': + if i++; i == len(format) { + goto K + } + padding = '0' | ^paddingMask + b = format[i] + goto L + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + width = int(b & 0x0F) + for i++; i < len(format); i++ { + if b = format[i]; b <= '9' && '0' <= b { + width = min(width*10+int(b&0x0F), 1024) + } else { + break + } + } + if padding == ^paddingMask { + padding = ' ' | ^paddingMask + } + if i == len(format) { + goto K + } + goto L + case 'Y': + buf = appendInt(buf, year, or(width, 4), padding) + case 'y': + buf = appendInt(buf, abs(year%100), max(width, 2), padding) + case 'C': + c := year / 100 + z := year < 0 && c == 0 + if z { + c = -1 + } + buf = appendInt(buf, c, max(width, 2), padding) + if z { + buf[len(buf)-1] = '0' + } + case 'g': + year, _ := t.ISOWeek() + buf = appendInt(buf, abs(year%100), max(width, 2), padding) + case 'G': + year, _ := t.ISOWeek() + buf = appendInt(buf, year, or(width, 4), padding) + case 'm': + buf = appendInt(buf, int(month), max(width, 2), padding) + case 'B': + buf = appendString(buf, longMonthNames[month-1], width, padding, upper, swap) + case 'b', 'h': + buf = appendString(buf, shortMonthNames[month-1], width, padding, upper, swap) + case 'A': + buf = appendString(buf, longWeekNames[t.Weekday()], width, padding, upper, swap) + case 'a': + buf = appendString(buf, shortWeekNames[t.Weekday()], width, padding, upper, swap) + case 'w': + buf = appendInt(buf, int(t.Weekday()), width, padding) + case 'u': + buf = appendInt(buf, or(int(t.Weekday()), 7), width, padding) + case 'V': + _, week := t.ISOWeek() + buf = appendInt(buf, week, max(width, 2), padding) + case 'U': + week := (t.YearDay() + 6 - int(t.Weekday())) / 7 + buf = appendInt(buf, week, max(width, 2), padding) + case 'W': + week := t.YearDay() + if int(t.Weekday()) > 0 { + week -= int(t.Weekday()) - 7 + } + week /= 7 + buf = appendInt(buf, week, max(width, 2), padding) + case 'e': + if padding < ^paddingMask { + padding = ' ' + } + fallthrough + case 'd': + buf = appendInt(buf, day, max(width, 2), padding) + case 'j': + buf = appendInt(buf, t.YearDay(), max(width, 3), padding) + case 'k': + if padding < ^paddingMask { + padding = ' ' + } + fallthrough + case 'H': + buf = appendInt(buf, hour, max(width, 2), padding) + case 'l': + if padding < ^paddingMask { + padding = ' ' + } + fallthrough + case 'I': + buf = appendInt(buf, or(hour%12, 12), max(width, 2), padding) + case 'P': + swap = !(upper || swap) + fallthrough + case 'p': + if hour < 12 { + buf = appendString(buf, "AM", width, padding, upper, swap) + } else { + buf = appendString(buf, "PM", width, padding, upper, swap) + } + case 'M': + buf = appendInt(buf, minute, max(width, 2), padding) + case 'S': + buf = appendInt(buf, second, max(width, 2), padding) + case 's': + if padding < ^paddingMask { + padding = ' ' + } + buf = appendInt64(buf, t.Unix(), width, padding) + case 'f': + buf = appendInt(buf, t.Nanosecond()/1000, or(width, 6), padding) + case 'Z', 'z': + name, offset := t.Zone() + if b == 'Z' && name != "" { + buf = appendString(buf, name, width, padding, upper, swap) + break + } + i := len(buf) + if padding != ^paddingMask { + for ; width > 1; width-- { + buf = append(buf, padding&paddingMask) + } + } + j := len(buf) + if offset < 0 { + buf = append(buf, '-') + offset = -offset + } else { + buf = append(buf, '+') + } + k := len(buf) + buf = appendInt(buf, offset/3600, 2, padding) + if buf[k] == ' ' { + buf[k-1], buf[k] = buf[k], buf[k-1] + } + if offset %= 3600; colons <= 2 || offset != 0 { + if colons != 0 { + buf = append(buf, ':') + } + buf = appendInt(buf, offset/60, 2, '0') + if offset %= 60; colons == 2 || colons == 3 && offset != 0 { + buf = append(buf, ':') + buf = appendInt(buf, offset, 2, '0') + } + } + colons = 0 + if k = min(len(buf)-j-1, j-i); k > 0 { + copy(buf[j-k:], buf[j:]) + buf = buf[:len(buf)-k] + if padding&paddingMask == '0' { + buf[i], buf[j-k] = buf[j-k], buf[i] + } + } + case ':': + if pending != "" { + buf = append(buf, ':') + } else { + colons = 1 + M: + for i++; i < len(format); i++ { + switch format[i] { + case ':': + colons++ + case 'z': + if colons > 3 { + i++ + break M + } + b = 'z' + goto L + default: + break M + } + } + buf = appendLast(buf, format[:i], width, padding) + i-- + colons = 0 + } + case 't': + buf = appendString(buf, "\t", width, padding, false, false) + case 'n': + buf = appendString(buf, "\n", width, padding, false, false) + case '%': + buf = appendString(buf, "%", width, padding, false, false) + case 'c': + pending, swap = "a b e H:M:S Y", false + case '+': + pending, swap = "a b e H:M:S Z Y", false + case 'v': + pending, swap = "e-b-Y", false + case 'r': + pending, swap = "I:M:S p", false + case 'F': + pending = "Y-m-d" + case 'D', 'x': + pending = "m/d/y" + case 'T', 'X': + pending = "H:M:S" + case 'R': + pending = "H:M" + default: + if pending == "" { + buf = appendLast(buf, format[:i], width-1, padding) + } + buf = append(buf, b) + } + if pending != "" { + b, pending, width, padding = pending[0], pending[1:], 0, '0' + goto L + } + } else { + buf = append(buf, b) + } + } + return buf +K: + return appendLast(buf, format, width, padding) +} + +const smalls = "" + + "00010203040506070809" + + "10111213141516171819" + + "20212223242526272829" + + "30313233343536373839" + + "40414243444546474849" + + "50515253545556575859" + + "60616263646566676869" + + "70717273747576777879" + + "80818283848586878889" + + "90919293949596979899" + +func appendInt(buf []byte, num, width int, padding byte) []byte { + var digits int + switch { + default: + fallthrough + case num < 0: + return appendInt64(buf, int64(num), width, padding) + case num < 100 && width == 2 && padding == '0': + return append(buf, smalls[num*2:num*2+2]...) + case num < 10: + digits = 1 + case num < 100: + digits = 2 + case num < 1000: + digits = 3 + case num < 10000: + digits = 4 + } + if padding != ^paddingMask { + padding &= paddingMask + for ; width > digits; width-- { + buf = append(buf, padding) + } + } + switch digits { + case 4: + buf = append(buf, byte(num/1000)|'0') + num %= 1000 + fallthrough + case 3: + buf = append(buf, byte(num/100)|'0') + num %= 100 + fallthrough + case 2: + buf = append(buf, byte(num/10)|'0') + num %= 10 + fallthrough + default: + return append(buf, byte(num)|'0') + } +} + +func appendInt64(buf []byte, num int64, width int, padding byte) []byte { + if padding == ^paddingMask { + return strconv.AppendInt(buf, num, 10) + } + padding &= paddingMask + i := len(buf) + for ; width > 1; width-- { + buf = append(buf, padding) + } + j := len(buf) + buf = strconv.AppendInt(buf, num, 10) + if k := min(len(buf)-j-1, j-i); k > 0 { + copy(buf[j-k:], buf[j:]) + buf = buf[:len(buf)-k] + if j -= k; buf[i] == '0' && buf[j] == '-' { + buf[i], buf[j] = '-', '0' + } + } + return buf +} + +func appendString(buf []byte, str string, width int, padding byte, upper, swap bool) []byte { + if width > len(str) && padding != ^paddingMask { + if padding < ^paddingMask { + padding = ' ' + } else { + padding &= paddingMask + } + for width -= len(str); width > 0; width-- { + buf = append(buf, padding) + } + } + switch { + case swap: + if str[1] < 'a' { + for _, b := range []byte(str) { + buf = append(buf, b|0x20) + } + break + } + fallthrough + case upper: + for _, b := range []byte(str) { + buf = append(buf, b&0x5F) + } + default: + buf = append(buf, str...) + } + return buf +} + +func appendLast(buf []byte, format string, width int, padding byte) []byte { + for i := len(format) - 1; i >= 0; i-- { + if format[i] == '%' { + buf = appendString(buf, format[i:], width, padding, false, false) + break + } + } + return buf +} + +func or(x, y int) int { + if x != 0 { + return x + } + return y +} + +func abs(x int) int { + if x >= 0 { + return x + } + return -x +} + +const paddingMask byte = 0x7F + +var longMonthNames = []string{ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", +} + +var shortMonthNames = []string{ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", +} + +var longWeekNames = []string{ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", +} + +var shortWeekNames = []string{ + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat", +} diff --git a/vendor/github.com/itchyny/timefmt-go/parse.go b/vendor/github.com/itchyny/timefmt-go/parse.go new file mode 100644 index 000000000000..ab60634662a3 --- /dev/null +++ b/vendor/github.com/itchyny/timefmt-go/parse.go @@ -0,0 +1,474 @@ +package timefmt + +import ( + "errors" + "fmt" + "time" +) + +// Parse time string using the format. +func Parse(source, format string) (t time.Time, err error) { + return parse(source, format, time.UTC, time.Local) +} + +// ParseInLocation parses time string with the default location. +// The location is also used to parse the time zone name (%Z). +func ParseInLocation(source, format string, loc *time.Location) (t time.Time, err error) { + return parse(source, format, loc, loc) +} + +func parse(source, format string, loc, base *time.Location) (t time.Time, err error) { + year, month, day, hour, minute, second, nanosecond := 1900, 1, 0, 0, 0, 0, 0 + defer func() { + if err != nil { + err = fmt.Errorf("failed to parse %q with %q: %w", source, format, err) + } + }() + var j, week, weekday, yday, colons, sign int + century, weekstart := -1, time.Weekday(-1) + var pm, hasISOYear, hasZoneName, hasZoneOffset bool + var pending string + for i, l := 0, len(source); i < len(format); i++ { + if b := format[i]; b == '%' { + if i++; i == len(format) { + err = errors.New(`stray "%"`) + return + } + b = format[i] + L: + switch b { + case 'G': + hasISOYear = true + fallthrough + case 'Y': + sign, j = parseSign(source, j, l) + if year, j, err = parseInt(source, j, 4, 0, 9999, b); err != nil { + return + } + year *= sign + case 'g': + hasISOYear = true + fallthrough + case 'y': + if year, j, err = parseInt(source, j, 2, 0, 99, b); err != nil { + return + } + if year < 69 { + year += 2000 + } else { + year += 1900 + } + case 'C': + sign, j = parseSign(source, j, l) + if sign < 0 { + err = errors.New(`negative century is not supported for "%C"`) + return + } + if century, j, err = parseInt(source, j, 2, 0, 99, 'C'); err != nil { + return + } + case 'm': + if month, j, err = parseInt(source, j, 2, 1, 12, 'm'); err != nil { + return + } + case 'B': + if month, j, err = parseAny(source, j, longMonthNames, 'B'); err != nil { + return + } + case 'b', 'h': + if month, j, err = parseAny(source, j, shortMonthNames, b); err != nil { + return + } + case 'A': + if weekday, j, err = parseAny(source, j, longWeekNames, 'A'); err != nil { + return + } + case 'a': + if weekday, j, err = parseAny(source, j, shortWeekNames, 'a'); err != nil { + return + } + case 'w': + if weekday, j, err = parseInt(source, j, 1, 0, 6, 'w'); err != nil { + return + } + weekday++ + case 'u': + if weekday, j, err = parseInt(source, j, 1, 1, 7, 'u'); err != nil { + return + } + weekday = weekday%7 + 1 + case 'V': + if week, j, err = parseInt(source, j, 2, 1, 53, b); err != nil { + return + } + weekstart = time.Thursday + weekday = or(weekday, 2) + case 'U': + if week, j, err = parseInt(source, j, 2, 0, 53, b); err != nil { + return + } + weekstart = time.Sunday + weekday = or(weekday, 1) + case 'W': + if week, j, err = parseInt(source, j, 2, 0, 53, b); err != nil { + return + } + weekstart = time.Monday + weekday = or(weekday, 2) + case 'e': + if j < l && source[j] == ' ' { + j++ + } + fallthrough + case 'd': + if day, j, err = parseInt(source, j, 2, 1, 31, b); err != nil { + return + } + case 'j': + if yday, j, err = parseInt(source, j, 3, 1, 366, 'j'); err != nil { + return + } + case 'k': + if j < l && source[j] == ' ' { + j++ + } + fallthrough + case 'H': + if hour, j, err = parseInt(source, j, 2, 0, 23, b); err != nil { + return + } + case 'l': + if j < l && source[j] == ' ' { + j++ + } + fallthrough + case 'I': + if hour, j, err = parseInt(source, j, 2, 1, 12, b); err != nil { + return + } + if hour == 12 { + hour = 0 + } + case 'P', 'p': + var ampm int + if ampm, j, err = parseAny(source, j, []string{"AM", "PM"}, b); err != nil { + return + } + pm = ampm == 2 + case 'M': + if minute, j, err = parseInt(source, j, 2, 0, 59, 'M'); err != nil { + return + } + case 'S': + if second, j, err = parseInt(source, j, 2, 0, 60, 'S'); err != nil { + return + } + case 's': + sign, j = parseSign(source, j, l) + var unix int64 + if unix, j, err = parseInt64(source, j, 19, 's'); err != nil { + return + } + t = time.Unix(int64(sign)*unix, 0).In(time.UTC) + var mon time.Month + year, mon, day = t.Date() + hour, minute, second = t.Clock() + month = int(mon) + case 'f': + microsecond, i := 0, j + if microsecond, j, err = parseInt(source, j, 6, 0, 999999, 'f'); err != nil { + return + } + for i = j - i; i < 6; i++ { + microsecond *= 10 + } + nanosecond = microsecond * 1000 + case 'Z': + i := j + for ; j < l; j++ { + if c := source[j]; c < 'A' || 'Z' < c { + break + } + } + t, err = time.ParseInLocation("MST", source[i:j], base) + if err != nil { + err = fmt.Errorf(`cannot parse %q with "%%Z"`, source[i:j]) + return + } + if hasZoneOffset { + name, _ := t.Zone() + _, offset := locationZone(loc) + loc = time.FixedZone(name, offset) + } else { + loc = t.Location() + } + hasZoneName = true + case 'z': + if j >= l { + err = parseZFormatError(colons) + return + } + sign = 1 + switch source[j] { + case '-': + sign = -1 + fallthrough + case '+': + hour, minute, second, i := 0, 0, 0, j+1 + if hour, j, _ = parseInt(source, i, 2, 0, 23, 'z'); j != i+2 { + err = parseZFormatError(colons) + return + } + if j >= l || source[j] != ':' { + if colons > 0 && colons < 3 { + err = expectedColonForZFormatError(colons) + return + } + } else if j++; colons == 0 { + colons = 4 + } + i = j + if minute, j, _ = parseInt(source, i, 2, 0, 59, 'z'); j != i+2 { + if colons > 0 && colons != 3 { + err = parseZFormatError(colons & 3) + return + } + j = i + } else if colons > 1 { + if j >= l || source[j] != ':' { + if colons < 3 { + err = expectedColonForZFormatError(colons) + return + } + } else { + i = j + 1 + if second, j, _ = parseInt(source, i, 2, 0, 59, 'z'); j != i+2 { + if colons < 3 { + err = parseZFormatError(colons) + return + } + j = i - 1 + } + } + } + var name string + if hasZoneName { + name, _ = locationZone(loc) + } + loc, colons = time.FixedZone(name, sign*((hour*60+minute)*60+second)), 0 + hasZoneOffset = true + case 'Z': + loc, colons, j = time.UTC, 0, j+1 + default: + err = parseZFormatError(colons) + return + } + case ':': + if pending != "" { + if j >= l || source[j] != b { + err = expectedFormatError(b) + return + } + j++ + } else { + for colons = 1; colons <= 3; colons++ { + if i++; i == len(format) { + break + } else if b = format[i]; b == 'z' { + goto L + } else if b != ':' || colons == 3 { + break + } + } + err = expectedZAfterColonError(colons) + return + } + case 't', 'n': + i := j + K: + for ; j < l; j++ { + switch source[j] { + case ' ', '\t', '\n', '\v', '\f', '\r': + default: + break K + } + } + if i == j { + err = fmt.Errorf(`expected a space for "%%%c"`, b) + return + } + case '%': + if j >= l || source[j] != b { + err = expectedFormatError(b) + return + } + j++ + case 'c': + pending = "a b e H:M:S Y" + case '+': + pending = "a b e H:M:S Z Y" + case 'v': + pending = "e-b-Y" + case 'r': + pending = "I:M:S p" + case 'F': + pending = "Y-m-d" + case 'D', 'x': + pending = "m/d/y" + case 'T', 'X': + pending = "H:M:S" + case 'R': + pending = "H:M" + default: + if pending == "" { + err = fmt.Errorf(`unexpected format "%%%c"`, b) + return + } + if j >= l || source[j] != b { + err = expectedFormatError(b) + return + } + j++ + } + if pending != "" { + b, pending = pending[0], pending[1:] + goto L + } + } else if j >= l || source[j] != b { + err = expectedFormatError(b) + return + } else { + j++ + } + } + if j < len(source) { + err = fmt.Errorf("unparsed string %q", source[j:]) + return + } + if pm { + hour += 12 + } + if century >= 0 { + year = century*100 + year%100 + } + if day == 0 { + if yday > 0 { + if hasISOYear { + err = errors.New(`use "%Y" to parse non-ISO year for "%j"`) + return + } + return time.Date(year, time.January, yday, hour, minute, second, nanosecond, loc), nil + } + if weekstart >= time.Sunday { + if weekstart == time.Thursday { + if !hasISOYear { + err = errors.New(`use "%G" to parse ISO year for "%V"`) + return + } + } else if hasISOYear { + err = errors.New(`use "%Y" to parse non-ISO year for "%U" or "%W"`) + return + } + if weekstart > time.Sunday && weekday == 1 { + week++ + } + t := time.Date(year, time.January, -int(weekstart), hour, minute, second, nanosecond, loc) + return t.AddDate(0, 0, week*7-int(t.Weekday())+weekday-1), nil + } + day = 1 + } + return time.Date(year, time.Month(month), day, hour, minute, second, nanosecond, loc), nil +} + +func locationZone(loc *time.Location) (name string, offset int) { + return time.Date(2000, time.January, 1, 0, 0, 0, 0, loc).Zone() +} + +type parseFormatError byte + +func (err parseFormatError) Error() string { + return fmt.Sprintf(`cannot parse "%%%c"`, byte(err)) +} + +type expectedFormatError byte + +func (err expectedFormatError) Error() string { + return fmt.Sprintf("expected %q", byte(err)) +} + +type parseZFormatError int + +func (err parseZFormatError) Error() string { + return `cannot parse "%` + `:::z"`[3-err:] +} + +type expectedColonForZFormatError int + +func (err expectedColonForZFormatError) Error() string { + return `expected ':' for "%` + `:::z"`[3-err:] +} + +type expectedZAfterColonError int + +func (err expectedZAfterColonError) Error() string { + return `expected 'z' after "%` + `:::"`[3-err:] +} + +func parseSign(source string, index, l int) (int, int) { + if index < l && source[index] == '-' { + return -1, index + 1 + } + return 1, index +} + +// parseInt parses an integer from source. This is intentionally not a +// wrapper of parseInt64 to avoid the overhead of int64 arithmetic and +// function call indirection in the hot path (~20% slower in benchmarks). +func parseInt(source string, index, size, minimum, maximum int, format byte) (int, int, error) { + var value int + i := index + for size = min(i+size, len(source)); i < size; i++ { + if b := source[i] - '0'; b < 10 { + value = value*10 + int(b) + } else { + break + } + } + if i == index || value < minimum || maximum < value { + return 0, 0, parseFormatError(format) + } + return value, i, nil +} + +func parseInt64(source string, index, size int, format byte) (int64, int, error) { + var value int64 + i := index + for size = min(i+size, len(source)); i < size; i++ { + if b := source[i] - '0'; b < 10 { + value = value*10 + int64(b) + } else { + break + } + } + if i == index { + return 0, 0, parseFormatError(format) + } + return value, i, nil +} + +func parseAny(source string, index int, candidates []string, format byte) (int, int, error) { +L: + for i, xs := range candidates { + j := index + for k := 0; k < len(xs); k, j = k+1, j+1 { + if j >= len(source) { + continue L + } + if x, y := xs[k], source[j]; x != y && x|0x20 != y|0x20 { + continue L + } + } + return i + 1, j, nil + } + return 0, 0, parseFormatError(format) +} diff --git a/vendor/github.com/itchyny/timefmt-go/timefmt.go b/vendor/github.com/itchyny/timefmt-go/timefmt.go new file mode 100644 index 000000000000..45bf6ae9031f --- /dev/null +++ b/vendor/github.com/itchyny/timefmt-go/timefmt.go @@ -0,0 +1,2 @@ +// Package timefmt provides functions for formatting and parsing date time strings. +package timefmt diff --git a/vendor/modules.txt b/vendor/modules.txt index 3607c7a4e61f..c961dd23ee39 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -166,7 +166,10 @@ github.com/cenkalti/backoff/v5 # github.com/cespare/xxhash/v2 v2.3.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 -# github.com/clipperhouse/uax29/v2 v2.2.0 +# github.com/clipperhouse/stringish v0.1.1 +## explicit; go 1.18 +github.com/clipperhouse/stringish +# github.com/clipperhouse/uax29/v2 v2.3.0 ## explicit; go 1.18 github.com/clipperhouse/uax29/v2/graphemes github.com/clipperhouse/uax29/v2/internal/iterators @@ -566,6 +569,12 @@ github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1 # github.com/inconshreveable/mousetrap v1.1.0 ## explicit; go 1.18 github.com/inconshreveable/mousetrap +# github.com/itchyny/gojq v0.12.19 +## explicit; go 1.24.0 +github.com/itchyny/gojq +# github.com/itchyny/timefmt-go v0.1.8 +## explicit; go 1.24 +github.com/itchyny/timefmt-go # github.com/json-iterator/go v1.1.12 ## explicit; go 1.12 github.com/json-iterator/go