mirror of
https://github.com/mikefarah/yq.git
synced 2024-11-12 13:48:06 +00:00
Add operator!
This commit is contained in:
parent
3d6a231722
commit
1ce30b25dc
107
pkg/yqlib/doc/Add.md
Normal file
107
pkg/yqlib/doc/Add.md
Normal file
@ -0,0 +1,107 @@
|
||||
Add behaves differently according to the type of the LHS:
|
||||
- arrays: concatenate
|
||||
- number scalars: arithmetic addition (soon)
|
||||
- string scalars: concatenate (soon)
|
||||
## Concatenate arrays
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
b:
|
||||
- 3
|
||||
- 4
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a + .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
```
|
||||
|
||||
## Concatenate null to array
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a + null' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- 1
|
||||
- 2
|
||||
```
|
||||
|
||||
## Add object to array
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
c:
|
||||
cat: meow
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a + .c' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- 1
|
||||
- 2
|
||||
- cat: meow
|
||||
```
|
||||
|
||||
## Add string to array
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a + "hello"' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
- 1
|
||||
- 2
|
||||
- hello
|
||||
```
|
||||
|
||||
## Update array (append)
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
b:
|
||||
- 3
|
||||
- 4
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a = .a + .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
b:
|
||||
- 3
|
||||
- 4
|
||||
```
|
||||
|
4
pkg/yqlib/doc/headers/Add.md
Normal file
4
pkg/yqlib/doc/headers/Add.md
Normal file
@ -0,0 +1,4 @@
|
||||
Add behaves differently according to the type of the LHS:
|
||||
- arrays: concatenate
|
||||
- number scalars: arithmetic addition (soon)
|
||||
- string scalars: concatenate (soon)
|
@ -36,7 +36,8 @@ var AssignStyle = &OperationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 4
|
||||
var AssignTag = &OperationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: AssignTagOperator}
|
||||
var AssignComment = &OperationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: AssignCommentsOperator}
|
||||
|
||||
var Multiply = &OperationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 40, Handler: MultiplyOperator}
|
||||
var Multiply = &OperationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 45, Handler: MultiplyOperator}
|
||||
var Add = &OperationType{Type: "ADD", NumArgs: 2, Precedence: 45, Handler: AddOperator}
|
||||
|
||||
var Equals = &OperationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: EqualsOperator}
|
||||
var CreateMap = &OperationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 40, Handler: CreateMapOperator}
|
||||
|
69
pkg/yqlib/operator_add.go
Normal file
69
pkg/yqlib/operator_add.go
Normal file
@ -0,0 +1,69 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"container/list"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func toNodes(candidates *list.List) []*yaml.Node {
|
||||
|
||||
if candidates.Len() == 0 {
|
||||
return []*yaml.Node{}
|
||||
}
|
||||
candidate := candidates.Front().Value.(*CandidateNode)
|
||||
|
||||
if candidate.Node.Tag == "!!null" {
|
||||
return []*yaml.Node{}
|
||||
}
|
||||
|
||||
switch candidate.Node.Kind {
|
||||
case yaml.SequenceNode:
|
||||
return candidate.Node.Content
|
||||
default:
|
||||
return []*yaml.Node{candidate.Node}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func AddOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
|
||||
log.Debugf("Add operator")
|
||||
var results = list.New()
|
||||
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for el := lhs.Front(); el != nil; el = el.Next() {
|
||||
lhsCandidate := el.Value.(*CandidateNode)
|
||||
lhsNode := UnwrapDoc(lhsCandidate.Node)
|
||||
|
||||
var newBlank = &CandidateNode{
|
||||
Path: lhsCandidate.Path,
|
||||
Document: lhsCandidate.Document,
|
||||
Filename: lhsCandidate.Filename,
|
||||
Node: &yaml.Node{},
|
||||
}
|
||||
|
||||
switch lhsNode.Kind {
|
||||
case yaml.MappingNode:
|
||||
return nil, fmt.Errorf("Maps not yet supported for addition")
|
||||
case yaml.SequenceNode:
|
||||
newBlank.Node.Kind = yaml.SequenceNode
|
||||
newBlank.Node.Style = lhsNode.Style
|
||||
newBlank.Node.Tag = "!!seq"
|
||||
newBlank.Node.Content = append(lhsNode.Content, toNodes(rhs)...)
|
||||
results.PushBack(newBlank)
|
||||
case yaml.ScalarNode:
|
||||
return nil, fmt.Errorf("Scalars not yet supported for addition")
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
55
pkg/yqlib/operator_add_test.go
Normal file
55
pkg/yqlib/operator_add_test.go
Normal file
@ -0,0 +1,55 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var addOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Concatenate arrays",
|
||||
document: `{a: [1,2], b: [3,4]}`,
|
||||
expression: `.a + .b`,
|
||||
expected: []string{
|
||||
"D0, P[a], (!!seq)::[1, 2, 3, 4]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Concatenate null to array",
|
||||
document: `{a: [1,2]}`,
|
||||
expression: `.a + null`,
|
||||
expected: []string{
|
||||
"D0, P[a], (!!seq)::[1, 2]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Add object to array",
|
||||
document: `{a: [1,2], c: {cat: meow}}`,
|
||||
expression: `.a + .c`,
|
||||
expected: []string{
|
||||
"D0, P[a], (!!seq)::[1, 2, {cat: meow}]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Add string to array",
|
||||
document: `{a: [1,2]}`,
|
||||
expression: `.a + "hello"`,
|
||||
expected: []string{
|
||||
"D0, P[a], (!!seq)::[1, 2, hello]\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Update array (append)",
|
||||
document: `{a: [1,2], b: [3,4]}`,
|
||||
expression: `.a = .a + .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: [1, 2, 3, 4], b: [3, 4]}\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestAddOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range addOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Add", addOperatorScenarios)
|
||||
}
|
@ -252,6 +252,7 @@ func initLexer() (*lex.Lexer, error) {
|
||||
lexer.Add([]byte(`\{`), literalToken(OpenCollectObject, false))
|
||||
lexer.Add([]byte(`\}`), literalToken(CloseCollectObject, true))
|
||||
lexer.Add([]byte(`\*`), opToken(Multiply))
|
||||
lexer.Add([]byte(`\+`), opToken(Add))
|
||||
|
||||
err := lexer.Compile()
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user