mirror of
https://github.com/taigrr/yq
synced 2025-01-18 04:53:17 -08:00
Compare commits
111 Commits
cross_func
...
v4.6.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5911ab2929 | ||
|
|
2ed5b2ff59 | ||
|
|
111c6e0be1 | ||
|
|
81136ad57e | ||
|
|
a6cd250987 | ||
|
|
ee1f55630f | ||
|
|
9072e8d3b3 | ||
|
|
99b08fd612 | ||
|
|
b2317a14ef | ||
|
|
3e5f7b147f | ||
|
|
c4faa70143 | ||
|
|
4d6d07ec43 | ||
|
|
bd0818c481 | ||
|
|
26742b2597 | ||
|
|
64c618c041 | ||
|
|
c4c8e5e7b0 | ||
|
|
e02ad4d7e8 | ||
|
|
dd17f072cf | ||
|
|
429c3ca65b | ||
|
|
cfcac6d1dc | ||
|
|
a5ddbca97f | ||
|
|
30027a8cf4 | ||
|
|
f92a42e4f8 | ||
|
|
3c466dc66e | ||
|
|
0816e16d30 | ||
|
|
802d54e14e | ||
|
|
10600dd29a | ||
|
|
3a464272d4 | ||
|
|
691efadfac | ||
|
|
6efe4c4797 | ||
|
|
9e56b364c2 | ||
|
|
85ec32e3db | ||
|
|
5c73132c8e | ||
|
|
c6efd5519b | ||
|
|
3bae44be68 | ||
|
|
48a7c59c4b | ||
|
|
851fbd8cf5 | ||
|
|
820a3320be | ||
|
|
61f569aebb | ||
|
|
9ff51cd066 | ||
|
|
9dd6d11362 | ||
|
|
83139e21d9 | ||
|
|
c77001f969 | ||
|
|
1be3b31bbc | ||
|
|
6c14a80991 | ||
|
|
76bd1896e9 | ||
|
|
c63801a8a5 | ||
|
|
f7cfdc29e1 | ||
|
|
07c6549a58 | ||
|
|
29f40dad59 | ||
|
|
fe33e7fcfe | ||
|
|
0707525b29 | ||
|
|
62acee54c3 | ||
|
|
d21c94cf4f | ||
|
|
626e9cacaf | ||
|
|
02ef99560d | ||
|
|
c59209f041 | ||
|
|
947ffb6986 | ||
|
|
1a03031297 | ||
|
|
2c7db0071a | ||
|
|
0484d0232b | ||
|
|
91c72d2d9e | ||
|
|
09ec740d45 | ||
|
|
532dbd81a5 | ||
|
|
e86f83fb69 | ||
|
|
7d5b6b5442 | ||
|
|
b749973fe0 | ||
|
|
ba223df4ac | ||
|
|
e6336bcb85 | ||
|
|
9ae03e0a1c | ||
|
|
55712afea6 | ||
|
|
7518dac99c | ||
|
|
49ac2bac13 | ||
|
|
e28df367eb | ||
|
|
90ec05be54 | ||
|
|
8f5270cc63 | ||
|
|
286590b01e | ||
|
|
c1cf8b4e34 | ||
|
|
461661112c | ||
|
|
578f2c27f9 | ||
|
|
6ed037a9f6 | ||
|
|
69386316f3 | ||
|
|
a0e1f65b20 | ||
|
|
8027f4c568 | ||
|
|
b13eb7083e | ||
|
|
b505240d09 | ||
|
|
7a184bef78 | ||
|
|
34bc33d5c5 | ||
|
|
4d8b64d05c | ||
|
|
2d9cc3c107 | ||
|
|
018a6e616d | ||
|
|
7fa2b20b48 | ||
|
|
316e0d4d5a | ||
|
|
a0d4c51e27 | ||
|
|
ceff2cc18d | ||
|
|
db62a16007 | ||
|
|
2a6e423d2d | ||
|
|
5a1b81cbfc | ||
|
|
8c1f7dfbd7 | ||
|
|
2e81384eed | ||
|
|
fbf36037c9 | ||
|
|
2957210e65 | ||
|
|
bde419aaee | ||
|
|
9b185a4409 | ||
|
|
0c777a4967 | ||
|
|
e9591e0cd5 | ||
|
|
04491e13c3 | ||
|
|
5aff50a345 | ||
|
|
90d55fb52a | ||
|
|
e6f97518f3 | ||
|
|
f4a44e7313 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
github: mikefarah
|
||||||
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -40,17 +40,7 @@ jobs:
|
|||||||
IMAGE_NAME: mikefarah/yq
|
IMAGE_NAME: mikefarah/yq
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Get latest release tag
|
- uses: actions/checkout@v2
|
||||||
uses: oprypin/find-latest-tag@v1
|
|
||||||
with:
|
|
||||||
repository: mikefarah/yq # The repository to scan.
|
|
||||||
releases-only: true # We know that all relevant tags have a GitHub release for them.
|
|
||||||
id: yq
|
|
||||||
|
|
||||||
- name: Clone source code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
ref: ${{ steps.yq.outputs.tag }}
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ yq() {
|
|||||||
|
|
||||||
### Go Get:
|
### Go Get:
|
||||||
```
|
```
|
||||||
GO111MODULE=on go get github.com/mikefarah/yq
|
GO111MODULE=on go get github.com/mikefarah/yq/v4
|
||||||
```
|
```
|
||||||
|
|
||||||
## Community Supported Installation methods
|
## Community Supported Installation methods
|
||||||
|
|||||||
@@ -42,14 +42,21 @@ func evaluateAll(cmd *cobra.Command, args []string) error {
|
|||||||
colorsEnabled = true
|
colorsEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if writeInplace && len(args) < 2 {
|
firstFileIndex := -1
|
||||||
|
if !nullInput && len(args) == 1 {
|
||||||
|
firstFileIndex = 0
|
||||||
|
} else if len(args) > 1 {
|
||||||
|
firstFileIndex = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if writeInplace && (firstFileIndex == -1) {
|
||||||
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
|
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if writeInplace {
|
if writeInplace {
|
||||||
// only use colors if its forced
|
// only use colors if its forced
|
||||||
colorsEnabled = forceColor
|
colorsEnabled = forceColor
|
||||||
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[1])
|
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[firstFileIndex])
|
||||||
out, err = writeInPlaceHandler.CreateTempFile()
|
out, err = writeInPlaceHandler.CreateTempFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -62,14 +62,21 @@ func evaluateSequence(cmd *cobra.Command, args []string) error {
|
|||||||
colorsEnabled = true
|
colorsEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if writeInplace && len(args) < 2 {
|
firstFileIndex := -1
|
||||||
|
if !nullInput && len(args) == 1 {
|
||||||
|
firstFileIndex = 0
|
||||||
|
} else if len(args) > 1 {
|
||||||
|
firstFileIndex = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if writeInplace && (firstFileIndex == -1) {
|
||||||
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
|
return fmt.Errorf("Write inplace flag only applicable when giving an expression and at least one file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if writeInplace {
|
if writeInplace {
|
||||||
// only use colors if its forced
|
// only use colors if its forced
|
||||||
colorsEnabled = forceColor
|
colorsEnabled = forceColor
|
||||||
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[1])
|
writeInPlaceHandler := yqlib.NewWriteInPlaceHandler(args[firstFileIndex])
|
||||||
out, err = writeInPlaceHandler.CreateTempFile()
|
out, err = writeInPlaceHandler.CreateTempFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -102,7 +109,7 @@ func evaluateSequence(cmd *cobra.Command, args []string) error {
|
|||||||
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer)
|
err = streamEvaluator.EvaluateFiles(processExpression(""), []string{args[0]}, printer)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = streamEvaluator.EvaluateFiles(args[0], args[1:], printer)
|
err = streamEvaluator.EvaluateFiles(processExpression(args[0]), args[1:], printer)
|
||||||
}
|
}
|
||||||
completedSuccessfully = err == nil
|
completedSuccessfully = err == nil
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ var (
|
|||||||
GitDescribe string
|
GitDescribe string
|
||||||
|
|
||||||
// Version is main version number that is being run at the moment.
|
// Version is main version number that is being run at the moment.
|
||||||
Version = "4.4.0"
|
Version = "4.6.1"
|
||||||
|
|
||||||
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
|
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
|
||||||
// then it means that it is a final release. Otherwise, this is a pre-release
|
// then it means that it is a final release. Otherwise, this is a pre-release
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
a: simple # just the best
|
a:
|
||||||
b: [1, 2]
|
key1: "value1"
|
||||||
c:
|
key2: 2.6
|
||||||
test: 1
|
ab:
|
||||||
|
key1: 6
|
||||||
|
key2: "h"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM mikefarah/yq:4.4.0
|
FROM mikefarah/yq:4.6.1
|
||||||
|
|
||||||
COPY entrypoint.sh /entrypoint.sh
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
|||||||
8
go.mod
8
go.mod
@@ -1,14 +1,14 @@
|
|||||||
module github.com/mikefarah/yq/v4
|
module github.com/mikefarah/yq/v4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/elliotchance/orderedmap v1.3.0
|
github.com/elliotchance/orderedmap v1.4.0
|
||||||
github.com/fatih/color v1.10.0
|
github.com/fatih/color v1.10.0
|
||||||
github.com/goccy/go-yaml v1.8.4
|
github.com/goccy/go-yaml v1.8.8
|
||||||
github.com/jinzhu/copier v0.1.0
|
github.com/jinzhu/copier v0.2.3
|
||||||
github.com/spf13/cobra v1.1.1
|
github.com/spf13/cobra v1.1.1
|
||||||
github.com/timtadh/data-structures v0.5.3 // indirect
|
github.com/timtadh/data-structures v0.5.3 // indirect
|
||||||
github.com/timtadh/lexmachine v0.2.2
|
github.com/timtadh/lexmachine v0.2.2
|
||||||
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
|
||||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
|
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
)
|
)
|
||||||
|
|||||||
19
go.sum
19
go.sum
@@ -36,8 +36,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
github.com/elliotchance/orderedmap v1.3.0 h1:k6m77/d0zCXTjsk12nX40TkEBkSICq8T4s6R6bpCqU0=
|
github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A=
|
||||||
github.com/elliotchance/orderedmap v1.3.0/go.mod h1:8hdSl6jmveQw8ScByd3AaNHNk51RhbTazdqtTty+NFw=
|
github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||||
@@ -52,8 +52,8 @@ github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTM
|
|||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/goccy/go-yaml v1.8.4 h1:AOEdR7aQgbgwHznGe3BLkDQVujxCPUpHOZZcQcp8Y3M=
|
github.com/goccy/go-yaml v1.8.8 h1:MGfRB1GeSn/hWXYWS2Pt67iC2GJNnebdIro01ddyucA=
|
||||||
github.com/goccy/go-yaml v1.8.4/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
github.com/goccy/go-yaml v1.8.8/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
@@ -101,8 +101,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
|||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
github.com/jinzhu/copier v0.1.0 h1:Vh8xALtH3rrKGB/XIRe5d0yCTHPZFauWPLvdpDAbi88=
|
github.com/jinzhu/copier v0.2.3 h1:Oe09ju+9qft7TffZ7l/04AB2f8u1+V4ZMxmp/nnqeOs=
|
||||||
github.com/jinzhu/copier v0.1.0/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro=
|
github.com/jinzhu/copier v0.2.3/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
@@ -182,6 +182,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||||
github.com/timtadh/data-structures v0.5.3 h1:F2tEjoG9qWIyUjbvXVgJqEOGJPMIiYn7U5W5mE+i/vQ=
|
github.com/timtadh/data-structures v0.5.3 h1:F2tEjoG9qWIyUjbvXVgJqEOGJPMIiYn7U5W5mE+i/vQ=
|
||||||
github.com/timtadh/data-structures v0.5.3/go.mod h1:9R4XODhJ8JdWFEI8P/HJKqxuJctfBQw6fDibMQny2oU=
|
github.com/timtadh/data-structures v0.5.3/go.mod h1:9R4XODhJ8JdWFEI8P/HJKqxuJctfBQw6fDibMQny2oU=
|
||||||
@@ -257,8 +259,8 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 h1:/dSxr6gT0FNI1MO5WLJo8mTmItROeOKTkDn+7OwWBos=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||||
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
@@ -324,6 +326,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ func (e *allAtOnceEvaluator) EvaluateCandidateNodes(expression string, inputCand
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return e.treeNavigator.GetMatchingNodes(inputCandidates, node)
|
context, err := e.treeNavigator.GetMatchingNodes(Context{MatchingNodes: inputCandidates}, node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return context.MatchingNodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer) error {
|
func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer) error {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package yqlib
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
@@ -18,10 +16,15 @@ type CandidateNode struct {
|
|||||||
// when performing op against all nodes given, this will treat all the nodes as one
|
// when performing op against all nodes given, this will treat all the nodes as one
|
||||||
// (e.g. top level cross document merge). This property does not propegate to child nodes.
|
// (e.g. top level cross document merge). This property does not propegate to child nodes.
|
||||||
EvaluateTogether bool
|
EvaluateTogether bool
|
||||||
|
IsMapKey bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *CandidateNode) GetKey() string {
|
func (n *CandidateNode) GetKey() string {
|
||||||
return fmt.Sprintf("%v - %v", n.Document, n.Path)
|
keyPrefix := ""
|
||||||
|
if n.IsMapKey {
|
||||||
|
keyPrefix = "key-"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%v%v - %v", keyPrefix, n.Document, n.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *CandidateNode) CreateChild(path interface{}, node *yaml.Node) *CandidateNode {
|
func (n *CandidateNode) CreateChild(path interface{}, node *yaml.Node) *CandidateNode {
|
||||||
@@ -63,11 +66,10 @@ func (n *CandidateNode) UpdateFrom(other *CandidateNode) {
|
|||||||
n.UpdateAttributesFrom(other)
|
n.UpdateAttributesFrom(other)
|
||||||
n.Node.Content = other.Node.Content
|
n.Node.Content = other.Node.Content
|
||||||
n.Node.Value = other.Node.Value
|
n.Node.Value = other.Node.Value
|
||||||
n.Node.Alias = other.Node.Alias
|
|
||||||
n.Node.Anchor = other.Node.Anchor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) {
|
func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) {
|
||||||
|
log.Debug("UpdateAttributesFrom: n: %v other: %v", n.GetKey(), other.GetKey())
|
||||||
if n.Node.Kind != other.Node.Kind {
|
if n.Node.Kind != other.Node.Kind {
|
||||||
// clear out the contents when switching to a different type
|
// clear out the contents when switching to a different type
|
||||||
// e.g. map to array
|
// e.g. map to array
|
||||||
@@ -76,6 +78,8 @@ func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) {
|
|||||||
}
|
}
|
||||||
n.Node.Kind = other.Node.Kind
|
n.Node.Kind = other.Node.Kind
|
||||||
n.Node.Tag = other.Node.Tag
|
n.Node.Tag = other.Node.Tag
|
||||||
|
n.Node.Alias = other.Node.Alias
|
||||||
|
n.Node.Anchor = other.Node.Anchor
|
||||||
|
|
||||||
// merge will pickup the style of the new thing
|
// merge will pickup the style of the new thing
|
||||||
// when autocreating nodes
|
// when autocreating nodes
|
||||||
@@ -86,46 +90,3 @@ func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) {
|
|||||||
n.Node.HeadComment = n.Node.HeadComment + other.Node.HeadComment
|
n.Node.HeadComment = n.Node.HeadComment + other.Node.HeadComment
|
||||||
n.Node.LineComment = n.Node.LineComment + other.Node.LineComment
|
n.Node.LineComment = n.Node.LineComment + other.Node.LineComment
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *CandidateNode) PathStackToString() string {
|
|
||||||
return mergePathStackToString(n.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func mergePathStackToString(pathStack []interface{}) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
for index, path := range pathStack {
|
|
||||||
switch path.(type) {
|
|
||||||
case int, int64:
|
|
||||||
// if arrayMergeStrategy == AppendArrayMergeStrategy {
|
|
||||||
// sb.WriteString("[+]")
|
|
||||||
// } else {
|
|
||||||
sb.WriteString(fmt.Sprintf("[%v]", path))
|
|
||||||
// }
|
|
||||||
|
|
||||||
default:
|
|
||||||
s := fmt.Sprintf("%v", path)
|
|
||||||
var _, errParsingInt = strconv.ParseInt(s, 10, 64) // nolint
|
|
||||||
|
|
||||||
hasSpecial := strings.Contains(s, ".") || strings.Contains(s, "[") || strings.Contains(s, "]") || strings.Contains(s, "\"")
|
|
||||||
hasDoubleQuotes := strings.Contains(s, "\"")
|
|
||||||
wrappingCharacterStart := "\""
|
|
||||||
wrappingCharacterEnd := "\""
|
|
||||||
if hasDoubleQuotes {
|
|
||||||
wrappingCharacterStart = "("
|
|
||||||
wrappingCharacterEnd = ")"
|
|
||||||
}
|
|
||||||
if hasSpecial || errParsingInt == nil {
|
|
||||||
sb.WriteString(wrappingCharacterStart)
|
|
||||||
}
|
|
||||||
sb.WriteString(s)
|
|
||||||
if hasSpecial || errParsingInt == nil {
|
|
||||||
sb.WriteString(wrappingCharacterEnd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if index < len(pathStack)-1 {
|
|
||||||
sb.WriteString(".")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|||||||
54
pkg/yqlib/context.go
Normal file
54
pkg/yqlib/context.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
MatchingNodes *list.List
|
||||||
|
Variables map[string]*list.List
|
||||||
|
DontAutoCreate bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Context) SingleChildContext(candidate *CandidateNode) Context {
|
||||||
|
list := list.New()
|
||||||
|
list.PushBack(candidate)
|
||||||
|
return n.ChildContext(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Context) GetVariable(name string) *list.List {
|
||||||
|
if n.Variables == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return n.Variables[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Context) SetVariable(name string, value *list.List) {
|
||||||
|
if n.Variables == nil {
|
||||||
|
n.Variables = make(map[string]*list.List)
|
||||||
|
}
|
||||||
|
n.Variables[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Context) ChildContext(results *list.List) Context {
|
||||||
|
clone := Context{}
|
||||||
|
err := copier.Copy(&clone, n)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error cloning context :(")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
clone.MatchingNodes = results
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Context) Clone() Context {
|
||||||
|
clone := Context{}
|
||||||
|
err := copier.Copy(&clone, n)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error cloning context :(")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return clone
|
||||||
|
}
|
||||||
@@ -3,16 +3,14 @@ package yqlib
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"container/list"
|
|
||||||
|
|
||||||
logging "gopkg.in/op/go-logging.v1"
|
logging "gopkg.in/op/go-logging.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DataTreeNavigator interface {
|
type DataTreeNavigator interface {
|
||||||
// given a list of CandidateEntities and a expressionNode,
|
// given the context and a expressionNode,
|
||||||
// this will process the list against the given expressionNode and return
|
// this will process the against the given expressionNode and return
|
||||||
// a new list of matching candidates
|
// a new context of matching candidates
|
||||||
GetMatchingNodes(matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error)
|
GetMatchingNodes(context Context, expressionNode *ExpressionNode) (Context, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type dataTreeNavigator struct {
|
type dataTreeNavigator struct {
|
||||||
@@ -22,22 +20,22 @@ func NewDataTreeNavigator() DataTreeNavigator {
|
|||||||
return &dataTreeNavigator{}
|
return &dataTreeNavigator{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func (d *dataTreeNavigator) GetMatchingNodes(context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
if expressionNode == nil {
|
if expressionNode == nil {
|
||||||
log.Debugf("getMatchingNodes - nothing to do")
|
log.Debugf("getMatchingNodes - nothing to do")
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
log.Debugf("Processing Op: %v", expressionNode.Operation.toString())
|
log.Debugf("Processing Op: %v", expressionNode.Operation.toString())
|
||||||
if log.IsEnabledFor(logging.DEBUG) {
|
if log.IsEnabledFor(logging.DEBUG) {
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
log.Debug(NodeToString(el.Value.(*CandidateNode)))
|
log.Debug(NodeToString(el.Value.(*CandidateNode)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Debug(">>")
|
log.Debug(">>")
|
||||||
handler := expressionNode.Operation.OperationType.Handler
|
handler := expressionNode.Operation.OperationType.Handler
|
||||||
if handler != nil {
|
if handler != nil {
|
||||||
return handler(d, matchingNodes, expressionNode)
|
return handler(d, context, expressionNode)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Unknown operator %v", expressionNode.Operation.OperationType)
|
return Context{}, fmt.Errorf("Unknown operator %v", expressionNode.Operation.OperationType)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,3 +209,15 @@ will output
|
|||||||
a: 4
|
a: 4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Add to null
|
||||||
|
Adding to null simply returns the rhs
|
||||||
|
|
||||||
|
Running
|
||||||
|
```bash
|
||||||
|
yq eval --null-input 'null + "cat"'
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
cat
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,24 @@ true
|
|||||||
false
|
false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Don't match string
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
- cat
|
||||||
|
- goat
|
||||||
|
- dog
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.[] | (. != "*at")' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
false
|
||||||
|
false
|
||||||
|
true
|
||||||
|
```
|
||||||
|
|
||||||
## Match number
|
## Match number
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -47,6 +65,24 @@ true
|
|||||||
false
|
false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Dont match number
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
- 3
|
||||||
|
- 4
|
||||||
|
- 5
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.[] | (. != 4)' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
true
|
||||||
|
false
|
||||||
|
true
|
||||||
|
```
|
||||||
|
|
||||||
## Match nulls
|
## Match nulls
|
||||||
Running
|
Running
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -35,6 +35,26 @@ will output
|
|||||||
0
|
0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Get file indices of multiple documents
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: cat
|
||||||
|
```
|
||||||
|
And another sample another.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: cat
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval-all 'fileIndex' sample.yml another.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
0
|
||||||
|
---
|
||||||
|
1
|
||||||
|
```
|
||||||
|
|
||||||
## Get file index alias
|
## Get file index alias
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -16,6 +16,20 @@ will output
|
|||||||
3
|
3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## null length
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: null
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a | length' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
0
|
||||||
|
```
|
||||||
|
|
||||||
## Map length
|
## Map length
|
||||||
returns number of entries
|
returns number of entries
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,34 @@
|
|||||||
Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently only objects are supported, which have the effect of merging the RHS into the LHS.
|
Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently numbers, arrays and objects are supported.
|
||||||
|
|
||||||
To concatenate arrays when merging objects, use the *+ form (see examples below). This will recursively merge objects, appending arrays when it encounters them.
|
## Objects and arrays - merging
|
||||||
|
Objects are merged deeply matching on matching keys. By default, array values override and are not deeply merged.
|
||||||
|
|
||||||
To merge only existing fields, use the *? form. Note that this can be used with the concatenate arrays too *+?.
|
|
||||||
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
||||||
|
|
||||||
Multiplication of strings and numbers are not yet supported.
|
### Merge Flags
|
||||||
|
You can control how objects are merged by using one or more of the following flags. Multiple flags can be used together, e.g. `.a *+? .b`. See examples below
|
||||||
|
|
||||||
## Merging files
|
- `+` to append arrays
|
||||||
|
- `?` to only merge existing fields
|
||||||
|
- `d` to deeply merge arrays
|
||||||
|
|
||||||
|
### Merging files
|
||||||
Note the use of `eval-all` to ensure all documents are loaded into memory.
|
Note the use of `eval-all` to ensure all documents are loaded into memory.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' file1.yaml file2.yaml
|
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' file1.yaml file2.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Multiply integers
|
||||||
|
Running
|
||||||
|
```bash
|
||||||
|
yq eval --null-input '3 * 4'
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
12
|
||||||
|
```
|
||||||
|
|
||||||
## Merge objects together, returning merged result only
|
## Merge objects together, returning merged result only
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -71,7 +86,6 @@ Given a sample.yml file of:
|
|||||||
a: {things: great}
|
a: {things: great}
|
||||||
b:
|
b:
|
||||||
also: "me"
|
also: "me"
|
||||||
|
|
||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
@@ -191,6 +205,32 @@ thing:
|
|||||||
- 4
|
- 4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Merge, deeply merging arrays
|
||||||
|
Merging arrays deeply means arrays are merge like objects, with indexes as their key. In this case, we merge the first item in the array, and do nothing with the second.
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
- name: fred
|
||||||
|
age: 12
|
||||||
|
- name: bob
|
||||||
|
age: 32
|
||||||
|
b:
|
||||||
|
- name: fred
|
||||||
|
age: 34
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a *d .b' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
- name: fred
|
||||||
|
age: 34
|
||||||
|
- name: bob
|
||||||
|
age: 32
|
||||||
|
```
|
||||||
|
|
||||||
## Merge to prefix an element
|
## Merge to prefix an element
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
76
pkg/yqlib/doc/Reduce.md
Normal file
76
pkg/yqlib/doc/Reduce.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
Reduce is a powerful way to process a collection of data into a new form.
|
||||||
|
|
||||||
|
```
|
||||||
|
<exp> as $<name> ireduce (<init>; <block>)
|
||||||
|
```
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
.[] as $item ireduce (0; . + $item)
|
||||||
|
```
|
||||||
|
|
||||||
|
On the LHS we are configuring the collection of items that will be reduced `<exp>` as well as what each element will be called `$<name>`. Note that the array has been splatted into its individual elements.
|
||||||
|
|
||||||
|
On the RHS there is `<init>`, the starting value of the accumulator and `<block>`, the expression that will update the accumulator for each element in the collection. Note that within the block expression, `.` will evaluate to the current value of the accumulator.
|
||||||
|
|
||||||
|
## yq vs jq syntax
|
||||||
|
Reduce syntax in `yq` is a little different from `jq` - as `yq` (currently) isn't as sophisticated as `jq` and its only supports infix notation (e.g. a + b, where the operator is in the middle of the two parameters) - where as `jq` uses a mix of infix notation with _prefix_ notation (e.g. `reduce a b` is like writing `+ a b`).
|
||||||
|
|
||||||
|
To that end, the reduce operator is called `ireduce` for backwards compatability if a `jq` like prefix version of `reduce` is ever added.
|
||||||
|
|
||||||
|
|
||||||
|
## Sum numbers
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
- 10
|
||||||
|
- 2
|
||||||
|
- 5
|
||||||
|
- 3
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.[] as $item ireduce (0; . + $item)' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
20
|
||||||
|
```
|
||||||
|
|
||||||
|
## Merge all yaml files together
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: cat
|
||||||
|
```
|
||||||
|
And another sample another.yml file of:
|
||||||
|
```yaml
|
||||||
|
b: dog
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval-all '. as $item ireduce ({}; . * $item )' sample.yml another.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
a: cat
|
||||||
|
b: dog
|
||||||
|
```
|
||||||
|
|
||||||
|
## Convert an array to an object
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
- name: Cathy
|
||||||
|
has: apples
|
||||||
|
- name: Bob
|
||||||
|
has: bananas
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.[] as $item ireduce ({}; .[$item | .name] = ($item | .has) )' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
Cathy: apples
|
||||||
|
Bob: bananas
|
||||||
|
```
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ c: banana
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Special characters
|
## Special characters
|
||||||
Use quotes around path elements with special characters
|
Use quotes with brackets around path elements with special characters
|
||||||
|
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -41,7 +41,23 @@ Given a sample.yml file of:
|
|||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
yq eval '."{}"' sample.yml
|
yq eval '.["{}"]' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
frog
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keys with spaces
|
||||||
|
Use quotes with brackets around path elements with special characters
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
"red rabbit": frog
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.["red rabbit"]' sample.yml
|
||||||
```
|
```
|
||||||
will output
|
will output
|
||||||
```yaml
|
```yaml
|
||||||
58
pkg/yqlib/doc/Variable Operators.md
Normal file
58
pkg/yqlib/doc/Variable Operators.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
For more complex scenarios, variables can be used to hold values of expression to be used in other expressions.
|
||||||
|
|
||||||
|
## Single value variable
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a: cat
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a as $foo | $foo' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
cat
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multi value variable
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
- cat
|
||||||
|
- dog
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.[] as $foo | $foo' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
cat
|
||||||
|
dog
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using variables as a lookup
|
||||||
|
Example taken from [jq](https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...)
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
"posts":
|
||||||
|
- "title": Frist psot
|
||||||
|
"author": anon
|
||||||
|
- "title": A well-written article
|
||||||
|
"author": person1
|
||||||
|
"realnames":
|
||||||
|
"anon": Anonymous Coward
|
||||||
|
"person1": Person McPherson
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.realnames as $names | .posts[] | {"title":.title, "author": $names[.author]}' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
title: Frist psot
|
||||||
|
author: Anonymous Coward
|
||||||
|
title: A well-written article
|
||||||
|
author: Person McPherson
|
||||||
|
```
|
||||||
|
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently only objects are supported, which have the effect of merging the RHS into the LHS.
|
Like the multiple operator in jq, depending on the operands, this multiply operator will do different things. Currently numbers, arrays and objects are supported.
|
||||||
|
|
||||||
To concatenate arrays when merging objects, use the *+ form (see examples below). This will recursively merge objects, appending arrays when it encounters them.
|
## Objects and arrays - merging
|
||||||
|
Objects are merged deeply matching on matching keys. By default, array values override and are not deeply merged.
|
||||||
|
|
||||||
To merge only existing fields, use the *? form. Note that this can be used with the concatenate arrays too *+?.
|
|
||||||
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
Note that when merging objects, this operator returns the merged object (not the parent). This will be clearer in the examples below.
|
||||||
|
|
||||||
Multiplication of strings and numbers are not yet supported.
|
### Merge Flags
|
||||||
|
You can control how objects are merged by using one or more of the following flags. Multiple flags can be used together, e.g. `.a *+? .b`. See examples below
|
||||||
|
|
||||||
## Merging files
|
- `+` to append arrays
|
||||||
|
- `?` to only merge existing fields
|
||||||
|
- `d` to deeply merge arrays
|
||||||
|
|
||||||
|
### Merging files
|
||||||
Note the use of `eval-all` to ensure all documents are loaded into memory.
|
Note the use of `eval-all` to ensure all documents are loaded into memory.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
21
pkg/yqlib/doc/headers/Reduce.md
Normal file
21
pkg/yqlib/doc/headers/Reduce.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
Reduce is a powerful way to process a collection of data into a new form.
|
||||||
|
|
||||||
|
```
|
||||||
|
<exp> as $<name> ireduce (<init>; <block>)
|
||||||
|
```
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
.[] as $item ireduce (0; . + $item)
|
||||||
|
```
|
||||||
|
|
||||||
|
On the LHS we are configuring the collection of items that will be reduced `<exp>` as well as what each element will be called `$<name>`. Note that the array has been splatted into its individual elements.
|
||||||
|
|
||||||
|
On the RHS there is `<init>`, the starting value of the accumulator and `<block>`, the expression that will update the accumulator for each element in the collection. Note that within the block expression, `.` will evaluate to the current value of the accumulator.
|
||||||
|
|
||||||
|
## yq vs jq syntax
|
||||||
|
Reduce syntax in `yq` is a little different from `jq` - as `yq` (currently) isn't as sophisticated as `jq` and its only supports infix notation (e.g. a + b, where the operator is in the middle of the two parameters) - where as `jq` uses a mix of infix notation with _prefix_ notation (e.g. `reduce a b` is like writing `+ a b`).
|
||||||
|
|
||||||
|
To that end, the reduce operator is called `ireduce` for backwards compatability if a `jq` like prefix version of `reduce` is ever added.
|
||||||
|
|
||||||
1
pkg/yqlib/doc/headers/Variable Operators.md
Normal file
1
pkg/yqlib/doc/headers/Variable Operators.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
For more complex scenarios, variables can be used to hold values of expression to be used in other expressions.
|
||||||
@@ -76,6 +76,8 @@ func mapKeysToStrings(node *yaml.Node) {
|
|||||||
|
|
||||||
func NewJsonEncoder(destination io.Writer, indent int) Encoder {
|
func NewJsonEncoder(destination io.Writer, indent int) Encoder {
|
||||||
var encoder = json.NewEncoder(destination)
|
var encoder = json.NewEncoder(destination)
|
||||||
|
encoder.SetEscapeHTML(false) // do not escape html chars e.g. &, <, >
|
||||||
|
|
||||||
var indentString = ""
|
var indentString = ""
|
||||||
|
|
||||||
for index := 0; index < indent; index++ {
|
for index := 0; index < indent; index++ {
|
||||||
@@ -153,11 +155,15 @@ func (o *orderedMap) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o orderedMap) MarshalJSON() ([]byte, error) {
|
func (o orderedMap) MarshalJSON() ([]byte, error) {
|
||||||
if o.kv == nil {
|
|
||||||
return json.Marshal(o.altVal)
|
|
||||||
}
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
enc := json.NewEncoder(buf)
|
enc := json.NewEncoder(buf)
|
||||||
|
enc.SetEscapeHTML(false) // do not escape html chars e.g. &, <, >
|
||||||
|
if o.kv == nil {
|
||||||
|
if err := enc.Encode(o.altVal); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
buf.WriteByte('{')
|
buf.WriteByte('{')
|
||||||
for idx, el := range o.kv {
|
for idx, el := range o.kv {
|
||||||
if err := enc.Encode(el.K); err != nil {
|
if err := enc.Encode(el.K); err != nil {
|
||||||
|
|||||||
@@ -9,29 +9,11 @@ import (
|
|||||||
"github.com/mikefarah/yq/v4/test"
|
"github.com/mikefarah/yq/v4/test"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sampleYaml = `zabbix: winner
|
func yamlToJson(sampleYaml string, indent int) string {
|
||||||
apple: great
|
|
||||||
banana:
|
|
||||||
- {cobra: kai, angus: bob}
|
|
||||||
`
|
|
||||||
|
|
||||||
var expectedJson = `{
|
|
||||||
"zabbix": "winner",
|
|
||||||
"apple": "great",
|
|
||||||
"banana": [
|
|
||||||
{
|
|
||||||
"cobra": "kai",
|
|
||||||
"angus": "bob"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
func TestJsonEncoderPreservesObjectOrder(t *testing.T) {
|
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
writer := bufio.NewWriter(&output)
|
writer := bufio.NewWriter(&output)
|
||||||
|
|
||||||
var jsonEncoder = NewJsonEncoder(writer, 2)
|
var jsonEncoder = NewJsonEncoder(writer, indent)
|
||||||
inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0)
|
inputs, err := readDocuments(strings.NewReader(sampleYaml), "sample.yml", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -42,6 +24,33 @@ func TestJsonEncoderPreservesObjectOrder(t *testing.T) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
test.AssertResult(t, expectedJson, output.String())
|
|
||||||
|
|
||||||
|
return strings.TrimSuffix(output.String(), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonEncoderPreservesObjectOrder(t *testing.T) {
|
||||||
|
var sampleYaml = `zabbix: winner
|
||||||
|
apple: great
|
||||||
|
banana:
|
||||||
|
- {cobra: kai, angus: bob}
|
||||||
|
`
|
||||||
|
var expectedJson = `{
|
||||||
|
"zabbix": "winner",
|
||||||
|
"apple": "great",
|
||||||
|
"banana": [
|
||||||
|
{
|
||||||
|
"cobra": "kai",
|
||||||
|
"angus": "bob"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
var actualJson = yamlToJson(sampleYaml, 2)
|
||||||
|
test.AssertResult(t, expectedJson, actualJson)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonEncoderDoesNotEscapeHTMLChars(t *testing.T) {
|
||||||
|
var sampleYaml = `build: "( ./lint && ./format && ./compile ) < src.code"`
|
||||||
|
var expectedJson = `{"build":"( ./lint && ./format && ./compile ) < src.code"}`
|
||||||
|
var actualJson = yamlToJson(sampleYaml, 0)
|
||||||
|
test.AssertResult(t, expectedJson, actualJson)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var myPathTokeniser = newExpressionTokeniser()
|
|
||||||
var myPathPostfixer = newExpressionPostFixer()
|
|
||||||
|
|
||||||
type ExpressionNode struct {
|
type ExpressionNode struct {
|
||||||
Operation *Operation
|
Operation *Operation
|
||||||
Lhs *ExpressionNode
|
Lhs *ExpressionNode
|
||||||
@@ -19,19 +16,21 @@ type ExpressionParser interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type expressionParserImpl struct {
|
type expressionParserImpl struct {
|
||||||
|
pathTokeniser expressionTokeniser
|
||||||
|
pathPostFixer expressionPostFixer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExpressionParser() ExpressionParser {
|
func NewExpressionParser() ExpressionParser {
|
||||||
return &expressionParserImpl{}
|
return &expressionParserImpl{newExpressionTokeniser(), newExpressionPostFixer()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *expressionParserImpl) ParseExpression(expression string) (*ExpressionNode, error) {
|
func (p *expressionParserImpl) ParseExpression(expression string) (*ExpressionNode, error) {
|
||||||
tokens, err := myPathTokeniser.Tokenise(expression)
|
tokens, err := p.pathTokeniser.Tokenise(expression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var Operations []*Operation
|
var Operations []*Operation
|
||||||
Operations, err = myPathPostfixer.ConvertToPostfix(tokens)
|
Operations, err = p.pathPostFixer.ConvertToPostfix(tokens)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -70,7 +69,7 @@ func (p *expressionParserImpl) createExpressionTree(postFixPath []*Operation) (*
|
|||||||
stack = append(stack, &newNode)
|
stack = append(stack, &newNode)
|
||||||
}
|
}
|
||||||
if len(stack) != 1 {
|
if len(stack) != 1 {
|
||||||
return nil, fmt.Errorf("expected end of expression but found '%v', please check expression syntax", strings.TrimSpace(stack[1].Operation.StringValue))
|
return nil, fmt.Errorf("Bad expression, please check expression syntax")
|
||||||
}
|
}
|
||||||
return stack[0], nil
|
return stack[0], nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,5 +38,5 @@ func TestPathTreeOneArgForOneArgOp(t *testing.T) {
|
|||||||
|
|
||||||
func TestPathTreeExtraArgs(t *testing.T) {
|
func TestPathTreeExtraArgs(t *testing.T) {
|
||||||
_, err := NewExpressionParser().ParseExpression("sortKeys(.) explode(.)")
|
_, err := NewExpressionParser().ParseExpression("sortKeys(.) explode(.)")
|
||||||
test.AssertResultComplex(t, "expected end of expression but found 'explode', please check expression syntax", err.Error())
|
test.AssertResultComplex(t, "Bad expression, please check expression syntax", err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,20 +20,22 @@ func newExpressionPostFixer() expressionPostFixer {
|
|||||||
func popOpToResult(opStack []*token, result []*Operation) ([]*token, []*Operation) {
|
func popOpToResult(opStack []*token, result []*Operation) ([]*token, []*Operation) {
|
||||||
var newOp *token
|
var newOp *token
|
||||||
opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
|
opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
|
||||||
|
log.Debugf("popped %v from opstack to results", newOp.toString(true))
|
||||||
return opStack, append(result, newOp.Operation)
|
return opStack, append(result, newOp.Operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Operation, error) {
|
func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Operation, error) {
|
||||||
var result []*Operation
|
var result []*Operation
|
||||||
// surround the whole thing with quotes
|
// surround the whole thing with brackets
|
||||||
var opStack = []*token{&token{TokenType: openBracket}}
|
var opStack = []*token{{TokenType: openBracket}}
|
||||||
var tokens = append(infixTokens, &token{TokenType: closeBracket})
|
var tokens = append(infixTokens, &token{TokenType: closeBracket})
|
||||||
|
|
||||||
for _, currentToken := range tokens {
|
for _, currentToken := range tokens {
|
||||||
log.Debugf("postfix processing currentToken %v, %v", currentToken.toString(), currentToken.Operation)
|
log.Debugf("postfix processing currentToken %v", currentToken.toString(true))
|
||||||
switch currentToken.TokenType {
|
switch currentToken.TokenType {
|
||||||
case openBracket, openCollect, openCollectObject:
|
case openBracket, openCollect, openCollectObject:
|
||||||
opStack = append(opStack, currentToken)
|
opStack = append(opStack, currentToken)
|
||||||
|
log.Debugf("put %v onto the opstack", currentToken.toString(true))
|
||||||
case closeCollect, closeCollectObject:
|
case closeCollect, closeCollectObject:
|
||||||
var opener tokenType = openCollect
|
var opener tokenType = openCollect
|
||||||
var collectOperator *operationType = collectOpType
|
var collectOperator *operationType = collectOpType
|
||||||
@@ -41,23 +43,23 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
|
|||||||
opener = openCollectObject
|
opener = openCollectObject
|
||||||
collectOperator = collectObjectOpType
|
collectOperator = collectObjectOpType
|
||||||
}
|
}
|
||||||
itemsInMiddle := false
|
|
||||||
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener {
|
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != opener {
|
||||||
opStack, result = popOpToResult(opStack, result)
|
opStack, result = popOpToResult(opStack, result)
|
||||||
itemsInMiddle = true
|
|
||||||
}
|
|
||||||
if !itemsInMiddle {
|
|
||||||
// must be an empty collection, add the empty object as a LHS parameter
|
|
||||||
result = append(result, &Operation{OperationType: emptyOpType})
|
|
||||||
}
|
}
|
||||||
if len(opStack) == 0 {
|
if len(opStack) == 0 {
|
||||||
return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket")
|
return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket")
|
||||||
}
|
}
|
||||||
// now we should have [] as the last element on the opStack, get rid of it
|
// now we should have [ as the last element on the opStack, get rid of it
|
||||||
opStack = opStack[0 : len(opStack)-1]
|
opStack = opStack[0 : len(opStack)-1]
|
||||||
|
log.Debugf("deleteing open bracket from opstack")
|
||||||
|
|
||||||
//and append a collect to the opStack
|
//and append a collect to the opStack
|
||||||
opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: shortPipeOpType}})
|
result = append(result, &Operation{OperationType: collectOperator})
|
||||||
opStack = append(opStack, &token{TokenType: operationToken, Operation: &Operation{OperationType: collectOperator}})
|
log.Debugf("put collect onto the result")
|
||||||
|
result = append(result, &Operation{OperationType: shortPipeOpType})
|
||||||
|
log.Debugf("put shortpipe onto the result")
|
||||||
|
|
||||||
case closeBracket:
|
case closeBracket:
|
||||||
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket {
|
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != openBracket {
|
||||||
opStack, result = popOpToResult(opStack, result)
|
opStack, result = popOpToResult(opStack, result)
|
||||||
@@ -73,11 +75,12 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
|
|||||||
// pop off higher precedent operators onto the result
|
// pop off higher precedent operators onto the result
|
||||||
for len(opStack) > 0 &&
|
for len(opStack) > 0 &&
|
||||||
opStack[len(opStack)-1].TokenType == operationToken &&
|
opStack[len(opStack)-1].TokenType == operationToken &&
|
||||||
opStack[len(opStack)-1].Operation.OperationType.Precedence >= currentPrecedence {
|
opStack[len(opStack)-1].Operation.OperationType.Precedence > currentPrecedence {
|
||||||
opStack, result = popOpToResult(opStack, result)
|
opStack, result = popOpToResult(opStack, result)
|
||||||
}
|
}
|
||||||
// add this operator to the opStack
|
// add this operator to the opStack
|
||||||
opStack = append(opStack, currentToken)
|
opStack = append(opStack, currentToken)
|
||||||
|
log.Debugf("put %v onto the opstack", currentToken.toString(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,40 +12,70 @@ var pathTests = []struct {
|
|||||||
expectedTokens []interface{}
|
expectedTokens []interface{}
|
||||||
expectedPostFix []interface{}
|
expectedPostFix []interface{}
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
`.a as $item ireduce (0; . + $item)`, // note - add code to shuffle reduce to this position for postfix
|
||||||
|
append(make([]interface{}, 0), "a", "ASSIGN_VARIABLE", "GET_VARIABLE", "REDUCE", "(", "0 (int64)", "BLOCK", "SELF", "ADD", "GET_VARIABLE", ")"),
|
||||||
|
append(make([]interface{}, 0), "a", "GET_VARIABLE", "ASSIGN_VARIABLE", "0 (int64)", "SELF", "GET_VARIABLE", "ADD", "BLOCK", "REDUCE"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`.a | .b | .c`,
|
||||||
|
append(make([]interface{}, 0), "a", "PIPE", "b", "PIPE", "c"),
|
||||||
|
append(make([]interface{}, 0), "a", "b", "c", "PIPE", "PIPE"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
`[]`,
|
`[]`,
|
||||||
append(make([]interface{}, 0), "[", "]"),
|
append(make([]interface{}, 0), "[", "EMPTY", "]"),
|
||||||
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`{}`,
|
||||||
|
append(make([]interface{}, 0), "{", "EMPTY", "}"),
|
||||||
|
append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`[{}]`,
|
||||||
|
append(make([]interface{}, 0), "[", "{", "EMPTY", "}", "]"),
|
||||||
|
append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE", "COLLECT", "SHORT_PIPE"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`.realnames as $names | $names["anon"]`,
|
||||||
|
append(make([]interface{}, 0), "realnames", "ASSIGN_VARIABLE", "GET_VARIABLE", "PIPE", "GET_VARIABLE", "TRAVERSE_ARRAY", "[", "anon (string)", "]"),
|
||||||
|
append(make([]interface{}, 0), "realnames", "GET_VARIABLE", "ASSIGN_VARIABLE", "GET_VARIABLE", "anon (string)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "PIPE"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`.b[.a]`,
|
||||||
|
append(make([]interface{}, 0), "b", "TRAVERSE_ARRAY", "[", "a", "]"),
|
||||||
|
append(make([]interface{}, 0), "b", "a", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
`.[]`,
|
`.[]`,
|
||||||
append(make([]interface{}, 0), "TRAVERSE_ARRAY", "[", "]"),
|
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
||||||
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
append(make([]interface{}, 0), "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a[]`,
|
`.a[]`,
|
||||||
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]"),
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
||||||
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a.[]`,
|
`.a.[]`,
|
||||||
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]"),
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
||||||
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a[0]`,
|
`.a[0]`,
|
||||||
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
||||||
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a.[0]`,
|
`.a.[0]`,
|
||||||
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
||||||
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a[].c`,
|
`.a[].c`,
|
||||||
append(make([]interface{}, 0), "a", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]", "SHORT_PIPE", "c"),
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "SHORT_PIPE", "c"),
|
||||||
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "c", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "c", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`[3]`,
|
`[3]`,
|
||||||
@@ -69,18 +99,18 @@ var pathTests = []struct {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.a | .[].b == "apple"`,
|
`.a | .[].b == "apple"`,
|
||||||
append(make([]interface{}, 0), "a", "PIPE", "TRAVERSE_ARRAY", "[", "]", "SHORT_PIPE", "b", "EQUALS", "apple (string)"),
|
append(make([]interface{}, 0), "a", "PIPE", "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "SHORT_PIPE", "b", "EQUALS", "apple (string)"),
|
||||||
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "apple (string)", "EQUALS", "PIPE"),
|
append(make([]interface{}, 0), "a", "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "apple (string)", "EQUALS", "PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`(.a | .[].b) == "apple"`,
|
`(.a | .[].b) == "apple"`,
|
||||||
append(make([]interface{}, 0), "(", "a", "PIPE", "TRAVERSE_ARRAY", "[", "]", "SHORT_PIPE", "b", ")", "EQUALS", "apple (string)"),
|
append(make([]interface{}, 0), "(", "a", "PIPE", "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "SHORT_PIPE", "b", ")", "EQUALS", "apple (string)"),
|
||||||
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "PIPE", "apple (string)", "EQUALS"),
|
append(make([]interface{}, 0), "a", "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "b", "SHORT_PIPE", "PIPE", "apple (string)", "EQUALS"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`.[] | select(. == "*at")`,
|
`.[] | select(. == "*at")`,
|
||||||
append(make([]interface{}, 0), "TRAVERSE_ARRAY", "[", "]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at (string)", ")"),
|
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at (string)", ")"),
|
||||||
append(make([]interface{}, 0), "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"),
|
append(make([]interface{}, 0), "SELF", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`[true]`,
|
`[true]`,
|
||||||
@@ -113,9 +143,9 @@ var pathTests = []struct {
|
|||||||
append(make([]interface{}, 0), "a", "mike (string)", "CREATE_MAP", "COLLECT_OBJECT", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "mike (string)", "CREATE_MAP", "COLLECT_OBJECT", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`{.a: .c, .b.[]: .f.g.[]}`,
|
`{.a: .c, .b.[]: .f.g[]}`,
|
||||||
append(make([]interface{}, 0), "{", "a", "CREATE_MAP", "c", "UNION", "b", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]", "CREATE_MAP", "f", "SHORT_PIPE", "g", "SHORT_PIPE", "TRAVERSE_ARRAY", "[", "]", "}"),
|
append(make([]interface{}, 0), "{", "a", "CREATE_MAP", "c", "UNION", "b", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "CREATE_MAP", "f", "SHORT_PIPE", "g", "TRAVERSE_ARRAY", "[", "EMPTY", "]", "}"),
|
||||||
append(make([]interface{}, 0), "a", "c", "CREATE_MAP", "b", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "f", "g", "SHORT_PIPE", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "CREATE_MAP", "UNION", "COLLECT_OBJECT", "SHORT_PIPE"),
|
append(make([]interface{}, 0), "a", "c", "CREATE_MAP", "b", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "f", "g", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "SHORT_PIPE", "CREATE_MAP", "UNION", "COLLECT_OBJECT", "SHORT_PIPE"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`explode(.a.b)`,
|
`explode(.a.b)`,
|
||||||
@@ -167,11 +197,6 @@ var pathTests = []struct {
|
|||||||
append(make([]interface{}, 0), "foo*", "PIPE", "(", "SELF", "ASSIGN_STYLE", "flow (string)", ")"),
|
append(make([]interface{}, 0), "foo*", "PIPE", "(", "SELF", "ASSIGN_STYLE", "flow (string)", ")"),
|
||||||
append(make([]interface{}, 0), "foo*", "SELF", "flow (string)", "ASSIGN_STYLE", "PIPE"),
|
append(make([]interface{}, 0), "foo*", "SELF", "flow (string)", "ASSIGN_STYLE", "PIPE"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
`{}`,
|
|
||||||
append(make([]interface{}, 0), "{", "}"),
|
|
||||||
append(make([]interface{}, 0), "EMPTY", "COLLECT_OBJECT", "SHORT_PIPE"),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var tokeniser = newExpressionTokeniser()
|
var tokeniser = newExpressionTokeniser()
|
||||||
@@ -185,7 +210,7 @@ func TestPathParsing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
var tokenValues []interface{}
|
var tokenValues []interface{}
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
tokenValues = append(tokenValues, token.toString())
|
tokenValues = append(tokenValues, token.toString(false))
|
||||||
}
|
}
|
||||||
test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path))
|
test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path))
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,11 @@ type token struct {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *token) toString() string {
|
func (t *token) toString(detail bool) string {
|
||||||
if t.TokenType == operationToken {
|
if t.TokenType == operationToken {
|
||||||
log.Debug("toString, its an op")
|
if detail {
|
||||||
|
return fmt.Sprintf("%v (%v)", t.Operation.toString(), t.Operation.OperationType.Precedence)
|
||||||
|
}
|
||||||
return t.Operation.toString()
|
return t.Operation.toString()
|
||||||
} else if t.TokenType == openBracket {
|
} else if t.TokenType == openBracket {
|
||||||
return "("
|
return "("
|
||||||
@@ -98,6 +100,9 @@ func multiplyWithPrefs() lex.Action {
|
|||||||
if strings.Contains(options, "?") {
|
if strings.Contains(options, "?") {
|
||||||
prefs.TraversePrefs = traversePreferences{DontAutoCreate: true}
|
prefs.TraversePrefs = traversePreferences{DontAutoCreate: true}
|
||||||
}
|
}
|
||||||
|
if strings.Contains(options, "d") {
|
||||||
|
prefs.DeepMergeArrays = true
|
||||||
|
}
|
||||||
op := &Operation{OperationType: multiplyOpType, Value: multiplyOpType.Type, StringValue: options, Preferences: prefs}
|
op := &Operation{OperationType: multiplyOpType, Value: multiplyOpType.Type, StringValue: options, Preferences: prefs}
|
||||||
return &token{TokenType: operationToken, Operation: op}, nil
|
return &token{TokenType: operationToken, Operation: op}, nil
|
||||||
}
|
}
|
||||||
@@ -180,6 +185,19 @@ func stringValue(wrapped bool) lex.Action {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getVariableOpToken() lex.Action {
|
||||||
|
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
||||||
|
value := string(m.Bytes)
|
||||||
|
|
||||||
|
value = value[1:]
|
||||||
|
|
||||||
|
getVarOperation := createValueOperation(value, value)
|
||||||
|
getVarOperation.OperationType = getVariableOpType
|
||||||
|
|
||||||
|
return &token{TokenType: operationToken, Operation: getVarOperation, CheckForPostTraverse: true}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func envOp(strenv bool) lex.Action {
|
func envOp(strenv bool) lex.Action {
|
||||||
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
||||||
value := string(m.Bytes)
|
value := string(m.Bytes)
|
||||||
@@ -237,6 +255,8 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`or`), opToken(orOpType))
|
lexer.Add([]byte(`or`), opToken(orOpType))
|
||||||
lexer.Add([]byte(`and`), opToken(andOpType))
|
lexer.Add([]byte(`and`), opToken(andOpType))
|
||||||
lexer.Add([]byte(`not`), opToken(notOpType))
|
lexer.Add([]byte(`not`), opToken(notOpType))
|
||||||
|
lexer.Add([]byte(`ireduce`), opToken(reduceOpType))
|
||||||
|
lexer.Add([]byte(`;`), opToken(blockOpType))
|
||||||
lexer.Add([]byte(`\/\/`), opToken(alternativeOpType))
|
lexer.Add([]byte(`\/\/`), opToken(alternativeOpType))
|
||||||
|
|
||||||
lexer.Add([]byte(`documentIndex`), opToken(getDocumentIndexOpType))
|
lexer.Add([]byte(`documentIndex`), opToken(getDocumentIndexOpType))
|
||||||
@@ -269,6 +289,7 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`collect`), opToken(collectOpType))
|
lexer.Add([]byte(`collect`), opToken(collectOpType))
|
||||||
|
|
||||||
lexer.Add([]byte(`\s*==\s*`), opToken(equalsOpType))
|
lexer.Add([]byte(`\s*==\s*`), opToken(equalsOpType))
|
||||||
|
lexer.Add([]byte(`\s*!=\s*`), opToken(notEqualsOpType))
|
||||||
lexer.Add([]byte(`\s*=\s*`), assignOpToken(false))
|
lexer.Add([]byte(`\s*=\s*`), assignOpToken(false))
|
||||||
|
|
||||||
lexer.Add([]byte(`del`), opToken(deleteChildOpType))
|
lexer.Add([]byte(`del`), opToken(deleteChildOpType))
|
||||||
@@ -301,11 +322,13 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`\]`), literalToken(closeCollect, true))
|
lexer.Add([]byte(`\]`), literalToken(closeCollect, true))
|
||||||
lexer.Add([]byte(`\{`), literalToken(openCollectObject, false))
|
lexer.Add([]byte(`\{`), literalToken(openCollectObject, false))
|
||||||
lexer.Add([]byte(`\}`), literalToken(closeCollectObject, true))
|
lexer.Add([]byte(`\}`), literalToken(closeCollectObject, true))
|
||||||
lexer.Add([]byte(`\*[\+|\?]*`), multiplyWithPrefs())
|
lexer.Add([]byte(`\*[\+|\?d]*`), multiplyWithPrefs())
|
||||||
lexer.Add([]byte(`\+`), opToken(addOpType))
|
lexer.Add([]byte(`\+`), opToken(addOpType))
|
||||||
lexer.Add([]byte(`\+=`), opToken(addAssignOpType))
|
lexer.Add([]byte(`\+=`), opToken(addAssignOpType))
|
||||||
|
lexer.Add([]byte(`\$[a-zA-Z_-0-9]+`), getVariableOpToken())
|
||||||
|
lexer.Add([]byte(`as`), opToken(assignVariableOpType))
|
||||||
|
|
||||||
err := lexer.Compile()
|
err := lexer.CompileNFA()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -339,7 +362,7 @@ func (p *expressionTokeniserImpl) Tokenise(expression string) ([]*token, error)
|
|||||||
|
|
||||||
if tok != nil {
|
if tok != nil {
|
||||||
currentToken := tok.(*token)
|
currentToken := tok.(*token)
|
||||||
log.Debugf("Tokenising %v", currentToken.toString())
|
log.Debugf("Tokenising %v", currentToken.toString(true))
|
||||||
tokens = append(tokens, currentToken)
|
tokens = append(tokens, currentToken)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -369,6 +392,12 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
|
|||||||
//need to put a traverse array then a collect currentToken
|
//need to put a traverse array then a collect currentToken
|
||||||
// do this by adding traverse then converting currentToken to collect
|
// do this by adding traverse then converting currentToken to collect
|
||||||
|
|
||||||
|
if index == 0 || tokens[index-1].TokenType != operationToken ||
|
||||||
|
tokens[index-1].Operation.OperationType != traversePathOpType {
|
||||||
|
op := &Operation{OperationType: selfReferenceOpType, StringValue: "SELF"}
|
||||||
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
|
}
|
||||||
|
|
||||||
op := &Operation{OperationType: traverseArrayOpType, StringValue: "TRAVERSE_ARRAY"}
|
op := &Operation{OperationType: traverseArrayOpType, StringValue: "TRAVERSE_ARRAY"}
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
|
|
||||||
@@ -386,6 +415,14 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
|
|||||||
|
|
||||||
postProcessedTokens = append(postProcessedTokens, currentToken)
|
postProcessedTokens = append(postProcessedTokens, currentToken)
|
||||||
|
|
||||||
|
if index != len(tokens)-1 &&
|
||||||
|
((currentToken.TokenType == openCollect && tokens[index+1].TokenType == closeCollect) ||
|
||||||
|
(currentToken.TokenType == openCollectObject && tokens[index+1].TokenType == closeCollectObject)) {
|
||||||
|
|
||||||
|
op := &Operation{OperationType: emptyOpType, StringValue: "EMPTY"}
|
||||||
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
|
}
|
||||||
|
|
||||||
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
||||||
tokens[index+1].TokenType == operationToken &&
|
tokens[index+1].TokenType == operationToken &&
|
||||||
tokens[index+1].Operation.OperationType == traversePathOpType {
|
tokens[index+1].Operation.OperationType == traversePathOpType {
|
||||||
@@ -395,18 +432,8 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
|
|||||||
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
||||||
tokens[index+1].TokenType == openCollect {
|
tokens[index+1].TokenType == openCollect {
|
||||||
|
|
||||||
op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"}
|
op := &Operation{OperationType: traverseArrayOpType}
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
|
|
||||||
op = &Operation{OperationType: traverseArrayOpType}
|
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
|
||||||
}
|
|
||||||
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
|
||||||
tokens[index+1].TokenType == traverseArrayCollect {
|
|
||||||
|
|
||||||
op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"}
|
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return postProcessedTokens, skipNextToken
|
return postProcessedTokens, skipNextToken
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ type operationType struct {
|
|||||||
|
|
||||||
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
|
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
|
||||||
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
|
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
|
||||||
|
var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator}
|
||||||
|
|
||||||
|
var blockOpType = &operationType{Type: "BLOCK", Precedence: 10, NumArgs: 2, Handler: emptyOperator}
|
||||||
|
|
||||||
var unionOpType = &operationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: unionOperator}
|
var unionOpType = &operationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: unionOperator}
|
||||||
|
|
||||||
@@ -35,6 +38,7 @@ var addAssignOpType = &operationType{Type: "ADD_ASSIGN", NumArgs: 2, Precedence:
|
|||||||
|
|
||||||
var assignAttributesOpType = &operationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: assignAttributesOperator}
|
var assignAttributesOpType = &operationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: assignAttributesOperator}
|
||||||
var assignStyleOpType = &operationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: assignStyleOperator}
|
var assignStyleOpType = &operationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: assignStyleOperator}
|
||||||
|
var assignVariableOpType = &operationType{Type: "ASSIGN_VARIABLE", NumArgs: 2, Precedence: 40, Handler: assignVariableOperator}
|
||||||
var assignTagOpType = &operationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: assignTagOperator}
|
var assignTagOpType = &operationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: assignTagOperator}
|
||||||
var assignCommentOpType = &operationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: assignCommentsOperator}
|
var assignCommentOpType = &operationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: assignCommentsOperator}
|
||||||
var assignAnchorOpType = &operationType{Type: "ASSIGN_ANCHOR", NumArgs: 2, Precedence: 40, Handler: assignAnchorOperator}
|
var assignAnchorOpType = &operationType{Type: "ASSIGN_ANCHOR", NumArgs: 2, Precedence: 40, Handler: assignAnchorOperator}
|
||||||
@@ -45,6 +49,7 @@ var addOpType = &operationType{Type: "ADD", NumArgs: 2, Precedence: 42, Handler:
|
|||||||
var alternativeOpType = &operationType{Type: "ALTERNATIVE", NumArgs: 2, Precedence: 42, Handler: alternativeOperator}
|
var alternativeOpType = &operationType{Type: "ALTERNATIVE", NumArgs: 2, Precedence: 42, Handler: alternativeOperator}
|
||||||
|
|
||||||
var equalsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: equalsOperator}
|
var equalsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: equalsOperator}
|
||||||
|
var notEqualsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: notEqualsOperator}
|
||||||
var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 40, Handler: createMapOperator}
|
var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 40, Handler: createMapOperator}
|
||||||
|
|
||||||
var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence: 45, Handler: pipeOperator}
|
var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence: 45, Handler: pipeOperator}
|
||||||
@@ -52,6 +57,7 @@ var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence:
|
|||||||
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
||||||
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: collectOperator}
|
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: collectOperator}
|
||||||
var splitDocumentOpType = &operationType{Type: "SPLIT_DOC", NumArgs: 0, Precedence: 50, Handler: splitDocumentOperator}
|
var splitDocumentOpType = &operationType{Type: "SPLIT_DOC", NumArgs: 0, Precedence: 50, Handler: splitDocumentOperator}
|
||||||
|
var getVariableOpType = &operationType{Type: "GET_VARIABLE", NumArgs: 0, Precedence: 55, Handler: getVariableOperator}
|
||||||
var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator}
|
var getStyleOpType = &operationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: getStyleOperator}
|
||||||
var getTagOpType = &operationType{Type: "GET_TAG", NumArgs: 0, Precedence: 50, Handler: getTagOperator}
|
var getTagOpType = &operationType{Type: "GET_TAG", NumArgs: 0, Precedence: 50, Handler: getTagOperator}
|
||||||
var getCommentOpType = &operationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50, Handler: getCommentsOperator}
|
var getCommentOpType = &operationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50, Handler: getCommentsOperator}
|
||||||
@@ -70,14 +76,14 @@ var splitStringOpType = &operationType{Type: "SPLIT", NumArgs: 1, Precedence: 50
|
|||||||
var keysOpType = &operationType{Type: "KEYS", NumArgs: 0, Precedence: 50, Handler: keysOperator}
|
var keysOpType = &operationType{Type: "KEYS", NumArgs: 0, Precedence: 50, Handler: keysOperator}
|
||||||
|
|
||||||
var collectObjectOpType = &operationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: collectObjectOperator}
|
var collectObjectOpType = &operationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: collectObjectOperator}
|
||||||
var traversePathOpType = &operationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 50, Handler: traversePathOperator}
|
var traversePathOpType = &operationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 55, Handler: traversePathOperator}
|
||||||
var traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 1, Precedence: 50, Handler: traverseArrayOperator}
|
var traverseArrayOpType = &operationType{Type: "TRAVERSE_ARRAY", NumArgs: 2, Precedence: 50, Handler: traverseArrayOperator}
|
||||||
|
|
||||||
var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 50, Handler: selfOperator}
|
var selfReferenceOpType = &operationType{Type: "SELF", NumArgs: 0, Precedence: 55, Handler: selfOperator}
|
||||||
var valueOpType = &operationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: valueOperator}
|
var valueOpType = &operationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: valueOperator}
|
||||||
var envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler: envOperator}
|
var envOpType = &operationType{Type: "ENV", NumArgs: 0, Precedence: 50, Handler: envOperator}
|
||||||
var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator}
|
var notOpType = &operationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: notOperator}
|
||||||
var emptyOpType = &operationType{Type: "EMPTY", NumArgs: 50, Handler: emptyOperator}
|
var emptyOpType = &operationType{Type: "EMPTY", Precedence: 50, Handler: emptyOperator}
|
||||||
|
|
||||||
var recursiveDescentOpType = &operationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: recursiveDescentOperator}
|
var recursiveDescentOpType = &operationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: recursiveDescentOperator}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package yqlib
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"container/list"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
@@ -15,12 +14,12 @@ func createAddOp(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode {
|
|||||||
Rhs: rhs}
|
Rhs: rhs}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addAssignOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func addAssignOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
assignmentOp := &Operation{OperationType: assignOpType}
|
assignmentOp := &Operation{OperationType: assignOpType}
|
||||||
assignmentOp.UpdateAssign = false
|
assignmentOp.UpdateAssign = false
|
||||||
|
|
||||||
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: expressionNode.Lhs, Rhs: createAddOp(expressionNode.Lhs, expressionNode.Rhs)}
|
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: expressionNode.Lhs, Rhs: createAddOp(expressionNode.Lhs, expressionNode.Rhs)}
|
||||||
return d.GetMatchingNodes(matchingNodes, assignmentOpNode)
|
return d.GetMatchingNodes(context, assignmentOpNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func toNodes(candidate *CandidateNode) []*yaml.Node {
|
func toNodes(candidate *CandidateNode) []*yaml.Node {
|
||||||
@@ -37,19 +36,24 @@ func toNodes(candidate *CandidateNode) []*yaml.Node {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func addOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("Add operator")
|
log.Debugf("Add operator")
|
||||||
|
|
||||||
return crossFunction(d, matchingNodes, expressionNode, add)
|
return crossFunction(d, context, expressionNode, add)
|
||||||
}
|
}
|
||||||
|
|
||||||
func add(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
lhs.Node = unwrapDoc(lhs.Node)
|
lhs.Node = unwrapDoc(lhs.Node)
|
||||||
rhs.Node = unwrapDoc(rhs.Node)
|
rhs.Node = unwrapDoc(rhs.Node)
|
||||||
|
|
||||||
target := lhs.CreateChild(nil, &yaml.Node{})
|
|
||||||
lhsNode := lhs.Node
|
lhsNode := lhs.Node
|
||||||
|
|
||||||
|
if lhsNode.Tag == "!!null" {
|
||||||
|
return lhs.CreateChild(nil, rhs.Node), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
target := lhs.CreateChild(nil, &yaml.Node{})
|
||||||
|
|
||||||
switch lhsNode.Kind {
|
switch lhsNode.Kind {
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
return nil, fmt.Errorf("Maps not yet supported for addition")
|
return nil, fmt.Errorf("Maps not yet supported for addition")
|
||||||
|
|||||||
@@ -112,6 +112,14 @@ var addOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::{a: 4}\n",
|
"D0, P[], (doc)::{a: 4}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Add to null",
|
||||||
|
subdescription: "Adding to null simply returns the rhs",
|
||||||
|
expression: `null + "cat"`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!str)::cat\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddOperatorScenarios(t *testing.T) {
|
func TestAddOperatorScenarios(t *testing.T) {
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
)
|
|
||||||
|
|
||||||
// corssFunction no matches
|
// corssFunction no matches
|
||||||
// can boolean use crossfunction
|
// can boolean use crossfunction
|
||||||
|
|
||||||
func alternativeOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- alternative")
|
log.Debugf("-- alternative")
|
||||||
return crossFunction(d, matchingNodes, expressionNode, alternativeFunc)
|
return crossFunction(d, context, expressionNode, alternativeFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func alternativeFunc(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
lhs.Node = unwrapDoc(lhs.Node)
|
lhs.Node = unwrapDoc(lhs.Node)
|
||||||
rhs.Node = unwrapDoc(rhs.Node)
|
rhs.Node = unwrapDoc(rhs.Node)
|
||||||
log.Debugf("Alternative LHS: %v", lhs.Node.Tag)
|
log.Debugf("Alternative LHS: %v", lhs.Node.Tag)
|
||||||
|
|||||||
@@ -6,146 +6,146 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func assignAliasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
log.Debugf("AssignAlias operator!")
|
log.Debugf("AssignAlias operator!")
|
||||||
|
|
||||||
aliasName := ""
|
aliasName := ""
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
aliasName = rhs.Front().Value.(*CandidateNode).Node.Value
|
aliasName = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
log.Debugf("Setting aliasName : %v", candidate.GetKey())
|
log.Debugf("Setting aliasName : %v", candidate.GetKey())
|
||||||
|
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
aliasName = rhs.Front().Value.(*CandidateNode).Node.Value
|
aliasName = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate.Node.Kind = yaml.AliasNode
|
candidate.Node.Kind = yaml.AliasNode
|
||||||
candidate.Node.Value = aliasName
|
candidate.Node.Value = aliasName
|
||||||
}
|
}
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAliasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func getAliasOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("GetAlias operator!")
|
log.Debugf("GetAlias operator!")
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Node.Value, Tag: "!!str"}
|
node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Node.Value, Tag: "!!str"}
|
||||||
result := candidate.CreateChild(nil, node)
|
result := candidate.CreateChild(nil, node)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignAnchorOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func assignAnchorOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
log.Debugf("AssignAnchor operator!")
|
log.Debugf("AssignAnchor operator!")
|
||||||
|
|
||||||
anchorName := ""
|
anchorName := ""
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
anchorName = rhs.Front().Value.(*CandidateNode).Node.Value
|
anchorName = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
log.Debugf("Setting anchorName of : %v", candidate.GetKey())
|
log.Debugf("Setting anchorName of : %v", candidate.GetKey())
|
||||||
|
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
anchorName = rhs.Front().Value.(*CandidateNode).Node.Value
|
anchorName = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate.Node.Anchor = anchorName
|
candidate.Node.Anchor = anchorName
|
||||||
}
|
}
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAnchorOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func getAnchorOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("GetAnchor operator!")
|
log.Debugf("GetAnchor operator!")
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
anchor := candidate.Node.Anchor
|
anchor := candidate.Node.Anchor
|
||||||
node := &yaml.Node{Kind: yaml.ScalarNode, Value: anchor, Tag: "!!str"}
|
node := &yaml.Node{Kind: yaml.ScalarNode, Value: anchor, Tag: "!!str"}
|
||||||
result := candidate.CreateChild(nil, node)
|
result := candidate.CreateChild(nil, node)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func explodeOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func explodeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- ExplodeOperation")
|
log.Debugf("-- ExplodeOperation")
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
for childEl := rhs.Front(); childEl != nil; childEl = childEl.Next() {
|
for childEl := rhs.MatchingNodes.Front(); childEl != nil; childEl = childEl.Next() {
|
||||||
err = explodeNode(childEl.Value.(*CandidateNode).Node)
|
err = explodeNode(childEl.Value.(*CandidateNode).Node, context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchMap, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func explodeNode(node *yaml.Node) error {
|
func explodeNode(node *yaml.Node, context Context) error {
|
||||||
node.Anchor = ""
|
node.Anchor = ""
|
||||||
switch node.Kind {
|
switch node.Kind {
|
||||||
case yaml.SequenceNode, yaml.DocumentNode:
|
case yaml.SequenceNode, yaml.DocumentNode:
|
||||||
for index, contentNode := range node.Content {
|
for index, contentNode := range node.Content {
|
||||||
log.Debugf("exploding index %v", index)
|
log.Debugf("exploding index %v", index)
|
||||||
errorInContent := explodeNode(contentNode)
|
errorInContent := explodeNode(contentNode, context)
|
||||||
if errorInContent != nil {
|
if errorInContent != nil {
|
||||||
return errorInContent
|
return errorInContent
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ func explodeNode(node *yaml.Node) error {
|
|||||||
valueNode := node.Content[index+1]
|
valueNode := node.Content[index+1]
|
||||||
log.Debugf("traversing %v", keyNode.Value)
|
log.Debugf("traversing %v", keyNode.Value)
|
||||||
if keyNode.Value != "<<" {
|
if keyNode.Value != "<<" {
|
||||||
err := overrideEntry(node, keyNode, valueNode, index, newContent)
|
err := overrideEntry(node, keyNode, valueNode, index, context.ChildContext(newContent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -178,14 +178,14 @@ func explodeNode(node *yaml.Node) error {
|
|||||||
log.Debugf("an alias merge list!")
|
log.Debugf("an alias merge list!")
|
||||||
for index := 0; index < len(valueNode.Content); index = index + 1 {
|
for index := 0; index < len(valueNode.Content); index = index + 1 {
|
||||||
aliasNode := valueNode.Content[index]
|
aliasNode := valueNode.Content[index]
|
||||||
err := applyAlias(node, aliasNode.Alias, index, newContent)
|
err := applyAlias(node, aliasNode.Alias, index, context.ChildContext(newContent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("an alias merge!")
|
log.Debugf("an alias merge!")
|
||||||
err := applyAlias(node, valueNode.Alias, index, newContent)
|
err := applyAlias(node, valueNode.Alias, index, context.ChildContext(newContent))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ func explodeNode(node *yaml.Node) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyAlias(node *yaml.Node, alias *yaml.Node, aliasIndex int, newContent *list.List) error {
|
func applyAlias(node *yaml.Node, alias *yaml.Node, aliasIndex int, newContent Context) error {
|
||||||
if alias == nil {
|
if alias == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -221,15 +221,15 @@ func applyAlias(node *yaml.Node, alias *yaml.Node, aliasIndex int, newContent *l
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func overrideEntry(node *yaml.Node, key *yaml.Node, value *yaml.Node, startIndex int, newContent *list.List) error {
|
func overrideEntry(node *yaml.Node, key *yaml.Node, value *yaml.Node, startIndex int, newContent Context) error {
|
||||||
|
|
||||||
err := explodeNode(value)
|
err := explodeNode(value, newContent)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for newEl := newContent.Front(); newEl != nil; newEl = newEl.Next() {
|
for newEl := newContent.MatchingNodes.Front(); newEl != nil; newEl = newEl.Next() {
|
||||||
valueEl := newEl.Next() // move forward twice
|
valueEl := newEl.Next() // move forward twice
|
||||||
keyNode := newEl.Value.(*yaml.Node)
|
keyNode := newEl.Value.(*yaml.Node)
|
||||||
log.Debugf("checking new content %v:%v", keyNode.Value, valueEl.Value.(*yaml.Node).Value)
|
log.Debugf("checking new content %v:%v", keyNode.Value, valueEl.Value.(*yaml.Node).Value)
|
||||||
@@ -250,12 +250,12 @@ func overrideEntry(node *yaml.Node, key *yaml.Node, value *yaml.Node, startIndex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = explodeNode(key)
|
err = explodeNode(key, newContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Debugf("adding %v:%v", key.Value, value.Value)
|
log.Debugf("adding %v:%v", key.Value, value.Value)
|
||||||
newContent.PushBack(key)
|
newContent.MatchingNodes.PushBack(key)
|
||||||
newContent.PushBack(value)
|
newContent.MatchingNodes.PushBack(value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,28 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import "container/list"
|
func assignUpdateOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
func assignUpdateOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
|
||||||
lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
var rhs *list.List
|
var rhs Context
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err = d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
rhs, err = d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err = d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
rhs, err = d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab the first value
|
// grab the first value
|
||||||
first := rhs.Front()
|
first := rhs.MatchingNodes.Front()
|
||||||
|
|
||||||
if first != nil {
|
if first != nil {
|
||||||
rhsCandidate := first.Value.(*CandidateNode)
|
rhsCandidate := first.Value.(*CandidateNode)
|
||||||
@@ -33,30 +31,31 @@ func assignUpdateOperator(d *dataTreeNavigator, matchingNodes *list.List, expres
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// does not update content or values
|
// does not update content or values
|
||||||
func assignAttributesOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func assignAttributesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs)
|
log.Debug("getting lhs matching nodes for update")
|
||||||
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab the first value
|
// grab the first value
|
||||||
first := rhs.Front()
|
first := rhs.MatchingNodes.Front()
|
||||||
|
|
||||||
if first != nil {
|
if first != nil {
|
||||||
candidate.UpdateAttributesFrom(first.Value.(*CandidateNode))
|
candidate.UpdateAttributesFrom(first.Value.(*CandidateNode))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ func isTruthy(c *CandidateNode) (bool, error) {
|
|||||||
|
|
||||||
type boolOp func(bool, bool) bool
|
type boolOp func(bool, bool) bool
|
||||||
|
|
||||||
func performBoolOp(op boolOp) func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func performBoolOp(op boolOp) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
return func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
lhs.Node = unwrapDoc(lhs.Node)
|
lhs.Node = unwrapDoc(lhs.Node)
|
||||||
rhs.Node = unwrapDoc(rhs.Node)
|
rhs.Node = unwrapDoc(rhs.Node)
|
||||||
|
|
||||||
@@ -44,35 +44,35 @@ func performBoolOp(op boolOp) func(d *dataTreeNavigator, lhs *CandidateNode, rhs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func orOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func orOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- orOp")
|
log.Debugf("-- orOp")
|
||||||
return crossFunction(d, matchingNodes, expressionNode, performBoolOp(
|
return crossFunction(d, context, expressionNode, performBoolOp(
|
||||||
func(b1 bool, b2 bool) bool {
|
func(b1 bool, b2 bool) bool {
|
||||||
return b1 || b2
|
return b1 || b2
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func andOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- AndOp")
|
log.Debugf("-- AndOp")
|
||||||
return crossFunction(d, matchingNodes, expressionNode, performBoolOp(
|
return crossFunction(d, context, expressionNode, performBoolOp(
|
||||||
func(b1 bool, b2 bool) bool {
|
func(b1 bool, b2 bool) bool {
|
||||||
return b1 && b2
|
return b1 && b2
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func notOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func notOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- notOperation")
|
log.Debugf("-- notOperation")
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
log.Debug("notOperation checking %v", candidate)
|
log.Debug("notOperation checking %v", candidate)
|
||||||
truthy, errDecoding := isTruthy(candidate)
|
truthy, errDecoding := isTruthy(candidate)
|
||||||
if errDecoding != nil {
|
if errDecoding != nil {
|
||||||
return nil, errDecoding
|
return Context{}, errDecoding
|
||||||
}
|
}
|
||||||
result := createBooleanCandidate(candidate, !truthy)
|
result := createBooleanCandidate(candidate, !truthy)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,21 +6,21 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func collectOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func collectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- collectOperation")
|
log.Debugf("-- collectOperation")
|
||||||
|
|
||||||
if matchMap.Len() == 0 {
|
if context.MatchingNodes.Len() == 0 {
|
||||||
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq", Value: "[]"}
|
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq", Value: "[]"}
|
||||||
candidate := &CandidateNode{Node: node}
|
candidate := &CandidateNode{Node: node}
|
||||||
return nodeToMap(candidate), nil
|
return context.SingleChildContext(candidate), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
|
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
|
||||||
var collectC *CandidateNode
|
var collectC *CandidateNode
|
||||||
if matchMap.Front() != nil {
|
if context.MatchingNodes.Front() != nil {
|
||||||
collectC = matchMap.Front().Value.(*CandidateNode).CreateChild(nil, node)
|
collectC = context.MatchingNodes.Front().Value.(*CandidateNode).CreateChild(nil, node)
|
||||||
if len(collectC.Path) > 0 {
|
if len(collectC.Path) > 0 {
|
||||||
collectC.Path = collectC.Path[:len(collectC.Path)-1]
|
collectC.Path = collectC.Path[:len(collectC.Path)-1]
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ func collectOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *
|
|||||||
collectC = &CandidateNode{Node: node}
|
collectC = &CandidateNode{Node: node}
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
log.Debugf("Collecting %v", NodeToString(candidate))
|
log.Debugf("Collecting %v", NodeToString(candidate))
|
||||||
node.Content = append(node.Content, unwrapDoc(candidate.Node))
|
node.Content = append(node.Content, unwrapDoc(candidate.Node))
|
||||||
@@ -36,5 +36,5 @@ func collectOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *
|
|||||||
|
|
||||||
results.PushBack(collectC)
|
results.PushBack(collectC)
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,22 +17,22 @@ import (
|
|||||||
...
|
...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func collectObjectOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func collectObjectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- collectObjectOperation")
|
log.Debugf("-- collectObjectOperation")
|
||||||
|
|
||||||
if matchMap.Len() == 0 {
|
if context.MatchingNodes.Len() == 0 {
|
||||||
node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Value: "{}"}
|
node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Value: "{}"}
|
||||||
candidate := &CandidateNode{Node: node}
|
candidate := &CandidateNode{Node: node}
|
||||||
return nodeToMap(candidate), nil
|
return context.SingleChildContext(candidate), nil
|
||||||
}
|
}
|
||||||
first := matchMap.Front().Value.(*CandidateNode)
|
first := context.MatchingNodes.Front().Value.(*CandidateNode)
|
||||||
var rotated []*list.List = make([]*list.List, len(first.Node.Content))
|
var rotated []*list.List = make([]*list.List, len(first.Node.Content))
|
||||||
|
|
||||||
for i := 0; i < len(first.Node.Content); i++ {
|
for i := 0; i < len(first.Node.Content); i++ {
|
||||||
rotated[i] = list.New()
|
rotated[i] = list.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidateNode := el.Value.(*CandidateNode)
|
candidateNode := el.Value.(*CandidateNode)
|
||||||
for i := 0; i < len(first.Node.Content); i++ {
|
for i := 0; i < len(first.Node.Content); i++ {
|
||||||
rotated[i].PushBack(candidateNode.CreateChild(i, candidateNode.Node.Content[i]))
|
rotated[i].PushBack(candidateNode.CreateChild(i, candidateNode.Node.Content[i]))
|
||||||
@@ -41,59 +41,59 @@ func collectObjectOperator(d *dataTreeNavigator, matchMap *list.List, expression
|
|||||||
|
|
||||||
newObject := list.New()
|
newObject := list.New()
|
||||||
for i := 0; i < len(first.Node.Content); i++ {
|
for i := 0; i < len(first.Node.Content); i++ {
|
||||||
additions, err := collect(d, list.New(), rotated[i])
|
additions, err := collect(d, context.ChildContext(list.New()), rotated[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
newObject.PushBackList(additions)
|
newObject.PushBackList(additions.MatchingNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
return newObject, nil
|
return context.ChildContext(newObject), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func collect(d *dataTreeNavigator, aggregate *list.List, remainingMatches *list.List) (*list.List, error) {
|
func collect(d *dataTreeNavigator, context Context, remainingMatches *list.List) (Context, error) {
|
||||||
if remainingMatches.Len() == 0 {
|
if remainingMatches.Len() == 0 {
|
||||||
return aggregate, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate := remainingMatches.Remove(remainingMatches.Front()).(*CandidateNode)
|
candidate := remainingMatches.Remove(remainingMatches.Front()).(*CandidateNode)
|
||||||
|
|
||||||
splatted, err := splat(d, nodeToMap(candidate),
|
splatted, err := splat(d, context.SingleChildContext(candidate),
|
||||||
traversePreferences{DontFollowAlias: true, IncludeMapKeys: false})
|
traversePreferences{DontFollowAlias: true, IncludeMapKeys: false})
|
||||||
|
|
||||||
for splatEl := splatted.Front(); splatEl != nil; splatEl = splatEl.Next() {
|
for splatEl := splatted.MatchingNodes.Front(); splatEl != nil; splatEl = splatEl.Next() {
|
||||||
splatEl.Value.(*CandidateNode).Path = nil
|
splatEl.Value.(*CandidateNode).Path = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if aggregate.Len() == 0 {
|
if context.MatchingNodes.Len() == 0 {
|
||||||
return collect(d, splatted, remainingMatches)
|
return collect(d, splatted, remainingMatches)
|
||||||
}
|
}
|
||||||
|
|
||||||
newAgg := list.New()
|
newAgg := list.New()
|
||||||
|
|
||||||
for el := aggregate.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
aggCandidate := el.Value.(*CandidateNode)
|
aggCandidate := el.Value.(*CandidateNode)
|
||||||
for splatEl := splatted.Front(); splatEl != nil; splatEl = splatEl.Next() {
|
for splatEl := splatted.MatchingNodes.Front(); splatEl != nil; splatEl = splatEl.Next() {
|
||||||
splatCandidate := splatEl.Value.(*CandidateNode)
|
splatCandidate := splatEl.Value.(*CandidateNode)
|
||||||
newCandidate, err := aggCandidate.Copy()
|
newCandidate, err := aggCandidate.Copy()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newCandidate.Path = nil
|
newCandidate.Path = nil
|
||||||
|
|
||||||
newCandidate, err = multiply(multiplyPreferences{AppendArrays: false})(d, newCandidate, splatCandidate)
|
newCandidate, err = multiply(multiplyPreferences{AppendArrays: false})(d, context, newCandidate, splatCandidate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
newAgg.PushBack(newCandidate)
|
newAgg.PushBack(newCandidate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return collect(d, newAgg, remainingMatches)
|
return collect(d, context.ChildContext(newAgg), remainingMatches)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,41 +13,41 @@ type commentOpPreferences struct {
|
|||||||
FootComment bool
|
FootComment bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func assignCommentsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
log.Debugf("AssignComments operator!")
|
log.Debugf("AssignComments operator!")
|
||||||
|
|
||||||
lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences := expressionNode.Operation.Preferences.(commentOpPreferences)
|
preferences := expressionNode.Operation.Preferences.(commentOpPreferences)
|
||||||
|
|
||||||
comment := ""
|
comment := ""
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
comment = rhs.Front().Value.(*CandidateNode).Node.Value
|
comment = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
comment = rhs.Front().Value.(*CandidateNode).Node.Value
|
comment = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,15 +63,15 @@ func assignCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, expr
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func getCommentsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
preferences := expressionNode.Operation.Preferences.(commentOpPreferences)
|
preferences := expressionNode.Operation.Preferences.(commentOpPreferences)
|
||||||
log.Debugf("GetComments operator!")
|
log.Debugf("GetComments operator!")
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
comment := ""
|
comment := ""
|
||||||
if preferences.LineComment {
|
if preferences.LineComment {
|
||||||
@@ -87,5 +87,5 @@ func getCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, express
|
|||||||
result := candidate.CreateChild(nil, node)
|
result := candidate.CreateChild(nil, node)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createMapOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func createMapOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- createMapOperation")
|
log.Debugf("-- createMapOperation")
|
||||||
|
|
||||||
//each matchingNodes entry should turn into a sequence of keys to create.
|
//each matchingNodes entry should turn into a sequence of keys to create.
|
||||||
@@ -18,29 +18,29 @@ func createMapOperator(d *dataTreeNavigator, matchingNodes *list.List, expressio
|
|||||||
|
|
||||||
sequences := list.New()
|
sequences := list.New()
|
||||||
|
|
||||||
if matchingNodes.Len() > 0 {
|
if context.MatchingNodes.Len() > 0 {
|
||||||
|
|
||||||
for matchingNodeEl := matchingNodes.Front(); matchingNodeEl != nil; matchingNodeEl = matchingNodeEl.Next() {
|
for matchingNodeEl := context.MatchingNodes.Front(); matchingNodeEl != nil; matchingNodeEl = matchingNodeEl.Next() {
|
||||||
matchingNode := matchingNodeEl.Value.(*CandidateNode)
|
matchingNode := matchingNodeEl.Value.(*CandidateNode)
|
||||||
sequenceNode, err := sequenceFor(d, matchingNode, expressionNode)
|
sequenceNode, err := sequenceFor(d, context, matchingNode, expressionNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
sequences.PushBack(sequenceNode)
|
sequences.PushBack(sequenceNode)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sequenceNode, err := sequenceFor(d, nil, expressionNode)
|
sequenceNode, err := sequenceFor(d, context, nil, expressionNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
sequences.PushBack(sequenceNode)
|
sequences.PushBack(sequenceNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeToMap(&CandidateNode{Node: listToNodeSeq(sequences), Document: document, Path: path}), nil
|
return context.SingleChildContext(&CandidateNode{Node: listToNodeSeq(sequences), Document: document, Path: path}), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sequenceFor(d *dataTreeNavigator, matchingNode *CandidateNode, expressionNode *ExpressionNode) (*CandidateNode, error) {
|
func sequenceFor(d *dataTreeNavigator, context Context, matchingNode *CandidateNode, expressionNode *ExpressionNode) (*CandidateNode, error) {
|
||||||
var path []interface{}
|
var path []interface{}
|
||||||
var document uint = 0
|
var document uint = 0
|
||||||
var matches = list.New()
|
var matches = list.New()
|
||||||
@@ -48,11 +48,11 @@ func sequenceFor(d *dataTreeNavigator, matchingNode *CandidateNode, expressionNo
|
|||||||
if matchingNode != nil {
|
if matchingNode != nil {
|
||||||
path = matchingNode.Path
|
path = matchingNode.Path
|
||||||
document = matchingNode.Document
|
document = matchingNode.Document
|
||||||
matches = nodeToMap(matchingNode)
|
matches.PushBack(matchingNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
mapPairs, err := crossFunction(d, matches, expressionNode,
|
mapPairs, err := crossFunction(d, context.ChildContext(matches), expressionNode,
|
||||||
func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
node := yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
node := yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
||||||
log.Debugf("LHS:", NodeToString(lhs))
|
log.Debugf("LHS:", NodeToString(lhs))
|
||||||
log.Debugf("RHS:", NodeToString(rhs))
|
log.Debugf("RHS:", NodeToString(rhs))
|
||||||
@@ -67,7 +67,7 @@ func sequenceFor(d *dataTreeNavigator, matchingNode *CandidateNode, expressionNo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
innerList := listToNodeSeq(mapPairs)
|
innerList := listToNodeSeq(mapPairs.MatchingNodes)
|
||||||
innerList.Style = yaml.FlowStyle
|
innerList.Style = yaml.FlowStyle
|
||||||
return &CandidateNode{Node: innerList, Document: document, Path: path}, nil
|
return &CandidateNode{Node: innerList, Document: document, Path: path}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,55 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func deleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
contextToUse := context.Clone()
|
||||||
nodesToDelete, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
contextToUse.DontAutoCreate = true
|
||||||
|
nodesToDelete, err := d.GetMatchingNodes(contextToUse, expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
//need to iterate backwards to ensure correct indices when deleting multiple
|
||||||
for el := nodesToDelete.Front(); el != nil; el = el.Next() {
|
for el := nodesToDelete.MatchingNodes.Back(); el != nil; el = el.Prev() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
deleteImmediateChildOp := &Operation{
|
if len(candidate.Path) > 0 {
|
||||||
OperationType: deleteImmediateChildOpType,
|
deleteImmediateChildOp := &Operation{
|
||||||
Value: candidate.Path[len(candidate.Path)-1],
|
OperationType: deleteImmediateChildOpType,
|
||||||
}
|
Value: candidate.Path[len(candidate.Path)-1],
|
||||||
|
}
|
||||||
|
|
||||||
deleteImmediateChildOpNode := &ExpressionNode{
|
deleteImmediateChildOpNode := &ExpressionNode{
|
||||||
Operation: deleteImmediateChildOp,
|
Operation: deleteImmediateChildOp,
|
||||||
Rhs: createTraversalTree(candidate.Path[0:len(candidate.Path)-1], traversePreferences{}),
|
Rhs: createTraversalTree(candidate.Path[0:len(candidate.Path)-1], traversePreferences{}, false),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := d.GetMatchingNodes(matchingNodes, deleteImmediateChildOpNode)
|
_, err := d.GetMatchingNodes(contextToUse, deleteImmediateChildOpNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteImmediateChildOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func deleteImmediateChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
parents, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
parents, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
childPath := expressionNode.Operation.Value
|
childPath := expressionNode.Operation.Value
|
||||||
|
|
||||||
log.Debug("childPath to remove %v", childPath)
|
log.Debug("childPath to remove %v", childPath)
|
||||||
|
|
||||||
for el := parents.Front(); el != nil; el = el.Next() {
|
for el := parents.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
parent := el.Value.(*CandidateNode)
|
parent := el.Value.(*CandidateNode)
|
||||||
parentNode := unwrapDoc(parent.Node)
|
parentNode := unwrapDoc(parent.Node)
|
||||||
if parentNode.Kind == yaml.MappingNode {
|
if parentNode.Kind == yaml.MappingNode {
|
||||||
@@ -55,11 +57,11 @@ func deleteImmediateChildOperator(d *dataTreeNavigator, matchingNodes *list.List
|
|||||||
} else if parentNode.Kind == yaml.SequenceNode {
|
} else if parentNode.Kind == yaml.SequenceNode {
|
||||||
deleteFromArray(parent, childPath)
|
deleteFromArray(parent, childPath)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("Cannot delete nodes from parent of tag %v", parentNode.Tag)
|
return Context{}, fmt.Errorf("Cannot delete nodes from parent of tag %v", parentNode.Tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteFromMap(candidate *CandidateNode, childPath interface{}) {
|
func deleteFromMap(candidate *CandidateNode, childPath interface{}) {
|
||||||
|
|||||||
@@ -37,6 +37,38 @@ var deleteOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::[1, 3]\n",
|
"D0, P[], (doc)::[1, 3]\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `a: [1,2,3]`,
|
||||||
|
expression: `del(.a[])`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: []\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `a: [10,x,10, 10, x, 10]`,
|
||||||
|
expression: `del(.a[] | select(. == 10))`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::a: [x, x]\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `a: {thing1: yep, thing2: cool, thing3: hi, b: {thing1: cool, great: huh}}`,
|
||||||
|
expression: `del(..)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::{}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `a: {thing1: yep, thing2: cool, thing3: hi, b: {thing1: cool, great: huh}}`,
|
||||||
|
expression: `del(.. | select(tag == "!!map") | (.b.thing1,.thing2))`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::a: {thing1: yep, thing3: hi, b: {great: huh}}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Delete nested entry in array",
|
description: "Delete nested entry in array",
|
||||||
document: `[{a: cat, b: dog}]`,
|
document: `[{a: cat, b: dog}]`,
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDocumentIndexOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func getDocumentIndexOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Document), Tag: "!!int"}
|
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Document), Tag: "!!int"}
|
||||||
scalar := candidate.CreateChild(nil, node)
|
scalar := candidate.CreateChild(nil, node)
|
||||||
results.PushBack(scalar)
|
results.PushBack(scalar)
|
||||||
}
|
}
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -13,7 +12,7 @@ type envOpPreferences struct {
|
|||||||
StringValue bool
|
StringValue bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func envOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func envOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
envName := expressionNode.Operation.CandidateNode.Node.Value
|
envName := expressionNode.Operation.CandidateNode.Node.Value
|
||||||
log.Debug("EnvOperator, env name:", envName)
|
log.Debug("EnvOperator, env name:", envName)
|
||||||
|
|
||||||
@@ -29,13 +28,13 @@ func envOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *Expr
|
|||||||
Value: rawValue,
|
Value: rawValue,
|
||||||
}
|
}
|
||||||
} else if rawValue == "" {
|
} else if rawValue == "" {
|
||||||
return nil, fmt.Errorf("Value for env variable '%v' not provided in env()", envName)
|
return Context{}, fmt.Errorf("Value for env variable '%v' not provided in env()", envName)
|
||||||
} else {
|
} else {
|
||||||
var dataBucket yaml.Node
|
var dataBucket yaml.Node
|
||||||
decoder := yaml.NewDecoder(strings.NewReader(rawValue))
|
decoder := yaml.NewDecoder(strings.NewReader(rawValue))
|
||||||
errorReading := decoder.Decode(&dataBucket)
|
errorReading := decoder.Decode(&dataBucket)
|
||||||
if errorReading != nil {
|
if errorReading != nil {
|
||||||
return nil, errorReading
|
return Context{}, errorReading
|
||||||
}
|
}
|
||||||
//first node is a doc
|
//first node is a doc
|
||||||
node = unwrapDoc(&dataBucket)
|
node = unwrapDoc(&dataBucket)
|
||||||
@@ -46,5 +45,5 @@ func envOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *Expr
|
|||||||
|
|
||||||
target := &CandidateNode{Node: node}
|
target := &CandidateNode{Node: node}
|
||||||
|
|
||||||
return nodeToMap(target), nil
|
return context.SingleChildContext(target), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,33 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import (
|
import "gopkg.in/yaml.v3"
|
||||||
"container/list"
|
|
||||||
)
|
|
||||||
|
|
||||||
func equalsOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- equalsOperation")
|
log.Debugf("-- equalsOperation")
|
||||||
return crossFunction(d, matchingNodes, expressionNode, isEquals)
|
return crossFunction(d, context, expressionNode, isEquals(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEquals(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
value := false
|
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
|
value := false
|
||||||
|
|
||||||
lhsNode := unwrapDoc(lhs.Node)
|
lhsNode := unwrapDoc(lhs.Node)
|
||||||
rhsNode := unwrapDoc(rhs.Node)
|
rhsNode := unwrapDoc(rhs.Node)
|
||||||
|
|
||||||
if lhsNode.Tag == "!!null" {
|
if lhsNode.Tag == "!!null" {
|
||||||
value = (rhsNode.Tag == "!!null")
|
value = (rhsNode.Tag == "!!null")
|
||||||
} else {
|
} else if lhsNode.Kind == yaml.ScalarNode && rhsNode.Kind == yaml.ScalarNode {
|
||||||
value = matchKey(lhsNode.Value, rhsNode.Value)
|
value = matchKey(lhsNode.Value, rhsNode.Value)
|
||||||
|
}
|
||||||
|
log.Debugf("%v == %v ? %v", NodeToString(lhs), NodeToString(rhs), value)
|
||||||
|
if flip {
|
||||||
|
value = !value
|
||||||
|
}
|
||||||
|
return createBooleanCandidate(lhs, value), nil
|
||||||
}
|
}
|
||||||
log.Debugf("%v == %v ? %v", NodeToString(lhs), NodeToString(rhs), value)
|
}
|
||||||
return createBooleanCandidate(lhs, value), nil
|
|
||||||
|
func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
log.Debugf("-- equalsOperation")
|
||||||
|
return crossFunction(d, context, expressionNode, isEquals(true))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ var equalsOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!bool)::false\n",
|
"D0, P[], (!!bool)::false\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: "{a: { b: {things: \"\"}, f: [1], g: [] }}",
|
||||||
|
expression: ".. | select(. == \"\")",
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a b things], (!!str)::\"\"\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Match string",
|
description: "Match string",
|
||||||
document: `[cat,goat,dog]`,
|
document: `[cat,goat,dog]`,
|
||||||
@@ -23,7 +31,18 @@ var equalsOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[1], (!!bool)::true\n",
|
"D0, P[1], (!!bool)::true\n",
|
||||||
"D0, P[2], (!!bool)::false\n",
|
"D0, P[2], (!!bool)::false\n",
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
|
description: "Don't match string",
|
||||||
|
document: `[cat,goat,dog]`,
|
||||||
|
expression: `.[] | (. != "*at")`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[0], (!!bool)::false\n",
|
||||||
|
"D0, P[1], (!!bool)::false\n",
|
||||||
|
"D0, P[2], (!!bool)::true\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
description: "Match number",
|
description: "Match number",
|
||||||
document: `[3, 4, 5]`,
|
document: `[3, 4, 5]`,
|
||||||
expression: `.[] | (. == 4)`,
|
expression: `.[] | (. == 4)`,
|
||||||
@@ -32,7 +51,18 @@ var equalsOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[1], (!!bool)::true\n",
|
"D0, P[1], (!!bool)::true\n",
|
||||||
"D0, P[2], (!!bool)::false\n",
|
"D0, P[2], (!!bool)::false\n",
|
||||||
},
|
},
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
|
description: "Dont match number",
|
||||||
|
document: `[3, 4, 5]`,
|
||||||
|
expression: `.[] | (. != 4)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[0], (!!bool)::true\n",
|
||||||
|
"D0, P[1], (!!bool)::false\n",
|
||||||
|
"D0, P[2], (!!bool)::true\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `a: { cat: {b: apple, c: whatever}, pat: {b: banana} }`,
|
document: `a: { cat: {b: apple, c: whatever}, pat: {b: banana} }`,
|
||||||
expression: `.a | (.[].b == "apple")`,
|
expression: `.a | (.[].b == "apple")`,
|
||||||
|
|||||||
@@ -7,32 +7,32 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getFilenameOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func getFilenameOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("GetFilename")
|
log.Debugf("GetFilename")
|
||||||
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Filename, Tag: "!!str"}
|
node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Filename, Tag: "!!str"}
|
||||||
result := candidate.CreateChild(nil, node)
|
result := candidate.CreateChild(nil, node)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFileIndexOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func getFileIndexOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("GetFileIndex")
|
log.Debugf("GetFileIndex")
|
||||||
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.FileIndex), Tag: "!!int"}
|
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.FileIndex), Tag: "!!int"}
|
||||||
result := candidate.CreateChild(nil, node)
|
result := candidate.CreateChild(nil, node)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,16 @@ var fileOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!int)::0\n",
|
"D0, P[], (!!int)::0\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Get file indices of multiple documents",
|
||||||
|
document: `{a: cat}`,
|
||||||
|
document2: `{a: cat}`,
|
||||||
|
expression: `fileIndex`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!int)::0\n",
|
||||||
|
"D0, P[], (!!int)::1\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Get file index alias",
|
description: "Get file index alias",
|
||||||
document: `{a: cat}`,
|
document: `{a: cat}`,
|
||||||
|
|||||||
@@ -7,20 +7,20 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func hasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func hasOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
log.Debugf("-- hasOperation")
|
log.Debugf("-- hasOperation")
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
wanted := rhs.Front().Value.(*CandidateNode).Node
|
wanted := rhs.MatchingNodes.Front().Value.(*CandidateNode).Node
|
||||||
wantedKey := wanted.Value
|
wantedKey := wanted.Value
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
// grab the first value
|
// grab the first value
|
||||||
@@ -41,7 +41,7 @@ func hasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode
|
|||||||
if wanted.Tag == "!!int" {
|
if wanted.Tag == "!!int" {
|
||||||
var number, errParsingInt = strconv.ParseInt(wantedKey, 10, 64) // nolint
|
var number, errParsingInt = strconv.ParseInt(wantedKey, 10, 64) // nolint
|
||||||
if errParsingInt != nil {
|
if errParsingInt != nil {
|
||||||
return nil, errParsingInt
|
return Context{}, errParsingInt
|
||||||
}
|
}
|
||||||
candidateHasKey = int64(len(contents)) > number
|
candidateHasKey = int64(len(contents)) > number
|
||||||
}
|
}
|
||||||
@@ -50,5 +50,5 @@ func hasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode
|
|||||||
results.PushBack(createBooleanCandidate(candidate, false))
|
results.PushBack(createBooleanCandidate(candidate, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func keysOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func keysOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- keysOperator")
|
log.Debugf("-- keysOperator")
|
||||||
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := unwrapDoc(candidate.Node)
|
node := unwrapDoc(candidate.Node)
|
||||||
var targetNode *yaml.Node
|
var targetNode *yaml.Node
|
||||||
@@ -21,14 +21,14 @@ func keysOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *Exp
|
|||||||
} else if node.Kind == yaml.SequenceNode {
|
} else if node.Kind == yaml.SequenceNode {
|
||||||
targetNode = getIndicies(node)
|
targetNode = getIndicies(node)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("Cannot get keys of %v, keys only works for maps and arrays", node.Tag)
|
return Context{}, fmt.Errorf("Cannot get keys of %v, keys only works for maps and arrays", node.Tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := candidate.CreateChild(nil, targetNode)
|
result := candidate.CreateChild(nil, targetNode)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMapKeys(node *yaml.Node) *yaml.Node {
|
func getMapKeys(node *yaml.Node) *yaml.Node {
|
||||||
|
|||||||
@@ -7,17 +7,21 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func lengthOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func lengthOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- lengthOperation")
|
log.Debugf("-- lengthOperation")
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
targetNode := unwrapDoc(candidate.Node)
|
targetNode := unwrapDoc(candidate.Node)
|
||||||
var length int
|
var length int
|
||||||
switch targetNode.Kind {
|
switch targetNode.Kind {
|
||||||
case yaml.ScalarNode:
|
case yaml.ScalarNode:
|
||||||
length = len(targetNode.Value)
|
if targetNode.Tag == "!!null" {
|
||||||
|
length = 0
|
||||||
|
} else {
|
||||||
|
length = len(targetNode.Value)
|
||||||
|
}
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
length = len(targetNode.Content) / 2
|
length = len(targetNode.Content) / 2
|
||||||
case yaml.SequenceNode:
|
case yaml.SequenceNode:
|
||||||
@@ -31,5 +35,5 @@ func lengthOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *E
|
|||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,30 @@ var lengthOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[a], (!!int)::3\n",
|
"D0, P[a], (!!int)::3\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "null length",
|
||||||
|
document: `{a: null}`,
|
||||||
|
expression: `.a | length`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a], (!!int)::0\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `{a: ~}`,
|
||||||
|
expression: `.a | length`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a], (!!int)::0\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `{a: key no exist}`,
|
||||||
|
expression: `.b | length`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[b], (!!int)::0\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Map length",
|
description: "Map length",
|
||||||
subdescription: "returns number of entries",
|
subdescription: "returns number of entries",
|
||||||
|
|||||||
@@ -9,77 +9,19 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type crossFunctionCalculation func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
|
|
||||||
|
|
||||||
func doCrossFunc(d *dataTreeNavigator, contextList *list.List, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (*list.List, error) {
|
|
||||||
var results = list.New()
|
|
||||||
lhs, err := d.GetMatchingNodes(contextList, expressionNode.Lhs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
log.Debugf("crossFunction LHS len: %v", lhs.Len())
|
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(contextList, expressionNode.Rhs)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
|
||||||
lhsCandidate := el.Value.(*CandidateNode)
|
|
||||||
|
|
||||||
for rightEl := rhs.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
|
||||||
log.Debugf("Applying calc")
|
|
||||||
rhsCandidate := rightEl.Value.(*CandidateNode)
|
|
||||||
resultCandidate, err := calculation(d, lhsCandidate, rhsCandidate)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
results.PushBack(resultCandidate)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func crossFunction(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (*list.List, error) {
|
|
||||||
var results = list.New()
|
|
||||||
|
|
||||||
var evaluateAllTogether = true
|
|
||||||
for matchEl := matchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
|
|
||||||
evaluateAllTogether = evaluateAllTogether && matchEl.Value.(*CandidateNode).EvaluateTogether
|
|
||||||
if !evaluateAllTogether {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if evaluateAllTogether {
|
|
||||||
return doCrossFunc(d, matchingNodes, expressionNode, calculation)
|
|
||||||
}
|
|
||||||
|
|
||||||
for matchEl := matchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
|
|
||||||
contextList := nodeToMap(matchEl.Value.(*CandidateNode))
|
|
||||||
innerResults, err := doCrossFunc(d, contextList, expressionNode, calculation)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
results.PushBackList(innerResults)
|
|
||||||
}
|
|
||||||
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type multiplyPreferences struct {
|
type multiplyPreferences struct {
|
||||||
AppendArrays bool
|
AppendArrays bool
|
||||||
TraversePrefs traversePreferences
|
DeepMergeArrays bool
|
||||||
|
TraversePrefs traversePreferences
|
||||||
}
|
}
|
||||||
|
|
||||||
func multiplyOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func multiplyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- MultiplyOperator")
|
log.Debugf("-- MultiplyOperator")
|
||||||
return crossFunction(d, matchingNodes, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences)))
|
return crossFunction(d, context, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
return func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
lhs.Node = unwrapDoc(lhs.Node)
|
lhs.Node = unwrapDoc(lhs.Node)
|
||||||
rhs.Node = unwrapDoc(rhs.Node)
|
rhs.Node = unwrapDoc(rhs.Node)
|
||||||
log.Debugf("Multipling LHS: %v", lhs.Node.Tag)
|
log.Debugf("Multipling LHS: %v", lhs.Node.Tag)
|
||||||
@@ -89,18 +31,38 @@ func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, lhs *C
|
|||||||
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
|
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
|
||||||
|
|
||||||
var newBlank = lhs.CreateChild(nil, &yaml.Node{})
|
var newBlank = lhs.CreateChild(nil, &yaml.Node{})
|
||||||
var newThing, err = mergeObjects(d, newBlank, lhs, multiplyPreferences{})
|
var newThing, err = mergeObjects(d, context, newBlank, lhs, multiplyPreferences{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return mergeObjects(d, newThing, rhs, preferences)
|
return mergeObjects(d, context, newThing, rhs, preferences)
|
||||||
} else if lhs.Node.Tag == "!!int" && rhs.Node.Tag == "!!int" {
|
} else if lhs.Node.Tag == "!!int" && rhs.Node.Tag == "!!int" {
|
||||||
return multiplyIntegers(lhs, rhs)
|
return multiplyIntegers(lhs, rhs)
|
||||||
|
} else if (lhs.Node.Tag == "!!int" || lhs.Node.Tag == "!!float") && (rhs.Node.Tag == "!!int" || rhs.Node.Tag == "!!float") {
|
||||||
|
return multiplyFloats(lhs, rhs)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Cannot multiply %v with %v", lhs.Node.Tag, rhs.Node.Tag)
|
return nil, fmt.Errorf("Cannot multiply %v with %v", lhs.Node.Tag, rhs.Node.Tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func multiplyFloats(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
|
target := lhs.CreateChild(nil, &yaml.Node{})
|
||||||
|
target.Node.Kind = yaml.ScalarNode
|
||||||
|
target.Node.Style = lhs.Node.Style
|
||||||
|
target.Node.Tag = "!!float"
|
||||||
|
|
||||||
|
lhsNum, err := strconv.ParseFloat(lhs.Node.Value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rhsNum, err := strconv.ParseFloat(rhs.Node.Value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
target.Node.Value = fmt.Sprintf("%v", lhsNum*rhsNum)
|
||||||
|
return target, nil
|
||||||
|
}
|
||||||
|
|
||||||
func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
target := lhs.CreateChild(nil, &yaml.Node{})
|
target := lhs.CreateChild(nil, &yaml.Node{})
|
||||||
target.Node.Kind = yaml.ScalarNode
|
target.Node.Kind = yaml.ScalarNode
|
||||||
@@ -119,14 +81,14 @@ func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, e
|
|||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) {
|
func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) {
|
||||||
shouldAppendArrays := preferences.AppendArrays
|
shouldAppendArrays := preferences.AppendArrays
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
// shouldn't recurse arrays if appending
|
// shouldn't recurse arrays if appending
|
||||||
prefs := recursiveDescentPreferences{RecurseArray: !shouldAppendArrays,
|
prefs := recursiveDescentPreferences{RecurseArray: !shouldAppendArrays,
|
||||||
TraversePreferences: traversePreferences{DontFollowAlias: true}}
|
TraversePreferences: traversePreferences{DontFollowAlias: true, IncludeMapKeys: true}}
|
||||||
err := recursiveDecent(d, results, nodeToMap(rhs), prefs)
|
err := recursiveDecent(d, results, context.SingleChildContext(rhs), prefs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -137,7 +99,12 @@ func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
for el := results.Front(); el != nil; el = el.Next() {
|
for el := results.Front(); el != nil; el = el.Next() {
|
||||||
err := applyAssignment(d, pathIndexToStartFrom, lhs, el.Value.(*CandidateNode), preferences)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
if candidate.Node.Tag == "!!merge" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err := applyAssignment(d, context, pathIndexToStartFrom, lhs, candidate, preferences)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -145,24 +112,25 @@ func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode,
|
|||||||
return lhs, nil
|
return lhs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error {
|
func applyAssignment(d *dataTreeNavigator, context Context, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error {
|
||||||
shouldAppendArrays := preferences.AppendArrays
|
shouldAppendArrays := preferences.AppendArrays
|
||||||
log.Debugf("merge - applyAssignment lhs %v, rhs: %v", NodeToString(lhs), NodeToString(rhs))
|
log.Debugf("merge - applyAssignment lhs %v, rhs: %v", lhs.GetKey(), rhs.GetKey())
|
||||||
|
|
||||||
lhsPath := rhs.Path[pathIndexToStartFrom:]
|
lhsPath := rhs.Path[pathIndexToStartFrom:]
|
||||||
|
|
||||||
assignmentOp := &Operation{OperationType: assignAttributesOpType}
|
assignmentOp := &Operation{OperationType: assignAttributesOpType}
|
||||||
if rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode {
|
if shouldAppendArrays && rhs.Node.Kind == yaml.SequenceNode {
|
||||||
|
assignmentOp.OperationType = addAssignOpType
|
||||||
|
} else if !preferences.DeepMergeArrays && rhs.Node.Kind == yaml.SequenceNode ||
|
||||||
|
(rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode) {
|
||||||
assignmentOp.OperationType = assignOpType
|
assignmentOp.OperationType = assignOpType
|
||||||
assignmentOp.UpdateAssign = false
|
assignmentOp.UpdateAssign = false
|
||||||
} else if shouldAppendArrays && rhs.Node.Kind == yaml.SequenceNode {
|
|
||||||
assignmentOp.OperationType = addAssignOpType
|
|
||||||
}
|
}
|
||||||
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhs}
|
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhs}
|
||||||
|
|
||||||
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath, preferences.TraversePrefs), Rhs: &ExpressionNode{Operation: rhsOp}}
|
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath, preferences.TraversePrefs, rhs.IsMapKey), Rhs: &ExpressionNode{Operation: rhsOp}}
|
||||||
|
|
||||||
_, err := d.GetMatchingNodes(nodeToMap(lhs), assignmentOpNode)
|
_, err := d.GetMatchingNodes(context.SingleChildContext(lhs), assignmentOpNode)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var multiplyOperatorScenarios = []expressionScenario{
|
var multiplyOperatorScenarios = []expressionScenario{
|
||||||
|
{
|
||||||
|
description: "Multiply integers",
|
||||||
|
expression: `3 * 4`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!int)::12\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
expression: `3 * 4.5`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!float)::13.5\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
expression: `4.5 * 3`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!float)::13.5\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: {also: [1]}, b: {also: me}}`,
|
document: `{a: {also: [1]}, b: {also: me}}`,
|
||||||
@@ -13,6 +34,30 @@ var multiplyOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
|
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: "# b\nb:\n # a\n a: cat",
|
||||||
|
expression: "{} * .",
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::# b\nb:\n # a\n a: cat\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: "# b\nb:\n # a\n a: cat",
|
||||||
|
expression: ". * {}",
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::# b\nb:\n # a\n a: cat\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `{a: &a { b: &b { c: &c cat } } }`,
|
||||||
|
expression: `{} * .`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::{a: &a {b: &b {c: &c cat}}}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: 2, b: 5}`,
|
document: `{a: 2, b: 5}`,
|
||||||
@@ -92,7 +137,7 @@ var multiplyOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: {things: great}, b: {also: me}}`,
|
document: `{a: {things: great}, b: {also: me}}`,
|
||||||
expression: `. * {"a":.b}`,
|
expression: `. * {"a": .b}`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n",
|
"D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n",
|
||||||
},
|
},
|
||||||
@@ -100,16 +145,10 @@ var multiplyOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
description: "Merge keeps style of LHS",
|
description: "Merge keeps style of LHS",
|
||||||
dontFormatInputForDoc: true,
|
dontFormatInputForDoc: true,
|
||||||
document: `a: {things: great}
|
document: "a: {things: great}\nb:\n also: \"me\"",
|
||||||
b:
|
expression: `. * {"a":.b}`,
|
||||||
also: "me"
|
|
||||||
`,
|
|
||||||
expression: `. * {"a":.b}`,
|
|
||||||
expected: []string{
|
expected: []string{
|
||||||
`D0, P[], (!!map)::a: {things: great, also: "me"}
|
"D0, P[], (!!map)::a: {things: great, also: \"me\"}\nb:\n also: \"me\"\n",
|
||||||
b:
|
|
||||||
also: "me"
|
|
||||||
`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -139,11 +178,19 @@ b:
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [{thing: one}], b: [{missing: two, thing: two}]}`,
|
document: `{a: [{thing: one}], b: [{missing: two, thing: two}]}`,
|
||||||
expression: `.a *? .b`,
|
expression: `.a *?d .b`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a], (!!seq)::[{thing: two}]\n",
|
"D0, P[a], (!!seq)::[{thing: two}]\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `{a: {array: [1]}, b: {}}`,
|
||||||
|
expression: `.b *+ .a`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[b], (!!map)::{array: [1]}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Merge, appending arrays",
|
description: "Merge, appending arrays",
|
||||||
document: `{a: {array: [1, 2, animal: dog], value: coconut}, b: {array: [3, 4, animal: cat], value: banana}}`,
|
document: `{a: {array: [1, 2, animal: dog], value: coconut}, b: {array: [3, 4, animal: cat], value: banana}}`,
|
||||||
@@ -160,6 +207,15 @@ b:
|
|||||||
"D0, P[a], (!!map)::{thing: [1, 2, 3, 4]}\n",
|
"D0, P[a], (!!map)::{thing: [1, 2, 3, 4]}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Merge, deeply merging arrays",
|
||||||
|
subdescription: "Merging arrays deeply means arrays are merge like objects, with indexes as their key. In this case, we merge the first item in the array, and do nothing with the second.",
|
||||||
|
document: `{a: [{name: fred, age: 12}, {name: bob, age: 32}], b: [{name: fred, age: 34}]}`,
|
||||||
|
expression: `.a *d .b`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a], (!!seq)::[{name: fred, age: 34}, {name: bob, age: 32}]\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Merge to prefix an element",
|
description: "Merge to prefix an element",
|
||||||
document: `{a: cat, b: dog}`,
|
document: `{a: cat, b: dog}`,
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ func createPathNodeFor(pathElement interface{}) *yaml.Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPathOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func getPathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("GetPath")
|
log.Debugf("GetPath")
|
||||||
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
|
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
|
||||||
|
|
||||||
@@ -35,5 +35,5 @@ func getPathOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionN
|
|||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import "container/list"
|
func pipeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
func pipeOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
//lhs may update the variable context, we should pass that into the RHS
|
||||||
lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs)
|
// BUT we still return the original context back (see jq)
|
||||||
|
// https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...
|
||||||
|
|
||||||
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
return d.GetMatchingNodes(lhs, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(lhs, expressionNode.Rhs)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
return context.ChildContext(rhs.MatchingNodes), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ type recursiveDescentPreferences struct {
|
|||||||
RecurseArray bool
|
RecurseArray bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func recursiveDescentOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func recursiveDescentOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
preferences := expressionNode.Operation.Preferences.(recursiveDescentPreferences)
|
preferences := expressionNode.Operation.Preferences.(recursiveDescentPreferences)
|
||||||
err := recursiveDecent(d, results, matchMap, preferences)
|
err := recursiveDecent(d, results, context, preferences)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.List, preferences recursiveDescentPreferences) error {
|
func recursiveDecent(d *dataTreeNavigator, results *list.List, context Context, preferences recursiveDescentPreferences) error {
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
candidate.Node = unwrapDoc(candidate.Node)
|
candidate.Node = unwrapDoc(candidate.Node)
|
||||||
@@ -35,7 +35,7 @@ func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.Li
|
|||||||
if candidate.Node.Kind != yaml.AliasNode && len(candidate.Node.Content) > 0 &&
|
if candidate.Node.Kind != yaml.AliasNode && len(candidate.Node.Content) > 0 &&
|
||||||
(preferences.RecurseArray || candidate.Node.Kind != yaml.SequenceNode) {
|
(preferences.RecurseArray || candidate.Node.Kind != yaml.SequenceNode) {
|
||||||
|
|
||||||
children, err := splat(d, nodeToMap(candidate), preferences.TraversePreferences)
|
children, err := splat(d, context.SingleChildContext(candidate), preferences.TraversePreferences)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
59
pkg/yqlib/operator_reduce.go
Normal file
59
pkg/yqlib/operator_reduce.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func reduceOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
log.Debugf("-- reduceOp")
|
||||||
|
//.a as $var reduce (0; . + $var)
|
||||||
|
//lhs is the assignment operator
|
||||||
|
//rhs is the reduce block
|
||||||
|
// '.' refers to the current accumulator, initialised to 0
|
||||||
|
// $var references a single element from the .a
|
||||||
|
|
||||||
|
//ensure lhs is actually an assignment
|
||||||
|
//and rhs is a block (empty)
|
||||||
|
if expressionNode.Lhs.Operation.OperationType != assignVariableOpType {
|
||||||
|
return Context{}, fmt.Errorf("reduce must be given a variables assignment, got %v instead", expressionNode.Lhs.Operation.OperationType.Type)
|
||||||
|
} else if expressionNode.Rhs.Operation.OperationType != blockOpType {
|
||||||
|
return Context{}, fmt.Errorf("reduce must be given a block, got %v instead", expressionNode.Rhs.Operation.OperationType.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayExpNode := expressionNode.Lhs.Lhs
|
||||||
|
array, err := d.GetMatchingNodes(context, arrayExpNode)
|
||||||
|
|
||||||
|
log.Debugf("array of %v things", array.MatchingNodes.Len())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
variableName := expressionNode.Lhs.Rhs.Operation.StringValue
|
||||||
|
|
||||||
|
initExp := expressionNode.Rhs.Lhs
|
||||||
|
|
||||||
|
accum, err := d.GetMatchingNodes(context, initExp)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("with variable %v", variableName)
|
||||||
|
|
||||||
|
blockExp := expressionNode.Rhs.Rhs
|
||||||
|
for el := array.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
log.Debugf("REDUCING WITH %v", NodeToString(candidate))
|
||||||
|
l := list.New()
|
||||||
|
l.PushBack(candidate)
|
||||||
|
accum.SetVariable(variableName, l)
|
||||||
|
|
||||||
|
accum, err = d.GetMatchingNodes(accum, blockExp)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return accum, nil
|
||||||
|
}
|
||||||
40
pkg/yqlib/operator_reduce_test.go
Normal file
40
pkg/yqlib/operator_reduce_test.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var reduceOperatorScenarios = []expressionScenario{
|
||||||
|
{
|
||||||
|
description: "Sum numbers",
|
||||||
|
document: `[10,2, 5, 3]`,
|
||||||
|
expression: `.[] as $item ireduce (0; . + $item)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!int)::20\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Merge all yaml files together",
|
||||||
|
document: `a: cat`,
|
||||||
|
document2: `b: dog`,
|
||||||
|
expression: `. as $item ireduce ({}; . * $item )`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::a: cat\nb: dog\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Convert an array to an object",
|
||||||
|
document: `[{name: Cathy, has: apples},{name: Bob, has: bananas}]`,
|
||||||
|
expression: `.[] as $item ireduce ({}; .[$item | .name] = ($item | .has) )`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::Cathy: apples\nBob: bananas\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReduceOperatorScenarios(t *testing.T) {
|
||||||
|
for _, tt := range reduceOperatorScenarios {
|
||||||
|
testScenario(t, &tt)
|
||||||
|
}
|
||||||
|
documentScenarios(t, "Reduce", reduceOperatorScenarios)
|
||||||
|
}
|
||||||
@@ -4,28 +4,29 @@ import (
|
|||||||
"container/list"
|
"container/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
func selectOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func selectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
log.Debugf("-- selectOperation")
|
log.Debugf("-- selectOperation")
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
childContext := context.SingleChildContext(candidate)
|
||||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
childContext.DontAutoCreate = true
|
||||||
|
rhs, err := d.GetMatchingNodes(childContext, expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab the first value
|
// grab the first value
|
||||||
first := rhs.Front()
|
first := rhs.MatchingNodes.Front()
|
||||||
|
|
||||||
if first != nil {
|
if first != nil {
|
||||||
result := first.Value.(*CandidateNode)
|
result := first.Value.(*CandidateNode)
|
||||||
includeResult, errDecoding := isTruthy(result)
|
includeResult, errDecoding := isTruthy(result)
|
||||||
if errDecoding != nil {
|
if errDecoding != nil {
|
||||||
return nil, errDecoding
|
return Context{}, errDecoding
|
||||||
}
|
}
|
||||||
|
|
||||||
if includeResult {
|
if includeResult {
|
||||||
@@ -33,5 +34,5 @@ func selectOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ var selectOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[1], (!!str)::goat\n",
|
"D0, P[1], (!!str)::goat\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `[{animal: cat, legs: {cool: true}}, {animal: fish}]`,
|
||||||
|
expression: `(.[] | select(.legs.cool == true).canWalk) = true | (.[] | .alive.things) = "yes"`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (doc)::[{animal: cat, legs: {cool: true}, canWalk: true, alive: {things: yes}}, {animal: fish, alive: {things: yes}}]\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `[hot, fot, dog]`,
|
document: `[hot, fot, dog]`,
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import "container/list"
|
func selfOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
return context, nil
|
||||||
func selfOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
|
||||||
return matchMap, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,32 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sortKeysOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func sortKeysOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for childEl := rhs.Front(); childEl != nil; childEl = childEl.Next() {
|
for childEl := rhs.MatchingNodes.Front(); childEl != nil; childEl = childEl.Next() {
|
||||||
node := unwrapDoc(childEl.Value.(*CandidateNode).Node)
|
node := unwrapDoc(childEl.Value.(*CandidateNode).Node)
|
||||||
if node.Kind == yaml.MappingNode {
|
if node.Kind == yaml.MappingNode {
|
||||||
sortKeys(node)
|
sortKeys(node)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortKeys(node *yaml.Node) {
|
func sortKeys(node *yaml.Node) {
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import (
|
func splitDocumentOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
"container/list"
|
|
||||||
)
|
|
||||||
|
|
||||||
func splitDocumentOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
|
||||||
log.Debugf("-- splitDocumentOperator")
|
log.Debugf("-- splitDocumentOperator")
|
||||||
|
|
||||||
var index uint = 0
|
var index uint = 0
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
candidate.Document = index
|
candidate.Document = index
|
||||||
index = index + 1
|
index = index + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchMap, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,32 +8,32 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func joinStringOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func joinStringOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- joinStringOperator")
|
log.Debugf("-- joinStringOperator")
|
||||||
joinStr := ""
|
joinStr := ""
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(matchMap, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
joinStr = rhs.Front().Value.(*CandidateNode).Node.Value
|
joinStr = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := unwrapDoc(candidate.Node)
|
node := unwrapDoc(candidate.Node)
|
||||||
if node.Kind != yaml.SequenceNode {
|
if node.Kind != yaml.SequenceNode {
|
||||||
return nil, fmt.Errorf("Cannot join with %v, can only join arrays of scalars", node.Tag)
|
return Context{}, fmt.Errorf("Cannot join with %v, can only join arrays of scalars", node.Tag)
|
||||||
}
|
}
|
||||||
targetNode := join(node.Content, joinStr)
|
targetNode := join(node.Content, joinStr)
|
||||||
result := candidate.CreateChild(nil, targetNode)
|
result := candidate.CreateChild(nil, targetNode)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func join(content []*yaml.Node, joinStr string) *yaml.Node {
|
func join(content []*yaml.Node, joinStr string) *yaml.Node {
|
||||||
@@ -49,35 +49,35 @@ func join(content []*yaml.Node, joinStr string) *yaml.Node {
|
|||||||
return &yaml.Node{Kind: yaml.ScalarNode, Value: strings.Join(stringsToJoin, joinStr), Tag: "!!str"}
|
return &yaml.Node{Kind: yaml.ScalarNode, Value: strings.Join(stringsToJoin, joinStr), Tag: "!!str"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitStringOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func splitStringOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- splitStringOperator")
|
log.Debugf("-- splitStringOperator")
|
||||||
splitStr := ""
|
splitStr := ""
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(matchMap, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
splitStr = rhs.Front().Value.(*CandidateNode).Node.Value
|
splitStr = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := unwrapDoc(candidate.Node)
|
node := unwrapDoc(candidate.Node)
|
||||||
if node.Tag == "!!null" {
|
if node.Tag == "!!null" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if node.Tag != "!!str" {
|
if node.Tag != "!!str" {
|
||||||
return nil, fmt.Errorf("Cannot split %v, can only split strings", node.Tag)
|
return Context{}, fmt.Errorf("Cannot split %v, can only split strings", node.Tag)
|
||||||
}
|
}
|
||||||
targetNode := split(node.Value, splitStr)
|
targetNode := split(node.Value, splitStr)
|
||||||
result := candidate.CreateChild(nil, targetNode)
|
result := candidate.CreateChild(nil, targetNode)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func split(value string, spltStr string) *yaml.Node {
|
func split(value string, spltStr string) *yaml.Node {
|
||||||
|
|||||||
@@ -26,43 +26,43 @@ func parseStyle(customStyle string) (yaml.Style, error) {
|
|||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
log.Debugf("AssignStyleOperator: %v")
|
log.Debugf("AssignStyleOperator: %v")
|
||||||
var style yaml.Style
|
var style yaml.Style
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
style, err = parseStyle(rhs.Front().Value.(*CandidateNode).Node.Value)
|
style, err = parseStyle(rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
log.Debugf("Setting style of : %v", candidate.GetKey())
|
log.Debugf("Setting style of : %v", candidate.GetKey())
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
style, err = parseStyle(rhs.Front().Value.(*CandidateNode).Node.Value)
|
style, err = parseStyle(rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,15 +70,15 @@ func assignStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, express
|
|||||||
candidate.Node.Style = style
|
candidate.Node.Style = style
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func getStyleOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("GetStyleOperator")
|
log.Debugf("GetStyleOperator")
|
||||||
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
var style string
|
var style string
|
||||||
switch candidate.Node.Style {
|
switch candidate.Node.Style {
|
||||||
@@ -104,5 +104,5 @@ func getStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, expression
|
|||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,58 +6,58 @@ import (
|
|||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func assignTagOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
log.Debugf("AssignTagOperator: %v")
|
log.Debugf("AssignTagOperator: %v")
|
||||||
tag := ""
|
tag := ""
|
||||||
|
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
tag = rhs.Front().Value.(*CandidateNode).Node.Value
|
tag = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
log.Debugf("Setting tag of : %v", candidate.GetKey())
|
log.Debugf("Setting tag of : %v", candidate.GetKey())
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rhs.Front() != nil {
|
if rhs.MatchingNodes.Front() != nil {
|
||||||
tag = rhs.Front().Value.(*CandidateNode).Node.Value
|
tag = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unwrapDoc(candidate.Node).Tag = tag
|
unwrapDoc(candidate.Node).Tag = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchingNodes, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTagOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func getTagOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("GetTagOperator")
|
log.Debugf("GetTagOperator")
|
||||||
|
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
node := &yaml.Node{Kind: yaml.ScalarNode, Value: unwrapDoc(candidate.Node).Tag, Tag: "!!str"}
|
node := &yaml.Node{Kind: yaml.ScalarNode, Value: unwrapDoc(candidate.Node).Tag, Tag: "!!str"}
|
||||||
result := candidate.CreateChild(nil, node)
|
result := candidate.CreateChild(nil, node)
|
||||||
results.PushBack(result)
|
results.PushBack(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,31 +10,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type traversePreferences struct {
|
type traversePreferences struct {
|
||||||
DontFollowAlias bool
|
DontFollowAlias bool
|
||||||
IncludeMapKeys bool
|
IncludeMapKeys bool
|
||||||
DontAutoCreate bool // by default, we automatically create entries on the fly.
|
DontAutoCreate bool // by default, we automatically create entries on the fly.
|
||||||
|
DontIncludeMapValues bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func splat(d *dataTreeNavigator, matches *list.List, prefs traversePreferences) (*list.List, error) {
|
func splat(d *dataTreeNavigator, context Context, prefs traversePreferences) (Context, error) {
|
||||||
return traverseNodesWithArrayIndices(matches, make([]*yaml.Node, 0), prefs)
|
return traverseNodesWithArrayIndices(context, make([]*yaml.Node, 0), prefs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func traversePathOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func traversePathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- Traversing")
|
log.Debugf("-- traversePathOperator")
|
||||||
var matchingNodeMap = list.New()
|
var matches = list.New()
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
newNodes, err := traverse(d, el.Value.(*CandidateNode), expressionNode.Operation)
|
newNodes, err := traverse(d, context, el.Value.(*CandidateNode), expressionNode.Operation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
matchingNodeMap.PushBackList(newNodes)
|
matches.PushBackList(newNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchingNodeMap, nil
|
return context.ChildContext(matches), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, operation *Operation) (*list.List, error) {
|
func traverse(d *dataTreeNavigator, context Context, matchingNode *CandidateNode, operation *Operation) (*list.List, error) {
|
||||||
log.Debug("Traversing %v", NodeToString(matchingNode))
|
log.Debug("Traversing %v", NodeToString(matchingNode))
|
||||||
value := matchingNode.Node
|
value := matchingNode.Node
|
||||||
|
|
||||||
@@ -55,7 +56,7 @@ func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, operation *Oper
|
|||||||
switch value.Kind {
|
switch value.Kind {
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
log.Debug("its a map with %v entries", len(value.Content)/2)
|
log.Debug("its a map with %v entries", len(value.Content)/2)
|
||||||
return traverseMap(matchingNode, operation.StringValue, operation.Preferences.(traversePreferences), false)
|
return traverseMap(context, matchingNode, operation.StringValue, operation.Preferences.(traversePreferences), false)
|
||||||
|
|
||||||
case yaml.SequenceNode:
|
case yaml.SequenceNode:
|
||||||
log.Debug("its a sequence of %v things!", len(value.Content))
|
log.Debug("its a sequence of %v things!", len(value.Content))
|
||||||
@@ -64,43 +65,59 @@ func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, operation *Oper
|
|||||||
case yaml.AliasNode:
|
case yaml.AliasNode:
|
||||||
log.Debug("its an alias!")
|
log.Debug("its an alias!")
|
||||||
matchingNode.Node = matchingNode.Node.Alias
|
matchingNode.Node = matchingNode.Node.Alias
|
||||||
return traverse(d, matchingNode, operation)
|
return traverse(d, context, matchingNode, operation)
|
||||||
case yaml.DocumentNode:
|
case yaml.DocumentNode:
|
||||||
log.Debug("digging into doc node")
|
log.Debug("digging into doc node")
|
||||||
|
|
||||||
return traverse(d, matchingNode.CreateChild(nil, matchingNode.Node.Content[0]), operation)
|
return traverse(d, context, matchingNode.CreateChild(nil, matchingNode.Node.Content[0]), operation)
|
||||||
default:
|
default:
|
||||||
return list.New(), nil
|
return list.New(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func traverseArrayOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
// rhs is a collect expression that will yield indexes to retreive of the arrays
|
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
//lhs may update the variable context, we should pass that into the RHS
|
||||||
|
// BUT we still return the original context back (see jq)
|
||||||
|
// https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...
|
||||||
|
|
||||||
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var indicesToTraverse = rhs.Front().Value.(*CandidateNode).Node.Content
|
// rhs is a collect expression that will yield indexes to retreive of the arrays
|
||||||
return traverseNodesWithArrayIndices(matchingNodes, indicesToTraverse, traversePreferences{})
|
|
||||||
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Content
|
||||||
|
|
||||||
|
//now we traverse the result of the lhs against the indices we found
|
||||||
|
result, err := traverseNodesWithArrayIndices(lhs, indicesToTraverse, traversePreferences{})
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
return context.ChildContext(result.MatchingNodes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func traverseNodesWithArrayIndices(matchingNodes *list.List, indicesToTraverse []*yaml.Node, prefs traversePreferences) (*list.List, error) {
|
func traverseNodesWithArrayIndices(context Context, indicesToTraverse []*yaml.Node, prefs traversePreferences) (Context, error) {
|
||||||
var matchingNodeMap = list.New()
|
var matchingNodeMap = list.New()
|
||||||
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
newNodes, err := traverseArrayIndices(candidate, indicesToTraverse, prefs)
|
newNodes, err := traverseArrayIndices(context, candidate, indicesToTraverse, prefs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
matchingNodeMap.PushBackList(newNodes)
|
matchingNodeMap.PushBackList(newNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchingNodeMap, nil
|
return context.ChildContext(matchingNodeMap), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func traverseArrayIndices(matchingNode *CandidateNode, indicesToTraverse []*yaml.Node, prefs traversePreferences) (*list.List, error) { // call this if doc / alias like the other traverse
|
func traverseArrayIndices(context Context, matchingNode *CandidateNode, indicesToTraverse []*yaml.Node, prefs traversePreferences) (*list.List, error) { // call this if doc / alias like the other traverse
|
||||||
node := matchingNode.Node
|
node := matchingNode.Node
|
||||||
if node.Tag == "!!null" {
|
if node.Tag == "!!null" {
|
||||||
log.Debugf("OperatorArrayTraverse got a null - turning it into an empty array")
|
log.Debugf("OperatorArrayTraverse got a null - turning it into an empty array")
|
||||||
@@ -111,28 +128,28 @@ func traverseArrayIndices(matchingNode *CandidateNode, indicesToTraverse []*yaml
|
|||||||
|
|
||||||
if node.Kind == yaml.AliasNode {
|
if node.Kind == yaml.AliasNode {
|
||||||
matchingNode.Node = node.Alias
|
matchingNode.Node = node.Alias
|
||||||
return traverseArrayIndices(matchingNode, indicesToTraverse, prefs)
|
return traverseArrayIndices(context, matchingNode, indicesToTraverse, prefs)
|
||||||
} else if node.Kind == yaml.SequenceNode {
|
} else if node.Kind == yaml.SequenceNode {
|
||||||
return traverseArrayWithIndices(matchingNode, indicesToTraverse)
|
return traverseArrayWithIndices(matchingNode, indicesToTraverse)
|
||||||
} else if node.Kind == yaml.MappingNode {
|
} else if node.Kind == yaml.MappingNode {
|
||||||
return traverseMapWithIndices(matchingNode, indicesToTraverse, prefs)
|
return traverseMapWithIndices(context, matchingNode, indicesToTraverse, prefs)
|
||||||
} else if node.Kind == yaml.DocumentNode {
|
} else if node.Kind == yaml.DocumentNode {
|
||||||
return traverseArrayIndices(matchingNode.CreateChild(nil, matchingNode.Node.Content[0]), indicesToTraverse, prefs)
|
return traverseArrayIndices(context, matchingNode.CreateChild(nil, matchingNode.Node.Content[0]), indicesToTraverse, prefs)
|
||||||
}
|
}
|
||||||
log.Debugf("OperatorArrayTraverse skipping %v as its a %v", matchingNode, node.Tag)
|
log.Debugf("OperatorArrayTraverse skipping %v as its a %v", matchingNode, node.Tag)
|
||||||
return list.New(), nil
|
return list.New(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func traverseMapWithIndices(candidate *CandidateNode, indices []*yaml.Node, prefs traversePreferences) (*list.List, error) {
|
func traverseMapWithIndices(context Context, candidate *CandidateNode, indices []*yaml.Node, prefs traversePreferences) (*list.List, error) {
|
||||||
if len(indices) == 0 {
|
if len(indices) == 0 {
|
||||||
return traverseMap(candidate, "", prefs, true)
|
return traverseMap(context, candidate, "", prefs, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchingNodeMap = list.New()
|
var matchingNodeMap = list.New()
|
||||||
|
|
||||||
for _, indexNode := range indices {
|
for _, indexNode := range indices {
|
||||||
log.Debug("traverseMapWithIndices: %v", indexNode.Value)
|
log.Debug("traverseMapWithIndices: %v", indexNode.Value)
|
||||||
newNodes, err := traverseMap(candidate, indexNode.Value, prefs, false)
|
newNodes, err := traverseMap(context, candidate, indexNode.Value, prefs, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -187,7 +204,7 @@ func keyMatches(key *yaml.Node, wantedKey string) bool {
|
|||||||
return matchKey(key.Value, wantedKey)
|
return matchKey(key.Value, wantedKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func traverseMap(matchingNode *CandidateNode, key string, prefs traversePreferences, splat bool) (*list.List, error) {
|
func traverseMap(context Context, matchingNode *CandidateNode, key string, prefs traversePreferences, splat bool) (*list.List, error) {
|
||||||
var newMatches = orderedmap.NewOrderedMap()
|
var newMatches = orderedmap.NewOrderedMap()
|
||||||
err := doTraverseMap(newMatches, matchingNode, key, prefs, splat)
|
err := doTraverseMap(newMatches, matchingNode, key, prefs, splat)
|
||||||
|
|
||||||
@@ -195,13 +212,24 @@ func traverseMap(matchingNode *CandidateNode, key string, prefs traversePreferen
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !prefs.DontAutoCreate && newMatches.Len() == 0 {
|
if !prefs.DontAutoCreate && !context.DontAutoCreate && newMatches.Len() == 0 {
|
||||||
//no matches, create one automagically
|
//no matches, create one automagically
|
||||||
valueNode := &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"}
|
valueNode := &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"}
|
||||||
|
keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: key}
|
||||||
node := matchingNode.Node
|
node := matchingNode.Node
|
||||||
node.Content = append(node.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: key}, valueNode)
|
node.Content = append(node.Content, keyNode, valueNode)
|
||||||
candidateNode := matchingNode.CreateChild(key, valueNode)
|
|
||||||
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
if prefs.IncludeMapKeys {
|
||||||
|
log.Debug("including key")
|
||||||
|
candidateNode := matchingNode.CreateChild(key, keyNode)
|
||||||
|
candidateNode.IsMapKey = true
|
||||||
|
newMatches.Set(fmt.Sprintf("keyOf-%v", candidateNode.GetKey()), candidateNode)
|
||||||
|
}
|
||||||
|
if !prefs.DontIncludeMapValues {
|
||||||
|
log.Debug("including value")
|
||||||
|
candidateNode := matchingNode.CreateChild(key, valueNode)
|
||||||
|
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results := list.New()
|
results := list.New()
|
||||||
@@ -237,11 +265,16 @@ func doTraverseMap(newMatches *orderedmap.OrderedMap, candidate *CandidateNode,
|
|||||||
} else if splat || keyMatches(key, wantedKey) {
|
} else if splat || keyMatches(key, wantedKey) {
|
||||||
log.Debug("MATCHED")
|
log.Debug("MATCHED")
|
||||||
if prefs.IncludeMapKeys {
|
if prefs.IncludeMapKeys {
|
||||||
|
log.Debug("including key")
|
||||||
candidateNode := candidate.CreateChild(key.Value, key)
|
candidateNode := candidate.CreateChild(key.Value, key)
|
||||||
|
candidateNode.IsMapKey = true
|
||||||
newMatches.Set(fmt.Sprintf("keyOf-%v", candidateNode.GetKey()), candidateNode)
|
newMatches.Set(fmt.Sprintf("keyOf-%v", candidateNode.GetKey()), candidateNode)
|
||||||
}
|
}
|
||||||
candidateNode := candidate.CreateChild(key.Value, value)
|
if !prefs.DontIncludeMapValues {
|
||||||
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
log.Debug("including value")
|
||||||
|
candidateNode := candidate.CreateChild(key.Value, value)
|
||||||
|
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,13 +47,30 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Special characters",
|
description: "Special characters",
|
||||||
subdescription: "Use quotes around path elements with special characters",
|
subdescription: "Use quotes with brackets around path elements with special characters",
|
||||||
document: `{"{}": frog}`,
|
document: `{"{}": frog}`,
|
||||||
expression: `."{}"`,
|
expression: `.["{}"]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[{}], (!!str)::frog\n",
|
"D0, P[{}], (!!str)::frog\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "Keys with spaces",
|
||||||
|
subdescription: "Use quotes with brackets around path elements with special characters",
|
||||||
|
document: `{"red rabbit": frog}`,
|
||||||
|
expression: `.["red rabbit"]`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[red rabbit], (!!str)::frog\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `{"flying fox": frog}`,
|
||||||
|
expression: `.["flying fox"]`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[flying fox], (!!str)::frog\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Dynamic keys",
|
description: "Dynamic keys",
|
||||||
subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`,
|
subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`,
|
||||||
@@ -63,6 +80,14 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[apple], (!!str)::crispy yum\n",
|
"D0, P[apple], (!!str)::crispy yum\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
skipDoc: true,
|
||||||
|
document: `{b: apple, fruit: {apple: yum, banana: smooth}}`,
|
||||||
|
expression: `.fruit[.b]`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[fruit apple], (!!str)::yum\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
description: "Children don't exist",
|
description: "Children don't exist",
|
||||||
subdescription: "Nodes are added dynamically while traversing",
|
subdescription: "Nodes are added dynamically while traversing",
|
||||||
@@ -83,7 +108,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{}`,
|
document: `{}`,
|
||||||
expression: `.a.[1]`,
|
expression: `.a[1]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a 1], (!!null)::null\n",
|
"D0, P[a 1], (!!null)::null\n",
|
||||||
},
|
},
|
||||||
@@ -154,7 +179,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: &cat {c: frog}, b: *cat}`,
|
document: `{a: &cat {c: frog}, b: *cat}`,
|
||||||
expression: `.b.[]`,
|
expression: `.b[]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[b c], (!!str)::frog\n",
|
"D0, P[b c], (!!str)::frog\n",
|
||||||
},
|
},
|
||||||
@@ -236,7 +261,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: mergeDocSample,
|
document: mergeDocSample,
|
||||||
expression: `.foobar.[]`,
|
expression: `.foobar[]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[foobar c], (!!str)::foo_c\n",
|
"D0, P[foobar c], (!!str)::foo_c\n",
|
||||||
"D0, P[foobar a], (!!str)::foo_a\n",
|
"D0, P[foobar a], (!!str)::foo_a\n",
|
||||||
@@ -298,7 +323,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: mergeDocSample,
|
document: mergeDocSample,
|
||||||
expression: `.foobarList.[]`,
|
expression: `.foobarList[]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[foobarList b], (!!str)::bar_b\n",
|
"D0, P[foobarList b], (!!str)::bar_b\n",
|
||||||
"D0, P[foobarList a], (!!str)::foo_a\n",
|
"D0, P[foobarList a], (!!str)::foo_a\n",
|
||||||
@@ -344,7 +369,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [a,b,c]}`,
|
document: `{a: [a,b,c]}`,
|
||||||
expression: `.a.[0, 2]`,
|
expression: `.a[0, 2]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a 0], (!!str)::a\n",
|
"D0, P[a 0], (!!str)::a\n",
|
||||||
"D0, P[a 2], (!!str)::c\n",
|
"D0, P[a 2], (!!str)::c\n",
|
||||||
@@ -361,7 +386,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [a,b,c]}`,
|
document: `{a: [a,b,c]}`,
|
||||||
expression: `.a.[-1]`,
|
expression: `.a[-1]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a -1], (!!str)::c\n",
|
"D0, P[a -1], (!!str)::c\n",
|
||||||
},
|
},
|
||||||
@@ -377,7 +402,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [a,b,c]}`,
|
document: `{a: [a,b,c]}`,
|
||||||
expression: `.a.[-2]`,
|
expression: `.a[-2]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a -2], (!!str)::b\n",
|
"D0, P[a -2], (!!str)::b\n",
|
||||||
},
|
},
|
||||||
@@ -395,7 +420,7 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: `{a: [a,b,c]}`,
|
document: `{a: [a,b,c]}`,
|
||||||
expression: `.a.[]`,
|
expression: `.a[]`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[a 0], (!!str)::a\n",
|
"D0, P[a 0], (!!str)::a\n",
|
||||||
"D0, P[a 1], (!!str)::b\n",
|
"D0, P[a 1], (!!str)::b\n",
|
||||||
@@ -418,5 +443,5 @@ func TestTraversePathOperatorScenarios(t *testing.T) {
|
|||||||
for _, tt := range traversePathOperatorScenarios {
|
for _, tt := range traversePathOperatorScenarios {
|
||||||
testScenario(t, &tt)
|
testScenario(t, &tt)
|
||||||
}
|
}
|
||||||
documentScenarios(t, "Traverse", traversePathOperatorScenarios)
|
documentScenarios(t, "Traverse (Read)", traversePathOperatorScenarios)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import "container/list"
|
func unionOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
func unionOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
|
||||||
lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
for el := rhs.Front(); el != nil; el = el.Next() {
|
for el := rhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
node := el.Value.(*CandidateNode)
|
node := el.Value.(*CandidateNode)
|
||||||
lhs.PushBack(node)
|
lhs.MatchingNodes.PushBack(node)
|
||||||
}
|
}
|
||||||
return lhs, nil
|
return lhs, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
import "container/list"
|
func valueOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
func valueOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
|
||||||
log.Debug("value = %v", expressionNode.Operation.CandidateNode.Node.Value)
|
log.Debug("value = %v", expressionNode.Operation.CandidateNode.Node.Value)
|
||||||
return nodeToMap(expressionNode.Operation.CandidateNode), nil
|
return context.SingleChildContext(expressionNode.Operation.CandidateNode), nil
|
||||||
}
|
}
|
||||||
|
|||||||
29
pkg/yqlib/operator_variables.go
Normal file
29
pkg/yqlib/operator_variables.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getVariableOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
variableName := expressionNode.Operation.StringValue
|
||||||
|
log.Debug("getVariableOperator %v", variableName)
|
||||||
|
result := context.GetVariable(variableName)
|
||||||
|
if result == nil {
|
||||||
|
result = list.New()
|
||||||
|
}
|
||||||
|
return context.ChildContext(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignVariableOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, nil
|
||||||
|
}
|
||||||
|
if expressionNode.Rhs.Operation.OperationType.Type != "GET_VARIABLE" {
|
||||||
|
return Context{}, fmt.Errorf("RHS of 'as' operator must be a variable name e.g. $foo")
|
||||||
|
}
|
||||||
|
variableName := expressionNode.Rhs.Operation.StringValue
|
||||||
|
context.SetVariable(variableName, lhs.MatchingNodes)
|
||||||
|
return context, nil
|
||||||
|
}
|
||||||
45
pkg/yqlib/operator_variables_test.go
Normal file
45
pkg/yqlib/operator_variables_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var variableOperatorScenarios = []expressionScenario{
|
||||||
|
{
|
||||||
|
description: "Single value variable",
|
||||||
|
document: `a: cat`,
|
||||||
|
expression: `.a as $foo | $foo`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a], (!!str)::cat\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Multi value variable",
|
||||||
|
document: `[cat, dog]`,
|
||||||
|
expression: `.[] as $foo | $foo`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[0], (!!str)::cat\n",
|
||||||
|
"D0, P[1], (!!str)::dog\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Using variables as a lookup",
|
||||||
|
subdescription: "Example taken from [jq](https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|...)",
|
||||||
|
document: `{"posts": [{"title": "Frist psot", "author": "anon"},
|
||||||
|
{"title": "A well-written article", "author": "person1"}],
|
||||||
|
"realnames": {"anon": "Anonymous Coward",
|
||||||
|
"person1": "Person McPherson"}}`,
|
||||||
|
expression: `.realnames as $names | .posts[] | {"title":.title, "author": $names[.author]}`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!map)::title: \"Frist psot\"\nauthor: \"Anonymous Coward\"\n",
|
||||||
|
"D0, P[], (!!map)::title: \"A well-written article\"\nauthor: \"Person McPherson\"\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVariableOperatorScenarios(t *testing.T) {
|
||||||
|
for _, tt := range variableOperatorScenarios {
|
||||||
|
testScenario(t, &tt)
|
||||||
|
}
|
||||||
|
documentScenarios(t, "Variable Operators", variableOperatorScenarios)
|
||||||
|
}
|
||||||
@@ -4,10 +4,11 @@ import (
|
|||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type operatorHandler func(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error)
|
type operatorHandler func(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error)
|
||||||
|
|
||||||
func unwrapDoc(node *yaml.Node) *yaml.Node {
|
func unwrapDoc(node *yaml.Node) *yaml.Node {
|
||||||
if node.Kind == yaml.DocumentNode {
|
if node.Kind == yaml.DocumentNode {
|
||||||
@@ -16,8 +17,67 @@ func unwrapDoc(node *yaml.Node) *yaml.Node {
|
|||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
func emptyOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
func emptyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
return list.New(), nil
|
context.MatchingNodes = list.New()
|
||||||
|
return context, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type crossFunctionCalculation func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
|
||||||
|
|
||||||
|
func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (Context, error) {
|
||||||
|
var results = list.New()
|
||||||
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
log.Debugf("crossFunction LHS len: %v", lhs.MatchingNodes.Len())
|
||||||
|
|
||||||
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
|
lhsCandidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
|
for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
||||||
|
log.Debugf("Applying calc")
|
||||||
|
rhsCandidate := rightEl.Value.(*CandidateNode)
|
||||||
|
resultCandidate, err := calculation(d, context, lhsCandidate, rhsCandidate)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
results.PushBack(resultCandidate)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return context.ChildContext(results), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func crossFunction(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (Context, error) {
|
||||||
|
var results = list.New()
|
||||||
|
|
||||||
|
var evaluateAllTogether = true
|
||||||
|
for matchEl := context.MatchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
|
||||||
|
evaluateAllTogether = evaluateAllTogether && matchEl.Value.(*CandidateNode).EvaluateTogether
|
||||||
|
if !evaluateAllTogether {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if evaluateAllTogether {
|
||||||
|
return doCrossFunc(d, context, expressionNode, calculation)
|
||||||
|
}
|
||||||
|
|
||||||
|
for matchEl := context.MatchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
|
||||||
|
innerResults, err := doCrossFunc(d, context.SingleChildContext(matchEl.Value.(*CandidateNode)), expressionNode, calculation)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
results.PushBackList(innerResults.MatchingNodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.ChildContext(results), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode {
|
func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode {
|
||||||
@@ -29,22 +89,25 @@ func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode {
|
|||||||
return owner.CreateChild(nil, node)
|
return owner.CreateChild(nil, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeToMap(candidate *CandidateNode) *list.List {
|
func createTraversalTree(path []interface{}, traversePrefs traversePreferences, targetKey bool) *ExpressionNode {
|
||||||
elMap := list.New()
|
|
||||||
elMap.PushBack(candidate)
|
|
||||||
return elMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTraversalTree(path []interface{}, traversePrefs traversePreferences) *ExpressionNode {
|
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
return &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
return &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
||||||
} else if len(path) == 1 {
|
} else if len(path) == 1 {
|
||||||
return &ExpressionNode{Operation: &Operation{OperationType: traversePathOpType, Preferences: traversePrefs, Value: path[0], StringValue: fmt.Sprintf("%v", path[0])}}
|
lastPrefs := traversePrefs
|
||||||
|
if targetKey {
|
||||||
|
err := copier.Copy(&lastPrefs, traversePrefs)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
lastPrefs.IncludeMapKeys = true
|
||||||
|
lastPrefs.DontIncludeMapValues = true
|
||||||
|
}
|
||||||
|
return &ExpressionNode{Operation: &Operation{OperationType: traversePathOpType, Preferences: lastPrefs, Value: path[0], StringValue: fmt.Sprintf("%v", path[0])}}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExpressionNode{
|
return &ExpressionNode{
|
||||||
Operation: &Operation{OperationType: shortPipeOpType},
|
Operation: &Operation{OperationType: shortPipeOpType},
|
||||||
Lhs: createTraversalTree(path[0:1], traversePrefs),
|
Lhs: createTraversalTree(path[0:1], traversePrefs, false),
|
||||||
Rhs: createTraversalTree(path[1:], traversePrefs),
|
Rhs: createTraversalTree(path[1:], traversePrefs, targetKey),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ type expressionScenario struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testScenario(t *testing.T, s *expressionScenario) {
|
func testScenario(t *testing.T, s *expressionScenario) {
|
||||||
var results *list.List
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
node, err := NewExpressionParser().ParseExpression(s.expression)
|
node, err := NewExpressionParser().ParseExpression(s.expression)
|
||||||
@@ -66,13 +65,13 @@ func testScenario(t *testing.T, s *expressionScenario) {
|
|||||||
os.Setenv("myenv", s.environmentVariable)
|
os.Setenv("myenv", s.environmentVariable)
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err = NewDataTreeNavigator().GetMatchingNodes(inputs, node)
|
context, err := NewDataTreeNavigator().GetMatchingNodes(Context{MatchingNodes: inputs}, node)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(fmt.Errorf("%v: %v", err, s.expression))
|
t.Error(fmt.Errorf("%v: %v", err, s.expression))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
test.AssertResultComplexWithContext(t, s.expected, resultsToString(results), fmt.Sprintf("exp: %v\ndoc: %v", s.expression, s.document))
|
test.AssertResultComplexWithContext(t, s.expected, resultsToString(context.MatchingNodes), fmt.Sprintf("exp: %v\ndoc: %v", s.expression, s.document))
|
||||||
}
|
}
|
||||||
|
|
||||||
func resultsToString(results *list.List) []string {
|
func resultsToString(results *list.List) []string {
|
||||||
@@ -252,12 +251,12 @@ func documentOutput(t *testing.T, w *bufio.Writer, s expressionScenario, formatt
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results, err := NewDataTreeNavigator().GetMatchingNodes(inputs, node)
|
context, err := NewDataTreeNavigator().GetMatchingNodes(Context{MatchingNodes: inputs}, node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err, s.expression)
|
t.Error(err, s.expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = printer.PrintResults(results)
|
err = printer.PrintResults(context.MatchingNodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err, s.expression)
|
t.Error(err, s.expression)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,14 +74,14 @@ func (p *resultsPrinter) safelyFlush(writer *bufio.Writer) {
|
|||||||
|
|
||||||
func (p *resultsPrinter) PrintResults(matchingNodes *list.List) error {
|
func (p *resultsPrinter) PrintResults(matchingNodes *list.List) error {
|
||||||
log.Debug("PrintResults for %v matches", matchingNodes.Len())
|
log.Debug("PrintResults for %v matches", matchingNodes.Len())
|
||||||
var err error
|
|
||||||
if p.outputToJSON {
|
if p.outputToJSON {
|
||||||
explodeOp := Operation{OperationType: explodeOpType}
|
explodeOp := Operation{OperationType: explodeOpType}
|
||||||
explodeNode := ExpressionNode{Operation: &explodeOp}
|
explodeNode := ExpressionNode{Operation: &explodeOp}
|
||||||
matchingNodes, err = p.treeNavigator.GetMatchingNodes(matchingNodes, &explodeNode)
|
context, err := p.treeNavigator.GetMatchingNodes(Context{MatchingNodes: matchingNodes}, &explodeNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
matchingNodes = context.MatchingNodes
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferedWriter := bufio.NewWriter(p.writer)
|
bufferedWriter := bufio.NewWriter(p.writer)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package yqlib
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"container/list"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -16,6 +17,12 @@ a: apple
|
|||||||
a: coconut
|
a: coconut
|
||||||
`
|
`
|
||||||
|
|
||||||
|
func nodeToList(candidate *CandidateNode) *list.List {
|
||||||
|
elMap := list.New()
|
||||||
|
elMap.PushBack(candidate)
|
||||||
|
return elMap
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrinterMultipleDocsInSequence(t *testing.T) {
|
func TestPrinterMultipleDocsInSequence(t *testing.T) {
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
var writer = bufio.NewWriter(&output)
|
var writer = bufio.NewWriter(&output)
|
||||||
@@ -27,13 +34,13 @@ func TestPrinterMultipleDocsInSequence(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
el := inputs.Front()
|
el := inputs.Front()
|
||||||
sample1 := nodeToMap(el.Value.(*CandidateNode))
|
sample1 := nodeToList(el.Value.(*CandidateNode))
|
||||||
|
|
||||||
el = el.Next()
|
el = el.Next()
|
||||||
sample2 := nodeToMap(el.Value.(*CandidateNode))
|
sample2 := nodeToList(el.Value.(*CandidateNode))
|
||||||
|
|
||||||
el = el.Next()
|
el = el.Next()
|
||||||
sample3 := nodeToMap(el.Value.(*CandidateNode))
|
sample3 := nodeToList(el.Value.(*CandidateNode))
|
||||||
|
|
||||||
err = printer.PrintResults(sample1)
|
err = printer.PrintResults(sample1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -68,19 +75,19 @@ func TestPrinterMultipleFilesInSequence(t *testing.T) {
|
|||||||
elNode := el.Value.(*CandidateNode)
|
elNode := el.Value.(*CandidateNode)
|
||||||
elNode.Document = 0
|
elNode.Document = 0
|
||||||
elNode.FileIndex = 0
|
elNode.FileIndex = 0
|
||||||
sample1 := nodeToMap(elNode)
|
sample1 := nodeToList(elNode)
|
||||||
|
|
||||||
el = el.Next()
|
el = el.Next()
|
||||||
elNode = el.Value.(*CandidateNode)
|
elNode = el.Value.(*CandidateNode)
|
||||||
elNode.Document = 0
|
elNode.Document = 0
|
||||||
elNode.FileIndex = 1
|
elNode.FileIndex = 1
|
||||||
sample2 := nodeToMap(elNode)
|
sample2 := nodeToList(elNode)
|
||||||
|
|
||||||
el = el.Next()
|
el = el.Next()
|
||||||
elNode = el.Value.(*CandidateNode)
|
elNode = el.Value.(*CandidateNode)
|
||||||
elNode.Document = 0
|
elNode.Document = 0
|
||||||
elNode.FileIndex = 2
|
elNode.FileIndex = 2
|
||||||
sample3 := nodeToMap(elNode)
|
sample3 := nodeToList(elNode)
|
||||||
|
|
||||||
err = printer.PrintResults(sample1)
|
err = printer.PrintResults(sample1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ func (s *streamEvaluator) EvaluateNew(expression string, printer Printer) error
|
|||||||
inputList := list.New()
|
inputList := list.New()
|
||||||
inputList.PushBack(candidateNode)
|
inputList.PushBack(candidateNode)
|
||||||
|
|
||||||
matches, errorParsing := s.treeNavigator.GetMatchingNodes(inputList, node)
|
result, errorParsing := s.treeNavigator.GetMatchingNodes(Context{MatchingNodes: inputList}, node)
|
||||||
if errorParsing != nil {
|
if errorParsing != nil {
|
||||||
return errorParsing
|
return errorParsing
|
||||||
}
|
}
|
||||||
return printer.PrintResults(matches)
|
return printer.PrintResults(result.MatchingNodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer) error {
|
func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer) error {
|
||||||
@@ -97,11 +97,11 @@ func (s *streamEvaluator) Evaluate(filename string, reader io.Reader, node *Expr
|
|||||||
inputList := list.New()
|
inputList := list.New()
|
||||||
inputList.PushBack(candidateNode)
|
inputList.PushBack(candidateNode)
|
||||||
|
|
||||||
matches, errorParsing := s.treeNavigator.GetMatchingNodes(inputList, node)
|
result, errorParsing := s.treeNavigator.GetMatchingNodes(Context{MatchingNodes: inputList}, node)
|
||||||
if errorParsing != nil {
|
if errorParsing != nil {
|
||||||
return errorParsing
|
return errorParsing
|
||||||
}
|
}
|
||||||
err := printer.PrintResults(matches)
|
err := printer.PrintResults(result.MatchingNodes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
- increment version in snapcraft.yaml
|
- increment version in snapcraft.yaml
|
||||||
- increment version in github-action/Dockerfile
|
- increment version in github-action/Dockerfile
|
||||||
- make sure local build passes
|
- make sure local build passes
|
||||||
|
- commit version update changes
|
||||||
- tag git with same version number
|
- tag git with same version number
|
||||||
- commit vX tag - this will trigger github actions
|
- commit vX tag - this will trigger github actions
|
||||||
- use github actions to publish docker and make github release
|
- use github actions to publish docker and make github release
|
||||||
- check github updated yq action in marketplace
|
- check github updated yq action in marketplace
|
||||||
|
|
||||||
|
|
||||||
- snapcraft
|
- snapcraft
|
||||||
- will auto create a candidate, test it works then promote
|
- will auto create a candidate, test it works then promote
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: yq
|
name: yq
|
||||||
version: '4.4.0'
|
version: '4.6.1'
|
||||||
summary: A lightweight and portable command-line YAML processor
|
summary: A lightweight and portable command-line YAML processor
|
||||||
description: |
|
description: |
|
||||||
The aim of the project is to be the jq or sed of yaml files.
|
The aim of the project is to be the jq or sed of yaml files.
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user