diff --git a/go.mod b/go.mod
index 94ec882022..f3b996fad2 100644
--- a/go.mod
+++ b/go.mod
@@ -7,10 +7,10 @@ toolchain go1.21.8
require (
github.com/Masterminds/semver v1.5.0
github.com/Pallinder/go-randomdata v1.2.0
- github.com/argoproj/argo-cd/v2 v2.8.17
+ github.com/argoproj/argo-cd/v2 v2.9.20
github.com/argoproj/argo-workflows/v3 v3.4.3
- github.com/argoproj/gitops-engine v0.7.1-0.20231013183858-f15cf615b814
- github.com/aws/aws-sdk-go v1.44.290
+ github.com/argoproj/gitops-engine v0.7.1-0.20240715141028-c68bce0f979c
+ github.com/aws/aws-sdk-go v1.44.317
github.com/aws/aws-sdk-go-v2/service/ecr v1.20.0
github.com/caarlos0/env v3.5.0+incompatible
github.com/caarlos0/env/v6 v6.7.2
@@ -60,9 +60,9 @@ require (
github.com/robfig/cron/v3 v3.0.1
github.com/satori/go.uuid v1.2.0
github.com/stretchr/testify v1.8.4
- github.com/tidwall/gjson v1.14.3
+ github.com/tidwall/gjson v1.14.4
github.com/tidwall/sjson v1.2.4
- github.com/xanzy/go-gitlab v0.86.0
+ github.com/xanzy/go-gitlab v0.91.1
github.com/xeipuuv/gojsonschema v1.2.0
github.com/yannh/kubeconform v0.5.0
github.com/zclconf/go-cty v1.13.2
@@ -121,7 +121,7 @@ require (
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
- github.com/antonmedv/expr v1.12.5 // indirect
+ github.com/antonmedv/expr v1.15.2 // indirect
github.com/apparentlymart/go-textseg v1.0.0 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/argoproj/pkg v0.13.7-0.20230627120311-a4dd357b057e // indirect
@@ -130,7 +130,7 @@ require (
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect
github.com/bombsimon/logrusr/v2 v2.0.1 // indirect
- github.com/bradleyfalzon/ghinstallation/v2 v2.5.0 // indirect
+ github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 // indirect
github.com/casbin/govaluate v1.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
@@ -165,7 +165,7 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/gnostic v0.6.9 // indirect
- github.com/google/go-github/v53 v53.0.0 // indirect
+ github.com/google/go-github/v53 v53.2.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/s2a-go v0.1.4 // indirect
@@ -233,8 +233,8 @@ require (
github.com/sergi/go-diff v1.1.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
- github.com/skeema/knownhosts v1.2.1 // indirect
- github.com/spf13/cast v1.5.0 // indirect
+ github.com/skeema/knownhosts v1.2.2 // indirect
+ github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.2.0 // indirect
@@ -291,7 +291,7 @@ require (
k8s.io/kube-aggregator v0.26.4 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
mellium.im/sasl v0.3.1 // indirect
- oras.land/oras-go/v2 v2.2.0 // indirect
+ oras.land/oras-go/v2 v2.3.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
diff --git a/go.sum b/go.sum
index b7bcc091fe..41c7bbac97 100644
--- a/go.sum
+++ b/go.sum
@@ -76,35 +76,36 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
-github.com/alicebob/miniredis/v2 v2.30.3 h1:hrqDB4cHFSHQf4gO3xu6YKQg8PqJpNjLYsQAFYHstqw=
-github.com/alicebob/miniredis/v2 v2.30.3/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg=
+github.com/alicebob/miniredis/v2 v2.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOSchFS7Eo=
+github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18=
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
-github.com/antonmedv/expr v1.12.5 h1:Fq4okale9swwL3OeLLs9WD9H6GbgBLJyN/NUHRv+n0E=
-github.com/antonmedv/expr v1.12.5/go.mod h1:FPC8iWArxls7axbVLsW+kpg1mz29A1b2M6jt+hZfDkU=
+github.com/antonmedv/expr v1.15.2 h1:afFXpDWIC2n3bF+kTZE1JvFo+c34uaM3sTqh8z0xfdU=
+github.com/antonmedv/expr v1.15.2/go.mod h1:0E/6TxnOlRNp81GMzX9QfDPAmHo2Phg00y4JUv1ihsE=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
-github.com/argoproj/argo-cd/v2 v2.8.17 h1:PPAGPj37RBoxWfplu2HSjLJ6bZY75FPgy9h4mcKyxqQ=
-github.com/argoproj/argo-cd/v2 v2.8.17/go.mod h1:qdkZvAPT+uurfySoFIlptZ402k/h1ZWuEJ511yd8GOE=
+github.com/argoproj/argo-cd/v2 v2.9.20 h1:3VO6lrl8fE7tBxv3kli+hF83amMtbq+6uJkREMjxzsE=
+github.com/argoproj/argo-cd/v2 v2.9.20/go.mod h1:V9EKQR1U5kJV/aLVRgUV46muOStnP6C5c4wTeT6nkoY=
github.com/argoproj/argo-workflows/v3 v3.4.3 h1:4pt7+Rjy9Lzq/r6dWp6wL8mr3ucPHSsGIlWwoP3fueM=
github.com/argoproj/argo-workflows/v3 v3.4.3/go.mod h1:Od1rQK5j9/WefqFaUsIwAqTialDhLlhups0RE/WYzz4=
-github.com/argoproj/gitops-engine v0.7.1-0.20231013183858-f15cf615b814 h1:oTaLRbCwjnGtScIX2ZRdIEDsiDxonwh9/BbUxdXrjYc=
-github.com/argoproj/gitops-engine v0.7.1-0.20231013183858-f15cf615b814/go.mod h1:1TchqKw9XmYYZluyEHa1dTJQoZgbV6PhabB/e8Wf3KY=
+github.com/argoproj/gitops-engine v0.7.1-0.20240715141028-c68bce0f979c h1:kkHx4mvqnUCLruADf1t/aO6yXnLcrl6rhsINaJomukc=
+github.com/argoproj/gitops-engine v0.7.1-0.20240715141028-c68bce0f979c/go.mod h1:/GMN0JuoJUUpnKlNLp2Wn/mfK8sglFsdPn+eoxSddmg=
github.com/argoproj/pkg v0.13.7-0.20230627120311-a4dd357b057e h1:kuLQvJqwwRMQTheT4MFyKVM8Txncu21CHT4yBWUl1Mk=
github.com/argoproj/pkg v0.13.7-0.20230627120311-a4dd357b057e/go.mod h1:xBN5PLx2MoK63dmPfMo/PGBvd77K1Y0m/rzZOe4cs1s=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/aws/aws-sdk-go v1.44.290 h1:Md4+os9DQtJjow0lWLMzeJljsimD+XS2xwwHDr5Z+Lk=
github.com/aws/aws-sdk-go v1.44.290/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
+github.com/aws/aws-sdk-go v1.44.317 h1:+8XWrLmGMwPPXSRSLPzhgcGnzJ2mYkgkrcB9C/GnSOU=
+github.com/aws/aws-sdk-go v1.44.317/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw=
@@ -123,8 +124,8 @@ github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvz
github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bombsimon/logrusr/v2 v2.0.1 h1:1VgxVNQMCvjirZIYaT9JYn6sAVGVEcNtRE0y4mvaOAM=
github.com/bombsimon/logrusr/v2 v2.0.1/go.mod h1:ByVAX+vHdLGAfdroiMg6q0zgq2FODY2lc5YJvzmOJio=
-github.com/bradleyfalzon/ghinstallation/v2 v2.5.0 h1:yaYcGQ7yEIGbsJfW/9z7v1sLiZg/5rSNNXwmMct5XaE=
-github.com/bradleyfalzon/ghinstallation/v2 v2.5.0/go.mod h1:amcvPQMrRkWNdueWOjPytGL25xQGzox7425qMgzo+Vo=
+github.com/bradleyfalzon/ghinstallation/v2 v2.6.0 h1:IRY7Xy588KylkoycsUhFpW7cdGpy5Y5BPsz4IfuJtGk=
+github.com/bradleyfalzon/ghinstallation/v2 v2.6.0/go.mod h1:oQ3etOwN3TRH4EwgW5/7MxSVMGlMlzG/O8TU7eYdoSk=
github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao=
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k=
@@ -245,8 +246,8 @@ github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
-github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
-github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
+github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
+github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@@ -272,6 +273,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
+github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
+github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -390,8 +393,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
-github.com/google/go-github/v53 v53.0.0 h1:T1RyHbSnpHYnoF0ZYKiIPSgPtuJ8G6vgc0MKodXsQDQ=
-github.com/google/go-github/v53 v53.0.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao=
+github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI=
+github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -761,13 +764,13 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
-github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
+github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
+github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
-github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
+github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
+github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
@@ -797,8 +800,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
-github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
+github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
@@ -818,8 +821,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
-github.com/xanzy/go-gitlab v0.86.0 h1:jR8V9cK9jXRQDb46KOB20NCF3ksY09luaG0IfXE6p7w=
-github.com/xanzy/go-gitlab v0.86.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
+github.com/xanzy/go-gitlab v0.91.1 h1:gnV57IPGYywWer32oXKBcdmc8dVxeKl3AauV8Bu17rw=
+github.com/xanzy/go-gitlab v0.91.1/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -1288,8 +1291,8 @@ launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80
launchpad.net/xmlpath v0.0.0-20130614043138-000000000004/go.mod h1:vqyExLOM3qBx7mvYRkoxjSCF945s0mbe7YynlKYXtsA=
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
-oras.land/oras-go/v2 v2.2.0 h1:E1fqITD56Eg5neZbxBtAdZVgDHD6wBabJo6xESTcQyo=
-oras.land/oras-go/v2 v2.2.0/go.mod h1:pXjn0+KfarspMHHNR3A56j3tgvr+mxArHuI8qVn59v8=
+oras.land/oras-go/v2 v2.3.0 h1:lqX1aXdN+DAmDTKjiDyvq85cIaI4RkIKp/PghWlAGIU=
+oras.land/oras-go/v2 v2.3.0/go.mod h1:GeAwLuC4G/JpNwkd+bSZ6SkDMGaaYglt6YK2WvZP7uQ=
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
diff --git a/vendor/github.com/antonmedv/expr/README.md b/vendor/github.com/antonmedv/expr/README.md
index 242431f2ce..0e11943d31 100644
--- a/vendor/github.com/antonmedv/expr/README.md
+++ b/vendor/github.com/antonmedv/expr/README.md
@@ -2,45 +2,53 @@
[](https://github.com/antonmedv/expr/actions/workflows/test.yml)
[](https://goreportcard.com/report/github.com/antonmedv/expr)
[](https://godoc.org/github.com/antonmedv/expr)
+[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:expr)
+
+**Expr** is a Go-centric expression language designed to deliver dynamic configurations with unparalleled accuracy, safety, and speed.
-**Expr** package provides an engine that can compile and evaluate expressions.
-An expression is a one-liner that returns a value (mostly, but not limited to, booleans).
-It is designed for simplicity, speed and safety.
+```js
+// Allow only admins and moderators to moderate comments.
+user.Group in ["admin", "moderator"] || user.Id == comment.UserId
+```
-The purpose of the package is to allow users to use expressions inside configuration for more complex logic.
-It is a perfect candidate for the foundation of a _business rule engine_.
-The idea is to let configure things in a dynamic way without recompile of a program:
+```js
+// Ensure all tweets are less than 240 characters.
+all(Tweets, .Size <= 240)
+```
-```coffeescript
-# Get the special price if
-user.Group in ["good_customers", "collaborator"]
+## Features
-# Promote article to the homepage when
-len(article.Comments) > 100 and article.Category not in ["misc"]
+**Expr** is a safe, fast, and intuitive expression evaluator optimized for the Go language.
+Here are its standout features:
-# Send an alert when
-product.Stock < 15
-```
+### Safety and Isolation
+* **Memory-Safe**: Expr is designed with a focus on safety, ensuring that programs do not access unrelated memory or introduce memory vulnerabilities.
+* **Side-Effect-Free**: Expressions evaluated in Expr only compute outputs from their inputs, ensuring no side-effects that can change state or produce unintended results.
+* **Always Terminating**: Expr is designed to prevent infinite loops, ensuring that every program will conclude in a reasonable amount of time.
-## Features
+### Go Integration
+* **Seamless with Go**: Integrate Expr into your Go projects without the need to redefine types.
-* Seamless integration with Go (no need to redefine types)
-* Static typing ([example](https://godoc.org/github.com/antonmedv/expr#example-Env)).
+### Static Typing
+* Ensures type correctness and prevents runtime type errors.
```go
out, err := expr.Compile(`name + age`)
// err: invalid operation + (mismatched types string and int)
// | name + age
// | .....^
```
-* User-friendly error messages.
-* Reasonable set of basic operators.
-* Builtins `all`, `none`, `any`, `one`, `filter`, `map`.
- ```coffeescript
- all(Tweets, {.Size <= 280})
- ```
-* Fast ([benchmarks](https://github.com/antonmedv/golang-expression-evaluation-comparison#readme)): uses bytecode virtual machine and optimizing compiler.
+
+### User-Friendly
+* Provides user-friendly error messages to assist with debugging and development.
+
+### Flexibility and Utility
+* **Rich Operators**: Offers a reasonable set of basic operators for a variety of applications.
+* **Built-in Functions**: Functions like `all`, `none`, `any`, `one`, `filter`, and `map` are provided out-of-the-box.
+
+### Performance
+* **Optimized for Speed**: Expr stands out in its performance, utilizing an optimizing compiler and a bytecode virtual machine. Check out these [benchmarks](https://github.com/antonmedv/golang-expression-evaluation-comparison#readme) for more details.
## Install
@@ -138,20 +146,24 @@ func main() {
## Who uses Expr?
-* [Aviasales](https://aviasales.ru) uses Expr as a business rule engine for our flight search engine.
-* [Wish.com](https://www.wish.com) uses Expr for decision-making rule engine in the Wish Assistant.
-* [Argo](https://argoproj.github.io) uses Expr in Argo Rollouts and Argo Workflows for Kubernetes.
-* [Crowdsec](https://crowdsec.net) uses Expr in a security automation tool.
-* [FACEIT](https://www.faceit.com) uses Expr to allow customization of its eSports matchmaking algorithm.
-* [qiniu](https://www.qiniu.com) uses Expr in trade systems.
-* [Junglee Games](https://www.jungleegames.com/) uses Expr for an in house marketing retention tool [Project Audience](https://www.linkedin.com/pulse/meet-project-audience-our-no-code-swiss-army-knife-product-bharti).
-* [OpenTelemetry](https://opentelemetry.io) uses Expr in the OpenTelemetry Collector.
-* [Philips Labs](https://github.com/philips-labs/tabia) uses Expr in Tabia, a tool for collecting insights on the characteristics of our code bases.
-* [CoreDNS](https://coredns.io) uses Expr in CoreDNS, a DNS server.
-* [Chaos Mesh](https://chaos-mesh.org) uses Expr in Chaos Mesh, a cloud-native Chaos Engineering platform.
-* [Milvus](https://milvus.io) uses Expr in Milvus, an open-source vector database.
-* [Visually.io](https://visually.io) uses Expr as a business rule engine for our personalization targeting algorithm.
-* [Akvorado](https://github.com/akvorado/akvorado) uses Expr to classify exporters and interfaces in network flows.
+* [Google](https://google.com) uses Expr as one of its expression languages on the [Google Cloud Platform](https://cloud.google.com).
+* [Uber](https://uber.com) uses Expr to allow customization of its Uber Eats marketplace.
+* [GoDaddy](https://godaddy.com) employs Expr for the customization of its GoDaddy Pro product.
+* [ByteDance](https://bytedance.com) incorporates Expr into its internal business rule engine.
+* [Aviasales](https://aviasales.ru) utilizes Expr as a business rule engine for its flight search engine.
+* [Wish.com](https://www.wish.com) employs Expr in its decision-making rule engine for the Wish Assistant.
+* [Argo](https://argoproj.github.io) integrates Expr into Argo Rollouts and Argo Workflows for Kubernetes.
+* [Crowdsec](https://crowdsec.net) incorporates Expr into its security automation tool.
+* [FACEIT](https://www.faceit.com) uses Expr to enhance customization of its eSports matchmaking algorithm.
+* [qiniu](https://www.qiniu.com) implements Expr in its trade systems.
+* [Junglee Games](https://www.jungleegames.com/) uses Expr for its in-house marketing retention tool, Project Audience.
+* [OpenTelemetry](https://opentelemetry.io) integrates Expr into the OpenTelemetry Collector.
+* [Philips Labs](https://github.com/philips-labs/tabia) employs Expr in Tabia, a tool designed to collect insights on their code bases.
+* [CoreDNS](https://coredns.io) uses Expr in CoreDNS, which is a DNS server.
+* [Chaos Mesh](https://chaos-mesh.org) incorporates Expr into Chaos Mesh, a cloud-native Chaos Engineering platform.
+* [Milvus](https://milvus.io) integrates Expr into Milvus, an open-source vector database.
+* [Visually.io](https://visually.io) employs Expr as a business rule engine for its personalization targeting algorithm.
+* [Akvorado](https://github.com/akvorado/akvorado) utilizes Expr to classify exporters and interfaces in network flows.
[Add your company too](https://github.com/antonmedv/expr/edit/master/README.md)
diff --git a/vendor/github.com/antonmedv/expr/SECURITY.md b/vendor/github.com/antonmedv/expr/SECURITY.md
new file mode 100644
index 0000000000..6e343ee367
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/SECURITY.md
@@ -0,0 +1,25 @@
+# Security Policy
+
+## Supported Versions
+
+Expr is generally backwards compatible with very few exceptions, so we
+recommend users to always use the latest version to experience stability,
+performance and security.
+
+We generally backport security issues to a single previous minor version,
+unless this is not possible or feasible with a reasonable effort.
+
+| Version | Supported |
+|---------|--------------------|
+| 1.15 | :white_check_mark: |
+| 1.14 | :white_check_mark: |
+| 1.13 | :white_check_mark: |
+| 1.12 | :white_check_mark: |
+| < 1.12 | :x: |
+
+## Reporting a Vulnerability
+
+If you believe you've discovered a serious vulnerability, please contact the
+Expr core team at anton+security@medv.io. We will evaluate your report and if
+necessary issue a fix and an advisory. If the issue was previously undisclosed,
+we'll also mention your name in the credits.
diff --git a/vendor/github.com/antonmedv/expr/ast/dump.go b/vendor/github.com/antonmedv/expr/ast/dump.go
new file mode 100644
index 0000000000..56bc7dbe2e
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/ast/dump.go
@@ -0,0 +1,59 @@
+package ast
+
+import (
+ "fmt"
+ "reflect"
+ "regexp"
+)
+
+func Dump(node Node) string {
+ return dump(reflect.ValueOf(node), "")
+}
+
+func dump(v reflect.Value, ident string) string {
+ if !v.IsValid() {
+ return "nil"
+ }
+ t := v.Type()
+ switch t.Kind() {
+ case reflect.Struct:
+ out := t.Name() + "{\n"
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ if isPrivate(f.Name) {
+ continue
+ }
+ s := v.Field(i)
+ out += fmt.Sprintf("%v%v: %v,\n", ident+"\t", f.Name, dump(s, ident+"\t"))
+ }
+ return out + ident + "}"
+ case reflect.Slice:
+ if v.Len() == 0 {
+ return t.String() + "{}"
+ }
+ out := t.String() + "{\n"
+ for i := 0; i < v.Len(); i++ {
+ s := v.Index(i)
+ out += fmt.Sprintf("%v%v,", ident+"\t", dump(s, ident+"\t"))
+ if i+1 < v.Len() {
+ out += "\n"
+ }
+ }
+ return out + "\n" + ident + "}"
+ case reflect.Ptr:
+ return dump(v.Elem(), ident)
+ case reflect.Interface:
+ return dump(reflect.ValueOf(v.Interface()), ident)
+
+ case reflect.String:
+ return fmt.Sprintf("%q", v)
+ default:
+ return fmt.Sprintf("%v", v)
+ }
+}
+
+var isCapital = regexp.MustCompile("^[A-Z]")
+
+func isPrivate(s string) bool {
+ return !isCapital.Match([]byte(s))
+}
diff --git a/vendor/github.com/antonmedv/expr/ast/func.go b/vendor/github.com/antonmedv/expr/ast/func.go
new file mode 100644
index 0000000000..a873c35e99
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/ast/func.go
@@ -0,0 +1,12 @@
+package ast
+
+import "reflect"
+
+type Function struct {
+ Name string
+ Func func(args ...any) (any, error)
+ Fast func(arg any) any
+ Types []reflect.Type
+ Validate func(args []reflect.Type) (reflect.Type, error)
+ Predicate bool
+}
diff --git a/vendor/github.com/antonmedv/expr/ast/node.go b/vendor/github.com/antonmedv/expr/ast/node.go
index e85f853e91..d037926c19 100644
--- a/vendor/github.com/antonmedv/expr/ast/node.go
+++ b/vendor/github.com/antonmedv/expr/ast/node.go
@@ -4,7 +4,6 @@ import (
"reflect"
"regexp"
- "github.com/antonmedv/expr/builtin"
"github.com/antonmedv/expr/file"
)
@@ -14,6 +13,7 @@ type Node interface {
SetLocation(file.Location)
Type() reflect.Type
SetType(reflect.Type)
+ String() string
}
func Patch(node *Node, newNode Node) {
@@ -50,7 +50,6 @@ type NilNode struct {
type IdentifierNode struct {
base
Value string
- Deref bool
FieldIndex []int
Method bool // true if method, false if field
MethodIndex int // index of method, set only if Method is true
@@ -78,7 +77,7 @@ type StringNode struct {
type ConstantNode struct {
base
- Value interface{}
+ Value any
}
type UnaryNode struct {
@@ -106,10 +105,9 @@ type MemberNode struct {
Property Node
Name string // Name of the filed or method. Used for error reporting.
Optional bool
- Deref bool
FieldIndex []int
- // TODO: Replace with a single MethodIndex field of &int type.
+ // TODO: Combine Method and MethodIndex into a single MethodIndex field of &int type.
Method bool
MethodIndex int
}
@@ -127,13 +125,15 @@ type CallNode struct {
Arguments []Node
Typed int
Fast bool
- Func *builtin.Function
+ Func *Function
}
type BuiltinNode struct {
base
Name string
Arguments []Node
+ Throws bool
+ Map Node
}
type ClosureNode struct {
@@ -143,6 +143,7 @@ type ClosureNode struct {
type PointerNode struct {
base
+ Name string
}
type ConditionalNode struct {
@@ -152,6 +153,13 @@ type ConditionalNode struct {
Exp2 Node
}
+type VariableDeclaratorNode struct {
+ base
+ Name string
+ Value Node
+ Expr Node
+}
+
type ArrayNode struct {
base
Nodes []Node
diff --git a/vendor/github.com/antonmedv/expr/ast/print.go b/vendor/github.com/antonmedv/expr/ast/print.go
index 56bc7dbe2e..dd9e0db0f9 100644
--- a/vendor/github.com/antonmedv/expr/ast/print.go
+++ b/vendor/github.com/antonmedv/expr/ast/print.go
@@ -1,59 +1,175 @@
package ast
import (
+ "encoding/json"
"fmt"
- "reflect"
- "regexp"
+ "strings"
+
+ "github.com/antonmedv/expr/parser/operator"
+ "github.com/antonmedv/expr/parser/utils"
)
-func Dump(node Node) string {
- return dump(reflect.ValueOf(node), "")
+func (n *NilNode) String() string {
+ return "nil"
+}
+
+func (n *IdentifierNode) String() string {
+ return n.Value
+}
+
+func (n *IntegerNode) String() string {
+ return fmt.Sprintf("%d", n.Value)
+}
+
+func (n *FloatNode) String() string {
+ return fmt.Sprintf("%v", n.Value)
}
-func dump(v reflect.Value, ident string) string {
- if !v.IsValid() {
+func (n *BoolNode) String() string {
+ return fmt.Sprintf("%t", n.Value)
+}
+
+func (n *StringNode) String() string {
+ return fmt.Sprintf("%q", n.Value)
+}
+
+func (n *ConstantNode) String() string {
+ if n.Value == nil {
return "nil"
}
- t := v.Type()
- switch t.Kind() {
- case reflect.Struct:
- out := t.Name() + "{\n"
- for i := 0; i < t.NumField(); i++ {
- f := t.Field(i)
- if isPrivate(f.Name) {
- continue
- }
- s := v.Field(i)
- out += fmt.Sprintf("%v%v: %v,\n", ident+"\t", f.Name, dump(s, ident+"\t"))
- }
- return out + ident + "}"
- case reflect.Slice:
- if v.Len() == 0 {
- return t.String() + "{}"
+ b, err := json.Marshal(n.Value)
+ if err != nil {
+ panic(err)
+ }
+ return string(b)
+}
+
+func (n *UnaryNode) String() string {
+ op := ""
+ if n.Operator == "not" {
+ op = fmt.Sprintf("%s ", n.Operator)
+ } else {
+ op = fmt.Sprintf("%s", n.Operator)
+ }
+ if _, ok := n.Node.(*BinaryNode); ok {
+ return fmt.Sprintf("%s(%s)", op, n.Node.String())
+ }
+ return fmt.Sprintf("%s%s", op, n.Node.String())
+}
+
+func (n *BinaryNode) String() string {
+ var left, right string
+ if b, ok := n.Left.(*BinaryNode); ok && operator.Less(b.Operator, n.Operator) {
+ left = fmt.Sprintf("(%s)", n.Left.String())
+ } else {
+ left = n.Left.String()
+ }
+ if b, ok := n.Right.(*BinaryNode); ok && operator.Less(b.Operator, n.Operator) {
+ right = fmt.Sprintf("(%s)", n.Right.String())
+ } else {
+ right = n.Right.String()
+ }
+ return fmt.Sprintf("%s %s %s", left, n.Operator, right)
+}
+
+func (n *ChainNode) String() string {
+ return n.Node.String()
+}
+
+func (n *MemberNode) String() string {
+ if n.Optional {
+ if str, ok := n.Property.(*StringNode); ok && utils.IsValidIdentifier(str.Value) {
+ return fmt.Sprintf("%s?.%s", n.Node.String(), str.Value)
+ } else {
+ return fmt.Sprintf("get(%s, %s)", n.Node.String(), n.Property.String())
}
- out := t.String() + "{\n"
- for i := 0; i < v.Len(); i++ {
- s := v.Index(i)
- out += fmt.Sprintf("%v%v,", ident+"\t", dump(s, ident+"\t"))
- if i+1 < v.Len() {
- out += "\n"
- }
+ }
+ if str, ok := n.Property.(*StringNode); ok && utils.IsValidIdentifier(str.Value) {
+ if _, ok := n.Node.(*PointerNode); ok {
+ return fmt.Sprintf(".%s", str.Value)
}
- return out + "\n" + ident + "}"
- case reflect.Ptr:
- return dump(v.Elem(), ident)
- case reflect.Interface:
- return dump(reflect.ValueOf(v.Interface()), ident)
+ return fmt.Sprintf("%s.%s", n.Node.String(), str.Value)
+ }
+ return fmt.Sprintf("%s[%s]", n.Node.String(), n.Property.String())
+}
+
+func (n *SliceNode) String() string {
+ if n.From == nil && n.To == nil {
+ return fmt.Sprintf("%s[:]", n.Node.String())
+ }
+ if n.From == nil {
+ return fmt.Sprintf("%s[:%s]", n.Node.String(), n.To.String())
+ }
+ if n.To == nil {
+ return fmt.Sprintf("%s[%s:]", n.Node.String(), n.From.String())
+ }
+ return fmt.Sprintf("%s[%s:%s]", n.Node.String(), n.From.String(), n.To.String())
+}
+
+func (n *CallNode) String() string {
+ arguments := make([]string, len(n.Arguments))
+ for i, arg := range n.Arguments {
+ arguments[i] = arg.String()
+ }
+ return fmt.Sprintf("%s(%s)", n.Callee.String(), strings.Join(arguments, ", "))
+}
+
+func (n *BuiltinNode) String() string {
+ arguments := make([]string, len(n.Arguments))
+ for i, arg := range n.Arguments {
+ arguments[i] = arg.String()
+ }
+ return fmt.Sprintf("%s(%s)", n.Name, strings.Join(arguments, ", "))
+}
- case reflect.String:
- return fmt.Sprintf("%q", v)
- default:
- return fmt.Sprintf("%v", v)
+func (n *ClosureNode) String() string {
+ return n.Node.String()
+}
+
+func (n *PointerNode) String() string {
+ return "#"
+}
+
+func (n *VariableDeclaratorNode) String() string {
+ return fmt.Sprintf("let %s = %s; %s", n.Name, n.Value.String(), n.Expr.String())
+}
+
+func (n *ConditionalNode) String() string {
+ var cond, exp1, exp2 string
+ if _, ok := n.Cond.(*ConditionalNode); ok {
+ cond = fmt.Sprintf("(%s)", n.Cond.String())
+ } else {
+ cond = n.Cond.String()
+ }
+ if _, ok := n.Exp1.(*ConditionalNode); ok {
+ exp1 = fmt.Sprintf("(%s)", n.Exp1.String())
+ } else {
+ exp1 = n.Exp1.String()
}
+ if _, ok := n.Exp2.(*ConditionalNode); ok {
+ exp2 = fmt.Sprintf("(%s)", n.Exp2.String())
+ } else {
+ exp2 = n.Exp2.String()
+ }
+ return fmt.Sprintf("%s ? %s : %s", cond, exp1, exp2)
+}
+
+func (n *ArrayNode) String() string {
+ nodes := make([]string, len(n.Nodes))
+ for i, node := range n.Nodes {
+ nodes[i] = node.String()
+ }
+ return fmt.Sprintf("[%s]", strings.Join(nodes, ", "))
}
-var isCapital = regexp.MustCompile("^[A-Z]")
+func (n *MapNode) String() string {
+ pairs := make([]string, len(n.Pairs))
+ for i, pair := range n.Pairs {
+ pairs[i] = pair.String()
+ }
+ return fmt.Sprintf("{%s}", strings.Join(pairs, ", "))
+}
-func isPrivate(s string) bool {
- return !isCapital.Match([]byte(s))
+func (n *PairNode) String() string {
+ return fmt.Sprintf("%s: %s", n.Key.String(), n.Value.String())
}
diff --git a/vendor/github.com/antonmedv/expr/ast/visitor.go b/vendor/github.com/antonmedv/expr/ast/visitor.go
index 351e5d72b2..287a755896 100644
--- a/vendor/github.com/antonmedv/expr/ast/visitor.go
+++ b/vendor/github.com/antonmedv/expr/ast/visitor.go
@@ -45,6 +45,9 @@ func Walk(node *Node, v Visitor) {
case *ClosureNode:
Walk(&n.Node, v)
case *PointerNode:
+ case *VariableDeclaratorNode:
+ Walk(&n.Value, v)
+ Walk(&n.Expr, v)
case *ConditionalNode:
Walk(&n.Cond, v)
Walk(&n.Exp1, v)
diff --git a/vendor/github.com/antonmedv/expr/builtin/builtin.go b/vendor/github.com/antonmedv/expr/builtin/builtin.go
index ad9376962e..d7248a8679 100644
--- a/vendor/github.com/antonmedv/expr/builtin/builtin.go
+++ b/vendor/github.com/antonmedv/expr/builtin/builtin.go
@@ -1,38 +1,104 @@
package builtin
import (
+ "encoding/base64"
+ "encoding/json"
"fmt"
"reflect"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/antonmedv/expr/ast"
+ "github.com/antonmedv/expr/vm/runtime"
)
var (
- anyType = reflect.TypeOf(new(interface{})).Elem()
- integerType = reflect.TypeOf(0)
- floatType = reflect.TypeOf(float64(0))
+ Index map[string]int
+ Names []string
)
-type Function struct {
- Name string
- Func func(args ...interface{}) (interface{}, error)
- Opcode int
- Types []reflect.Type
- Validate func(args []reflect.Type) (reflect.Type, error)
+func init() {
+ Index = make(map[string]int)
+ Names = make([]string, len(Builtins))
+ for i, fn := range Builtins {
+ Index[fn.Name] = i
+ Names[i] = fn.Name
+ }
}
-const (
- Len = iota + 1
- Abs
- Int
- Float
-)
-
-var Builtins = map[int]*Function{
- Len: {
- Name: "len",
- Opcode: Len,
+var Builtins = []*ast.Function{
+ {
+ Name: "all",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) bool)),
+ },
+ {
+ Name: "none",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) bool)),
+ },
+ {
+ Name: "any",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) bool)),
+ },
+ {
+ Name: "one",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) bool)),
+ },
+ {
+ Name: "filter",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) []any)),
+ },
+ {
+ Name: "map",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) any) []any)),
+ },
+ {
+ Name: "find",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) any)),
+ },
+ {
+ Name: "findIndex",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) int)),
+ },
+ {
+ Name: "findLast",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) any)),
+ },
+ {
+ Name: "findLastIndex",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) int)),
+ },
+ {
+ Name: "count",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) bool) int)),
+ },
+ {
+ Name: "groupBy",
+ Predicate: true,
+ Types: types(new(func([]any, func(any) any) map[any][]any)),
+ },
+ {
+ Name: "reduce",
+ Predicate: true,
+ Types: types(new(func([]any, func(any, any) any, any) any)),
+ },
+ {
+ Name: "len",
+ Fast: Len,
Validate: func(args []reflect.Type) (reflect.Type, error) {
if len(args) != 1 {
- return anyType, fmt.Errorf("invalid number of arguments for len (expected 1, got %d)", len(args))
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
switch kind(args[0]) {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String, reflect.Interface:
@@ -41,12 +107,17 @@ var Builtins = map[int]*Function{
return anyType, fmt.Errorf("invalid argument for len (type %s)", args[0])
},
},
- Abs: {
- Name: "abs",
- Opcode: Abs,
+ {
+ Name: "type",
+ Fast: Type,
+ Types: types(new(func(any) string)),
+ },
+ {
+ Name: "abs",
+ Fast: Abs,
Validate: func(args []reflect.Type) (reflect.Type, error) {
if len(args) != 1 {
- return anyType, fmt.Errorf("invalid number of arguments for abs (expected 1, got %d)", len(args))
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
switch kind(args[0]) {
case reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Interface:
@@ -55,12 +126,12 @@ var Builtins = map[int]*Function{
return anyType, fmt.Errorf("invalid argument for abs (type %s)", args[0])
},
},
- Int: {
- Name: "int",
- Opcode: Int,
+ {
+ Name: "int",
+ Fast: Int,
Validate: func(args []reflect.Type) (reflect.Type, error) {
if len(args) != 1 {
- return anyType, fmt.Errorf("invalid number of arguments for int (expected 1, got %d)", len(args))
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
switch kind(args[0]) {
case reflect.Interface:
@@ -73,12 +144,12 @@ var Builtins = map[int]*Function{
return anyType, fmt.Errorf("invalid argument for int (type %s)", args[0])
},
},
- Float: {
- Name: "float",
- Opcode: Float,
+ {
+ Name: "float",
+ Fast: Float,
Validate: func(args []reflect.Type) (reflect.Type, error) {
if len(args) != 1 {
- return anyType, fmt.Errorf("invalid number of arguments for float (expected 1, got %d)", len(args))
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
}
switch kind(args[0]) {
case reflect.Interface:
@@ -91,11 +162,772 @@ var Builtins = map[int]*Function{
return anyType, fmt.Errorf("invalid argument for float (type %s)", args[0])
},
},
-}
+ {
+ Name: "string",
+ Fast: String,
+ Types: types(new(func(any any) string)),
+ },
+ {
+ Name: "trim",
+ Func: func(args ...any) (any, error) {
+ if len(args) == 1 {
+ return strings.TrimSpace(args[0].(string)), nil
+ } else if len(args) == 2 {
+ return strings.Trim(args[0].(string), args[1].(string)), nil
+ } else {
+ return nil, fmt.Errorf("invalid number of arguments for trim (expected 1 or 2, got %d)", len(args))
+ }
+ },
+ Types: types(
+ strings.TrimSpace,
+ strings.Trim,
+ ),
+ },
+ {
+ Name: "trimPrefix",
+ Func: func(args ...any) (any, error) {
+ s := " "
+ if len(args) == 2 {
+ s = args[1].(string)
+ }
+ return strings.TrimPrefix(args[0].(string), s), nil
+ },
+ Types: types(
+ strings.TrimPrefix,
+ new(func(string) string),
+ ),
+ },
+ {
+ Name: "trimSuffix",
+ Func: func(args ...any) (any, error) {
+ s := " "
+ if len(args) == 2 {
+ s = args[1].(string)
+ }
+ return strings.TrimSuffix(args[0].(string), s), nil
+ },
+ Types: types(
+ strings.TrimSuffix,
+ new(func(string) string),
+ ),
+ },
+ {
+ Name: "upper",
+ Fast: func(arg any) any {
+ return strings.ToUpper(arg.(string))
+ },
+ Types: types(strings.ToUpper),
+ },
+ {
+ Name: "lower",
+ Fast: func(arg any) any {
+ return strings.ToLower(arg.(string))
+ },
+ Types: types(strings.ToLower),
+ },
+ {
+ Name: "split",
+ Func: func(args ...any) (any, error) {
+ if len(args) == 2 {
+ return strings.Split(args[0].(string), args[1].(string)), nil
+ } else if len(args) == 3 {
+ return strings.SplitN(args[0].(string), args[1].(string), runtime.ToInt(args[2])), nil
+ } else {
+ return nil, fmt.Errorf("invalid number of arguments for split (expected 2 or 3, got %d)", len(args))
+ }
+ },
+ Types: types(
+ strings.Split,
+ strings.SplitN,
+ ),
+ },
+ {
+ Name: "splitAfter",
+ Func: func(args ...any) (any, error) {
+ if len(args) == 2 {
+ return strings.SplitAfter(args[0].(string), args[1].(string)), nil
+ } else if len(args) == 3 {
+ return strings.SplitAfterN(args[0].(string), args[1].(string), runtime.ToInt(args[2])), nil
+ } else {
+ return nil, fmt.Errorf("invalid number of arguments for splitAfter (expected 2 or 3, got %d)", len(args))
+ }
+ },
+ Types: types(
+ strings.SplitAfter,
+ strings.SplitAfterN,
+ ),
+ },
+ {
+ Name: "replace",
+ Func: func(args ...any) (any, error) {
+ if len(args) == 4 {
+ return strings.Replace(args[0].(string), args[1].(string), args[2].(string), runtime.ToInt(args[3])), nil
+ } else if len(args) == 3 {
+ return strings.ReplaceAll(args[0].(string), args[1].(string), args[2].(string)), nil
+ } else {
+ return nil, fmt.Errorf("invalid number of arguments for replace (expected 3 or 4, got %d)", len(args))
+ }
+ },
+ Types: types(
+ strings.Replace,
+ strings.ReplaceAll,
+ ),
+ },
+ {
+ Name: "repeat",
+ Func: func(args ...any) (any, error) {
+ n := runtime.ToInt(args[1])
+ if n > 1e6 {
+ panic("memory budget exceeded")
+ }
+ return strings.Repeat(args[0].(string), n), nil
+ },
+ Types: types(strings.Repeat),
+ },
+ {
+ Name: "join",
+ Func: func(args ...any) (any, error) {
+ glue := ""
+ if len(args) == 2 {
+ glue = args[1].(string)
+ }
+ switch args[0].(type) {
+ case []string:
+ return strings.Join(args[0].([]string), glue), nil
+ case []any:
+ var s []string
+ for _, arg := range args[0].([]any) {
+ s = append(s, arg.(string))
+ }
+ return strings.Join(s, glue), nil
+ }
+ return nil, fmt.Errorf("invalid argument for join (type %s)", reflect.TypeOf(args[0]))
+ },
+ Types: types(
+ strings.Join,
+ new(func([]any, string) string),
+ new(func([]any) string),
+ new(func([]string, string) string),
+ new(func([]string) string),
+ ),
+ },
+ {
+ Name: "indexOf",
+ Func: func(args ...any) (any, error) {
+ return strings.Index(args[0].(string), args[1].(string)), nil
+ },
+ Types: types(strings.Index),
+ },
+ {
+ Name: "lastIndexOf",
+ Func: func(args ...any) (any, error) {
+ return strings.LastIndex(args[0].(string), args[1].(string)), nil
+ },
+ Types: types(strings.LastIndex),
+ },
+ {
+ Name: "hasPrefix",
+ Func: func(args ...any) (any, error) {
+ return strings.HasPrefix(args[0].(string), args[1].(string)), nil
+ },
+ Types: types(strings.HasPrefix),
+ },
+ {
+ Name: "hasSuffix",
+ Func: func(args ...any) (any, error) {
+ return strings.HasSuffix(args[0].(string), args[1].(string)), nil
+ },
+ Types: types(strings.HasSuffix),
+ },
+ {
+ Name: "max",
+ Func: Max,
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) == 0 {
+ return anyType, fmt.Errorf("not enough arguments to call max")
+ }
+ for _, arg := range args {
+ switch kind(arg) {
+ case reflect.Interface:
+ return anyType, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
+ default:
+ return anyType, fmt.Errorf("invalid argument for max (type %s)", arg)
+ }
+ }
+ return args[0], nil
+ },
+ },
+ {
+ Name: "min",
+ Func: Min,
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) == 0 {
+ return anyType, fmt.Errorf("not enough arguments to call min")
+ }
+ for _, arg := range args {
+ switch kind(arg) {
+ case reflect.Interface:
+ return anyType, nil
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
+ default:
+ return anyType, fmt.Errorf("invalid argument for min (type %s)", arg)
+ }
+ }
+ return args[0], nil
+ },
+ },
+ {
+ Name: "sum",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot sum %s", v.Kind())
+ }
+ sum := int64(0)
+ i := 0
+ for ; i < v.Len(); i++ {
+ it := deref(v.Index(i))
+ if it.CanInt() {
+ sum += it.Int()
+ } else if it.CanFloat() {
+ goto float
+ } else {
+ return nil, fmt.Errorf("cannot sum %s", it.Kind())
+ }
+ }
+ return int(sum), nil
+ float:
+ fSum := float64(sum)
+ for ; i < v.Len(); i++ {
+ it := deref(v.Index(i))
+ if it.CanInt() {
+ fSum += float64(it.Int())
+ } else if it.CanFloat() {
+ fSum += it.Float()
+ } else {
+ return nil, fmt.Errorf("cannot sum %s", it.Kind())
+ }
+ }
+ return fSum, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ default:
+ return anyType, fmt.Errorf("cannot sum %s", args[0])
+ }
+ return anyType, nil
+ },
+ },
+ {
+ Name: "mean",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot mean %s", v.Kind())
+ }
+ if v.Len() == 0 {
+ return 0.0, nil
+ }
+ sum := float64(0)
+ i := 0
+ for ; i < v.Len(); i++ {
+ it := deref(v.Index(i))
+ if it.CanInt() {
+ sum += float64(it.Int())
+ } else if it.CanFloat() {
+ sum += it.Float()
+ } else {
+ return nil, fmt.Errorf("cannot mean %s", it.Kind())
+ }
+ }
+ return sum / float64(i), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ default:
+ return anyType, fmt.Errorf("cannot avg %s", args[0])
+ }
+ return floatType, nil
+ },
+ },
+ {
+ Name: "median",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot median %s", v.Kind())
+ }
+ if v.Len() == 0 {
+ return 0.0, nil
+ }
+ s := make([]float64, v.Len())
+ for i := 0; i < v.Len(); i++ {
+ it := deref(v.Index(i))
+ if it.CanInt() {
+ s[i] = float64(it.Int())
+ } else if it.CanFloat() {
+ s[i] = it.Float()
+ } else {
+ return nil, fmt.Errorf("cannot median %s", it.Kind())
+ }
+ }
+ sort.Float64s(s)
+ if len(s)%2 == 0 {
+ return (s[len(s)/2-1] + s[len(s)/2]) / 2, nil
+ }
+ return s[len(s)/2], nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ default:
+ return anyType, fmt.Errorf("cannot median %s", args[0])
+ }
+ return floatType, nil
+ },
+ },
+ {
+ Name: "toJSON",
+ Func: func(args ...any) (any, error) {
+ b, err := json.MarshalIndent(args[0], "", " ")
+ if err != nil {
+ return nil, err
+ }
+ return string(b), nil
+ },
+ Types: types(new(func(any) string)),
+ },
+ {
+ Name: "fromJSON",
+ Func: func(args ...any) (any, error) {
+ var v any
+ err := json.Unmarshal([]byte(args[0].(string)), &v)
+ if err != nil {
+ return nil, err
+ }
+ return v, nil
+ },
+ Types: types(new(func(string) any)),
+ },
+ {
+ Name: "toBase64",
+ Func: func(args ...any) (any, error) {
+ return base64.StdEncoding.EncodeToString([]byte(args[0].(string))), nil
+ },
+ Types: types(new(func(string) string)),
+ },
+ {
+ Name: "fromBase64",
+ Func: func(args ...any) (any, error) {
+ b, err := base64.StdEncoding.DecodeString(args[0].(string))
+ if err != nil {
+ return nil, err
+ }
+ return string(b), nil
+ },
+ Types: types(new(func(string) string)),
+ },
+ {
+ Name: "now",
+ Func: func(args ...any) (any, error) {
+ return time.Now(), nil
+ },
+ Types: types(new(func() time.Time)),
+ },
+ {
+ Name: "duration",
+ Func: func(args ...any) (any, error) {
+ return time.ParseDuration(args[0].(string))
+ },
+ Types: types(time.ParseDuration),
+ },
+ {
+ Name: "date",
+ Func: func(args ...any) (any, error) {
+ date := args[0].(string)
+ if len(args) == 2 {
+ layout := args[1].(string)
+ return time.Parse(layout, date)
+ }
+ if len(args) == 3 {
+ layout := args[1].(string)
+ timeZone := args[2].(string)
+ tz, err := time.LoadLocation(timeZone)
+ if err != nil {
+ return nil, err
+ }
+ t, err := time.ParseInLocation(layout, date, tz)
+ if err != nil {
+ return nil, err
+ }
+ return t, nil
+ }
-func kind(t reflect.Type) reflect.Kind {
- if t == nil {
- return reflect.Invalid
- }
- return t.Kind()
+ layouts := []string{
+ "2006-01-02",
+ "15:04:05",
+ "2006-01-02 15:04:05",
+ time.RFC3339,
+ time.RFC822,
+ time.RFC850,
+ time.RFC1123,
+ }
+ for _, layout := range layouts {
+ t, err := time.Parse(layout, date)
+ if err == nil {
+ return t, nil
+ }
+ }
+ return nil, fmt.Errorf("invalid date %s", date)
+ },
+ Types: types(
+ new(func(string) time.Time),
+ new(func(string, string) time.Time),
+ new(func(string, string, string) time.Time),
+ ),
+ },
+ {
+ Name: "first",
+ Func: func(args ...any) (any, error) {
+ defer func() {
+ if r := recover(); r != nil {
+ return
+ }
+ }()
+ return runtime.Fetch(args[0], 0), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return anyType, nil
+ case reflect.Slice, reflect.Array:
+ return args[0].Elem(), nil
+ }
+ return anyType, fmt.Errorf("cannot get first element from %s", args[0])
+ },
+ },
+ {
+ Name: "last",
+ Func: func(args ...any) (any, error) {
+ defer func() {
+ if r := recover(); r != nil {
+ return
+ }
+ }()
+ return runtime.Fetch(args[0], -1), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return anyType, nil
+ case reflect.Slice, reflect.Array:
+ return args[0].Elem(), nil
+ }
+ return anyType, fmt.Errorf("cannot get last element from %s", args[0])
+ },
+ },
+ {
+ Name: "get",
+ Func: func(args ...any) (out any, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ return
+ }
+ }()
+ return runtime.Fetch(args[0], args[1]), nil
+ },
+ },
+ {
+ Name: "take",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 2 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 2, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot take from %s", v.Kind())
+ }
+ n := reflect.ValueOf(args[1])
+ if !n.CanInt() {
+ return nil, fmt.Errorf("cannot take %s elements", n.Kind())
+ }
+ if n.Int() > int64(v.Len()) {
+ return args[0], nil
+ }
+ return v.Slice(0, int(n.Int())).Interface(), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 2 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 2, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ default:
+ return anyType, fmt.Errorf("cannot take from %s", args[0])
+ }
+ switch kind(args[1]) {
+ case reflect.Interface, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ default:
+ return anyType, fmt.Errorf("cannot take %s elements", args[1])
+ }
+ return args[0], nil
+ },
+ },
+ {
+ Name: "keys",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Map {
+ return nil, fmt.Errorf("cannot get keys from %s", v.Kind())
+ }
+ keys := v.MapKeys()
+ out := make([]any, len(keys))
+ for i, key := range keys {
+ out[i] = key.Interface()
+ }
+ return out, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return arrayType, nil
+ case reflect.Map:
+ return arrayType, nil
+ }
+ return anyType, fmt.Errorf("cannot get keys from %s", args[0])
+ },
+ },
+ {
+ Name: "values",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Map {
+ return nil, fmt.Errorf("cannot get values from %s", v.Kind())
+ }
+ keys := v.MapKeys()
+ out := make([]any, len(keys))
+ for i, key := range keys {
+ out[i] = v.MapIndex(key).Interface()
+ }
+ return out, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface:
+ return arrayType, nil
+ case reflect.Map:
+ return arrayType, nil
+ }
+ return anyType, fmt.Errorf("cannot get values from %s", args[0])
+ },
+ },
+ {
+ Name: "toPairs",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Map {
+ return nil, fmt.Errorf("cannot transform %s to pairs", v.Kind())
+ }
+ keys := v.MapKeys()
+ out := make([][2]any, len(keys))
+ for i, key := range keys {
+ out[i] = [2]any{key.Interface(), v.MapIndex(key).Interface()}
+ }
+ return out, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Map:
+ return arrayType, nil
+ }
+ return anyType, fmt.Errorf("cannot transform %s to pairs", args[0])
+ },
+ },
+ {
+ Name: "fromPairs",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot transform %s from pairs", v)
+ }
+ out := reflect.MakeMap(mapType)
+ for i := 0; i < v.Len(); i++ {
+ pair := deref(v.Index(i))
+ if pair.Kind() != reflect.Array && pair.Kind() != reflect.Slice {
+ return nil, fmt.Errorf("invalid pair %v", pair)
+ }
+ if pair.Len() != 2 {
+ return nil, fmt.Errorf("invalid pair length %v", pair)
+ }
+ key := pair.Index(0)
+ value := pair.Index(1)
+ out.SetMapIndex(key, value)
+ }
+ return out.Interface(), nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ return mapType, nil
+ }
+ return anyType, fmt.Errorf("cannot transform %s from pairs", args[0])
+ },
+ },
+ {
+ Name: "sort",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 1 && len(args) != 2 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 1 or 2, got %d)", len(args))
+ }
+
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot sort %s", v.Kind())
+ }
+
+ orderBy := OrderBy{}
+ if len(args) == 2 {
+ dir, err := ascOrDesc(args[1])
+ if err != nil {
+ return nil, err
+ }
+ orderBy.Desc = dir
+ }
+
+ sortable, err := copyArray(v, orderBy)
+ if err != nil {
+ return nil, err
+ }
+ sort.Sort(sortable)
+ return sortable.Array, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 1 && len(args) != 2 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 1 or 2, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ default:
+ return anyType, fmt.Errorf("cannot sort %s", args[0])
+ }
+ if len(args) == 2 {
+ switch kind(args[1]) {
+ case reflect.String, reflect.Interface:
+ default:
+ return anyType, fmt.Errorf("invalid argument for sort (expected string, got %s)", args[1])
+ }
+ }
+ return arrayType, nil
+ },
+ },
+ {
+ Name: "sortBy",
+ Func: func(args ...any) (any, error) {
+ if len(args) != 2 && len(args) != 3 {
+ return nil, fmt.Errorf("invalid number of arguments (expected 2 or 3, got %d)", len(args))
+ }
+
+ v := reflect.ValueOf(args[0])
+ if v.Kind() != reflect.Slice && v.Kind() != reflect.Array {
+ return nil, fmt.Errorf("cannot sort %s", v.Kind())
+ }
+
+ orderBy := OrderBy{}
+
+ field, ok := args[1].(string)
+ if !ok {
+ return nil, fmt.Errorf("invalid argument for sort (expected string, got %s)", reflect.TypeOf(args[1]))
+ }
+ orderBy.Field = field
+
+ if len(args) == 3 {
+ dir, err := ascOrDesc(args[2])
+ if err != nil {
+ return nil, err
+ }
+ orderBy.Desc = dir
+ }
+
+ sortable, err := copyArray(v, orderBy)
+ if err != nil {
+ return nil, err
+ }
+ sort.Sort(sortable)
+ return sortable.Array, nil
+ },
+ Validate: func(args []reflect.Type) (reflect.Type, error) {
+ if len(args) != 2 && len(args) != 3 {
+ return anyType, fmt.Errorf("invalid number of arguments (expected 2 or 3, got %d)", len(args))
+ }
+ switch kind(args[0]) {
+ case reflect.Interface, reflect.Slice, reflect.Array:
+ default:
+ return anyType, fmt.Errorf("cannot sort %s", args[0])
+ }
+ switch kind(args[1]) {
+ case reflect.String, reflect.Interface:
+ default:
+ return anyType, fmt.Errorf("invalid argument for sort (expected string, got %s)", args[1])
+ }
+ if len(args) == 3 {
+ switch kind(args[2]) {
+ case reflect.String, reflect.Interface:
+ default:
+ return anyType, fmt.Errorf("invalid argument for sort (expected string, got %s)", args[1])
+ }
+ }
+ return arrayType, nil
+ },
+ },
}
diff --git a/vendor/github.com/antonmedv/expr/builtin/func.go b/vendor/github.com/antonmedv/expr/builtin/func.go
new file mode 100644
index 0000000000..7c042c6d28
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/builtin/func.go
@@ -0,0 +1,237 @@
+package builtin
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+
+ "github.com/antonmedv/expr/vm/runtime"
+)
+
+func Len(x any) any {
+ v := reflect.ValueOf(x)
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+ return v.Len()
+ default:
+ panic(fmt.Sprintf("invalid argument for len (type %T)", x))
+ }
+}
+
+func Type(arg any) any {
+ if arg == nil {
+ return "nil"
+ }
+ v := reflect.ValueOf(arg)
+ for {
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ } else if v.Kind() == reflect.Interface {
+ v = v.Elem()
+ } else {
+ break
+ }
+ }
+ if v.Type().Name() != "" && v.Type().PkgPath() != "" {
+ return fmt.Sprintf("%s.%s", v.Type().PkgPath(), v.Type().Name())
+ }
+ switch v.Type().Kind() {
+ case reflect.Invalid:
+ return "invalid"
+ case reflect.Bool:
+ return "bool"
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return "int"
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return "uint"
+ case reflect.Float32, reflect.Float64:
+ return "float"
+ case reflect.String:
+ return "string"
+ case reflect.Array, reflect.Slice:
+ return "array"
+ case reflect.Map:
+ return "map"
+ case reflect.Func:
+ return "func"
+ case reflect.Struct:
+ return "struct"
+ }
+ return "unknown"
+}
+
+func Abs(x any) any {
+ switch x.(type) {
+ case float32:
+ if x.(float32) < 0 {
+ return -x.(float32)
+ } else {
+ return x
+ }
+ case float64:
+ if x.(float64) < 0 {
+ return -x.(float64)
+ } else {
+ return x
+ }
+ case int:
+ if x.(int) < 0 {
+ return -x.(int)
+ } else {
+ return x
+ }
+ case int8:
+ if x.(int8) < 0 {
+ return -x.(int8)
+ } else {
+ return x
+ }
+ case int16:
+ if x.(int16) < 0 {
+ return -x.(int16)
+ } else {
+ return x
+ }
+ case int32:
+ if x.(int32) < 0 {
+ return -x.(int32)
+ } else {
+ return x
+ }
+ case int64:
+ if x.(int64) < 0 {
+ return -x.(int64)
+ } else {
+ return x
+ }
+ case uint:
+ if x.(uint) < 0 {
+ return -x.(uint)
+ } else {
+ return x
+ }
+ case uint8:
+ if x.(uint8) < 0 {
+ return -x.(uint8)
+ } else {
+ return x
+ }
+ case uint16:
+ if x.(uint16) < 0 {
+ return -x.(uint16)
+ } else {
+ return x
+ }
+ case uint32:
+ if x.(uint32) < 0 {
+ return -x.(uint32)
+ } else {
+ return x
+ }
+ case uint64:
+ if x.(uint64) < 0 {
+ return -x.(uint64)
+ } else {
+ return x
+ }
+ }
+ panic(fmt.Sprintf("invalid argument for abs (type %T)", x))
+}
+
+func Int(x any) any {
+ switch x := x.(type) {
+ case float32:
+ return int(x)
+ case float64:
+ return int(x)
+ case int:
+ return x
+ case int8:
+ return int(x)
+ case int16:
+ return int(x)
+ case int32:
+ return int(x)
+ case int64:
+ return int(x)
+ case uint:
+ return int(x)
+ case uint8:
+ return int(x)
+ case uint16:
+ return int(x)
+ case uint32:
+ return int(x)
+ case uint64:
+ return int(x)
+ case string:
+ i, err := strconv.Atoi(x)
+ if err != nil {
+ panic(fmt.Sprintf("invalid operation: int(%s)", x))
+ }
+ return i
+ default:
+ panic(fmt.Sprintf("invalid operation: int(%T)", x))
+ }
+}
+
+func Float(x any) any {
+ switch x := x.(type) {
+ case float32:
+ return float64(x)
+ case float64:
+ return x
+ case int:
+ return float64(x)
+ case int8:
+ return float64(x)
+ case int16:
+ return float64(x)
+ case int32:
+ return float64(x)
+ case int64:
+ return float64(x)
+ case uint:
+ return float64(x)
+ case uint8:
+ return float64(x)
+ case uint16:
+ return float64(x)
+ case uint32:
+ return float64(x)
+ case uint64:
+ return float64(x)
+ case string:
+ f, err := strconv.ParseFloat(x, 64)
+ if err != nil {
+ panic(fmt.Sprintf("invalid operation: float(%s)", x))
+ }
+ return f
+ default:
+ panic(fmt.Sprintf("invalid operation: float(%T)", x))
+ }
+}
+
+func String(arg any) any {
+ return fmt.Sprintf("%v", arg)
+}
+
+func Max(args ...any) (any, error) {
+ var max any
+ for _, arg := range args {
+ if max == nil || runtime.Less(max, arg) {
+ max = arg
+ }
+ }
+ return max, nil
+}
+
+func Min(args ...any) (any, error) {
+ var min any
+ for _, arg := range args {
+ if min == nil || runtime.More(min, arg) {
+ min = arg
+ }
+ }
+ return min, nil
+}
diff --git a/vendor/github.com/antonmedv/expr/builtin/sort.go b/vendor/github.com/antonmedv/expr/builtin/sort.go
new file mode 100644
index 0000000000..31e302c727
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/builtin/sort.go
@@ -0,0 +1,91 @@
+package builtin
+
+import (
+ "fmt"
+ "reflect"
+)
+
+type Sortable struct {
+ Array []any
+ Values []reflect.Value
+ OrderBy
+}
+
+type OrderBy struct {
+ Field string
+ Desc bool
+}
+
+func (s *Sortable) Len() int {
+ return len(s.Array)
+}
+
+func (s *Sortable) Swap(i, j int) {
+ s.Array[i], s.Array[j] = s.Array[j], s.Array[i]
+ s.Values[i], s.Values[j] = s.Values[j], s.Values[i]
+}
+
+func (s *Sortable) Less(i, j int) bool {
+ a, b := s.Values[i], s.Values[j]
+ switch a.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ if s.Desc {
+ return a.Int() > b.Int()
+ }
+ return a.Int() < b.Int()
+ case reflect.String:
+ if s.Desc {
+ return a.String() > b.String()
+ }
+ return a.String() < b.String()
+ default:
+ panic(fmt.Sprintf("sort: unsupported type %s", a.Kind()))
+ }
+}
+
+func copyArray(v reflect.Value, orderBy OrderBy) (*Sortable, error) {
+ s := &Sortable{
+ Array: make([]any, v.Len()),
+ Values: make([]reflect.Value, v.Len()),
+ OrderBy: orderBy,
+ }
+ var prev reflect.Value
+ for i := 0; i < s.Len(); i++ {
+ elem := deref(v.Index(i))
+ var value reflect.Value
+ switch elem.Kind() {
+ case reflect.Struct:
+ value = elem.FieldByName(s.Field)
+ case reflect.Map:
+ value = elem.MapIndex(reflect.ValueOf(s.Field))
+ default:
+ value = elem
+ }
+ value = deref(value)
+
+ s.Array[i] = elem.Interface()
+ s.Values[i] = value
+
+ if i == 0 {
+ prev = value
+ } else if value.Type() != prev.Type() {
+ return nil, fmt.Errorf("cannot sort array of different types (%s and %s)", value.Type(), prev.Type())
+ }
+ }
+ return s, nil
+}
+
+func ascOrDesc(arg any) (bool, error) {
+ dir, ok := arg.(string)
+ if !ok {
+ return false, fmt.Errorf("invalid argument for sort (expected string, got %s)", reflect.TypeOf(arg))
+ }
+ switch dir {
+ case "desc":
+ return true, nil
+ case "asc":
+ return false, nil
+ default:
+ return false, fmt.Errorf(`invalid argument for sort (expected "asc" or "desc", got %q)`, dir)
+ }
+}
diff --git a/vendor/github.com/antonmedv/expr/builtin/utils.go b/vendor/github.com/antonmedv/expr/builtin/utils.go
new file mode 100644
index 0000000000..eff8fdcd1d
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/builtin/utils.go
@@ -0,0 +1,65 @@
+package builtin
+
+import (
+ "fmt"
+ "reflect"
+)
+
+var (
+ anyType = reflect.TypeOf(new(any)).Elem()
+ integerType = reflect.TypeOf(0)
+ floatType = reflect.TypeOf(float64(0))
+ arrayType = reflect.TypeOf([]any{})
+ mapType = reflect.TypeOf(map[any]any{})
+)
+
+func kind(t reflect.Type) reflect.Kind {
+ if t == nil {
+ return reflect.Invalid
+ }
+ return t.Kind()
+}
+
+func types(types ...any) []reflect.Type {
+ ts := make([]reflect.Type, len(types))
+ for i, t := range types {
+ t := reflect.TypeOf(t)
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ if t.Kind() != reflect.Func {
+ panic("not a function")
+ }
+ ts[i] = t
+ }
+ return ts
+}
+
+func deref(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Interface {
+ if v.IsNil() {
+ return v
+ }
+ v = v.Elem()
+ }
+
+loop:
+ for v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ return v
+ }
+ indirect := reflect.Indirect(v)
+ switch indirect.Kind() {
+ case reflect.Struct, reflect.Map, reflect.Array, reflect.Slice:
+ break loop
+ default:
+ v = v.Elem()
+ }
+ }
+
+ if v.IsValid() {
+ return v
+ }
+
+ panic(fmt.Sprintf("cannot deref %s", v))
+}
diff --git a/vendor/github.com/antonmedv/expr/checker/checker.go b/vendor/github.com/antonmedv/expr/checker/checker.go
index 00025a33ce..efa1b7eb93 100644
--- a/vendor/github.com/antonmedv/expr/checker/checker.go
+++ b/vendor/github.com/antonmedv/expr/checker/checker.go
@@ -18,11 +18,7 @@ func Check(tree *parser.Tree, config *conf.Config) (t reflect.Type, err error) {
config = conf.New(nil)
}
- v := &visitor{
- config: config,
- collections: make([]reflect.Type, 0),
- parents: make([]ast.Node, 0),
- }
+ v := &checker{config: config}
t, _ = v.visit(tree.Node)
@@ -31,9 +27,15 @@ func Check(tree *parser.Tree, config *conf.Config) (t reflect.Type, err error) {
}
if v.config.Expect != reflect.Invalid {
+ if v.config.ExpectAny {
+ if isAny(t) {
+ return t, nil
+ }
+ }
+
switch v.config.Expect {
case reflect.Int, reflect.Int64, reflect.Float64:
- if !isNumber(t) && !isAny(t) {
+ if !isNumber(t) {
return nil, fmt.Errorf("expected %v, but got %v", v.config.Expect, t)
}
default:
@@ -49,19 +51,31 @@ func Check(tree *parser.Tree, config *conf.Config) (t reflect.Type, err error) {
return t, nil
}
-type visitor struct {
- config *conf.Config
- collections []reflect.Type
- parents []ast.Node
- err *file.Error
+type checker struct {
+ config *conf.Config
+ predicateScopes []predicateScope
+ varScopes []varScope
+ parents []ast.Node
+ err *file.Error
+}
+
+type predicateScope struct {
+ vtype reflect.Type
+ vars map[string]reflect.Type
+}
+
+type varScope struct {
+ name string
+ vtype reflect.Type
+ info info
}
type info struct {
method bool
- fn *builtin.Function
+ fn *ast.Function
}
-func (v *visitor) visit(node ast.Node) (reflect.Type, info) {
+func (v *checker) visit(node ast.Node) (reflect.Type, info) {
var t reflect.Type
var i info
v.parents = append(v.parents, node)
@@ -98,6 +112,8 @@ func (v *visitor) visit(node ast.Node) (reflect.Type, info) {
t, i = v.ClosureNode(n)
case *ast.PointerNode:
t, i = v.PointerNode(n)
+ case *ast.VariableDeclaratorNode:
+ t, i = v.VariableDeclaratorNode(n)
case *ast.ConditionalNode:
t, i = v.ConditionalNode(n)
case *ast.ArrayNode:
@@ -114,7 +130,7 @@ func (v *visitor) visit(node ast.Node) (reflect.Type, info) {
return t, i
}
-func (v *visitor) error(node ast.Node, format string, args ...interface{}) (reflect.Type, info) {
+func (v *checker) error(node ast.Node, format string, args ...any) (reflect.Type, info) {
if v.err == nil { // show first error
v.err = &file.Error{
Location: node.Location(),
@@ -124,29 +140,31 @@ func (v *visitor) error(node ast.Node, format string, args ...interface{}) (refl
return anyType, info{} // interface represent undefined type
}
-func (v *visitor) NilNode(*ast.NilNode) (reflect.Type, info) {
+func (v *checker) NilNode(*ast.NilNode) (reflect.Type, info) {
return nilType, info{}
}
-func (v *visitor) IdentifierNode(node *ast.IdentifierNode) (reflect.Type, info) {
+func (v *checker) IdentifierNode(node *ast.IdentifierNode) (reflect.Type, info) {
+ if s, ok := v.lookupVariable(node.Value); ok {
+ return s.vtype, s.info
+ }
+ if node.Value == "$env" {
+ return mapType, info{}
+ }
+ if fn, ok := v.config.Builtins[node.Value]; ok {
+ return functionType, info{fn: fn}
+ }
if fn, ok := v.config.Functions[node.Value]; ok {
- // Return anyType instead of func type as we don't know the arguments yet.
- // The func type can be one of the fn.Types. The type will be resolved
- // when the arguments are known in CallNode.
- return anyType, info{fn: fn}
- }
- if v.config.Types == nil {
- node.Deref = true
- } else if t, ok := v.config.Types[node.Value]; ok {
+ return functionType, info{fn: fn}
+ }
+ if t, ok := v.config.Types[node.Value]; ok {
if t.Ambiguous {
return v.error(node, "ambiguous identifier %v", node.Value)
}
- d, c := deref(t.Type)
- node.Deref = c
node.Method = t.Method
node.MethodIndex = t.MethodIndex
node.FieldIndex = t.FieldIndex
- return d, info{method: t.Method}
+ return t.Type, info{method: t.Method}
}
if v.config.Strict {
return v.error(node, "unknown name %v", node.Value)
@@ -157,29 +175,31 @@ func (v *visitor) IdentifierNode(node *ast.IdentifierNode) (reflect.Type, info)
return anyType, info{}
}
-func (v *visitor) IntegerNode(*ast.IntegerNode) (reflect.Type, info) {
+func (v *checker) IntegerNode(*ast.IntegerNode) (reflect.Type, info) {
return integerType, info{}
}
-func (v *visitor) FloatNode(*ast.FloatNode) (reflect.Type, info) {
+func (v *checker) FloatNode(*ast.FloatNode) (reflect.Type, info) {
return floatType, info{}
}
-func (v *visitor) BoolNode(*ast.BoolNode) (reflect.Type, info) {
+func (v *checker) BoolNode(*ast.BoolNode) (reflect.Type, info) {
return boolType, info{}
}
-func (v *visitor) StringNode(*ast.StringNode) (reflect.Type, info) {
+func (v *checker) StringNode(*ast.StringNode) (reflect.Type, info) {
return stringType, info{}
}
-func (v *visitor) ConstantNode(node *ast.ConstantNode) (reflect.Type, info) {
+func (v *checker) ConstantNode(node *ast.ConstantNode) (reflect.Type, info) {
return reflect.TypeOf(node.Value), info{}
}
-func (v *visitor) UnaryNode(node *ast.UnaryNode) (reflect.Type, info) {
+func (v *checker) UnaryNode(node *ast.UnaryNode) (reflect.Type, info) {
t, _ := v.visit(node.Node)
+ t = deref(t)
+
switch node.Operator {
case "!", "not":
@@ -205,10 +225,13 @@ func (v *visitor) UnaryNode(node *ast.UnaryNode) (reflect.Type, info) {
return v.error(node, `invalid operation: %v (mismatched type %v)`, node.Operator, t)
}
-func (v *visitor) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
+func (v *checker) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
l, _ := v.visit(node.Left)
r, _ := v.visit(node.Right)
+ l = deref(l)
+ r = deref(r)
+
// check operator overloading
if fns, ok := v.config.Operators[node.Operator]; ok {
t, _, ok := conf.FindSuitableOperatorOverload(fns, v.config.Types, l, r)
@@ -219,16 +242,7 @@ func (v *visitor) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
switch node.Operator {
case "==", "!=":
- if isNumber(l) && isNumber(r) {
- return boolType, info{}
- }
- if l == nil || r == nil { // It is possible to compare with nil.
- return boolType, info{}
- }
- if l.Kind() == r.Kind() {
- return boolType, info{}
- }
- if isAny(l) || isAny(r) {
+ if isComparable(l, r) {
return boolType, info{}
}
@@ -261,11 +275,14 @@ func (v *visitor) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
if isTime(l) && isTime(r) {
return durationType, info{}
}
+ if isTime(l) && isDuration(r) {
+ return timeType, info{}
+ }
if or(l, r, isNumber, isTime) {
return anyType, info{}
}
- case "/", "*":
+ case "*":
if isNumber(l) && isNumber(r) {
return combined(l, r), info{}
}
@@ -273,6 +290,14 @@ func (v *visitor) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
return anyType, info{}
}
+ case "/":
+ if isNumber(l) && isNumber(r) {
+ return floatType, info{}
+ }
+ if or(l, r, isNumber) {
+ return floatType, info{}
+ }
+
case "**", "^":
if isNumber(l) && isNumber(r) {
return floatType, info{}
@@ -311,9 +336,21 @@ func (v *visitor) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
return boolType, info{}
}
if isMap(r) {
+ if l == nil { // It is possible to compare with nil.
+ return boolType, info{}
+ }
+ if !isAny(l) && !l.AssignableTo(r.Key()) {
+ return v.error(node, "cannot use %v as type %v in map key", l, r.Key())
+ }
return boolType, info{}
}
if isArray(r) {
+ if l == nil { // It is possible to compare with nil.
+ return boolType, info{}
+ }
+ if !isComparable(l, r.Elem()) {
+ return v.error(node, "cannot use %v as type %v in array", l, r.Elem())
+ }
return boolType, info{}
}
if isAny(l) && anyOf(r, isString, isArray, isMap) {
@@ -378,14 +415,25 @@ func (v *visitor) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
return v.error(node, `invalid operation: %v (mismatched types %v and %v)`, node.Operator, l, r)
}
-func (v *visitor) ChainNode(node *ast.ChainNode) (reflect.Type, info) {
+func (v *checker) ChainNode(node *ast.ChainNode) (reflect.Type, info) {
return v.visit(node.Node)
}
-func (v *visitor) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
+func (v *checker) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
base, _ := v.visit(node.Node)
prop, _ := v.visit(node.Property)
+ if an, ok := node.Node.(*ast.IdentifierNode); ok && an.Value == "$env" {
+ // If the index is a constant string, can save some
+ // cycles later by finding the type of its referent
+ if name, ok := node.Property.(*ast.StringNode); ok {
+ if t, ok := v.config.Types[name.Value]; ok {
+ return t.Type, info{method: t.Method}
+ } // No error if no type found; it may be added to env between compile and run
+ }
+ return anyType, info{}
+ }
+
if name, ok := node.Property.(*ast.StringNode); ok {
if base == nil {
return v.error(node, "type %v has no field %v", base, name.Value)
@@ -393,7 +441,7 @@ func (v *visitor) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
// First, check methods defined on base type itself,
// independent of which type it is. Without dereferencing.
if m, ok := base.MethodByName(name.Value); ok {
- if base.Kind() == reflect.Interface {
+ if kind(base) == reflect.Interface {
// In case of interface type method will not have a receiver,
// and to prevent checker decreasing numbers of in arguments
// return method type as not method (second argument is false).
@@ -411,40 +459,33 @@ func (v *visitor) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
}
}
- if base.Kind() == reflect.Ptr {
+ if kind(base) == reflect.Ptr {
base = base.Elem()
}
- switch base.Kind() {
+ switch kind(base) {
case reflect.Interface:
- node.Deref = true
return anyType, info{}
case reflect.Map:
if prop != nil && !prop.AssignableTo(base.Key()) && !isAny(prop) {
return v.error(node.Property, "cannot use %v to get an element from %v", prop, base)
}
- t, c := deref(base.Elem())
- node.Deref = c
- return t, info{}
+ return base.Elem(), info{}
case reflect.Array, reflect.Slice:
if !isInteger(prop) && !isAny(prop) {
return v.error(node.Property, "array elements can only be selected using an integer (got %v)", prop)
}
- t, c := deref(base.Elem())
- node.Deref = c
- return t, info{}
+ return base.Elem(), info{}
case reflect.Struct:
if name, ok := node.Property.(*ast.StringNode); ok {
propertyName := name.Value
if field, ok := fetchField(base, propertyName); ok {
- t, c := deref(field.Type)
- node.Deref = c
node.FieldIndex = field.Index
node.Name = propertyName
- return t, info{}
+ return field.Type, info{}
}
if len(v.parents) > 1 {
if _, ok := v.parents[len(v.parents)-2].(*ast.CallNode); ok {
@@ -458,10 +499,10 @@ func (v *visitor) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
return v.error(node, "type %v[%v] is undefined", base, prop)
}
-func (v *visitor) SliceNode(node *ast.SliceNode) (reflect.Type, info) {
+func (v *checker) SliceNode(node *ast.SliceNode) (reflect.Type, info) {
t, _ := v.visit(node.Node)
- switch t.Kind() {
+ switch kind(t) {
case reflect.Interface:
// ok
case reflect.String, reflect.Array, reflect.Slice:
@@ -485,49 +526,12 @@ func (v *visitor) SliceNode(node *ast.SliceNode) (reflect.Type, info) {
return t, info{}
}
-func (v *visitor) CallNode(node *ast.CallNode) (reflect.Type, info) {
+func (v *checker) CallNode(node *ast.CallNode) (reflect.Type, info) {
fn, fnInfo := v.visit(node.Callee)
if fnInfo.fn != nil {
- f := fnInfo.fn
- node.Func = f
- if f.Validate != nil {
- args := make([]reflect.Type, len(node.Arguments))
- for i, arg := range node.Arguments {
- args[i], _ = v.visit(arg)
- }
- t, err := f.Validate(args)
- if err != nil {
- return v.error(node, "%v", err)
- }
- return t, info{}
- }
- if len(f.Types) == 0 {
- t, err := v.checkFunc(f.Name, functionType, false, node)
- if err != nil {
- if v.err == nil {
- v.err = err
- }
- return anyType, info{}
- }
- // No type was specified, so we assume the function returns any.
- return t, info{}
- }
- var lastErr *file.Error
- for _, t := range f.Types {
- outType, err := v.checkFunc(f.Name, t, false, node)
- if err != nil {
- lastErr = err
- continue
- }
- return outType, info{}
- }
- if lastErr != nil {
- if v.err == nil {
- v.err = lastErr
- }
- return anyType, info{}
- }
+ node.Func = fnInfo.fn
+ return v.checkFunction(fnInfo.fn, node, node.Arguments)
}
fnName := "function"
@@ -555,12 +559,12 @@ func (v *visitor) CallNode(node *ast.CallNode) (reflect.Type, info) {
fn.NumOut() == 1 &&
fn.Out(0).Kind() == reflect.Interface {
rest := fn.In(fn.NumIn() - 1) // function has only one param for functions and two for methods
- if rest.Kind() == reflect.Slice && rest.Elem().Kind() == reflect.Interface {
+ if kind(rest) == reflect.Slice && rest.Elem().Kind() == reflect.Interface {
node.Fast = true
}
}
- outType, err := v.checkFunc(fnName, fn, fnInfo.method, node)
+ outType, err := v.checkArguments(fnName, fn, fnInfo.method, node.Arguments, node)
if err != nil {
if v.err == nil {
v.err = err
@@ -575,7 +579,287 @@ func (v *visitor) CallNode(node *ast.CallNode) (reflect.Type, info) {
return v.error(node, "%v is not callable", fn)
}
-func (v *visitor) checkFunc(name string, fn reflect.Type, method bool, node *ast.CallNode) (reflect.Type, *file.Error) {
+func (v *checker) BuiltinNode(node *ast.BuiltinNode) (reflect.Type, info) {
+ switch node.Name {
+ case "all", "none", "any", "one":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+ return boolType, info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "filter":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+ if isAny(collection) {
+ return arrayType, info{}
+ }
+ return reflect.SliceOf(collection.Elem()), info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "map":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection, scopeVar{"index", integerType})
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ return reflect.SliceOf(closure.Out(0)), info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "count":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+
+ return integerType, info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "find", "findLast":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+ if isAny(collection) {
+ return anyType, info{}
+ }
+ return collection.Elem(), info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "findIndex", "findLastIndex":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
+ return v.error(node.Arguments[1], "predicate should return boolean (got %v)", closure.Out(0).String())
+ }
+ return integerType, info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "groupBy":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection)
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if isFunc(closure) &&
+ closure.NumOut() == 1 &&
+ closure.NumIn() == 1 && isAny(closure.In(0)) {
+
+ return reflect.TypeOf(map[any][]any{}), info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has one input and one output param")
+
+ case "reduce":
+ collection, _ := v.visit(node.Arguments[0])
+ if !isArray(collection) && !isAny(collection) {
+ return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+ }
+
+ v.begin(collection, scopeVar{"index", integerType}, scopeVar{"acc", anyType})
+ closure, _ := v.visit(node.Arguments[1])
+ v.end()
+
+ if len(node.Arguments) == 3 {
+ _, _ = v.visit(node.Arguments[2])
+ }
+
+ if isFunc(closure) && closure.NumOut() == 1 {
+ return closure.Out(0), info{}
+ }
+ return v.error(node.Arguments[1], "predicate should has two input and one output param")
+
+ }
+
+ if id, ok := builtin.Index[node.Name]; ok {
+ switch node.Name {
+ case "get":
+ return v.checkBuiltinGet(node)
+ }
+ return v.checkFunction(builtin.Builtins[id], node, node.Arguments)
+ }
+
+ return v.error(node, "unknown builtin %v", node.Name)
+}
+
+type scopeVar struct {
+ name string
+ vtype reflect.Type
+}
+
+func (v *checker) begin(vtype reflect.Type, vars ...scopeVar) {
+ scope := predicateScope{vtype: vtype, vars: make(map[string]reflect.Type)}
+ for _, v := range vars {
+ scope.vars[v.name] = v.vtype
+ }
+ v.predicateScopes = append(v.predicateScopes, scope)
+}
+
+func (v *checker) end() {
+ v.predicateScopes = v.predicateScopes[:len(v.predicateScopes)-1]
+}
+
+func (v *checker) checkBuiltinGet(node *ast.BuiltinNode) (reflect.Type, info) {
+ if len(node.Arguments) != 2 {
+ return v.error(node, "invalid number of arguments (expected 2, got %d)", len(node.Arguments))
+ }
+
+ val := node.Arguments[0]
+ prop := node.Arguments[1]
+ if id, ok := val.(*ast.IdentifierNode); ok && id.Value == "$env" {
+ if s, ok := prop.(*ast.StringNode); ok {
+ return v.config.Types[s.Value].Type, info{}
+ }
+ return anyType, info{}
+ }
+
+ t, _ := v.visit(val)
+
+ switch kind(t) {
+ case reflect.Interface:
+ return anyType, info{}
+ case reflect.Slice, reflect.Array:
+ p, _ := v.visit(prop)
+ if p == nil {
+ return v.error(prop, "cannot use nil as slice index")
+ }
+ if !isInteger(p) && !isAny(p) {
+ return v.error(prop, "non-integer slice index %v", p)
+ }
+ return t.Elem(), info{}
+ case reflect.Map:
+ p, _ := v.visit(prop)
+ if p == nil {
+ return v.error(prop, "cannot use nil as map index")
+ }
+ if !p.AssignableTo(t.Key()) && !isAny(p) {
+ return v.error(prop, "cannot use %v to get an element from %v", p, t)
+ }
+ return t.Elem(), info{}
+ }
+ return v.error(val, "type %v does not support indexing", t)
+}
+
+func (v *checker) checkFunction(f *ast.Function, node ast.Node, arguments []ast.Node) (reflect.Type, info) {
+ if f.Validate != nil {
+ args := make([]reflect.Type, len(arguments))
+ for i, arg := range arguments {
+ args[i], _ = v.visit(arg)
+ }
+ t, err := f.Validate(args)
+ if err != nil {
+ return v.error(node, "%v", err)
+ }
+ return t, info{}
+ } else if len(f.Types) == 0 {
+ t, err := v.checkArguments(f.Name, functionType, false, arguments, node)
+ if err != nil {
+ if v.err == nil {
+ v.err = err
+ }
+ return anyType, info{}
+ }
+ // No type was specified, so we assume the function returns any.
+ return t, info{}
+ }
+ var lastErr *file.Error
+ for _, t := range f.Types {
+ outType, err := v.checkArguments(f.Name, t, false, arguments, node)
+ if err != nil {
+ lastErr = err
+ continue
+ }
+ return outType, info{}
+ }
+ if lastErr != nil {
+ if v.err == nil {
+ v.err = lastErr
+ }
+ return anyType, info{}
+ }
+
+ return v.error(node, "no matching overload for %v", f.Name)
+}
+
+func (v *checker) checkArguments(name string, fn reflect.Type, method bool, arguments []ast.Node, node ast.Node) (reflect.Type, *file.Error) {
if isAny(fn) {
return anyType, nil
}
@@ -606,20 +890,20 @@ func (v *visitor) checkFunc(name string, fn reflect.Type, method bool, node *ast
}
if fn.IsVariadic() {
- if len(node.Arguments) < fnNumIn-1 {
+ if len(arguments) < fnNumIn-1 {
return anyType, &file.Error{
Location: node.Location(),
Message: fmt.Sprintf("not enough arguments to call %v", name),
}
}
} else {
- if len(node.Arguments) > fnNumIn {
+ if len(arguments) > fnNumIn {
return anyType, &file.Error{
Location: node.Location(),
Message: fmt.Sprintf("too many arguments to call %v", name),
}
}
- if len(node.Arguments) < fnNumIn {
+ if len(arguments) < fnNumIn {
return anyType, &file.Error{
Location: node.Location(),
Message: fmt.Sprintf("not enough arguments to call %v", name),
@@ -627,7 +911,7 @@ func (v *visitor) checkFunc(name string, fn reflect.Type, method bool, node *ast
}
}
- for i, arg := range node.Arguments {
+ for i, arg := range arguments {
t, _ := v.visit(arg)
var in reflect.Type
@@ -639,16 +923,21 @@ func (v *visitor) checkFunc(name string, fn reflect.Type, method bool, node *ast
in = fn.In(i + fnInOffset)
}
- if isIntegerOrArithmeticOperation(arg) && (isInteger(in) || isFloat(in)) {
- t = in
- setTypeForIntegers(arg, t)
+ if isFloat(in) {
+ traverseAndReplaceIntegerNodesWithFloatNodes(&arguments[i], in)
+ continue
+ }
+
+ if isInteger(in) && isInteger(t) && kind(t) != kind(in) {
+ traverseAndReplaceIntegerNodesWithIntegerNodes(&arguments[i], in)
+ continue
}
if t == nil {
continue
}
- if !t.AssignableTo(in) && t.Kind() != reflect.Interface {
+ if !t.AssignableTo(in) && kind(t) != reflect.Interface {
return anyType, &file.Error{
Location: arg.Location(),
Message: fmt.Sprintf("cannot use %v as argument (type %v) to call %v ", t, in, name),
@@ -659,118 +948,101 @@ func (v *visitor) checkFunc(name string, fn reflect.Type, method bool, node *ast
return fn.Out(0), nil
}
-func (v *visitor) BuiltinNode(node *ast.BuiltinNode) (reflect.Type, info) {
- switch node.Name {
- case "all", "none", "any", "one":
- collection, _ := v.visit(node.Arguments[0])
- if !isArray(collection) && !isAny(collection) {
- return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
- }
-
- v.collections = append(v.collections, collection)
- closure, _ := v.visit(node.Arguments[1])
- v.collections = v.collections[:len(v.collections)-1]
-
- if isFunc(closure) &&
- closure.NumOut() == 1 &&
- closure.NumIn() == 1 && isAny(closure.In(0)) {
-
- if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
- return v.error(node.Arguments[1], "closure should return boolean (got %v)", closure.Out(0).String())
- }
- return boolType, info{}
- }
- return v.error(node.Arguments[1], "closure should has one input and one output param")
-
- case "filter":
- collection, _ := v.visit(node.Arguments[0])
- if !isArray(collection) && !isAny(collection) {
- return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
- }
-
- v.collections = append(v.collections, collection)
- closure, _ := v.visit(node.Arguments[1])
- v.collections = v.collections[:len(v.collections)-1]
-
- if isFunc(closure) &&
- closure.NumOut() == 1 &&
- closure.NumIn() == 1 && isAny(closure.In(0)) {
-
- if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
- return v.error(node.Arguments[1], "closure should return boolean (got %v)", closure.Out(0).String())
- }
- if isAny(collection) {
- return arrayType, info{}
- }
- return reflect.SliceOf(collection.Elem()), info{}
- }
- return v.error(node.Arguments[1], "closure should has one input and one output param")
-
- case "map":
- collection, _ := v.visit(node.Arguments[0])
- if !isArray(collection) && !isAny(collection) {
- return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
- }
-
- v.collections = append(v.collections, collection)
- closure, _ := v.visit(node.Arguments[1])
- v.collections = v.collections[:len(v.collections)-1]
-
- if isFunc(closure) &&
- closure.NumOut() == 1 &&
- closure.NumIn() == 1 && isAny(closure.In(0)) {
-
- return reflect.SliceOf(closure.Out(0)), info{}
- }
- return v.error(node.Arguments[1], "closure should has one input and one output param")
-
- case "count":
- collection, _ := v.visit(node.Arguments[0])
- if !isArray(collection) && !isAny(collection) {
- return v.error(node.Arguments[0], "builtin %v takes only array (got %v)", node.Name, collection)
+func traverseAndReplaceIntegerNodesWithFloatNodes(node *ast.Node, newType reflect.Type) {
+ switch (*node).(type) {
+ case *ast.IntegerNode:
+ *node = &ast.FloatNode{Value: float64((*node).(*ast.IntegerNode).Value)}
+ (*node).SetType(newType)
+ case *ast.UnaryNode:
+ unaryNode := (*node).(*ast.UnaryNode)
+ traverseAndReplaceIntegerNodesWithFloatNodes(&unaryNode.Node, newType)
+ case *ast.BinaryNode:
+ binaryNode := (*node).(*ast.BinaryNode)
+ switch binaryNode.Operator {
+ case "+", "-", "*":
+ traverseAndReplaceIntegerNodesWithFloatNodes(&binaryNode.Left, newType)
+ traverseAndReplaceIntegerNodesWithFloatNodes(&binaryNode.Right, newType)
}
+ }
+}
- v.collections = append(v.collections, collection)
- closure, _ := v.visit(node.Arguments[1])
- v.collections = v.collections[:len(v.collections)-1]
-
- if isFunc(closure) &&
- closure.NumOut() == 1 &&
- closure.NumIn() == 1 && isAny(closure.In(0)) {
- if !isBool(closure.Out(0)) && !isAny(closure.Out(0)) {
- return v.error(node.Arguments[1], "closure should return boolean (got %v)", closure.Out(0).String())
- }
-
- return integerType, info{}
+func traverseAndReplaceIntegerNodesWithIntegerNodes(node *ast.Node, newType reflect.Type) {
+ switch (*node).(type) {
+ case *ast.IntegerNode:
+ (*node).SetType(newType)
+ case *ast.UnaryNode:
+ unaryNode := (*node).(*ast.UnaryNode)
+ traverseAndReplaceIntegerNodesWithIntegerNodes(&unaryNode.Node, newType)
+ case *ast.BinaryNode:
+ binaryNode := (*node).(*ast.BinaryNode)
+ switch binaryNode.Operator {
+ case "+", "-", "*":
+ traverseAndReplaceIntegerNodesWithIntegerNodes(&binaryNode.Left, newType)
+ traverseAndReplaceIntegerNodesWithIntegerNodes(&binaryNode.Right, newType)
}
- return v.error(node.Arguments[1], "closure should has one input and one output param")
-
- default:
- return v.error(node, "unknown builtin %v", node.Name)
}
}
-func (v *visitor) ClosureNode(node *ast.ClosureNode) (reflect.Type, info) {
+func (v *checker) ClosureNode(node *ast.ClosureNode) (reflect.Type, info) {
t, _ := v.visit(node.Node)
+ if t == nil {
+ return v.error(node.Node, "closure cannot be nil")
+ }
return reflect.FuncOf([]reflect.Type{anyType}, []reflect.Type{t}, false), info{}
}
-func (v *visitor) PointerNode(node *ast.PointerNode) (reflect.Type, info) {
- if len(v.collections) == 0 {
+func (v *checker) PointerNode(node *ast.PointerNode) (reflect.Type, info) {
+ if len(v.predicateScopes) == 0 {
return v.error(node, "cannot use pointer accessor outside closure")
}
+ scope := v.predicateScopes[len(v.predicateScopes)-1]
+ if node.Name == "" {
+ switch scope.vtype.Kind() {
+ case reflect.Interface:
+ return anyType, info{}
+ case reflect.Array, reflect.Slice:
+ return scope.vtype.Elem(), info{}
+ }
+ return v.error(node, "cannot use %v as array", scope)
+ }
+ if scope.vars != nil {
+ if t, ok := scope.vars[node.Name]; ok {
+ return t, info{}
+ }
+ }
+ return v.error(node, "unknown pointer #%v", node.Name)
+}
- collection := v.collections[len(v.collections)-1]
- switch collection.Kind() {
- case reflect.Interface:
- return anyType, info{}
- case reflect.Array, reflect.Slice:
- return collection.Elem(), info{}
+func (v *checker) VariableDeclaratorNode(node *ast.VariableDeclaratorNode) (reflect.Type, info) {
+ if _, ok := v.config.Types[node.Name]; ok {
+ return v.error(node, "cannot redeclare %v", node.Name)
+ }
+ if _, ok := v.config.Functions[node.Name]; ok {
+ return v.error(node, "cannot redeclare function %v", node.Name)
+ }
+ if _, ok := v.config.Builtins[node.Name]; ok {
+ return v.error(node, "cannot redeclare builtin %v", node.Name)
+ }
+ if _, ok := v.lookupVariable(node.Name); ok {
+ return v.error(node, "cannot redeclare variable %v", node.Name)
+ }
+ vtype, vinfo := v.visit(node.Value)
+ v.varScopes = append(v.varScopes, varScope{node.Name, vtype, vinfo})
+ t, i := v.visit(node.Expr)
+ v.varScopes = v.varScopes[:len(v.varScopes)-1]
+ return t, i
+}
+
+func (v *checker) lookupVariable(name string) (varScope, bool) {
+ for i := len(v.varScopes) - 1; i >= 0; i-- {
+ if v.varScopes[i].name == name {
+ return v.varScopes[i], true
+ }
}
- return v.error(node, "cannot use %v as array", collection)
+ return varScope{}, false
}
-func (v *visitor) ConditionalNode(node *ast.ConditionalNode) (reflect.Type, info) {
+func (v *checker) ConditionalNode(node *ast.ConditionalNode) (reflect.Type, info) {
c, _ := v.visit(node.Cond)
if !isBool(c) && !isAny(c) {
return v.error(node.Cond, "non-bool expression (type %v) used as condition", c)
@@ -794,27 +1066,27 @@ func (v *visitor) ConditionalNode(node *ast.ConditionalNode) (reflect.Type, info
return anyType, info{}
}
-func (v *visitor) ArrayNode(node *ast.ArrayNode) (reflect.Type, info) {
+func (v *checker) ArrayNode(node *ast.ArrayNode) (reflect.Type, info) {
for _, node := range node.Nodes {
v.visit(node)
}
return arrayType, info{}
}
-func (v *visitor) MapNode(node *ast.MapNode) (reflect.Type, info) {
+func (v *checker) MapNode(node *ast.MapNode) (reflect.Type, info) {
for _, pair := range node.Pairs {
v.visit(pair)
}
return mapType, info{}
}
-func (v *visitor) PairNode(node *ast.PairNode) (reflect.Type, info) {
+func (v *checker) PairNode(node *ast.PairNode) (reflect.Type, info) {
v.visit(node.Key)
v.visit(node.Value)
return nilType, info{}
}
-func (v *visitor) findTypedFunc(node *ast.CallNode, fn reflect.Type, method bool) {
+func (v *checker) findTypedFunc(node *ast.CallNode, fn reflect.Type, method bool) {
// OnCallTyped doesn't work for functions with variadic arguments,
// and doesn't work named function, like `type MyFunc func() int`.
// In PkgPath() is an empty string, it's unnamed function.
diff --git a/vendor/github.com/antonmedv/expr/checker/types.go b/vendor/github.com/antonmedv/expr/checker/types.go
index 7ccd894809..890d74a5f2 100644
--- a/vendor/github.com/antonmedv/expr/checker/types.go
+++ b/vendor/github.com/antonmedv/expr/checker/types.go
@@ -4,7 +4,6 @@ import (
"reflect"
"time"
- "github.com/antonmedv/expr/ast"
"github.com/antonmedv/expr/conf"
)
@@ -14,13 +13,12 @@ var (
integerType = reflect.TypeOf(0)
floatType = reflect.TypeOf(float64(0))
stringType = reflect.TypeOf("")
- arrayType = reflect.TypeOf([]interface{}{})
- mapType = reflect.TypeOf(map[string]interface{}{})
- anyType = reflect.TypeOf(new(interface{})).Elem()
+ arrayType = reflect.TypeOf([]any{})
+ mapType = reflect.TypeOf(map[string]any{})
+ anyType = reflect.TypeOf(new(any)).Elem()
timeType = reflect.TypeOf(time.Time{})
durationType = reflect.TypeOf(time.Duration(0))
- functionType = reflect.TypeOf(new(func(...interface{}) (interface{}, error))).Elem()
- errorType = reflect.TypeOf((*error)(nil)).Elem()
+ functionType = reflect.TypeOf(new(func(...any) (any, error))).Elem()
)
func combined(a, b reflect.Type) reflect.Type {
@@ -98,7 +96,7 @@ func isTime(t reflect.Type) bool {
return true
}
}
- return isAny(t)
+ return false
}
func isDuration(t reflect.Type) bool {
@@ -204,59 +202,43 @@ func fetchField(t reflect.Type, name string) (reflect.StructField, bool) {
return reflect.StructField{}, false
}
-func deref(t reflect.Type) (reflect.Type, bool) {
+func deref(t reflect.Type) reflect.Type {
if t == nil {
- return nil, false
+ return nil
}
if t.Kind() == reflect.Interface {
- return t, true
+ return t
}
- found := false
for t != nil && t.Kind() == reflect.Ptr {
e := t.Elem()
switch e.Kind() {
case reflect.Struct, reflect.Map, reflect.Array, reflect.Slice:
- return t, false
+ return t
default:
- found = true
t = e
}
}
- return t, found
+ return t
}
-func isIntegerOrArithmeticOperation(node ast.Node) bool {
- switch n := node.(type) {
- case *ast.IntegerNode:
- return true
- case *ast.UnaryNode:
- switch n.Operator {
- case "+", "-":
- return true
- }
- case *ast.BinaryNode:
- switch n.Operator {
- case "+", "/", "-", "*":
- return true
- }
+func kind(t reflect.Type) reflect.Kind {
+ if t == nil {
+ return reflect.Invalid
}
- return false
+ return t.Kind()
}
-func setTypeForIntegers(node ast.Node, t reflect.Type) {
- switch n := node.(type) {
- case *ast.IntegerNode:
- n.SetType(t)
- case *ast.UnaryNode:
- switch n.Operator {
- case "+", "-":
- setTypeForIntegers(n.Node, t)
- }
- case *ast.BinaryNode:
- switch n.Operator {
- case "+", "/", "-", "*":
- setTypeForIntegers(n.Left, t)
- setTypeForIntegers(n.Right, t)
- }
+func isComparable(l, r reflect.Type) bool {
+ if l == nil || r == nil {
+ return true
+ }
+ switch {
+ case l.Kind() == r.Kind():
+ return true
+ case isNumber(l) && isNumber(r):
+ return true
+ case isAny(l) || isAny(r):
+ return true
}
+ return false
}
diff --git a/vendor/github.com/antonmedv/expr/compiler/compiler.go b/vendor/github.com/antonmedv/expr/compiler/compiler.go
index 3cd32af0f2..8e26d8788e 100644
--- a/vendor/github.com/antonmedv/expr/compiler/compiler.go
+++ b/vendor/github.com/antonmedv/expr/compiler/compiler.go
@@ -5,6 +5,7 @@ import (
"reflect"
"github.com/antonmedv/expr/ast"
+ "github.com/antonmedv/expr/builtin"
"github.com/antonmedv/expr/conf"
"github.com/antonmedv/expr/file"
"github.com/antonmedv/expr/parser"
@@ -25,8 +26,9 @@ func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err erro
c := &compiler{
locations: make([]file.Location, 0),
- constantsIndex: make(map[interface{}]int),
+ constantsIndex: make(map[any]int),
functionsIndex: make(map[string]int),
+ debugInfo: make(map[string]string),
}
if config != nil {
@@ -49,10 +51,12 @@ func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err erro
Node: tree.Node,
Source: tree.Source,
Locations: c.locations,
+ Variables: c.variables,
Constants: c.constants,
Bytecode: c.bytecode,
Arguments: c.arguments,
Functions: c.functions,
+ DebugInfo: c.debugInfo,
}
return
}
@@ -60,10 +64,13 @@ func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err erro
type compiler struct {
locations []file.Location
bytecode []Opcode
- constants []interface{}
- constantsIndex map[interface{}]int
+ variables []any
+ scopes []scope
+ constants []any
+ constantsIndex map[any]int
functions []Function
functionsIndex map[string]int
+ debugInfo map[string]string
mapEnv bool
cast reflect.Kind
nodes []ast.Node
@@ -71,6 +78,11 @@ type compiler struct {
arguments []int
}
+type scope struct {
+ variableName string
+ index int
+}
+
func (c *compiler) emitLocation(loc file.Location, op Opcode, arg int) int {
c.bytecode = append(c.bytecode, op)
current := len(c.bytecode)
@@ -94,11 +106,11 @@ func (c *compiler) emit(op Opcode, args ...int) int {
return c.emitLocation(loc, op, arg)
}
-func (c *compiler) emitPush(value interface{}) int {
+func (c *compiler) emitPush(value any) int {
return c.emit(OpPush, c.addConstant(value))
}
-func (c *compiler) addConstant(constant interface{}) int {
+func (c *compiler) addConstant(constant any) int {
indexable := true
hash := constant
switch reflect.TypeOf(constant).Kind() {
@@ -126,16 +138,42 @@ func (c *compiler) addConstant(constant interface{}) int {
return p
}
-func (c *compiler) addFunction(node *ast.CallNode) int {
- if node.Func == nil {
+func (c *compiler) addVariable(name string) int {
+ c.variables = append(c.variables, nil)
+ p := len(c.variables) - 1
+ c.debugInfo[fmt.Sprintf("var_%d", p)] = name
+ return p
+}
+
+// emitFunction adds builtin.Function.Func to the program.Functions and emits call opcode.
+func (c *compiler) emitFunction(fn *ast.Function, argsLen int) {
+ switch argsLen {
+ case 0:
+ c.emit(OpCall0, c.addFunction(fn))
+ case 1:
+ c.emit(OpCall1, c.addFunction(fn))
+ case 2:
+ c.emit(OpCall2, c.addFunction(fn))
+ case 3:
+ c.emit(OpCall3, c.addFunction(fn))
+ default:
+ c.emit(OpLoadFunc, c.addFunction(fn))
+ c.emit(OpCallN, argsLen)
+ }
+}
+
+// addFunction adds builtin.Function.Func to the program.Functions and returns its index.
+func (c *compiler) addFunction(fn *ast.Function) int {
+ if fn == nil {
panic("function is nil")
}
- if p, ok := c.functionsIndex[node.Func.Name]; ok {
+ if p, ok := c.functionsIndex[fn.Name]; ok {
return p
}
p := len(c.functions)
- c.functions = append(c.functions, node.Func.Func)
- c.functionsIndex[node.Func.Name] = p
+ c.functions = append(c.functions, fn.Func)
+ c.functionsIndex[fn.Name] = p
+ c.debugInfo[fmt.Sprintf("func_%d", p)] = fn.Name
return p
}
@@ -187,6 +225,8 @@ func (c *compiler) compile(node ast.Node) {
c.ClosureNode(n)
case *ast.PointerNode:
c.PointerNode(n)
+ case *ast.VariableDeclaratorNode:
+ c.VariableDeclaratorNode(n)
case *ast.ConditionalNode:
c.ConditionalNode(n)
case *ast.ArrayNode:
@@ -205,6 +245,14 @@ func (c *compiler) NilNode(_ *ast.NilNode) {
}
func (c *compiler) IdentifierNode(node *ast.IdentifierNode) {
+ if index, ok := c.lookupVariable(node.Value); ok {
+ c.emit(OpLoadVar, index)
+ return
+ }
+ if node.Value == "$env" {
+ c.emit(OpLoadEnv)
+ return
+ }
if c.mapEnv {
c.emit(OpLoadFast, c.addConstant(node.Value))
} else if len(node.FieldIndex) > 0 {
@@ -220,11 +268,6 @@ func (c *compiler) IdentifierNode(node *ast.IdentifierNode) {
} else {
c.emit(OpLoadConst, c.addConstant(node.Value))
}
- if node.Deref {
- c.emit(OpDeref)
- } else if node.Type() == nil {
- c.emit(OpDeref)
- }
}
func (c *compiler) IntegerNode(node *ast.IntegerNode) {
@@ -264,7 +307,17 @@ func (c *compiler) IntegerNode(node *ast.IntegerNode) {
}
func (c *compiler) FloatNode(node *ast.FloatNode) {
- c.emitPush(node.Value)
+ t := node.Type()
+ if t == nil {
+ c.emitPush(node.Value)
+ return
+ }
+ switch t.Kind() {
+ case reflect.Float32:
+ c.emitPush(float32(node.Value))
+ case reflect.Float64:
+ c.emitPush(node.Value)
+ }
}
func (c *compiler) BoolNode(node *ast.BoolNode) {
@@ -285,6 +338,7 @@ func (c *compiler) ConstantNode(node *ast.ConstantNode) {
func (c *compiler) UnaryNode(node *ast.UnaryNode) {
c.compile(node.Node)
+ c.derefInNeeded(node.Node)
switch node.Operator {
@@ -309,7 +363,9 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) {
switch node.Operator {
case "==":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Left)
if l == r && l == reflect.Int {
c.emit(OpEqualInt)
@@ -321,114 +377,155 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) {
case "!=":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Left)
c.emit(OpEqual)
c.emit(OpNot)
case "or", "||":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
end := c.emit(OpJumpIfTrue, placeholder)
c.emit(OpPop)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.patchJump(end)
case "and", "&&":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
end := c.emit(OpJumpIfFalse, placeholder)
c.emit(OpPop)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.patchJump(end)
case "<":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpLess)
case ">":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpMore)
case "<=":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpLessOrEqual)
case ">=":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpMoreOrEqual)
case "+":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpAdd)
case "-":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpSubtract)
case "*":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpMultiply)
case "/":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpDivide)
case "%":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpModulo)
case "**", "^":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpExponent)
case "in":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpIn)
case "matches":
if node.Regexp != nil {
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.emit(OpMatchesConst, c.addConstant(node.Regexp))
} else {
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpMatches)
}
case "contains":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpContains)
case "startsWith":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpStartsWith)
case "endsWith":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpEndsWith)
case "..":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.emit(OpRange)
case "??":
c.compile(node.Left)
+ c.derefInNeeded(node.Left)
end := c.emit(OpJumpIfNotNil, placeholder)
c.emit(OpPop)
c.compile(node.Right)
+ c.derefInNeeded(node.Right)
c.patchJump(end)
default:
@@ -457,7 +554,6 @@ func (c *compiler) MemberNode(node *ast.MemberNode) {
return
}
op := OpFetch
- original := node
index := node.FieldIndex
path := []string{node.Name}
base := node.Node
@@ -466,21 +562,15 @@ func (c *compiler) MemberNode(node *ast.MemberNode) {
for !node.Optional {
ident, ok := base.(*ast.IdentifierNode)
if ok && len(ident.FieldIndex) > 0 {
- if ident.Deref {
- panic("IdentifierNode should not be dereferenced")
- }
index = append(ident.FieldIndex, index...)
path = append([]string{ident.Value}, path...)
c.emitLocation(ident.Location(), OpLoadField, c.addConstant(
&runtime.Field{Index: index, Path: path},
))
- goto deref
+ return
}
member, ok := base.(*ast.MemberNode)
if ok && len(member.FieldIndex) > 0 {
- if member.Deref {
- panic("MemberNode should not be dereferenced")
- }
index = append(member.FieldIndex, index...)
path = append([]string{member.Name}, path...)
node = member
@@ -505,13 +595,6 @@ func (c *compiler) MemberNode(node *ast.MemberNode) {
&runtime.Field{Index: index, Path: path},
))
}
-
-deref:
- if original.Deref {
- c.emit(OpDeref)
- } else if original.Type() == nil {
- c.emit(OpDeref)
- }
}
func (c *compiler) SliceNode(node *ast.SliceNode) {
@@ -534,23 +617,7 @@ func (c *compiler) CallNode(node *ast.CallNode) {
c.compile(arg)
}
if node.Func != nil {
- if node.Func.Opcode > 0 {
- c.emit(OpBuiltin, node.Func.Opcode)
- return
- }
- switch len(node.Arguments) {
- case 0:
- c.emit(OpCall0, c.addFunction(node))
- case 1:
- c.emit(OpCall1, c.addFunction(node))
- case 2:
- c.emit(OpCall2, c.addFunction(node))
- case 3:
- c.emit(OpCall3, c.addFunction(node))
- default:
- c.emit(OpLoadFunc, c.addFunction(node))
- c.emit(OpCallN, len(node.Arguments))
- }
+ c.emitFunction(node.Func, len(node.Arguments))
return
}
c.compile(node.Callee)
@@ -578,6 +645,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
c.emit(OpTrue)
c.patchJump(loopBreak)
c.emit(OpEnd)
+ return
case "none":
c.compile(node.Arguments[0])
@@ -592,6 +660,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
c.emit(OpTrue)
c.patchJump(loopBreak)
c.emit(OpEnd)
+ return
case "any":
c.compile(node.Arguments[0])
@@ -605,6 +674,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
c.emit(OpFalse)
c.patchJump(loopBreak)
c.emit(OpEnd)
+ return
case "one":
c.compile(node.Arguments[0])
@@ -619,6 +689,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
c.emitPush(1)
c.emit(OpEqual)
c.emit(OpEnd)
+ return
case "filter":
c.compile(node.Arguments[0])
@@ -627,12 +698,17 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
c.compile(node.Arguments[1])
c.emitCond(func() {
c.emit(OpIncrementCount)
- c.emit(OpPointer)
+ if node.Map != nil {
+ c.compile(node.Map)
+ } else {
+ c.emit(OpPointer)
+ }
})
})
c.emit(OpGetCount)
c.emit(OpEnd)
c.emit(OpArray)
+ return
case "map":
c.compile(node.Arguments[0])
@@ -643,6 +719,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
c.emit(OpGetLen)
c.emit(OpEnd)
c.emit(OpArray)
+ return
case "count":
c.compile(node.Arguments[0])
@@ -655,10 +732,144 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
})
c.emit(OpGetCount)
c.emit(OpEnd)
+ return
+
+ case "find":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ noop := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ if node.Map != nil {
+ c.compile(node.Map)
+ } else {
+ c.emit(OpPointer)
+ }
+ loopBreak = c.emit(OpJump, placeholder)
+ c.patchJump(noop)
+ c.emit(OpPop)
+ })
+ if node.Throws {
+ c.emit(OpPush, c.addConstant(fmt.Errorf("reflect: slice index out of range")))
+ c.emit(OpThrow)
+ } else {
+ c.emit(OpNil)
+ }
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "findIndex":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ noop := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ c.emit(OpGetIndex)
+ loopBreak = c.emit(OpJump, placeholder)
+ c.patchJump(noop)
+ c.emit(OpPop)
+ })
+ c.emit(OpNil)
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "findLast":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoopBackwards(func() {
+ c.compile(node.Arguments[1])
+ noop := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ if node.Map != nil {
+ c.compile(node.Map)
+ } else {
+ c.emit(OpPointer)
+ }
+ loopBreak = c.emit(OpJump, placeholder)
+ c.patchJump(noop)
+ c.emit(OpPop)
+ })
+ if node.Throws {
+ c.emit(OpPush, c.addConstant(fmt.Errorf("reflect: slice index out of range")))
+ c.emit(OpThrow)
+ } else {
+ c.emit(OpNil)
+ }
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "findLastIndex":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ var loopBreak int
+ c.emitLoopBackwards(func() {
+ c.compile(node.Arguments[1])
+ noop := c.emit(OpJumpIfFalse, placeholder)
+ c.emit(OpPop)
+ c.emit(OpGetIndex)
+ loopBreak = c.emit(OpJump, placeholder)
+ c.patchJump(noop)
+ c.emit(OpPop)
+ })
+ c.emit(OpNil)
+ c.patchJump(loopBreak)
+ c.emit(OpEnd)
+ return
+
+ case "groupBy":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ c.emit(OpGroupBy)
+ })
+ c.emit(OpGetGroupBy)
+ c.emit(OpEnd)
+ return
+
+ case "reduce":
+ c.compile(node.Arguments[0])
+ c.emit(OpBegin)
+ if len(node.Arguments) == 3 {
+ c.compile(node.Arguments[2])
+ c.emit(OpSetAcc)
+ } else {
+ c.emit(OpPointer)
+ c.emit(OpIncrementIndex)
+ c.emit(OpSetAcc)
+ }
+ c.emitLoop(func() {
+ c.compile(node.Arguments[1])
+ c.emit(OpSetAcc)
+ })
+ c.emit(OpGetAcc)
+ c.emit(OpEnd)
+ return
- default:
- panic(fmt.Sprintf("unknown builtin %v", node.Name))
}
+
+ if id, ok := builtin.Index[node.Name]; ok {
+ f := builtin.Builtins[id]
+ for _, arg := range node.Arguments {
+ c.compile(arg)
+ }
+ if f.Fast != nil {
+ c.emit(OpCallBuiltin1, id)
+ } else if f.Func != nil {
+ c.emitFunction(f, len(node.Arguments))
+ }
+ return
+ }
+
+ panic(fmt.Sprintf("unknown builtin %v", node.Name))
}
func (c *compiler) emitCond(body func()) {
@@ -679,7 +890,25 @@ func (c *compiler) emitLoop(body func()) {
body()
- c.emit(OpIncrementIt)
+ c.emit(OpIncrementIndex)
+ c.emit(OpJumpBackward, c.calcBackwardJump(begin))
+ c.patchJump(end)
+}
+
+func (c *compiler) emitLoopBackwards(body func()) {
+ c.emit(OpGetLen)
+ c.emit(OpInt, 1)
+ c.emit(OpSubtract)
+ c.emit(OpSetIndex)
+ begin := len(c.bytecode)
+ c.emit(OpGetIndex)
+ c.emit(OpInt, 0)
+ c.emit(OpMoreOrEqual)
+ end := c.emit(OpJumpIfFalse, placeholder)
+
+ body()
+
+ c.emit(OpDecrementIndex)
c.emit(OpJumpBackward, c.calcBackwardJump(begin))
c.patchJump(end)
}
@@ -689,7 +918,42 @@ func (c *compiler) ClosureNode(node *ast.ClosureNode) {
}
func (c *compiler) PointerNode(node *ast.PointerNode) {
- c.emit(OpPointer)
+ switch node.Name {
+ case "index":
+ c.emit(OpGetIndex)
+ case "acc":
+ c.emit(OpGetAcc)
+ case "":
+ c.emit(OpPointer)
+ default:
+ panic(fmt.Sprintf("unknown pointer %v", node.Name))
+ }
+}
+
+func (c *compiler) VariableDeclaratorNode(node *ast.VariableDeclaratorNode) {
+ c.compile(node.Value)
+ index := c.addVariable(node.Name)
+ c.emit(OpStore, index)
+ c.beginScope(node.Name, index)
+ c.compile(node.Expr)
+ c.endScope()
+}
+
+func (c *compiler) beginScope(name string, index int) {
+ c.scopes = append(c.scopes, scope{name, index})
+}
+
+func (c *compiler) endScope() {
+ c.scopes = c.scopes[:len(c.scopes)-1]
+}
+
+func (c *compiler) lookupVariable(name string) (int, bool) {
+ for i := len(c.scopes) - 1; i >= 0; i-- {
+ if c.scopes[i].variableName == name {
+ return c.scopes[i].index, true
+ }
+ }
+ return 0, false
}
func (c *compiler) ConditionalNode(node *ast.ConditionalNode) {
@@ -730,6 +994,13 @@ func (c *compiler) PairNode(node *ast.PairNode) {
c.compile(node.Value)
}
+func (c *compiler) derefInNeeded(node ast.Node) {
+ switch kind(node) {
+ case reflect.Ptr, reflect.Interface:
+ c.emit(OpDeref)
+ }
+}
+
func kind(node ast.Node) reflect.Kind {
t := node.Type()
if t == nil {
diff --git a/vendor/github.com/antonmedv/expr/conf/config.go b/vendor/github.com/antonmedv/expr/conf/config.go
index 1ac0fa7d29..5fb5e1194b 100644
--- a/vendor/github.com/antonmedv/expr/conf/config.go
+++ b/vendor/github.com/antonmedv/expr/conf/config.go
@@ -10,44 +10,49 @@ import (
)
type Config struct {
- Env interface{}
+ Env any
Types TypesTable
MapEnv bool
DefaultType reflect.Type
Operators OperatorsTable
Expect reflect.Kind
+ ExpectAny bool
Optimize bool
Strict bool
ConstFns map[string]reflect.Value
Visitors []ast.Visitor
- Functions map[string]*builtin.Function
+ Functions map[string]*ast.Function
+ Builtins map[string]*ast.Function
+ Disabled map[string]bool // disabled builtins
}
// CreateNew creates new config with default values.
func CreateNew() *Config {
c := &Config{
+ Optimize: true,
Operators: make(map[string][]string),
ConstFns: make(map[string]reflect.Value),
- Functions: make(map[string]*builtin.Function),
- Optimize: true,
+ Functions: make(map[string]*ast.Function),
+ Builtins: make(map[string]*ast.Function),
+ Disabled: make(map[string]bool),
}
for _, f := range builtin.Builtins {
- c.Functions[f.Name] = f
+ c.Builtins[f.Name] = f
}
return c
}
// New creates new config with environment.
-func New(env interface{}) *Config {
+func New(env any) *Config {
c := CreateNew()
c.WithEnv(env)
return c
}
-func (c *Config) WithEnv(env interface{}) {
+func (c *Config) WithEnv(env any) {
var mapEnv bool
var mapValueType reflect.Type
- if _, ok := env.(map[string]interface{}); ok {
+ if _, ok := env.(map[string]any); ok {
mapEnv = true
} else {
if reflect.ValueOf(env).Kind() == reflect.Map {
@@ -93,4 +98,20 @@ func (c *Config) Check() {
}
}
}
+ for fnName, t := range c.Types {
+ if kind(t.Type) == reflect.Func {
+ for _, b := range c.Builtins {
+ if b.Name == fnName {
+ panic(fmt.Errorf(`cannot override builtin %s(): use expr.DisableBuiltin("%s") to override`, b.Name, b.Name))
+ }
+ }
+ }
+ }
+ for _, f := range c.Functions {
+ for _, b := range c.Builtins {
+ if b.Name == f.Name {
+ panic(fmt.Errorf(`cannot override builtin %s(); use expr.DisableBuiltin("%s") to override`, f.Name, f.Name))
+ }
+ }
+ }
}
diff --git a/vendor/github.com/antonmedv/expr/conf/types_table.go b/vendor/github.com/antonmedv/expr/conf/types_table.go
index e917f5fa84..8ebb76c357 100644
--- a/vendor/github.com/antonmedv/expr/conf/types_table.go
+++ b/vendor/github.com/antonmedv/expr/conf/types_table.go
@@ -20,7 +20,7 @@ type TypesTable map[string]Tag
//
// If map is passed, all items will be treated as variables
// (key as name, value as type).
-func CreateTypesTable(i interface{}) TypesTable {
+func CreateTypesTable(i any) TypesTable {
if i == nil {
return nil
}
@@ -54,6 +54,9 @@ func CreateTypesTable(i interface{}) TypesTable {
for _, key := range v.MapKeys() {
value := v.MapIndex(key)
if key.Kind() == reflect.String && value.IsValid() && value.CanInterface() {
+ if key.String() == "$env" { // Could check for all keywords here
+ panic("attempt to misuse env keyword as env map key")
+ }
types[key.String()] = Tag{Type: reflect.TypeOf(value.Interface())}
}
}
@@ -94,10 +97,13 @@ func FieldsFromStruct(t reflect.Type) TypesTable {
}
}
}
-
- types[FieldName(f)] = Tag{
- Type: f.Type,
- FieldIndex: f.Index,
+ if fn := FieldName(f); fn == "$env" { // Could check for all keywords here
+ panic("attempt to misuse env keyword as env struct field tag")
+ } else {
+ types[FieldName(f)] = Tag{
+ Type: f.Type,
+ FieldIndex: f.Index,
+ }
}
}
}
@@ -115,6 +121,13 @@ func dereference(t reflect.Type) reflect.Type {
return t
}
+func kind(t reflect.Type) reflect.Kind {
+ if t == nil {
+ return reflect.Invalid
+ }
+ return t.Kind()
+}
+
func FieldName(field reflect.StructField) string {
if taggedName := field.Tag.Get("expr"); taggedName != "" {
return taggedName
diff --git a/vendor/github.com/antonmedv/expr/expr.go b/vendor/github.com/antonmedv/expr/expr.go
index 14f6af285c..eb9eb7683e 100644
--- a/vendor/github.com/antonmedv/expr/expr.go
+++ b/vendor/github.com/antonmedv/expr/expr.go
@@ -5,7 +5,6 @@ import (
"reflect"
"github.com/antonmedv/expr/ast"
- "github.com/antonmedv/expr/builtin"
"github.com/antonmedv/expr/checker"
"github.com/antonmedv/expr/compiler"
"github.com/antonmedv/expr/conf"
@@ -23,7 +22,7 @@ type Option func(c *conf.Config)
// as well as all fields of embedded structs and struct itself.
// If map is passed, all items will be treated as variables.
// Methods defined on this type will be available as functions.
-func Env(env interface{}) Option {
+func Env(env any) Option {
return func(c *conf.Config) {
c.WithEnv(env)
}
@@ -52,6 +51,13 @@ func ConstExpr(fn string) Option {
}
}
+// AsAny tells the compiler to expect any result.
+func AsAny() Option {
+ return func(c *conf.Config) {
+ c.ExpectAny = true
+ }
+}
+
// AsKind tells the compiler to expect kind of the result.
func AsKind(kind reflect.Kind) Option {
return func(c *conf.Config) {
@@ -102,7 +108,7 @@ func Patch(visitor ast.Visitor) Option {
}
// Function adds function to list of functions what will be available in expressions.
-func Function(name string, fn func(params ...interface{}) (interface{}, error), types ...interface{}) Option {
+func Function(name string, fn func(params ...any) (any, error), types ...any) Option {
return func(c *conf.Config) {
ts := make([]reflect.Type, len(types))
for i, t := range types {
@@ -115,7 +121,7 @@ func Function(name string, fn func(params ...interface{}) (interface{}, error),
}
ts[i] = t
}
- c.Functions[name] = &builtin.Function{
+ c.Functions[name] = &ast.Function{
Name: name,
Func: fn,
Types: ts,
@@ -123,13 +129,38 @@ func Function(name string, fn func(params ...interface{}) (interface{}, error),
}
}
+// DisableAllBuiltins disables all builtins.
+func DisableAllBuiltins() Option {
+ return func(c *conf.Config) {
+ for name := range c.Builtins {
+ c.Disabled[name] = true
+ }
+ }
+}
+
+// DisableBuiltin disables builtin function.
+func DisableBuiltin(name string) Option {
+ return func(c *conf.Config) {
+ c.Disabled[name] = true
+ }
+}
+
+// EnableBuiltin enables builtin function.
+func EnableBuiltin(name string) Option {
+ return func(c *conf.Config) {
+ delete(c.Disabled, name)
+ }
+}
+
// Compile parses and compiles given input expression to bytecode program.
func Compile(input string, ops ...Option) (*vm.Program, error) {
config := conf.CreateNew()
-
for _, op := range ops {
op(config)
}
+ for name := range config.Disabled {
+ delete(config.Builtins, name)
+ }
config.Check()
if len(config.Operators) > 0 {
@@ -139,7 +170,7 @@ func Compile(input string, ops ...Option) (*vm.Program, error) {
})
}
- tree, err := parser.Parse(input)
+ tree, err := parser.ParseWithConfig(input, config)
if err != nil {
return nil, err
}
@@ -151,15 +182,10 @@ func Compile(input string, ops ...Option) (*vm.Program, error) {
_, _ = checker.Check(tree, config)
ast.Walk(&tree.Node, v)
}
- _, err = checker.Check(tree, config)
- if err != nil {
- return nil, err
- }
- } else {
- _, err = checker.Check(tree, config)
- if err != nil {
- return nil, err
- }
+ }
+ _, err = checker.Check(tree, config)
+ if err != nil {
+ return nil, err
}
if config.Optimize {
@@ -181,12 +207,12 @@ func Compile(input string, ops ...Option) (*vm.Program, error) {
}
// Run evaluates given bytecode program.
-func Run(program *vm.Program, env interface{}) (interface{}, error) {
+func Run(program *vm.Program, env any) (any, error) {
return vm.Run(program, env)
}
// Eval parses, compiles and runs given input.
-func Eval(input string, env interface{}) (interface{}, error) {
+func Eval(input string, env any) (any, error) {
if _, ok := env.(Option); ok {
return nil, fmt.Errorf("misused expr.Eval: second argument (env) should be passed without expr.Env")
}
diff --git a/vendor/github.com/antonmedv/expr/file/error.go b/vendor/github.com/antonmedv/expr/file/error.go
index 1e7e81b947..edf202b045 100644
--- a/vendor/github.com/antonmedv/expr/file/error.go
+++ b/vendor/github.com/antonmedv/expr/file/error.go
@@ -10,7 +10,7 @@ type Error struct {
Location
Message string
Snippet string
- Prev error
+ Prev error
}
func (e *Error) Error() string {
@@ -45,7 +45,6 @@ func (e *Error) Bind(source *Source) *Error {
return e
}
-
func (e *Error) Unwrap() error {
return e.Prev
}
@@ -54,7 +53,6 @@ func (e *Error) Wrap(err error) {
e.Prev = err
}
-
func (e *Error) format() string {
if e.Location.Empty() {
return e.Message
diff --git a/vendor/github.com/antonmedv/expr/optimizer/const_expr.go b/vendor/github.com/antonmedv/expr/optimizer/const_expr.go
index 7ececb3dba..694c88bcf1 100644
--- a/vendor/github.com/antonmedv/expr/optimizer/const_expr.go
+++ b/vendor/github.com/antonmedv/expr/optimizer/const_expr.go
@@ -42,7 +42,7 @@ func (c *constExpr) Visit(node *Node) {
in := make([]reflect.Value, len(call.Arguments))
for i := 0; i < len(call.Arguments); i++ {
arg := call.Arguments[i]
- var param interface{}
+ var param any
switch a := arg.(type) {
case *NilNode:
diff --git a/vendor/github.com/antonmedv/expr/optimizer/filter_first.go b/vendor/github.com/antonmedv/expr/optimizer/filter_first.go
new file mode 100644
index 0000000000..852e2c268a
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/optimizer/filter_first.go
@@ -0,0 +1,38 @@
+package optimizer
+
+import (
+ . "github.com/antonmedv/expr/ast"
+)
+
+type filterFirst struct{}
+
+func (*filterFirst) Visit(node *Node) {
+ if member, ok := (*node).(*MemberNode); ok && member.Property != nil && !member.Optional {
+ if prop, ok := member.Property.(*IntegerNode); ok && prop.Value == 0 {
+ if filter, ok := member.Node.(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "find",
+ Arguments: filter.Arguments,
+ Throws: true, // to match the behavior of filter()[0]
+ Map: filter.Map,
+ })
+ }
+ }
+ }
+ if first, ok := (*node).(*BuiltinNode); ok &&
+ first.Name == "first" &&
+ len(first.Arguments) == 1 {
+ if filter, ok := first.Arguments[0].(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "find",
+ Arguments: filter.Arguments,
+ Throws: false, // as first() will return nil if not found
+ Map: filter.Map,
+ })
+ }
+ }
+}
diff --git a/vendor/github.com/antonmedv/expr/optimizer/filter_last.go b/vendor/github.com/antonmedv/expr/optimizer/filter_last.go
new file mode 100644
index 0000000000..0a072004be
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/optimizer/filter_last.go
@@ -0,0 +1,38 @@
+package optimizer
+
+import (
+ . "github.com/antonmedv/expr/ast"
+)
+
+type filterLast struct{}
+
+func (*filterLast) Visit(node *Node) {
+ if member, ok := (*node).(*MemberNode); ok && member.Property != nil && !member.Optional {
+ if prop, ok := member.Property.(*IntegerNode); ok && prop.Value == -1 {
+ if filter, ok := member.Node.(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "findLast",
+ Arguments: filter.Arguments,
+ Throws: true, // to match the behavior of filter()[-1]
+ Map: filter.Map,
+ })
+ }
+ }
+ }
+ if first, ok := (*node).(*BuiltinNode); ok &&
+ first.Name == "last" &&
+ len(first.Arguments) == 1 {
+ if filter, ok := first.Arguments[0].(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "findLast",
+ Arguments: filter.Arguments,
+ Throws: false, // as last() will return nil if not found
+ Map: filter.Map,
+ })
+ }
+ }
+}
diff --git a/vendor/github.com/antonmedv/expr/optimizer/filter_len.go b/vendor/github.com/antonmedv/expr/optimizer/filter_len.go
new file mode 100644
index 0000000000..2293de81d7
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/optimizer/filter_len.go
@@ -0,0 +1,22 @@
+package optimizer
+
+import (
+ . "github.com/antonmedv/expr/ast"
+)
+
+type filterLen struct{}
+
+func (*filterLen) Visit(node *Node) {
+ if ln, ok := (*node).(*BuiltinNode); ok &&
+ ln.Name == "len" &&
+ len(ln.Arguments) == 1 {
+ if filter, ok := ln.Arguments[0].(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ len(filter.Arguments) == 2 {
+ Patch(node, &BuiltinNode{
+ Name: "count",
+ Arguments: filter.Arguments,
+ })
+ }
+ }
+}
diff --git a/vendor/github.com/antonmedv/expr/optimizer/filter_map.go b/vendor/github.com/antonmedv/expr/optimizer/filter_map.go
new file mode 100644
index 0000000000..9044ac34d5
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/optimizer/filter_map.go
@@ -0,0 +1,25 @@
+package optimizer
+
+import (
+ . "github.com/antonmedv/expr/ast"
+)
+
+type filterMap struct{}
+
+func (*filterMap) Visit(node *Node) {
+ if mapBuiltin, ok := (*node).(*BuiltinNode); ok &&
+ mapBuiltin.Name == "map" &&
+ len(mapBuiltin.Arguments) == 2 {
+ if closure, ok := mapBuiltin.Arguments[1].(*ClosureNode); ok {
+ if filter, ok := mapBuiltin.Arguments[0].(*BuiltinNode); ok &&
+ filter.Name == "filter" &&
+ filter.Map == nil /* not already optimized */ {
+ Patch(node, &BuiltinNode{
+ Name: "filter",
+ Arguments: filter.Arguments,
+ Map: closure.Node,
+ })
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/antonmedv/expr/optimizer/fold.go b/vendor/github.com/antonmedv/expr/optimizer/fold.go
index b62b2d7ed4..670ebec094 100644
--- a/vendor/github.com/antonmedv/expr/optimizer/fold.go
+++ b/vendor/github.com/antonmedv/expr/optimizer/fold.go
@@ -1,6 +1,7 @@
package optimizer
import (
+ "fmt"
"math"
"reflect"
@@ -8,6 +9,12 @@ import (
"github.com/antonmedv/expr/file"
)
+var (
+ integerType = reflect.TypeOf(0)
+ floatType = reflect.TypeOf(float64(0))
+ stringType = reflect.TypeOf("")
+)
+
type fold struct {
applied bool
err *file.Error
@@ -18,11 +25,18 @@ func (fold *fold) Visit(node *Node) {
fold.applied = true
Patch(node, newNode)
}
- // for IntegerNode the type may have been changed from int->float
- // preserve this information by setting the type after the Patch
- patchWithType := func(newNode Node, leafType reflect.Type) {
+ patchWithType := func(newNode Node) {
patch(newNode)
- newNode.SetType(leafType)
+ switch newNode.(type) {
+ case *IntegerNode:
+ newNode.SetType(integerType)
+ case *FloatNode:
+ newNode.SetType(floatType)
+ case *StringNode:
+ newNode.SetType(stringType)
+ default:
+ panic(fmt.Sprintf("unknown type %T", newNode))
+ }
}
switch n := (*node).(type) {
@@ -30,17 +44,17 @@ func (fold *fold) Visit(node *Node) {
switch n.Operator {
case "-":
if i, ok := n.Node.(*IntegerNode); ok {
- patchWithType(&IntegerNode{Value: -i.Value}, n.Node.Type())
+ patchWithType(&IntegerNode{Value: -i.Value})
}
if i, ok := n.Node.(*FloatNode); ok {
- patchWithType(&FloatNode{Value: -i.Value}, n.Node.Type())
+ patchWithType(&FloatNode{Value: -i.Value})
}
case "+":
if i, ok := n.Node.(*IntegerNode); ok {
- patchWithType(&IntegerNode{Value: i.Value}, n.Node.Type())
+ patchWithType(&IntegerNode{Value: i.Value})
}
if i, ok := n.Node.(*FloatNode); ok {
- patchWithType(&FloatNode{Value: i.Value}, n.Node.Type())
+ patchWithType(&FloatNode{Value: i.Value})
}
case "!", "not":
if a := toBool(n.Node); a != nil {
@@ -55,28 +69,28 @@ func (fold *fold) Visit(node *Node) {
a := toInteger(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&IntegerNode{Value: a.Value + b.Value}, a.Type())
+ patchWithType(&IntegerNode{Value: a.Value + b.Value})
}
}
{
a := toInteger(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: float64(a.Value) + b.Value}, a.Type())
+ patchWithType(&FloatNode{Value: float64(a.Value) + b.Value})
}
}
{
a := toFloat(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: a.Value + float64(b.Value)}, a.Type())
+ patchWithType(&FloatNode{Value: a.Value + float64(b.Value)})
}
}
{
a := toFloat(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: a.Value + b.Value}, a.Type())
+ patchWithType(&FloatNode{Value: a.Value + b.Value})
}
}
{
@@ -91,28 +105,28 @@ func (fold *fold) Visit(node *Node) {
a := toInteger(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&IntegerNode{Value: a.Value - b.Value}, a.Type())
+ patchWithType(&IntegerNode{Value: a.Value - b.Value})
}
}
{
a := toInteger(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: float64(a.Value) - b.Value}, a.Type())
+ patchWithType(&FloatNode{Value: float64(a.Value) - b.Value})
}
}
{
a := toFloat(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: a.Value - float64(b.Value)}, a.Type())
+ patchWithType(&FloatNode{Value: a.Value - float64(b.Value)})
}
}
{
a := toFloat(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: a.Value - b.Value}, a.Type())
+ patchWithType(&FloatNode{Value: a.Value - b.Value})
}
}
case "*":
@@ -120,28 +134,28 @@ func (fold *fold) Visit(node *Node) {
a := toInteger(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&IntegerNode{Value: a.Value * b.Value}, a.Type())
+ patchWithType(&IntegerNode{Value: a.Value * b.Value})
}
}
{
a := toInteger(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: float64(a.Value) * b.Value}, a.Type())
+ patchWithType(&FloatNode{Value: float64(a.Value) * b.Value})
}
}
{
a := toFloat(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: a.Value * float64(b.Value)}, a.Type())
+ patchWithType(&FloatNode{Value: a.Value * float64(b.Value)})
}
}
{
a := toFloat(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: a.Value * b.Value}, a.Type())
+ patchWithType(&FloatNode{Value: a.Value * b.Value})
}
}
case "/":
@@ -149,28 +163,28 @@ func (fold *fold) Visit(node *Node) {
a := toInteger(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: float64(a.Value) / float64(b.Value)}, a.Type())
+ patchWithType(&FloatNode{Value: float64(a.Value) / float64(b.Value)})
}
}
{
a := toInteger(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: float64(a.Value) / b.Value}, a.Type())
+ patchWithType(&FloatNode{Value: float64(a.Value) / b.Value})
}
}
{
a := toFloat(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: a.Value / float64(b.Value)}, a.Type())
+ patchWithType(&FloatNode{Value: a.Value / float64(b.Value)})
}
}
{
a := toFloat(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: a.Value / b.Value}, a.Type())
+ patchWithType(&FloatNode{Value: a.Value / b.Value})
}
}
case "%":
@@ -191,28 +205,28 @@ func (fold *fold) Visit(node *Node) {
a := toInteger(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: math.Pow(float64(a.Value), float64(b.Value))}, a.Type())
+ patchWithType(&FloatNode{Value: math.Pow(float64(a.Value), float64(b.Value))})
}
}
{
a := toInteger(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: math.Pow(float64(a.Value), b.Value)}, a.Type())
+ patchWithType(&FloatNode{Value: math.Pow(float64(a.Value), b.Value)})
}
}
{
a := toFloat(n.Left)
b := toInteger(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: math.Pow(a.Value, float64(b.Value))}, a.Type())
+ patchWithType(&FloatNode{Value: math.Pow(a.Value, float64(b.Value))})
}
}
{
a := toFloat(n.Left)
b := toFloat(n.Right)
if a != nil && b != nil {
- patchWithType(&FloatNode{Value: math.Pow(a.Value, b.Value)}, a.Type())
+ patchWithType(&FloatNode{Value: math.Pow(a.Value, b.Value)})
}
}
case "and", "&&":
@@ -271,7 +285,7 @@ func (fold *fold) Visit(node *Node) {
return
}
}
- value := make([]interface{}, len(n.Nodes))
+ value := make([]any, len(n.Nodes))
for i, a := range n.Nodes {
switch b := a.(type) {
case *IntegerNode:
diff --git a/vendor/github.com/antonmedv/expr/optimizer/in_range.go b/vendor/github.com/antonmedv/expr/optimizer/in_range.go
index 7895249e0b..b361c6c39b 100644
--- a/vendor/github.com/antonmedv/expr/optimizer/in_range.go
+++ b/vendor/github.com/antonmedv/expr/optimizer/in_range.go
@@ -1,6 +1,8 @@
package optimizer
import (
+ "reflect"
+
. "github.com/antonmedv/expr/ast"
)
@@ -10,9 +12,16 @@ func (*inRange) Visit(node *Node) {
switch n := (*node).(type) {
case *BinaryNode:
if n.Operator == "in" {
- if rng, ok := n.Right.(*BinaryNode); ok && rng.Operator == ".." {
- if from, ok := rng.Left.(*IntegerNode); ok {
- if to, ok := rng.Right.(*IntegerNode); ok {
+ t := n.Left.Type()
+ if t == nil {
+ return
+ }
+ if t.Kind() != reflect.Int {
+ return
+ }
+ if rangeOp, ok := n.Right.(*BinaryNode); ok && rangeOp.Operator == ".." {
+ if from, ok := rangeOp.Left.(*IntegerNode); ok {
+ if to, ok := rangeOp.Right.(*IntegerNode); ok {
Patch(node, &BinaryNode{
Operator: "and",
Left: &BinaryNode{
diff --git a/vendor/github.com/antonmedv/expr/optimizer/optimizer.go b/vendor/github.com/antonmedv/expr/optimizer/optimizer.go
index 9c97496c8d..15f50fd736 100644
--- a/vendor/github.com/antonmedv/expr/optimizer/optimizer.go
+++ b/vendor/github.com/antonmedv/expr/optimizer/optimizer.go
@@ -33,5 +33,9 @@ func Optimize(node *Node, config *conf.Config) error {
}
Walk(node, &inRange{})
Walk(node, &constRange{})
+ Walk(node, &filterMap{})
+ Walk(node, &filterLen{})
+ Walk(node, &filterLast{})
+ Walk(node, &filterFirst{})
return nil
}
diff --git a/vendor/github.com/antonmedv/expr/parser/lexer/lexer.go b/vendor/github.com/antonmedv/expr/parser/lexer/lexer.go
index cfb1e8c61b..5db2dcbb52 100644
--- a/vendor/github.com/antonmedv/expr/parser/lexer/lexer.go
+++ b/vendor/github.com/antonmedv/expr/parser/lexer/lexer.go
@@ -150,7 +150,7 @@ func (l *lexer) acceptWord(word string) bool {
return true
}
-func (l *lexer) error(format string, args ...interface{}) stateFn {
+func (l *lexer) error(format string, args ...any) stateFn {
if l.err == nil { // show first error
l.err = &file.Error{
Location: l.loc,
diff --git a/vendor/github.com/antonmedv/expr/parser/lexer/state.go b/vendor/github.com/antonmedv/expr/parser/lexer/state.go
index 1212aa3217..bb150878a6 100644
--- a/vendor/github.com/antonmedv/expr/parser/lexer/state.go
+++ b/vendor/github.com/antonmedv/expr/parser/lexer/state.go
@@ -2,6 +2,8 @@ package lexer
import (
"strings"
+
+ "github.com/antonmedv/expr/parser/utils"
)
type stateFn func(*lexer) stateFn
@@ -11,7 +13,7 @@ func root(l *lexer) stateFn {
case r == eof:
l.emitEOF()
return nil
- case IsSpace(r):
+ case utils.IsSpace(r):
l.ignore()
return root
case r == '\'' || r == '"':
@@ -28,19 +30,24 @@ func root(l *lexer) stateFn {
return questionMark
case r == '/':
return slash
+ case r == '#':
+ return pointer
+ case r == '|':
+ l.accept("|")
+ l.emit(Operator)
case strings.ContainsRune("([{", r):
l.emit(Bracket)
case strings.ContainsRune(")]}", r):
l.emit(Bracket)
- case strings.ContainsRune("#,:%+-^", r): // single rune operator
+ case strings.ContainsRune(",:;%+-^", r): // single rune operator
l.emit(Operator)
- case strings.ContainsRune("&|!=*<>", r): // possible double rune operator
- l.accept("&|=*")
+ case strings.ContainsRune("&!=*<>", r): // possible double rune operator
+ l.accept("&=*")
l.emit(Operator)
case r == '.':
l.backup()
return dot
- case IsAlphaNumeric(r):
+ case utils.IsAlphaNumeric(r):
l.backup()
return identifier
default:
@@ -88,7 +95,7 @@ func (l *lexer) scanNumber() bool {
l.acceptRun(digits)
}
// Next thing mustn't be alphanumeric.
- if IsAlphaNumeric(l.peek()) {
+ if utils.IsAlphaNumeric(l.peek()) {
l.next()
return false
}
@@ -110,7 +117,7 @@ func identifier(l *lexer) stateFn {
loop:
for {
switch r := l.next(); {
- case IsAlphaNumeric(r):
+ case utils.IsAlphaNumeric(r):
// absorb
default:
l.backup()
@@ -119,6 +126,8 @@ loop:
return not
case "in", "or", "and", "matches", "contains", "startsWith", "endsWith":
l.emit(Operator)
+ case "let":
+ l.emit(Operator)
default:
l.emit(Identifier)
}
@@ -138,7 +147,7 @@ func not(l *lexer) stateFn {
// Get the next word.
for {
r := l.next()
- if IsAlphaNumeric(r) {
+ if utils.IsAlphaNumeric(r) {
// absorb
} else {
l.backup()
@@ -196,3 +205,19 @@ func multiLineComment(l *lexer) stateFn {
l.ignore()
return root
}
+
+func pointer(l *lexer) stateFn {
+ l.accept("#")
+ l.emit(Operator)
+ for {
+ switch r := l.next(); {
+ case utils.IsAlphaNumeric(r): // absorb
+ default:
+ l.backup()
+ if l.word() != "" {
+ l.emit(Identifier)
+ }
+ return root
+ }
+ }
+}
diff --git a/vendor/github.com/antonmedv/expr/parser/lexer/utils.go b/vendor/github.com/antonmedv/expr/parser/lexer/utils.go
index 72e3cf20c9..5c9e6b59de 100644
--- a/vendor/github.com/antonmedv/expr/parser/lexer/utils.go
+++ b/vendor/github.com/antonmedv/expr/parser/lexer/utils.go
@@ -2,23 +2,11 @@ package lexer
import (
"fmt"
+ "math"
"strings"
- "unicode"
"unicode/utf8"
)
-func IsSpace(r rune) bool {
- return unicode.IsSpace(r)
-}
-
-func IsAlphaNumeric(r rune) bool {
- return IsAlphabetic(r) || unicode.IsDigit(r)
-}
-
-func IsAlphabetic(r rune) bool {
- return r == '_' || r == '$' || unicode.IsLetter(r)
-}
-
var (
newlineNormalizer = strings.NewReplacer("\r\n", "\n", "\r", "\n")
)
@@ -44,7 +32,11 @@ func unescape(value string) (string, error) {
// The string contains escape characters.
// The following logic is adapted from `strconv/quote.go`
var runeTmp [utf8.UTFMax]byte
- buf := make([]byte, 0, 3*n/2)
+ size := 3 * uint64(n) / 2
+ if size >= math.MaxInt {
+ return "", fmt.Errorf("too large string")
+ }
+ buf := make([]byte, 0, size)
for len(value) > 0 {
c, multibyte, rest, err := unescapeChar(value)
if err != nil {
@@ -63,10 +55,10 @@ func unescape(value string) (string, error) {
// unescapeChar takes a string input and returns the following info:
//
-// value - the escaped unicode rune at the front of the string.
-// multibyte - whether the rune value might require multiple bytes to represent.
-// tail - the remainder of the input string.
-// err - error value, if the character could not be unescaped.
+// value - the escaped unicode rune at the front of the string.
+// multibyte - whether the rune value might require multiple bytes to represent.
+// tail - the remainder of the input string.
+// err - error value, if the character could not be unescaped.
//
// When multibyte is true the return value may still fit within a single byte,
// but a multibyte conversion is attempted which is more expensive than when the
diff --git a/vendor/github.com/antonmedv/expr/parser/operator/operator.go b/vendor/github.com/antonmedv/expr/parser/operator/operator.go
new file mode 100644
index 0000000000..455c06f17c
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/parser/operator/operator.go
@@ -0,0 +1,52 @@
+package operator
+
+type Associativity int
+
+const (
+ Left Associativity = iota + 1
+ Right
+)
+
+type Operator struct {
+ Precedence int
+ Associativity Associativity
+}
+
+func Less(a, b string) bool {
+ return Binary[a].Precedence < Binary[b].Precedence
+}
+
+var Unary = map[string]Operator{
+ "not": {50, Left},
+ "!": {50, Left},
+ "-": {90, Left},
+ "+": {90, Left},
+}
+
+var Binary = map[string]Operator{
+ "|": {0, Left},
+ "or": {10, Left},
+ "||": {10, Left},
+ "and": {15, Left},
+ "&&": {15, Left},
+ "==": {20, Left},
+ "!=": {20, Left},
+ "<": {20, Left},
+ ">": {20, Left},
+ ">=": {20, Left},
+ "<=": {20, Left},
+ "in": {20, Left},
+ "matches": {20, Left},
+ "contains": {20, Left},
+ "startsWith": {20, Left},
+ "endsWith": {20, Left},
+ "..": {25, Left},
+ "+": {30, Left},
+ "-": {30, Left},
+ "*": {60, Left},
+ "/": {60, Left},
+ "%": {60, Left},
+ "**": {100, Right},
+ "^": {100, Right},
+ "??": {500, Left},
+}
diff --git a/vendor/github.com/antonmedv/expr/parser/parser.go b/vendor/github.com/antonmedv/expr/parser/parser.go
index fd26fe18bd..d4a870f715 100644
--- a/vendor/github.com/antonmedv/expr/parser/parser.go
+++ b/vendor/github.com/antonmedv/expr/parser/parser.go
@@ -2,73 +2,35 @@ package parser
import (
"fmt"
+ "math"
"strconv"
"strings"
- "unicode/utf8"
. "github.com/antonmedv/expr/ast"
+ "github.com/antonmedv/expr/builtin"
+ "github.com/antonmedv/expr/conf"
"github.com/antonmedv/expr/file"
. "github.com/antonmedv/expr/parser/lexer"
+ "github.com/antonmedv/expr/parser/operator"
+ "github.com/antonmedv/expr/parser/utils"
)
-type associativity int
-
-const (
- left associativity = iota + 1
- right
-)
-
-type operator struct {
- precedence int
- associativity associativity
-}
-
-type builtin struct {
+var predicates = map[string]struct {
arity int
-}
-
-var unaryOperators = map[string]operator{
- "not": {50, left},
- "!": {50, left},
- "-": {90, left},
- "+": {90, left},
-}
-
-var binaryOperators = map[string]operator{
- "or": {10, left},
- "||": {10, left},
- "and": {15, left},
- "&&": {15, left},
- "==": {20, left},
- "!=": {20, left},
- "<": {20, left},
- ">": {20, left},
- ">=": {20, left},
- "<=": {20, left},
- "in": {20, left},
- "matches": {20, left},
- "contains": {20, left},
- "startsWith": {20, left},
- "endsWith": {20, left},
- "..": {25, left},
- "+": {30, left},
- "-": {30, left},
- "*": {60, left},
- "/": {60, left},
- "%": {60, left},
- "**": {100, right},
- "^": {100, right},
- "??": {500, left},
-}
-
-var builtins = map[string]builtin{
- "all": {2},
- "none": {2},
- "any": {2},
- "one": {2},
- "filter": {2},
- "map": {2},
- "count": {2},
+}{
+ "all": {2},
+ "none": {2},
+ "any": {2},
+ "one": {2},
+ "filter": {2},
+ "map": {2},
+ "count": {2},
+ "find": {2},
+ "findIndex": {2},
+ "findLast": {2},
+ "findLastIndex": {2},
+ "groupBy": {2},
+ "reduce": {3},
}
type parser struct {
@@ -77,6 +39,7 @@ type parser struct {
pos int
err *file.Error
depth int // closure call depth
+ config *conf.Config
}
type Tree struct {
@@ -85,6 +48,12 @@ type Tree struct {
}
func Parse(input string) (*Tree, error) {
+ return ParseWithConfig(input, &conf.Config{
+ Disabled: map[string]bool{},
+ })
+}
+
+func ParseWithConfig(input string, config *conf.Config) (*Tree, error) {
source := file.NewSource(input)
tokens, err := Lex(source)
@@ -95,6 +64,7 @@ func Parse(input string) (*Tree, error) {
p := &parser{
tokens: tokens,
current: tokens[0],
+ config: config,
}
node := p.parseExpression(0)
@@ -113,11 +83,11 @@ func Parse(input string) (*Tree, error) {
}, nil
}
-func (p *parser) error(format string, args ...interface{}) {
+func (p *parser) error(format string, args ...any) {
p.errorAt(p.current, format, args...)
}
-func (p *parser) errorAt(token Token, format string, args ...interface{}) {
+func (p *parser) errorAt(token Token, format string, args ...any) {
if p.err == nil { // show first error
p.err = &file.Error{
Location: token.Location,
@@ -146,14 +116,21 @@ func (p *parser) expect(kind Kind, values ...string) {
// parse functions
func (p *parser) parseExpression(precedence int) Node {
+ if precedence == 0 {
+ if p.current.Is(Operator, "let") {
+ return p.parseVariableDeclaration()
+ }
+ }
+
nodeLeft := p.parsePrimary()
- lastOperator := ""
+ prevOperator := ""
opToken := p.current
for opToken.Is(Operator) && p.err == nil {
negate := false
var notToken Token
+ // Handle "not *" operator, like "not in" or "not contains".
if opToken.Is(Operator, "not") {
p.next()
notToken = p.current
@@ -161,20 +138,25 @@ func (p *parser) parseExpression(precedence int) Node {
opToken = p.current
}
- if op, ok := binaryOperators[opToken.Value]; ok {
- if op.precedence >= precedence {
+ if op, ok := operator.Binary[opToken.Value]; ok {
+ if op.Precedence >= precedence {
p.next()
- if lastOperator == "??" && opToken.Value != "??" && !opToken.Is(Bracket, "(") {
+ if opToken.Value == "|" {
+ nodeLeft = p.parsePipe(nodeLeft)
+ goto next
+ }
+
+ if prevOperator == "??" && opToken.Value != "??" && !opToken.Is(Bracket, "(") {
p.errorAt(opToken, "Operator (%v) and coalesce expressions (??) cannot be mixed. Wrap either by parentheses.", opToken.Value)
break
}
var nodeRight Node
- if op.associativity == left {
- nodeRight = p.parseExpression(op.precedence + 1)
+ if op.Associativity == operator.Left {
+ nodeRight = p.parseExpression(op.Precedence + 1)
} else {
- nodeRight = p.parseExpression(op.precedence)
+ nodeRight = p.parseExpression(op.Precedence)
}
nodeLeft = &BinaryNode{
@@ -192,28 +174,71 @@ func (p *parser) parseExpression(precedence int) Node {
nodeLeft.SetLocation(notToken.Location)
}
- lastOperator = opToken.Value
- opToken = p.current
- continue
+ goto next
}
}
break
+
+ next:
+ prevOperator = opToken.Value
+ opToken = p.current
}
if precedence == 0 {
- nodeLeft = p.parseConditionalExpression(nodeLeft)
+ nodeLeft = p.parseConditional(nodeLeft)
}
return nodeLeft
}
+func (p *parser) parseVariableDeclaration() Node {
+ p.expect(Operator, "let")
+ variableName := p.current
+ p.expect(Identifier)
+ p.expect(Operator, "=")
+ value := p.parseExpression(0)
+ p.expect(Operator, ";")
+ node := p.parseExpression(0)
+ let := &VariableDeclaratorNode{
+ Name: variableName.Value,
+ Value: value,
+ Expr: node,
+ }
+ let.SetLocation(variableName.Location)
+ return let
+}
+
+func (p *parser) parseConditional(node Node) Node {
+ var expr1, expr2 Node
+ for p.current.Is(Operator, "?") && p.err == nil {
+ p.next()
+
+ if !p.current.Is(Operator, ":") {
+ expr1 = p.parseExpression(0)
+ p.expect(Operator, ":")
+ expr2 = p.parseExpression(0)
+ } else {
+ p.next()
+ expr1 = node
+ expr2 = p.parseExpression(0)
+ }
+
+ node = &ConditionalNode{
+ Cond: node,
+ Exp1: expr1,
+ Exp2: expr2,
+ }
+ }
+ return node
+}
+
func (p *parser) parsePrimary() Node {
token := p.current
if token.Is(Operator) {
- if op, ok := unaryOperators[token.Value]; ok {
+ if op, ok := operator.Unary[token.Value]; ok {
p.next()
- expr := p.parseExpression(op.precedence)
+ expr := p.parseExpression(op.Precedence)
node := &UnaryNode{
Operator: token.Value,
Node: expr,
@@ -232,10 +257,15 @@ func (p *parser) parsePrimary() Node {
if p.depth > 0 {
if token.Is(Operator, "#") || token.Is(Operator, ".") {
+ name := ""
if token.Is(Operator, "#") {
p.next()
+ if p.current.Is(Identifier) {
+ name = p.current.Value
+ p.next()
+ }
}
- node := &PointerNode{}
+ node := &PointerNode{Name: name}
node.SetLocation(token.Location)
return p.parsePostfixExpression(node)
}
@@ -245,34 +275,10 @@ func (p *parser) parsePrimary() Node {
}
}
- return p.parsePrimaryExpression()
-}
-
-func (p *parser) parseConditionalExpression(node Node) Node {
- var expr1, expr2 Node
- for p.current.Is(Operator, "?") && p.err == nil {
- p.next()
-
- if !p.current.Is(Operator, ":") {
- expr1 = p.parseExpression(0)
- p.expect(Operator, ":")
- expr2 = p.parseExpression(0)
- } else {
- p.next()
- expr1 = node
- expr2 = p.parseExpression(0)
- }
-
- node = &ConditionalNode{
- Cond: node,
- Exp1: expr1,
- Exp2: expr2,
- }
- }
- return node
+ return p.parseSecondary()
}
-func (p *parser) parsePrimaryExpression() Node {
+func (p *parser) parseSecondary() Node {
var node Node
token := p.current
@@ -294,7 +300,7 @@ func (p *parser) parsePrimaryExpression() Node {
node.SetLocation(token.Location)
return node
default:
- node = p.parseIdentifierExpression(token)
+ node = p.parseCall(token)
}
case Number:
@@ -305,6 +311,10 @@ func (p *parser) parsePrimaryExpression() Node {
if err != nil {
p.error("invalid hex literal: %v", err)
}
+ if number > math.MaxInt {
+ p.error("integer literal is too large")
+ return nil
+ }
node := &IntegerNode{Value: int(number)}
node.SetLocation(token.Location)
return node
@@ -321,6 +331,10 @@ func (p *parser) parsePrimaryExpression() Node {
if err != nil {
p.error("invalid integer literal: %v", err)
}
+ if number > math.MaxInt {
+ p.error("integer literal is too large")
+ return nil
+ }
node := &IntegerNode{Value: int(number)}
node.SetLocation(token.Location)
return node
@@ -345,14 +359,16 @@ func (p *parser) parsePrimaryExpression() Node {
return p.parsePostfixExpression(node)
}
-func (p *parser) parseIdentifierExpression(token Token) Node {
+func (p *parser) parseCall(token Token) Node {
var node Node
if p.current.Is(Bracket, "(") {
var arguments []Node
- if b, ok := builtins[token.Value]; ok {
+ if b, ok := predicates[token.Value]; ok {
p.expect(Bracket, "(")
- // TODO: Add builtins signatures.
+
+ // TODO: Refactor parser to use builtin.Builtins instead of predicates map.
+
if b.arity == 1 {
arguments = make([]Node, 1)
arguments[0] = p.parseExpression(0)
@@ -362,6 +378,18 @@ func (p *parser) parseIdentifierExpression(token Token) Node {
p.expect(Operator, ",")
arguments[1] = p.parseClosure()
}
+
+ if token.Value == "reduce" {
+ arguments = make([]Node, 2)
+ arguments[0] = p.parseExpression(0)
+ p.expect(Operator, ",")
+ arguments[1] = p.parseClosure()
+ if p.current.Is(Operator, ",") {
+ p.next()
+ arguments = append(arguments, p.parseExpression(0))
+ }
+ }
+
p.expect(Bracket, ")")
node = &BuiltinNode{
@@ -369,6 +397,12 @@ func (p *parser) parseIdentifierExpression(token Token) Node {
Arguments: arguments,
}
node.SetLocation(token.Location)
+ } else if _, ok := builtin.Index[token.Value]; ok && !p.config.Disabled[token.Value] {
+ node = &BuiltinNode{
+ Name: token.Value,
+ Arguments: p.parseArguments(),
+ }
+ node.SetLocation(token.Location)
} else {
callee := &IdentifierNode{Value: token.Value}
callee.SetLocation(token.Location)
@@ -487,7 +521,7 @@ func (p *parser) parsePostfixExpression(node Node) Node {
if propertyToken.Kind != Identifier &&
// Operators like "not" and "matches" are valid methods or property names.
- (propertyToken.Kind != Operator || !isValidIdentifier(propertyToken.Value)) {
+ (propertyToken.Kind != Operator || !utils.IsValidIdentifier(propertyToken.Value)) {
p.error("expected name")
}
@@ -578,20 +612,58 @@ func (p *parser) parsePostfixExpression(node Node) Node {
return node
}
-func isValidIdentifier(str string) bool {
- if len(str) == 0 {
- return false
- }
- h, w := utf8.DecodeRuneInString(str)
- if !IsAlphabetic(h) {
- return false
- }
- for _, r := range str[w:] {
- if !IsAlphaNumeric(r) {
- return false
+func (p *parser) parsePipe(node Node) Node {
+ identifier := p.current
+ p.expect(Identifier)
+
+ arguments := []Node{node}
+
+ if b, ok := predicates[identifier.Value]; ok {
+ p.expect(Bracket, "(")
+
+ // TODO: Refactor parser to use builtin.Builtins instead of predicates map.
+
+ if b.arity == 2 {
+ arguments = append(arguments, p.parseClosure())
+ }
+
+ if identifier.Value == "reduce" {
+ arguments = append(arguments, p.parseClosure())
+ if p.current.Is(Operator, ",") {
+ p.next()
+ arguments = append(arguments, p.parseExpression(0))
+ }
+ }
+
+ p.expect(Bracket, ")")
+
+ node = &BuiltinNode{
+ Name: identifier.Value,
+ Arguments: arguments,
+ }
+ node.SetLocation(identifier.Location)
+ } else if _, ok := builtin.Index[identifier.Value]; ok {
+ arguments = append(arguments, p.parseArguments()...)
+
+ node = &BuiltinNode{
+ Name: identifier.Value,
+ Arguments: arguments,
}
+ node.SetLocation(identifier.Location)
+ } else {
+ callee := &IdentifierNode{Value: identifier.Value}
+ callee.SetLocation(identifier.Location)
+
+ arguments = append(arguments, p.parseArguments()...)
+
+ node = &CallNode{
+ Callee: callee,
+ Arguments: arguments,
+ }
+ node.SetLocation(identifier.Location)
}
- return true
+
+ return node
}
func (p *parser) parseArguments() []Node {
diff --git a/vendor/github.com/antonmedv/expr/parser/utils/utils.go b/vendor/github.com/antonmedv/expr/parser/utils/utils.go
new file mode 100644
index 0000000000..947f9a4008
--- /dev/null
+++ b/vendor/github.com/antonmedv/expr/parser/utils/utils.go
@@ -0,0 +1,34 @@
+package utils
+
+import (
+ "unicode"
+ "unicode/utf8"
+)
+
+func IsValidIdentifier(str string) bool {
+ if len(str) == 0 {
+ return false
+ }
+ h, w := utf8.DecodeRuneInString(str)
+ if !IsAlphabetic(h) {
+ return false
+ }
+ for _, r := range str[w:] {
+ if !IsAlphaNumeric(r) {
+ return false
+ }
+ }
+ return true
+}
+
+func IsSpace(r rune) bool {
+ return unicode.IsSpace(r)
+}
+
+func IsAlphaNumeric(r rune) bool {
+ return IsAlphabetic(r) || unicode.IsDigit(r)
+}
+
+func IsAlphabetic(r rune) bool {
+ return r == '_' || r == '$' || unicode.IsLetter(r)
+}
diff --git a/vendor/github.com/antonmedv/expr/vm/generated.go b/vendor/github.com/antonmedv/expr/vm/generated.go
index 9fc7883e2d..1a0b365718 100644
--- a/vendor/github.com/antonmedv/expr/vm/generated.go
+++ b/vendor/github.com/antonmedv/expr/vm/generated.go
@@ -7,20 +7,20 @@ import (
"time"
)
-var FuncTypes = []interface{}{
+var FuncTypes = []any{
1: new(func() time.Duration),
2: new(func() time.Month),
3: new(func() time.Time),
4: new(func() time.Weekday),
5: new(func() []uint8),
- 6: new(func() []interface{}),
+ 6: new(func() []any),
7: new(func() bool),
8: new(func() uint8),
9: new(func() float64),
10: new(func() int),
11: new(func() int64),
- 12: new(func() interface{}),
- 13: new(func() map[string]interface{}),
+ 12: new(func() any),
+ 13: new(func() map[string]any),
14: new(func() int32),
15: new(func() string),
16: new(func() uint),
@@ -29,7 +29,7 @@ var FuncTypes = []interface{}{
19: new(func(time.Duration) time.Time),
20: new(func(time.Time) time.Duration),
21: new(func(time.Time) bool),
- 22: new(func([]interface{}, string) string),
+ 22: new(func([]any, string) string),
23: new(func([]string, string) string),
24: new(func(bool) bool),
25: new(func(bool) float64),
@@ -56,23 +56,23 @@ var FuncTypes = []interface{}{
46: new(func(string, int32) int),
47: new(func(string, string) bool),
48: new(func(string, string) string),
- 49: new(func(interface{}) bool),
- 50: new(func(interface{}) float64),
- 51: new(func(interface{}) int),
- 52: new(func(interface{}) string),
- 53: new(func(interface{}) interface{}),
- 54: new(func(interface{}) []interface{}),
- 55: new(func(interface{}) map[string]interface{}),
- 56: new(func([]interface{}) interface{}),
- 57: new(func([]interface{}) []interface{}),
- 58: new(func([]interface{}) map[string]interface{}),
- 59: new(func(interface{}, interface{}) bool),
- 60: new(func(interface{}, interface{}) string),
- 61: new(func(interface{}, interface{}) interface{}),
- 62: new(func(interface{}, interface{}) []interface{}),
+ 49: new(func(any) bool),
+ 50: new(func(any) float64),
+ 51: new(func(any) int),
+ 52: new(func(any) string),
+ 53: new(func(any) any),
+ 54: new(func(any) []any),
+ 55: new(func(any) map[string]any),
+ 56: new(func([]any) any),
+ 57: new(func([]any) []any),
+ 58: new(func([]any) map[string]any),
+ 59: new(func(any, any) bool),
+ 60: new(func(any, any) string),
+ 61: new(func(any, any) any),
+ 62: new(func(any, any) []any),
}
-func (vm *VM) call(fn interface{}, kind int) interface{} {
+func (vm *VM) call(fn any, kind int) any {
switch kind {
case 1:
return fn.(func() time.Duration)()
@@ -85,7 +85,7 @@ func (vm *VM) call(fn interface{}, kind int) interface{} {
case 5:
return fn.(func() []uint8)()
case 6:
- return fn.(func() []interface{})()
+ return fn.(func() []any)()
case 7:
return fn.(func() bool)()
case 8:
@@ -97,9 +97,9 @@ func (vm *VM) call(fn interface{}, kind int) interface{} {
case 11:
return fn.(func() int64)()
case 12:
- return fn.(func() interface{})()
+ return fn.(func() any)()
case 13:
- return fn.(func() map[string]interface{})()
+ return fn.(func() map[string]any)()
case 14:
return fn.(func() int32)()
case 15:
@@ -122,8 +122,8 @@ func (vm *VM) call(fn interface{}, kind int) interface{} {
return fn.(func(time.Time) bool)(arg1)
case 22:
arg2 := vm.pop().(string)
- arg1 := vm.pop().([]interface{})
- return fn.(func([]interface{}, string) string)(arg1, arg2)
+ arg1 := vm.pop().([]any)
+ return fn.(func([]any, string) string)(arg1, arg2)
case 23:
arg2 := vm.pop().(string)
arg1 := vm.pop().([]string)
@@ -212,50 +212,50 @@ func (vm *VM) call(fn interface{}, kind int) interface{} {
return fn.(func(string, string) string)(arg1, arg2)
case 49:
arg1 := vm.pop()
- return fn.(func(interface{}) bool)(arg1)
+ return fn.(func(any) bool)(arg1)
case 50:
arg1 := vm.pop()
- return fn.(func(interface{}) float64)(arg1)
+ return fn.(func(any) float64)(arg1)
case 51:
arg1 := vm.pop()
- return fn.(func(interface{}) int)(arg1)
+ return fn.(func(any) int)(arg1)
case 52:
arg1 := vm.pop()
- return fn.(func(interface{}) string)(arg1)
+ return fn.(func(any) string)(arg1)
case 53:
arg1 := vm.pop()
- return fn.(func(interface{}) interface{})(arg1)
+ return fn.(func(any) any)(arg1)
case 54:
arg1 := vm.pop()
- return fn.(func(interface{}) []interface{})(arg1)
+ return fn.(func(any) []any)(arg1)
case 55:
arg1 := vm.pop()
- return fn.(func(interface{}) map[string]interface{})(arg1)
+ return fn.(func(any) map[string]any)(arg1)
case 56:
- arg1 := vm.pop().([]interface{})
- return fn.(func([]interface{}) interface{})(arg1)
+ arg1 := vm.pop().([]any)
+ return fn.(func([]any) any)(arg1)
case 57:
- arg1 := vm.pop().([]interface{})
- return fn.(func([]interface{}) []interface{})(arg1)
+ arg1 := vm.pop().([]any)
+ return fn.(func([]any) []any)(arg1)
case 58:
- arg1 := vm.pop().([]interface{})
- return fn.(func([]interface{}) map[string]interface{})(arg1)
+ arg1 := vm.pop().([]any)
+ return fn.(func([]any) map[string]any)(arg1)
case 59:
arg2 := vm.pop()
arg1 := vm.pop()
- return fn.(func(interface{}, interface{}) bool)(arg1, arg2)
+ return fn.(func(any, any) bool)(arg1, arg2)
case 60:
arg2 := vm.pop()
arg1 := vm.pop()
- return fn.(func(interface{}, interface{}) string)(arg1, arg2)
+ return fn.(func(any, any) string)(arg1, arg2)
case 61:
arg2 := vm.pop()
arg1 := vm.pop()
- return fn.(func(interface{}, interface{}) interface{})(arg1, arg2)
+ return fn.(func(any, any) any)(arg1, arg2)
case 62:
arg2 := vm.pop()
arg1 := vm.pop()
- return fn.(func(interface{}, interface{}) []interface{})(arg1, arg2)
+ return fn.(func(any, any) []any)(arg1, arg2)
}
panic(fmt.Sprintf("unknown function kind (%v)", kind))
diff --git a/vendor/github.com/antonmedv/expr/vm/opcodes.go b/vendor/github.com/antonmedv/expr/vm/opcodes.go
index b3117e73c2..1106cd3f05 100644
--- a/vendor/github.com/antonmedv/expr/vm/opcodes.go
+++ b/vendor/github.com/antonmedv/expr/vm/opcodes.go
@@ -3,14 +3,18 @@ package vm
type Opcode byte
const (
- OpPush Opcode = iota
- OpPushInt
+ OpInvalid Opcode = iota
+ OpPush
+ OpInt
OpPop
+ OpStore
+ OpLoadVar
OpLoadConst
OpLoadField
OpLoadFast
OpLoadMethod
OpLoadFunc
+ OpLoadEnv
OpFetch
OpFetchField
OpMethod
@@ -55,17 +59,25 @@ const (
OpCallN
OpCallFast
OpCallTyped
- OpBuiltin
+ OpCallBuiltin1
OpArray
OpMap
OpLen
OpCast
OpDeref
- OpIncrementIt
+ OpIncrementIndex
+ OpDecrementIndex
OpIncrementCount
+ OpGetIndex
+ OpSetIndex
OpGetCount
OpGetLen
+ OpGetGroupBy
+ OpGetAcc
OpPointer
+ OpThrow
+ OpGroupBy
+ OpSetAcc
OpBegin
OpEnd // This opcode must be at the end of this list.
)
diff --git a/vendor/github.com/antonmedv/expr/vm/program.go b/vendor/github.com/antonmedv/expr/vm/program.go
index d424df14f4..c45a2bff23 100644
--- a/vendor/github.com/antonmedv/expr/vm/program.go
+++ b/vendor/github.com/antonmedv/expr/vm/program.go
@@ -3,6 +3,7 @@ package vm
import (
"bytes"
"fmt"
+ "io"
"reflect"
"regexp"
"strings"
@@ -18,15 +19,23 @@ type Program struct {
Node ast.Node
Source *file.Source
Locations []file.Location
- Constants []interface{}
+ Variables []any
+ Constants []any
Bytecode []Opcode
Arguments []int
Functions []Function
+ DebugInfo map[string]string
}
func (program *Program) Disassemble() string {
var buf bytes.Buffer
w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0)
+ program.Opcodes(w)
+ _ = w.Flush()
+ return buf.String()
+}
+
+func (program *Program) Opcodes(w io.Writer) {
ip := 0
for ip < len(program.Bytecode) {
pp := ip
@@ -46,8 +55,11 @@ func (program *Program) Disassemble() string {
argument := func(label string) {
_, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\n", pp, label, arg)
}
+ argumentWithInfo := func(label string, prefix string) {
+ _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, label, arg, program.DebugInfo[fmt.Sprintf("%s_%d", prefix, arg)])
+ }
constant := func(label string) {
- var c interface{}
+ var c any
if arg < len(program.Constants) {
c = program.Constants[arg]
} else {
@@ -64,24 +76,29 @@ func (program *Program) Disassemble() string {
}
_, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, label, arg, c)
}
- builtIn := func(label string) {
- f, ok := builtin.Builtins[arg]
- if !ok {
- panic(fmt.Sprintf("unknown builtin %v", arg))
- }
- _, _ = fmt.Fprintf(w, "%v\t%v\t%v\n", pp, "OpBuiltin", f.Name)
+ builtinArg := func(label string) {
+ _, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, label, arg, builtin.Builtins[arg].Name)
}
switch op {
+ case OpInvalid:
+ code("OpInvalid")
+
case OpPush:
constant("OpPush")
- case OpPushInt:
- argument("OpPushInt")
+ case OpInt:
+ argument("OpInt")
case OpPop:
code("OpPop")
+ case OpStore:
+ argumentWithInfo("OpStore", "var")
+
+ case OpLoadVar:
+ argumentWithInfo("OpLoadVar", "var")
+
case OpLoadConst:
constant("OpLoadConst")
@@ -97,6 +114,9 @@ func (program *Program) Disassemble() string {
case OpLoadFunc:
argument("OpLoadFunc")
+ case OpLoadEnv:
+ code("OpLoadEnv")
+
case OpFetch:
code("OpFetch")
@@ -209,16 +229,16 @@ func (program *Program) Disassemble() string {
argument("OpCall")
case OpCall0:
- argument("OpCall0")
+ argumentWithInfo("OpCall0", "func")
case OpCall1:
- argument("OpCall1")
+ argumentWithInfo("OpCall1", "func")
case OpCall2:
- argument("OpCall2")
+ argumentWithInfo("OpCall2", "func")
case OpCall3:
- argument("OpCall3")
+ argumentWithInfo("OpCall3", "func")
case OpCallN:
argument("OpCallN")
@@ -230,8 +250,8 @@ func (program *Program) Disassemble() string {
signature := reflect.TypeOf(FuncTypes[arg]).Elem().String()
_, _ = fmt.Fprintf(w, "%v\t%v\t<%v>\t%v\n", pp, "OpCallTyped", arg, signature)
- case OpBuiltin:
- builtIn("OpBuiltin")
+ case OpCallBuiltin1:
+ builtinArg("OpCallBuiltin1")
case OpArray:
code("OpArray")
@@ -248,21 +268,45 @@ func (program *Program) Disassemble() string {
case OpDeref:
code("OpDeref")
- case OpIncrementIt:
- code("OpIncrementIt")
+ case OpIncrementIndex:
+ code("OpIncrementIndex")
+
+ case OpDecrementIndex:
+ code("OpDecrementIndex")
case OpIncrementCount:
code("OpIncrementCount")
+ case OpGetIndex:
+ code("OpGetIndex")
+
+ case OpSetIndex:
+ code("OpSetIndex")
+
case OpGetCount:
code("OpGetCount")
case OpGetLen:
code("OpGetLen")
+ case OpGetGroupBy:
+ code("OpGetGroupBy")
+
+ case OpGetAcc:
+ code("OpGetAcc")
+
case OpPointer:
code("OpPointer")
+ case OpThrow:
+ code("OpThrow")
+
+ case OpGroupBy:
+ code("OpGroupBy")
+
+ case OpSetAcc:
+ code("OpSetAcc")
+
case OpBegin:
code("OpBegin")
@@ -270,9 +314,7 @@ func (program *Program) Disassemble() string {
code("OpEnd")
default:
- _, _ = fmt.Fprintf(w, "%v\t%#x\n", ip, op)
+ _, _ = fmt.Fprintf(w, "%v\t%#x (unknown)\n", ip, op)
}
}
- _ = w.Flush()
- return buf.String()
}
diff --git a/vendor/github.com/antonmedv/expr/vm/runtime/generated.go b/vendor/github.com/antonmedv/expr/vm/runtime/generated.go
index 09a4a200ed..720feb4554 100644
--- a/vendor/github.com/antonmedv/expr/vm/runtime/generated.go
+++ b/vendor/github.com/antonmedv/expr/vm/runtime/generated.go
@@ -344,6 +344,11 @@ func Equal(a, b interface{}) bool {
case time.Time:
return x.Equal(y)
}
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x == y
+ }
}
if IsNil(a) && IsNil(b) {
return true
@@ -687,6 +692,11 @@ func Less(a, b interface{}) bool {
case time.Time:
return x.Before(y)
}
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x < y
+ }
}
panic(fmt.Sprintf("invalid operation: %T < %T", a, b))
}
@@ -1027,6 +1037,11 @@ func More(a, b interface{}) bool {
case time.Time:
return x.After(y)
}
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x > y
+ }
}
panic(fmt.Sprintf("invalid operation: %T > %T", a, b))
}
@@ -1367,6 +1382,11 @@ func LessOrEqual(a, b interface{}) bool {
case time.Time:
return x.Before(y) || x.Equal(y)
}
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x <= y
+ }
}
panic(fmt.Sprintf("invalid operation: %T <= %T", a, b))
}
@@ -1707,6 +1727,11 @@ func MoreOrEqual(a, b interface{}) bool {
case time.Time:
return x.After(y) || x.Equal(y)
}
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x >= y
+ }
}
panic(fmt.Sprintf("invalid operation: %T >= %T", a, b))
}
@@ -2051,6 +2076,8 @@ func Add(a, b interface{}) interface{} {
switch y := b.(type) {
case time.Time:
return y.Add(x)
+ case time.Duration:
+ return x + y
}
}
panic(fmt.Sprintf("invalid operation: %T + %T", a, b))
@@ -2386,6 +2413,13 @@ func Subtract(a, b interface{}) interface{} {
switch y := b.(type) {
case time.Time:
return x.Sub(y)
+ case time.Duration:
+ return x.Add(-y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case time.Duration:
+ return x - y
}
}
panic(fmt.Sprintf("invalid operation: %T - %T", a, b))
@@ -2419,6 +2453,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case uint8:
switch y := b.(type) {
@@ -2446,6 +2482,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case uint16:
switch y := b.(type) {
@@ -2473,6 +2511,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case uint32:
switch y := b.(type) {
@@ -2500,6 +2540,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case uint64:
switch y := b.(type) {
@@ -2527,6 +2569,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case int:
switch y := b.(type) {
@@ -2554,6 +2598,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case int8:
switch y := b.(type) {
@@ -2581,6 +2627,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case int16:
switch y := b.(type) {
@@ -2608,6 +2656,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case int32:
switch y := b.(type) {
@@ -2635,6 +2685,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case int64:
switch y := b.(type) {
@@ -2662,6 +2714,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
case float32:
switch y := b.(type) {
@@ -2689,6 +2743,8 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return float64(x) * float64(y)
}
case float64:
switch y := b.(type) {
@@ -2716,6 +2772,37 @@ func Multiply(a, b interface{}) interface{} {
return float64(x) * float64(y)
case float64:
return float64(x) * float64(y)
+ case time.Duration:
+ return float64(x) * float64(y)
+ }
+ case time.Duration:
+ switch y := b.(type) {
+ case uint:
+ return time.Duration(x) * time.Duration(y)
+ case uint8:
+ return time.Duration(x) * time.Duration(y)
+ case uint16:
+ return time.Duration(x) * time.Duration(y)
+ case uint32:
+ return time.Duration(x) * time.Duration(y)
+ case uint64:
+ return time.Duration(x) * time.Duration(y)
+ case int:
+ return time.Duration(x) * time.Duration(y)
+ case int8:
+ return time.Duration(x) * time.Duration(y)
+ case int16:
+ return time.Duration(x) * time.Duration(y)
+ case int32:
+ return time.Duration(x) * time.Duration(y)
+ case int64:
+ return time.Duration(x) * time.Duration(y)
+ case float32:
+ return float64(x) * float64(y)
+ case float64:
+ return float64(x) * float64(y)
+ case time.Duration:
+ return time.Duration(x) * time.Duration(y)
}
}
panic(fmt.Sprintf("invalid operation: %T * %T", a, b))
diff --git a/vendor/github.com/antonmedv/expr/vm/runtime/runtime.go b/vendor/github.com/antonmedv/expr/vm/runtime/runtime.go
index b2eeb65d83..b03714e0fc 100644
--- a/vendor/github.com/antonmedv/expr/vm/runtime/runtime.go
+++ b/vendor/github.com/antonmedv/expr/vm/runtime/runtime.go
@@ -6,10 +6,9 @@ import (
"fmt"
"math"
"reflect"
- "strconv"
)
-func Fetch(from, i interface{}) interface{} {
+func Fetch(from, i any) any {
v := reflect.ValueOf(from)
kind := v.Kind()
if kind == reflect.Invalid {
@@ -82,7 +81,7 @@ type Field struct {
Path []string
}
-func FetchField(from interface{}, field *Field) interface{} {
+func FetchField(from any, field *Field) any {
v := reflect.ValueOf(from)
kind := v.Kind()
if kind != reflect.Invalid {
@@ -126,7 +125,7 @@ type Method struct {
Name string
}
-func FetchMethod(from interface{}, method *Method) interface{} {
+func FetchMethod(from any, method *Method) any {
v := reflect.ValueOf(from)
kind := v.Kind()
if kind != reflect.Invalid {
@@ -139,7 +138,7 @@ func FetchMethod(from interface{}, method *Method) interface{} {
panic(fmt.Sprintf("cannot fetch %v from %T", method.Name, from))
}
-func Deref(i interface{}) interface{} {
+func Deref(i any) any {
if i == nil {
return nil
}
@@ -153,13 +152,15 @@ func Deref(i interface{}) interface{} {
v = v.Elem()
}
- if v.Kind() == reflect.Ptr {
+loop:
+ for v.Kind() == reflect.Ptr {
if v.IsNil() {
return i
}
indirect := reflect.Indirect(v)
switch indirect.Kind() {
case reflect.Struct, reflect.Map, reflect.Array, reflect.Slice:
+ break loop
default:
v = v.Elem()
}
@@ -172,7 +173,7 @@ func Deref(i interface{}) interface{} {
panic(fmt.Sprintf("cannot dereference %v", i))
}
-func Slice(array, from, to interface{}) interface{} {
+func Slice(array, from, to any) any {
v := reflect.ValueOf(array)
switch v.Kind() {
@@ -182,9 +183,15 @@ func Slice(array, from, to interface{}) interface{} {
if a < 0 {
a = length + a
}
+ if a < 0 {
+ a = 0
+ }
if b < 0 {
b = length + b
}
+ if b < 0 {
+ b = 0
+ }
if b > length {
b = length
}
@@ -206,7 +213,7 @@ func Slice(array, from, to interface{}) interface{} {
panic(fmt.Sprintf("cannot slice %v", from))
}
-func In(needle interface{}, array interface{}) bool {
+func In(needle any, array any) bool {
if array == nil {
return false
}
@@ -260,10 +267,10 @@ func In(needle interface{}, array interface{}) bool {
return false
}
- panic(fmt.Sprintf(`operator "in"" not defined on %T`, array))
+ panic(fmt.Sprintf(`operator "in" not defined on %T`, array))
}
-func Len(a interface{}) interface{} {
+func Len(a any) int {
v := reflect.ValueOf(a)
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
@@ -273,7 +280,7 @@ func Len(a interface{}) interface{} {
}
}
-func Negate(i interface{}) interface{} {
+func Negate(i any) any {
switch v := i.(type) {
case float32:
return -v
@@ -304,7 +311,7 @@ func Negate(i interface{}) interface{} {
}
}
-func Exponent(a, b interface{}) float64 {
+func Exponent(a, b any) float64 {
return math.Pow(ToFloat64(a), ToFloat64(b))
}
@@ -320,7 +327,7 @@ func MakeRange(min, max int) []int {
return rng
}
-func ToInt(a interface{}) int {
+func ToInt(a any) int {
switch x := a.(type) {
case float32:
return int(x)
@@ -346,18 +353,12 @@ func ToInt(a interface{}) int {
return int(x)
case uint64:
return int(x)
- case string:
- i, err := strconv.Atoi(x)
- if err != nil {
- panic(fmt.Sprintf("invalid operation: int(%s)", x))
- }
- return i
default:
panic(fmt.Sprintf("invalid operation: int(%T)", x))
}
}
-func ToInt64(a interface{}) int64 {
+func ToInt64(a any) int64 {
switch x := a.(type) {
case float32:
return int64(x)
@@ -388,7 +389,7 @@ func ToInt64(a interface{}) int64 {
}
}
-func ToFloat64(a interface{}) float64 {
+func ToFloat64(a any) float64 {
switch x := a.(type) {
case float32:
return float64(x)
@@ -414,18 +415,12 @@ func ToFloat64(a interface{}) float64 {
return float64(x)
case uint64:
return float64(x)
- case string:
- f, err := strconv.ParseFloat(x, 64)
- if err != nil {
- panic(fmt.Sprintf("invalid operation: float(%s)", x))
- }
- return f
default:
panic(fmt.Sprintf("invalid operation: float(%T)", x))
}
}
-func IsNil(v interface{}) bool {
+func IsNil(v any) bool {
if v == nil {
return true
}
@@ -437,81 +432,3 @@ func IsNil(v interface{}) bool {
return false
}
}
-
-func Abs(x interface{}) interface{} {
- switch x.(type) {
- case float32:
- if x.(float32) < 0 {
- return -x.(float32)
- } else {
- return x
- }
- case float64:
- if x.(float64) < 0 {
- return -x.(float64)
- } else {
- return x
- }
- case int:
- if x.(int) < 0 {
- return -x.(int)
- } else {
- return x
- }
- case int8:
- if x.(int8) < 0 {
- return -x.(int8)
- } else {
- return x
- }
- case int16:
- if x.(int16) < 0 {
- return -x.(int16)
- } else {
- return x
- }
- case int32:
- if x.(int32) < 0 {
- return -x.(int32)
- } else {
- return x
- }
- case int64:
- if x.(int64) < 0 {
- return -x.(int64)
- } else {
- return x
- }
- case uint:
- if x.(uint) < 0 {
- return -x.(uint)
- } else {
- return x
- }
- case uint8:
- if x.(uint8) < 0 {
- return -x.(uint8)
- } else {
- return x
- }
- case uint16:
- if x.(uint16) < 0 {
- return -x.(uint16)
- } else {
- return x
- }
- case uint32:
- if x.(uint32) < 0 {
- return -x.(uint32)
- } else {
- return x
- }
- case uint64:
- if x.(uint64) < 0 {
- return -x.(uint64)
- } else {
- return x
- }
- }
- panic(fmt.Sprintf("invalid argument for abs (type %T)", x))
-}
diff --git a/vendor/github.com/antonmedv/expr/vm/vm.go b/vendor/github.com/antonmedv/expr/vm/vm.go
index af4fc5bf75..d020a31a84 100644
--- a/vendor/github.com/antonmedv/expr/vm/vm.go
+++ b/vendor/github.com/antonmedv/expr/vm/vm.go
@@ -13,12 +13,12 @@ import (
"github.com/antonmedv/expr/vm/runtime"
)
-var MemoryBudget int = 1e6
+var MemoryBudget uint = 1e6
var errorType = reflect.TypeOf((*error)(nil)).Elem()
-type Function = func(params ...interface{}) (interface{}, error)
+type Function = func(params ...any) (any, error)
-func Run(program *Program, env interface{}) (interface{}, error) {
+func Run(program *Program, env any) (any, error) {
if program == nil {
return nil, fmt.Errorf("program is nil")
}
@@ -28,21 +28,23 @@ func Run(program *Program, env interface{}) (interface{}, error) {
}
type VM struct {
- stack []interface{}
+ stack []any
ip int
scopes []*Scope
debug bool
step chan struct{}
curr chan int
- memory int
- memoryBudget int
+ memory uint
+ memoryBudget uint
}
type Scope struct {
- Array reflect.Value
- It int
- Len int
- Count int
+ Array reflect.Value
+ Index int
+ Len int
+ Count int
+ GroupBy map[any][]any
+ Acc any
}
func Debug() *VM {
@@ -54,7 +56,7 @@ func Debug() *VM {
return vm
}
-func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error) {
+func (vm *VM) Run(program *Program, env any) (_ any, err error) {
defer func() {
if r := recover(); r != nil {
f := &file.Error{
@@ -69,7 +71,7 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
}()
if vm.stack == nil {
- vm.stack = make([]interface{}, 0, 2)
+ vm.stack = make([]any, 0, 2)
} else {
vm.stack = vm.stack[0:0]
}
@@ -93,12 +95,24 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
switch op {
+ case OpInvalid:
+ panic("invalid opcode")
+
case OpPush:
vm.push(program.Constants[arg])
+ case OpInt:
+ vm.push(arg)
+
case OpPop:
vm.pop()
+ case OpStore:
+ program.Variables[arg] = vm.pop()
+
+ case OpLoadVar:
+ vm.push(program.Variables[arg])
+
case OpLoadConst:
vm.push(runtime.Fetch(env, program.Constants[arg]))
@@ -106,7 +120,7 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
vm.push(runtime.FetchField(env, program.Constants[arg].(*runtime.Field)))
case OpLoadFast:
- vm.push(env.(map[string]interface{})[program.Constants[arg].(string)])
+ vm.push(env.(map[string]any)[program.Constants[arg].(string)])
case OpLoadMethod:
vm.push(runtime.FetchMethod(env, program.Constants[arg].(*runtime.Method)))
@@ -123,6 +137,9 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
a := vm.pop()
vm.push(runtime.FetchField(a, program.Constants[arg].(*runtime.Field)))
+ case OpLoadEnv:
+ vm.push(env)
+
case OpMethod:
a := vm.pop()
vm.push(runtime.FetchMethod(a, program.Constants[arg].(*runtime.Method)))
@@ -184,7 +201,7 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
case OpJumpIfEnd:
scope := vm.Scope()
- if scope.It >= scope.Len {
+ if scope.Index >= scope.Len {
vm.ip += arg
}
@@ -252,11 +269,11 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
min := runtime.ToInt(a)
max := runtime.ToInt(b)
size := max - min + 1
- if vm.memory+size >= vm.memoryBudget {
- panic("memory budget exceeded")
+ if size <= 0 {
+ size = 0
}
+ vm.memGrow(uint(size))
vm.push(runtime.MakeRange(min, max))
- vm.memory += size
case OpMatches:
b := vm.pop()
@@ -351,7 +368,7 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
case OpCallN:
fn := vm.pop().(Function)
size := arg
- in := make([]interface{}, size)
+ in := make([]any, size)
for i := int(size) - 1; i >= 0; i-- {
in[i] = vm.pop()
}
@@ -362,51 +379,45 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
vm.push(out)
case OpCallFast:
- fn := vm.pop().(func(...interface{}) interface{})
+ fn := vm.pop().(func(...any) any)
size := arg
- in := make([]interface{}, size)
+ in := make([]any, size)
for i := int(size) - 1; i >= 0; i-- {
in[i] = vm.pop()
}
vm.push(fn(in...))
case OpCallTyped:
- fn := vm.pop()
- out := vm.call(fn, arg)
- vm.push(out)
+ vm.push(vm.call(vm.pop(), arg))
+
+ case OpCallBuiltin1:
+ vm.push(builtin.Builtins[arg].Fast(vm.pop()))
case OpArray:
size := vm.pop().(int)
- array := make([]interface{}, size)
+ vm.memGrow(uint(size))
+ array := make([]any, size)
for i := size - 1; i >= 0; i-- {
array[i] = vm.pop()
}
vm.push(array)
- vm.memory += size
- if vm.memory >= vm.memoryBudget {
- panic("memory budget exceeded")
- }
case OpMap:
size := vm.pop().(int)
- m := make(map[string]interface{})
+ vm.memGrow(uint(size))
+ m := make(map[string]any)
for i := size - 1; i >= 0; i-- {
value := vm.pop()
key := vm.pop()
m[key.(string)] = value
}
vm.push(m)
- vm.memory += size
- if vm.memory >= vm.memoryBudget {
- panic("memory budget exceeded")
- }
case OpLen:
vm.push(runtime.Len(vm.current()))
case OpCast:
- t := arg
- switch t {
+ switch arg {
case 0:
vm.push(runtime.ToInt(vm.pop()))
case 1:
@@ -419,14 +430,24 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
a := vm.pop()
vm.push(runtime.Deref(a))
- case OpIncrementIt:
+ case OpIncrementIndex:
+ vm.Scope().Index++
+
+ case OpDecrementIndex:
scope := vm.Scope()
- scope.It++
+ scope.Index--
case OpIncrementCount:
scope := vm.Scope()
scope.Count++
+ case OpGetIndex:
+ vm.push(vm.Scope().Index)
+
+ case OpSetIndex:
+ scope := vm.Scope()
+ scope.Index = vm.pop().(int)
+
case OpGetCount:
scope := vm.Scope()
vm.push(scope.Count)
@@ -435,9 +456,30 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
scope := vm.Scope()
vm.push(scope.Len)
+ case OpGetGroupBy:
+ vm.push(vm.Scope().GroupBy)
+
+ case OpGetAcc:
+ vm.push(vm.Scope().Acc)
+
+ case OpSetAcc:
+ vm.Scope().Acc = vm.pop()
+
case OpPointer:
scope := vm.Scope()
- vm.push(scope.Array.Index(scope.It).Interface())
+ vm.push(scope.Array.Index(scope.Index).Interface())
+
+ case OpThrow:
+ panic(vm.pop().(error))
+
+ case OpGroupBy:
+ scope := vm.Scope()
+ if scope.GroupBy == nil {
+ scope.GroupBy = make(map[any][]any)
+ }
+ it := scope.Array.Index(scope.Index).Interface()
+ key := vm.pop()
+ scope.GroupBy[key] = append(scope.GroupBy[key], it)
case OpBegin:
a := vm.pop()
@@ -450,24 +492,6 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
case OpEnd:
vm.scopes = vm.scopes[:len(vm.scopes)-1]
- case OpBuiltin:
- switch arg {
- case builtin.Len:
- vm.push(runtime.Len(vm.pop()))
-
- case builtin.Abs:
- vm.push(runtime.Abs(vm.pop()))
-
- case builtin.Int:
- vm.push(runtime.ToInt(vm.pop()))
-
- case builtin.Float:
- vm.push(runtime.ToFloat64(vm.pop()))
-
- default:
- panic(fmt.Sprintf("unknown builtin %v", arg))
- }
-
default:
panic(fmt.Sprintf("unknown bytecode %#x", op))
}
@@ -489,21 +513,28 @@ func (vm *VM) Run(program *Program, env interface{}) (_ interface{}, err error)
return nil, nil
}
-func (vm *VM) push(value interface{}) {
+func (vm *VM) push(value any) {
vm.stack = append(vm.stack, value)
}
-func (vm *VM) current() interface{} {
+func (vm *VM) current() any {
return vm.stack[len(vm.stack)-1]
}
-func (vm *VM) pop() interface{} {
+func (vm *VM) pop() any {
value := vm.stack[len(vm.stack)-1]
vm.stack = vm.stack[:len(vm.stack)-1]
return value
}
-func (vm *VM) Stack() []interface{} {
+func (vm *VM) memGrow(size uint) {
+ vm.memory += size
+ if vm.memory >= vm.memoryBudget {
+ panic("memory budget exceeded")
+ }
+}
+
+func (vm *VM) Stack() []any {
return vm.stack
}
diff --git a/vendor/github.com/argoproj/argo-cd/assets/badge.svg b/vendor/github.com/argoproj/argo-cd/assets/badge.svg
deleted file mode 100644
index a3234cfdf5..0000000000
--- a/vendor/github.com/argoproj/argo-cd/assets/badge.svg
+++ /dev/null
@@ -1,22 +0,0 @@
-
\ No newline at end of file
diff --git a/vendor/github.com/argoproj/argo-cd/assets/builtin-policy.csv b/vendor/github.com/argoproj/argo-cd/assets/builtin-policy.csv
deleted file mode 100644
index f74c5b8002..0000000000
--- a/vendor/github.com/argoproj/argo-cd/assets/builtin-policy.csv
+++ /dev/null
@@ -1,34 +0,0 @@
-# Built-in policy which defines two roles: role:readonly and role:admin,
-# and additionally assigns the admin user to the role:admin role.
-# There are two policy formats:
-# 1. Applications (which belong to a project):
-# p, , , , /