Added plain assignment

This commit is contained in:
Mike Farah 2020-11-19 17:08:13 +11:00
parent 36084a60a9
commit 75044e480c
9 changed files with 87 additions and 11 deletions

View File

@ -1,4 +1,10 @@
Updates the LHS using the expression on the RHS. Note that the RHS runs against the _original_ LHS value, so that you can evaluate a new value based on the old (e.g. increment).
This operator is used to update node values. It can be used in either the:
### plain form: `=`
Which will assign the LHS node values to the RHS node values. The RHS expression is run against the matching nodes in the pipeline.
### relative form: `|=`
This will do a similar thing to the plain form, however, the RHS expression is run against _the LHS nodes_. This is useful for updating values based on old values, e.g. increment.
## Examples
### Update parent to be the child value
Given a sample.yml file of:
@ -17,6 +23,23 @@ a:
g: foof
```
### Update to be the sibling value
Given a sample.yml file of:
```yaml
a:
b: child
b: sibling
```
then
```bash
yq eval '.a = .b' sample.yml
```
will output
```yaml
a: sibling
b: sibling
```
### Updated multiple paths
Given a sample.yml file of:
```yaml
@ -36,6 +59,24 @@ c: potatoe
```
### Update string value
Given a sample.yml file of:
```yaml
a:
b: apple
```
then
```bash
yq eval '.a.b = "frog"' sample.yml
```
will output
```yaml
a:
b: frog
```
### Update string value via |=
Note there is no difference between `=` and `|=` when the RHS is a scalar
Given a sample.yml file of:
```yaml
a:

View File

@ -33,7 +33,7 @@ e: true
```
then
```bash
yq eval '(.. | select(tag == "!!int")) tag = "!!str"' sample.yml
yq eval '(.. | select(tag == "!!int")) tag= "!!str"' sample.yml
```
will output
```yaml

View File

@ -0,0 +1,7 @@
This operator is used to update node values. It can be used in either the:
### plain form: `=`
Which will assign the LHS node values to the RHS node values. The RHS expression is run against the matching nodes in the pipeline.
### relative form: `|=`
This will do a similar thing to the plain form, however, the RHS expression is run against _the LHS nodes_. This is useful for updating values based on old values, e.g. increment.

View File

@ -1 +0,0 @@
Updates the LHS using the expression on the RHS. Note that the RHS runs against the _original_ LHS value, so that you can evaluate a new value based on the old (e.g. increment).

View File

@ -37,8 +37,6 @@ var Union = &OperationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: U
var Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignUpdateOperator}
// TODO: implement this
var PlainAssign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignUpdateOperator}
var AssignAttributes = &OperationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: AssignAttributesOperator}
var AssignStyle = &OperationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: AssignStyleOperator}
var AssignTag = &OperationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: AssignTagOperator}

View File

@ -2,15 +2,28 @@ package yqlib
import "container/list"
type AssignOpPreferences struct {
UpdateAssign bool
}
func AssignUpdateOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
preferences := pathNode.Operation.Preferences.(*AssignOpPreferences)
var rhs *list.List
if !preferences.UpdateAssign {
rhs, err = d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
}
for el := lhs.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
rhs, err := d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
if preferences.UpdateAssign {
rhs, err = d.GetMatchingNodes(nodeToMap(candidate), pathNode.Rhs)
}
if err != nil {
return nil, err

View File

@ -13,6 +13,14 @@ var assignOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::{a: {g: foof}}\n",
},
},
{
description: "Update to be the sibling value",
document: `{a: {b: child}, b: sibling}`,
expression: `.a = .b`,
expected: []string{
"D0, P[], (doc)::{a: sibling, b: sibling}\n",
},
},
{
description: "Updated multiple paths",
document: `{a: fieldA, b: fieldB, c: fieldC}`,
@ -24,6 +32,15 @@ var assignOperatorScenarios = []expressionScenario{
{
description: "Update string value",
document: `{a: {b: apple}}`,
expression: `.a.b = "frog"`,
expected: []string{
"D0, P[], (doc)::{a: {b: frog}}\n",
},
},
{
description: "Update string value via |=",
subdescription: "Note there is no difference between `=` and `|=` when the RHS is a scalar",
document: `{a: {b: apple}}`,
expression: `.a.b |= "frog"`,
expected: []string{
"D0, P[], (doc)::{a: {b: frog}}\n",
@ -99,5 +116,5 @@ func TestAssignOperatorScenarios(t *testing.T) {
for _, tt := range assignOperatorScenarios {
testScenario(t, &tt)
}
documentScenarios(t, "Update Assign Operator", assignOperatorScenarios)
documentScenarios(t, "Assign Operator", assignOperatorScenarios)
}

View File

@ -112,6 +112,7 @@ func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *Candid
assignmentOp := &Operation{OperationType: AssignAttributes}
if rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode {
assignmentOp.OperationType = Assign
assignmentOp.Preferences = &AssignOpPreferences{false}
}
rhsOp := &Operation{OperationType: ValueOp, CandidateNode: rhs}

View File

@ -215,11 +215,11 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`collect`), opToken(Collect))
lexer.Add([]byte(`\s*==\s*`), opToken(Equals))
lexer.Add([]byte(`\s*=\s*`), opToken(PlainAssign))
lexer.Add([]byte(`\s*=\s*`), opTokenWithPrefs(Assign, nil, &AssignOpPreferences{false}))
lexer.Add([]byte(`del`), opToken(DeleteChild))
lexer.Add([]byte(`\s*\|=\s*`), opToken(Assign))
lexer.Add([]byte(`\s*\|=\s*`), opTokenWithPrefs(Assign, nil, &AssignOpPreferences{true}))
lexer.Add([]byte(`\[-?[0-9]+\]`), arrayIndextoken(false))
lexer.Add([]byte(`\.\[-?[0-9]+\]`), arrayIndextoken(true))
@ -303,7 +303,7 @@ func (p *pathTokeniser) Tokenise(path string) ([]*Token, error) {
if index != len(tokens)-1 && token.AssignOperation != nil &&
tokens[index+1].TokenType == OperationToken &&
tokens[index+1].Operation.OperationType == PlainAssign {
tokens[index+1].Operation.OperationType == Assign {
token.Operation = token.AssignOperation
skipNextToken = true
}