Compare commits

..

1 Commits

Author SHA1 Message Date
vomba
1f89111452
fix: preserve correct parent references in explode merge anchor reconstruction
When explode resolves merge anchors (<<:), items copied from the
alias target retained the original parent pointer instead of being
set to the enclosing node being reconstructed. This caused GetPath()
to return wrong paths for children of merge-anchored nodes, making
subsequent merge operations target the wrong LHS keys.

In fixedReconstructAliasedMap, set copied.Parent = node after copy.

In reconstructAliasedMap (non-spec path), replace AddChild (which
creates sequence-style numeric-index entries) with AddKeyValueChild
to properly reconstruct mapping key-value pairs. AddKeyValueChild
also correctly sets parent references via SetParent.
2026-06-08 09:39:43 +02:00
29 changed files with 93 additions and 173 deletions

View File

@ -44,7 +44,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -55,7 +55,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@ -69,4 +69,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2

View File

@ -43,7 +43,7 @@ jobs:
man.md
- name: Install cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
uses: sigstore/cosign-installer@7e8b541eb2e61bf99390e1afd4be13a184e9ebc5 # v3.10.1
- name: Cross compile
run: |

View File

@ -73,6 +73,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
sarif_file: results.sarif

View File

@ -1,4 +1,4 @@
FROM golang:1.26.4@sha256:11fd8f7f63db3b6fb198797042ba4c40a4a34dc83325d3328ca3bc4bb7726786 AS builder
FROM golang:1.26.4@sha256:68cb6d68bed024785b69195b89af7ac7a444f27791435f98647edff595aa0479 AS builder
WORKDIR /go/src/mikefarah/yq
@ -10,7 +10,7 @@ RUN ./scripts/acceptance.sh
# Choose alpine as a base image to make this useful for CI, as many
# CI tools expect an interactive shell inside the container
FROM alpine:3@sha256:a2d49ea686c2adfe3c992e47dc3b5e7fa6e6b5055609400dc2acaeb241c829f4 AS production
FROM alpine:3@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11 AS production
LABEL maintainer="Mike Farah <mikefarah@users.noreply.github.com>"
COPY --from=builder /go/src/mikefarah/yq/yq /usr/bin/yq

View File

@ -1,4 +1,4 @@
FROM golang:1.26.4@sha256:11fd8f7f63db3b6fb198797042ba4c40a4a34dc83325d3328ca3bc4bb7726786
FROM golang:1.26.4@sha256:68cb6d68bed024785b69195b89af7ac7a444f27791435f98647edff595aa0479
COPY scripts/devtools.sh /opt/devtools.sh

8
go.mod
View File

@ -19,10 +19,10 @@ require (
github.com/spf13/pflag v1.0.10
github.com/yuin/gopher-lua v1.1.2
github.com/zclconf/go-cty v1.18.1
go.yaml.in/yaml/v4 v4.0.0-rc.5
go.yaml.in/yaml/v4 v4.0.0-rc.4
golang.org/x/mod v0.36.0
golang.org/x/net v0.55.0
golang.org/x/text v0.38.0
golang.org/x/text v0.37.0
)
require (
@ -33,9 +33,9 @@ require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/tools v0.45.0 // indirect
golang.org/x/tools v0.44.0 // indirect
)
go 1.25.0

16
go.sum
View File

@ -68,21 +68,21 @@ github.com/zclconf/go-cty v1.18.1/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+M
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v4 v4.0.0-rc.5 h1:JVliQq9EGOYaTgMi+k8BhUJyqcGk4ZqeuiN1Cirba9c=
go.yaml.in/yaml/v4 v4.0.0-rc.5/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -46,11 +46,7 @@ const (
func createStringScalarNode(stringValue string) *CandidateNode {
var node = &CandidateNode{Kind: ScalarNode}
node.Value = stringValue
if stringValue == "<<" {
node.Tag = "!!merge"
} else {
node.Tag = "!!str"
}
node.Tag = "!!str"
return node
}

View File

@ -106,31 +106,6 @@ func TestCreateScalarNodeScenarios(t *testing.T) {
}
}
type createStringScalarNodeScenario struct {
stringValue string
expectedTag string
}
var createStringScalarNodeScenarios = []createStringScalarNodeScenario{
{
stringValue: "yourKey",
expectedTag: "!!str",
},
{
stringValue: "<<",
expectedTag: "!!merge",
},
}
func TestCreateStringScalarNodeScenarios(t *testing.T) {
for _, tt := range createStringScalarNodeScenarios {
actual := createStringScalarNode(tt.stringValue)
test.AssertResultWithContext(t, tt.stringValue, actual.Value, fmt.Sprintf("Value for: %v", tt.stringValue))
test.AssertResultWithContext(t, tt.expectedTag, actual.Tag, fmt.Sprintf("Tag for: %v", tt.stringValue))
test.AssertResultWithContext(t, ScalarNode, actual.Kind, fmt.Sprintf("Kind for: %v", tt.stringValue))
}
}
func TestGetKeyForMapValue(t *testing.T) {
key := createStringScalarNode("yourKey")
n := CandidateNode{Key: key, Value: "meow", document: 3}

View File

@ -2,7 +2,7 @@
Use the `alias` and `anchor` operators to read and write yaml aliases and anchors. The `explode` operator normalises a yaml file (dereference (or expands) aliases and remove anchor names).
`yq` supports merge keys (like `<<: *blah`) from YAML 1.1. These are no longer part of the YAML 1.2 standard, but remain common in practice. Plain `<<:` keys are recognised as merge keys and round-trip as `<<:` without an explicit `!!merge` tag. When the source uses an explicit `!!merge` tag, that is preserved on output. Internally, when `yq` synthesises a `<<` map key (for example during merge operations), it tags the key as `!!merge` rather than `!!str`.
`yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag.
## NOTE --yaml-fix-merge-anchor-to-spec flag
@ -32,7 +32,7 @@ Given a sample.yml file of:
r: 10
- &SMALL
r: 1
- <<: *CENTRE
- !!merge <<: *CENTRE
r: 10
```
then
@ -213,10 +213,10 @@ item_value: &item_value
value: true
thingOne:
name: item_1
<<: *item_value
!!merge <<: *item_value
thingTwo:
name: item_2
<<: *item_value
!!merge <<: *item_value
```
then
```bash
@ -231,7 +231,7 @@ thingOne:
value: false
thingTwo:
name: item_2
<<: *item_value
!!merge <<: *item_value
```
## LEGACY: Explode with merge anchors
@ -249,13 +249,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -298,7 +298,7 @@ Given a sample.yml file of:
r: 10
- &SMALL
r: 1
- <<:
- !!merge <<:
- *CENTRE
- *BIG
```
@ -328,7 +328,7 @@ Given a sample.yml file of:
r: 10
- &SMALL
r: 1
- <<:
- !!merge <<:
- *BIG
- *LEFT
- *SMALL
@ -361,13 +361,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -411,7 +411,7 @@ Given a sample.yml file of:
r: 10
- &SMALL
r: 1
- <<:
- !!merge <<:
- *CENTRE
- *BIG
```
@ -442,7 +442,7 @@ Given a sample.yml file of:
r: 10
- &SMALL
r: 1
- <<:
- !!merge <<:
- *BIG
- *LEFT
- *SMALL
@ -467,7 +467,7 @@ Given a sample.yml file of:
```yaml
a:
b: &b 42
<<:
!!merge <<:
c: *b
```
then

View File

@ -55,7 +55,7 @@ yq '.a = .a / 0 | .b = .b / 0' sample.yml
```
will output
```yaml
a: +Inf
b: -Inf
a: !!float +Inf
b: !!float -Inf
```

View File

@ -2,7 +2,7 @@
Use the `alias` and `anchor` operators to read and write yaml aliases and anchors. The `explode` operator normalises a yaml file (dereference (or expands) aliases and remove anchor names).
`yq` supports merge keys (like `<<: *blah`) from YAML 1.1. These are no longer part of the YAML 1.2 standard, but remain common in practice. Plain `<<:` keys are recognised as merge keys and round-trip as `<<:` without an explicit `!!merge` tag. When the source uses an explicit `!!merge` tag, that is preserved on output. Internally, when `yq` synthesises a `<<` map key (for example during merge operations), it tags the key as `!!merge` rather than `!!str`.
`yq` supports merge aliases (like `<<: *blah`) however this is no longer in the standard yaml spec (1.2) and so `yq` will automatically add the `!!merge` tag to these nodes as it is effectively a custom tag.
## NOTE --yaml-fix-merge-anchor-to-spec flag

View File

@ -34,7 +34,7 @@ yq '.a = .a % .b' sample.yml
```
will output
```yaml
a: 2
a: !!float 2
b: 2.5
```
@ -69,7 +69,7 @@ yq '.a = .a % .b' sample.yml
```
will output
```yaml
a: NaN
a: !!float NaN
b: 0
```

View File

@ -471,13 +471,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -487,7 +487,7 @@ yq '.foobar * .foobarList' sample.yml
will output
```yaml
c: foobarList_c
<<:
!!merge <<:
- *foo
- *bar
thing: foobar_thing

View File

@ -131,13 +131,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -147,7 +147,7 @@ yq '.foobar | [..]' sample.yml
will output
```yaml
- c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
- foobar_c
- *foo

View File

@ -294,13 +294,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -325,13 +325,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -376,13 +376,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -409,13 +409,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -442,13 +442,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -477,13 +477,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -513,13 +513,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -546,13 +546,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -579,13 +579,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then
@ -614,13 +614,13 @@ bar: &bar
c: bar_c
foobarList:
b: foobarList_b
<<:
!!merge <<:
- *foo
- *bar
c: foobarList_c
foobar:
c: foobar_c
<<: *foo
!!merge <<: *foo
thing: foobar_thing
```
then

View File

@ -230,7 +230,8 @@ var goccyYamlFormatScenarios = []formatScenario{
description: "merge anchor",
skipDoc: true,
input: "a: &remember\n c: mike\nb:\n <<: *remember",
expected: "a: &remember\n c: mike\nb:\n <<: *remember\n",
// fine to have !!merge as that's what the current impl does
expected: "a: &remember\n c: mike\nb:\n !!merge <<: *remember\n",
},
{
description: "custom tag",

View File

@ -24,7 +24,7 @@ type parseSnippetScenario struct {
var parseSnippetScenarios = []parseSnippetScenario{
{
snippet: ":",
expectedError: "go-yaml load error in parser (while parsing a block mapping) at L1.C1: did not find expected key",
expectedError: "yaml: while parsing a block mapping at <unknown position>: did not find expected key",
},
{
snippet: "",

View File

@ -31,7 +31,7 @@ thingOne:
value: false
thingTwo:
name: item_2
<<: *item_value
!!merge <<: *item_value
`
var explodeMergeAnchorsFixedExpected = `D0, P[], (!!map)::foo:
@ -311,63 +311,7 @@ var badAnchorOperatorScenarios = []expressionScenario{
},
}
var mixedMergeTagStyleDocument = `
constants:
errorResponse: &errorResponse
status: 200
endpoints:
- condition: true
!!merge <<: *errorResponse
- condition: false
<<: *errorResponse
other:
!!merge <<: *errorResponse
somethingElse:
<<: *errorResponse
`
var mixedMergeTagStyleExplodedDocument = `
constants:
errorResponse:
status: 200
endpoints:
- condition: true
status: 200
- condition: false
status: 200
other:
status: 200
somethingElse:
status: 200
`
var anchorOperatorScenarios = []expressionScenario{
{
// mergeObjects previously skipped all !!merge-tagged nodes. Since !!merge only appears on
// << map keys, this meant applyAssignment was never called for the << key. It was later
// autocreated by createStringScalarNode("<<") with tag !!str, silently dropping !!merge.
// DontFollowAlias:true already prevents aliases being followed, so the skip was redundant.
// Old (buggy) output: "D0, P[], (!!map)::base: &base\n x: 1\ndest:\n <<: *base\n"
skipDoc: true,
description: "direct *+ preserves explicit !!merge tag on << key (regression for issue 2677)",
document: "base: &base\n x: 1\ndest:\n !!merge <<: *base\n",
expression: `. as $d | {} *+ $d`,
expected: []string{"D0, P[], (!!map)::base: &base\n x: 1\ndest:\n !!merge <<: *base\n"},
},
{
skipDoc: true,
description: "explicit !!merge tag on << key is preserved through ireduce merge",
document: mixedMergeTagStyleDocument,
expression: `. as $item ireduce ({}; . *+ $item)`,
expected: []string{"D0, P[], (!!map)::" + mixedMergeTagStyleDocument},
},
{
skipDoc: true,
description: "explode expands << merge keys regardless of explicit tag style (!!merge or plain)",
document: mixedMergeTagStyleDocument,
expression: `explode(.)`,
expected: []string{"D0, P[], (!!map)::" + mixedMergeTagStyleExplodedDocument},
},
{
skipDoc: true,
description: "merge anchor to alias alias",

View File

@ -45,7 +45,7 @@ var divideOperatorScenarios = []expressionScenario{
document: `{a: 1, b: -1}`,
expression: `.a = .a / 0 | .b = .b / 0`,
expected: []string{
"D0, P[], (!!map)::{a: +Inf, b: -Inf}\n",
"D0, P[], (!!map)::{a: !!float +Inf, b: !!float -Inf}\n",
},
},
{

View File

@ -77,7 +77,7 @@ var loadScenarios = []expressionScenario{
document: `{something: {file: "thing.yml"}, over: {here: [{file: "thing.yml"}]}}`,
expression: `(.. | select(has("file"))) |= load("../../examples/" + .file)`,
expected: []string{
"D0, P[], (!!map)::{something: {a: apple is included, b: cool.}, over: {here: [{a: apple is included,\n b: cool.}]}}\n",
"D0, P[], (!!map)::{something: {a: apple is included, b: cool.}, over: {here: [{a: apple is included, b: cool.}]}}\n",
},
},
{

View File

@ -37,7 +37,7 @@ var moduloOperatorScenarios = []expressionScenario{
document: `{a: 12, b: 2.5}`,
expression: `.a = .a % .b`,
expected: []string{
"D0, P[], (!!map)::{a: 2, b: 2.5}\n",
"D0, P[], (!!map)::{a: !!float 2, b: 2.5}\n",
},
},
{
@ -53,7 +53,7 @@ var moduloOperatorScenarios = []expressionScenario{
document: `{a: 1.1, b: 0}`,
expression: `.a = .a % .b`,
expected: []string{
"D0, P[], (!!map)::{a: NaN, b: 0}\n",
"D0, P[], (!!map)::{a: !!float NaN, b: 0}\n",
},
},
{
@ -70,7 +70,7 @@ var moduloOperatorScenarios = []expressionScenario{
document: "a: 2\nb: !goat 2.3",
expression: `.a = .a % .b`,
expected: []string{
"D0, P[], (!!map)::a: 2\nb: !goat 2.3\n",
"D0, P[], (!!map)::a: !!float 2\nb: !goat 2.3\n",
},
},
{

View File

@ -189,6 +189,10 @@ func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs
log.Debugf("going to applied assignment to LHS: %v with RHS: %v", NodeToString(lhs), NodeToString(candidate))
if candidate.Tag == "!!merge" {
continue
}
err := applyAssignment(d, context, pathIndexToStartFrom, lhs, candidate, preferences)
if err != nil {
return nil, err

View File

@ -121,7 +121,7 @@ var multiplyOperatorScenarios = []expressionScenario{
document: mergeArrayWithAnchors,
expression: `. * .`,
expected: []string{
"D0, P[], (!!map)::sample:\n - &a\n - <<: *a\n",
"D0, P[], (!!map)::sample:\n - &a\n - !!merge <<: *a\n",
},
},
{
@ -514,7 +514,7 @@ var multiplyOperatorScenarios = []expressionScenario{
environmentVariables: map[string]string{"originalPath": ".myArray", "otherPath": ".newArray", "idPath": ".a"},
expression: mergeExpression,
expected: []string{
"D0, P[], (!!map)::{myArray: [{a: apple, b: appleB2}, {a: kiwi, b: kiwiB}, {a: banana, b: bananaB, c: bananaC},\n {a: dingo, c: dingoC}], something: else}\n",
"D0, P[], (!!map)::{myArray: [{a: apple, b: appleB2}, {a: kiwi, b: kiwiB}, {a: banana, b: bananaB, c: bananaC}, {a: dingo, c: dingoC}], something: else}\n",
},
},
{
@ -546,7 +546,7 @@ var multiplyOperatorScenarios = []expressionScenario{
document: mergeDocSample,
expression: `.foobar * .foobarList`,
expected: []string{
"D0, P[foobar], (!!map)::c: foobarList_c\n<<: [*foo, *bar]\nthing: foobar_thing\nb: foobarList_b\n",
"D0, P[foobar], (!!map)::c: foobarList_c\n!!merge <<: [*foo, *bar]\nthing: foobar_thing\nb: foobarList_b\n",
},
},
{
@ -554,7 +554,7 @@ var multiplyOperatorScenarios = []expressionScenario{
document: document,
expression: `.b * .c`,
expected: []string{
"D0, P[b], (!!map)::{name: dog, <<: *cat}\n",
"D0, P[b], (!!map)::{name: dog, \"<<\": *cat}\n",
},
},
{
@ -581,7 +581,7 @@ var multiplyOperatorScenarios = []expressionScenario{
document: "a: 2\nb: !goat 3.5",
expression: ".a = .a * .b",
expected: []string{
"D0, P[], (!!map)::a: 7\nb: !goat 3.5\n",
"D0, P[], (!!map)::a: !!float 7\nb: !goat 3.5\n",
},
},
{

View File

@ -187,7 +187,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
document: mergeDocSample,
expression: `.foobar | [..]`,
expected: []string{
"D0, P[foobar], (!!seq)::- c: foobar_c\n <<: *foo\n thing: foobar_thing\n- foobar_c\n- *foo\n- foobar_thing\n",
"D0, P[foobar], (!!seq)::- c: foobar_c\n !!merge <<: *foo\n thing: foobar_thing\n- foobar_c\n- *foo\n- foobar_thing\n",
},
},
{
@ -195,7 +195,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
document: mergeDocSample,
expression: `.foobar | [...]`,
expected: []string{
"D0, P[foobar], (!!seq)::- c: foobar_c\n <<: *foo\n thing: foobar_thing\n- c\n- foobar_c\n- <<\n- *foo\n- thing\n- foobar_thing\n",
"D0, P[foobar], (!!seq)::- c: foobar_c\n !!merge <<: *foo\n thing: foobar_thing\n- c\n- foobar_c\n- !!merge <<\n- *foo\n- thing\n- foobar_thing\n",
},
},
{
@ -203,7 +203,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
document: mergeDocSample,
expression: `.foobarList | ..`,
expected: []string{
"D0, P[foobarList], (!!map)::b: foobarList_b\n<<: [*foo, *bar]\nc: foobarList_c\n",
"D0, P[foobarList], (!!map)::b: foobarList_b\n!!merge <<: [*foo, *bar]\nc: foobarList_c\n",
"D0, P[foobarList b], (!!str)::foobarList_b\n",
"D0, P[foobarList <<], (!!seq)::[*foo, *bar]\n",
"D0, P[foobarList << 0], (alias)::*foo\n",
@ -216,7 +216,7 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
document: mergeDocSample,
expression: `.foobarList | ...`,
expected: []string{
"D0, P[foobarList], (!!map)::b: foobarList_b\n<<: [*foo, *bar]\nc: foobarList_c\n",
"D0, P[foobarList], (!!map)::b: foobarList_b\n!!merge <<: [*foo, *bar]\nc: foobarList_c\n",
"D0, P[foobarList b], (!!str)::b\n",
"D0, P[foobarList b], (!!str)::foobarList_b\n",
"D0, P[foobarList <<], (!!merge)::<<\n",

View File

@ -98,7 +98,7 @@ var selectOperatorScenarios = []expressionScenario{
document: `[{animal: cat, legs: {cool: true}}, {animal: fish}]`,
expression: `(.[] | select(.legs.cool == true).canWalk) = true | (.[] | .alive.things) = "yes"`,
expected: []string{
"D0, P[], (!!seq)::[{animal: cat, legs: {cool: true}, canWalk: true, alive: {things: yes}}, {animal: fish,\n alive: {things: yes}}]\n",
"D0, P[], (!!seq)::[{animal: cat, legs: {cool: true}, canWalk: true, alive: {things: yes}}, {animal: fish, alive: {things: yes}}]\n",
},
},
{

View File

@ -37,7 +37,7 @@ var sortKeysOperatorScenarios = []expressionScenario{
document: `{bParent: {c: dog, array: [3,1,2]}, aParent: {z: donkey, x: [{c: yum, b: delish}, {b: ew, a: apple}]}}`,
expression: `sort_keys(..)`,
expected: []string{
"D0, P[], (!!map)::{aParent: {x: [{b: delish, c: yum}, {a: apple, b: ew}], z: donkey}, bParent: {array: [\n 3, 1, 2], c: dog}}\n",
"D0, P[], (!!map)::{aParent: {x: [{b: delish, c: yum}, {a: apple, b: ew}], z: donkey}, bParent: {array: [3, 1, 2], c: dog}}\n",
},
},
}

View File

@ -50,7 +50,7 @@ var styleOperatorScenarios = []expressionScenario{
document: "bing: &foo {x: z}\na:\n c: cat\n <<: [*foo]",
expression: `(... | select(tag=="!!str")) style="single"`,
expected: []string{
"D0, P[], (!!map)::'bing': &foo {'x': 'z'}\n'a':\n 'c': 'cat'\n <<: [*foo]\n",
"D0, P[], (!!map)::'bing': &foo {'x': 'z'}\n'a':\n 'c': 'cat'\n !!merge <<: [*foo]\n",
},
},
{

View File

@ -494,7 +494,7 @@ var traversePathOperatorScenarios = []expressionScenario{
document: mergeDocSample,
expression: `.foobar`,
expected: []string{
"D0, P[foobar], (!!map)::c: foobar_c\n<<: *foo\nthing: foobar_thing\n",
"D0, P[foobar], (!!map)::c: foobar_c\n!!merge <<: *foo\nthing: foobar_thing\n",
},
},
{
@ -518,7 +518,7 @@ var traversePathOperatorScenarios = []expressionScenario{
document: mergeDocSample,
expression: `.foobarList`,
expected: []string{
"D0, P[foobarList], (!!map)::b: foobarList_b\n<<: [*foo, *bar]\nc: foobarList_c\n",
"D0, P[foobarList], (!!map)::b: foobarList_b\n!!merge <<: [*foo, *bar]\nc: foobarList_c\n",
},
},
{