mirror of
https://github.com/taigrr/yq
synced 2025-01-18 04:53:17 -08:00
Compare commits
103 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -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.1"
|
Version = "4.5.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,4 @@
|
|||||||
FROM mikefarah/yq:4.4.1
|
FROM mikefarah/yq:4.5.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=
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -18,6 +18,20 @@ func (n *Context) SingleChildContext(candidate *CandidateNode) Context {
|
|||||||
return n.ChildContext(list)
|
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 {
|
func (n *Context) ChildContext(results *list.List) Context {
|
||||||
clone := Context{}
|
clone := Context{}
|
||||||
err := copier.Copy(&clone, n)
|
err := copier.Copy(&clone, n)
|
||||||
@@ -28,3 +42,13 @@ func (n *Context) ChildContext(results *list.List) Context {
|
|||||||
clone.MatchingNodes = results
|
clone.MatchingNodes = results
|
||||||
return clone
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ Given a sample.yml file of:
|
|||||||
a: {things: great}
|
a: {things: great}
|
||||||
b:
|
b:
|
||||||
also: "me"
|
also: "me"
|
||||||
|
|
||||||
```
|
```
|
||||||
then
|
then
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
0
pkg/yqlib/doc/Reduce.md
Normal file
0
pkg/yqlib/doc/Reduce.md
Normal file
@@ -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
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.
|
||||||
@@ -70,7 +70,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 "("
|
||||||
@@ -180,6 +182,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 : len(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 +252,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 +286,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))
|
||||||
@@ -304,6 +322,8 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`\*[\+|\?]*`), multiplyWithPrefs())
|
lexer.Add([]byte(`\*[\+|\?]*`), 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.Compile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -339,7 +359,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 +389,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 +412,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 +429,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: 5, 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}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ func assignUpdateOperator(d *dataTreeNavigator, context Context, expressionNode
|
|||||||
|
|
||||||
// does not update content or values
|
// does not update content or values
|
||||||
func assignAttributesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func assignAttributesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
log.Debug("getting lhs matching nodes for update")
|
||||||
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
|
|||||||
@@ -7,16 +7,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
contextToUse := context.Clone()
|
||||||
nodesToDelete, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
contextToUse.DontAutoCreate = true
|
||||||
|
nodesToDelete, err := d.GetMatchingNodes(contextToUse, expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
//need to iterate backwards to ensure correct indices when deleting multiple
|
||||||
for el := nodesToDelete.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := nodesToDelete.MatchingNodes.Back(); el != nil; el = el.Prev() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
|
if len(candidate.Path) > 0 {
|
||||||
deleteImmediateChildOp := &Operation{
|
deleteImmediateChildOp := &Operation{
|
||||||
OperationType: deleteImmediateChildOpType,
|
OperationType: deleteImmediateChildOpType,
|
||||||
Value: candidate.Path[len(candidate.Path)-1],
|
Value: candidate.Path[len(candidate.Path)-1],
|
||||||
@@ -24,14 +26,15 @@ func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
|
|
||||||
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(context, deleteImmediateChildOpNode)
|
_, err := d.GetMatchingNodes(contextToUse, deleteImmediateChildOpNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return context, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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}]`,
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package yqlib
|
package yqlib
|
||||||
|
|
||||||
|
import "gopkg.in/yaml.v3"
|
||||||
|
|
||||||
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- equalsOperation")
|
log.Debugf("-- equalsOperation")
|
||||||
return crossFunction(d, context, expressionNode, isEquals)
|
return crossFunction(d, context, expressionNode, isEquals(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEquals(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
|
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
value := false
|
value := false
|
||||||
|
|
||||||
lhsNode := unwrapDoc(lhs.Node)
|
lhsNode := unwrapDoc(lhs.Node)
|
||||||
@@ -13,9 +16,18 @@ func isEquals(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *Ca
|
|||||||
|
|
||||||
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)
|
log.Debugf("%v == %v ? %v", NodeToString(lhs), NodeToString(rhs), value)
|
||||||
|
if flip {
|
||||||
|
value = !value
|
||||||
|
}
|
||||||
return createBooleanCandidate(lhs, value), nil
|
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")`,
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs
|
|||||||
|
|
||||||
// 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, context.SingleChildContext(rhs), prefs)
|
err := recursiveDecent(d, results, context.SingleChildContext(rhs), prefs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -78,7 +78,12 @@ func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs
|
|||||||
}
|
}
|
||||||
|
|
||||||
for el := results.Front(); el != nil; el = el.Next() {
|
for el := results.Front(); el != nil; el = el.Next() {
|
||||||
err := applyAssignment(d, context, 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
|
||||||
}
|
}
|
||||||
@@ -88,7 +93,7 @@ func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs
|
|||||||
|
|
||||||
func applyAssignment(d *dataTreeNavigator, context Context, 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:]
|
||||||
|
|
||||||
@@ -101,7 +106,7 @@ func applyAssignment(d *dataTreeNavigator, context Context, pathIndexToStartFrom
|
|||||||
}
|
}
|
||||||
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(context.SingleChildContext(lhs), assignmentOpNode)
|
_, err := d.GetMatchingNodes(context.SingleChildContext(lhs), assignmentOpNode)
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,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}`,
|
||||||
@@ -100,16 +124,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:
|
|
||||||
also: "me"
|
|
||||||
`,
|
|
||||||
expression: `. * {"a":.b}`,
|
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"
|
|
||||||
`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
22
pkg/yqlib/operator_reduce_test.go
Normal file
22
pkg/yqlib/operator_reduce_test.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var reduceOperatorScenarios = []expressionScenario{
|
||||||
|
{
|
||||||
|
document: `[10,2, 5, 3]`,
|
||||||
|
expression: `.[] as $item ireduce (0; . + $item)`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[], (!!int)::20\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReduceOperatorScenarios(t *testing.T) {
|
||||||
|
for _, tt := range reduceOperatorScenarios {
|
||||||
|
testScenario(t, &tt)
|
||||||
|
}
|
||||||
|
// documentScenarios(t, "Reduce", reduceOperatorScenarios)
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ 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, context Context, prefs traversePreferences) (Context, error) {
|
func splat(d *dataTreeNavigator, context Context, prefs traversePreferences) (Context, error) {
|
||||||
@@ -20,7 +21,7 @@ func splat(d *dataTreeNavigator, context Context, prefs traversePreferences) (Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
func traversePathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func traversePathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- Traversing")
|
log.Debugf("-- traversePathOperator")
|
||||||
var matches = list.New()
|
var matches = list.New()
|
||||||
|
|
||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
@@ -75,6 +76,16 @@ func traverse(d *dataTreeNavigator, context Context, matchingNode *CandidateNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
|
|
||||||
|
//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 {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
|
||||||
// rhs is a collect expression that will yield indexes to retreive of the arrays
|
// rhs is a collect expression that will yield indexes to retreive of the arrays
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
@@ -83,7 +94,13 @@ func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode
|
|||||||
}
|
}
|
||||||
|
|
||||||
var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Content
|
var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Content
|
||||||
return traverseNodesWithArrayIndices(context, indicesToTraverse, traversePreferences{})
|
|
||||||
|
//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(context Context, indicesToTraverse []*yaml.Node, prefs traversePreferences) (Context, error) {
|
func traverseNodesWithArrayIndices(context Context, indicesToTraverse []*yaml.Node, prefs traversePreferences) (Context, error) {
|
||||||
@@ -198,11 +215,22 @@ func traverseMap(context Context, matchingNode *CandidateNode, key string, prefs
|
|||||||
if !prefs.DontAutoCreate && !context.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)
|
||||||
|
|
||||||
|
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)
|
candidateNode := matchingNode.CreateChild(key, valueNode)
|
||||||
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
results := list.New()
|
results := list.New()
|
||||||
i := 0
|
i := 0
|
||||||
@@ -237,13 +265,18 @@ 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)
|
||||||
}
|
}
|
||||||
|
if !prefs.DontIncludeMapValues {
|
||||||
|
log.Debug("including value")
|
||||||
candidateNode := candidate.CreateChild(key.Value, value)
|
candidateNode := candidate.CreateChild(key.Value, value)
|
||||||
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
newMatches.Set(candidateNode.GetKey(), candidateNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
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,6 +4,7 @@ import (
|
|||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -88,16 +89,25 @@ func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode {
|
|||||||
return owner.CreateChild(nil, node)
|
return owner.CreateChild(nil, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTraversalTree(path []interface{}, traversePrefs traversePreferences) *ExpressionNode {
|
func createTraversalTree(path []interface{}, traversePrefs traversePreferences, targetKey bool) *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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.1'
|
version: '4.5.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