mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
Added subtract operator (numbers only)
This commit is contained in:
parent
0249f00bd5
commit
12d3425b4a
71
pkg/yqlib/doc/Subtract.md
Normal file
71
pkg/yqlib/doc/Subtract.md
Normal file
@ -0,0 +1,71 @@
|
||||
|
||||
## Number subtraction - float
|
||||
If the lhs or rhs are floats then the expression will be calculated with floats.
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: 3
|
||||
b: 4.5
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a = .a - .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: -1.5
|
||||
b: 4.5
|
||||
```
|
||||
|
||||
## Number subtraction - float
|
||||
If the lhs or rhs are floats then the expression will be calculated with floats.
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: 3
|
||||
b: 4.5
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a = .a - .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: -1.5
|
||||
b: 4.5
|
||||
```
|
||||
|
||||
## Number subtraction - int
|
||||
If both the lhs and rhs are ints then the expression will be calculated with ints.
|
||||
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: 3
|
||||
b: 4
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.a = .a - .b' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: -1
|
||||
b: 4
|
||||
```
|
||||
|
||||
## Decrement numbers
|
||||
Given a sample.yml file of:
|
||||
```yaml
|
||||
a: 3
|
||||
b: 5
|
||||
```
|
||||
then
|
||||
```bash
|
||||
yq eval '.[] -= 1' sample.yml
|
||||
```
|
||||
will output
|
||||
```yaml
|
||||
a: 2
|
||||
b: 4
|
||||
```
|
||||
|
@ -325,6 +325,8 @@ func initLexer() (*lex.Lexer, error) {
|
||||
lexer.Add([]byte(`\*[\+|\?d]*`), multiplyWithPrefs())
|
||||
lexer.Add([]byte(`\+`), opToken(addOpType))
|
||||
lexer.Add([]byte(`\+=`), opToken(addAssignOpType))
|
||||
lexer.Add([]byte(`\-`), opToken(subtractOpType))
|
||||
lexer.Add([]byte(`\-=`), opToken(subtractAssignOpType))
|
||||
lexer.Add([]byte(`\$[a-zA-Z_-0-9]+`), getVariableOpToken())
|
||||
lexer.Add([]byte(`as`), opToken(assignVariableOpType))
|
||||
|
||||
|
@ -35,6 +35,7 @@ var pipeOpType = &operationType{Type: "PIPE", NumArgs: 2, Precedence: 30, Handle
|
||||
|
||||
var assignOpType = &operationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: assignUpdateOperator}
|
||||
var addAssignOpType = &operationType{Type: "ADD_ASSIGN", NumArgs: 2, Precedence: 40, Handler: addAssignOperator}
|
||||
var subtractAssignOpType = &operationType{Type: "SUBTRACT_ASSIGN", NumArgs: 2, Precedence: 40, Handler: subtractAssignOperator}
|
||||
|
||||
var assignAttributesOpType = &operationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: assignAttributesOperator}
|
||||
var assignStyleOpType = &operationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: assignStyleOperator}
|
||||
@ -46,6 +47,7 @@ var assignAliasOpType = &operationType{Type: "ASSIGN_ALIAS", NumArgs: 2, Precede
|
||||
|
||||
var multiplyOpType = &operationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 42, Handler: multiplyOperator}
|
||||
var addOpType = &operationType{Type: "ADD", NumArgs: 2, Precedence: 42, Handler: addOperator}
|
||||
var subtractOpType = &operationType{Type: "SUBTRACT", NumArgs: 2, Precedence: 42, Handler: subtractOperator}
|
||||
var alternativeOpType = &operationType{Type: "ALTERNATIVE", NumArgs: 2, Precedence: 42, Handler: alternativeOperator}
|
||||
|
||||
var equalsOpType = &operationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: equalsOperator}
|
||||
|
97
pkg/yqlib/operator_subtract.go
Normal file
97
pkg/yqlib/operator_subtract.go
Normal file
@ -0,0 +1,97 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"strconv"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func createSubtractOp(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode {
|
||||
return &ExpressionNode{Operation: &Operation{OperationType: subtractOpType},
|
||||
Lhs: lhs,
|
||||
Rhs: rhs}
|
||||
}
|
||||
|
||||
func subtractAssignOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||
assignmentOp := &Operation{OperationType: assignOpType}
|
||||
assignmentOp.UpdateAssign = true
|
||||
selfExpression := &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
||||
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: expressionNode.Lhs, Rhs: createSubtractOp(selfExpression, expressionNode.Rhs)}
|
||||
return d.GetMatchingNodes(context, assignmentOpNode)
|
||||
}
|
||||
|
||||
func subtractOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||
log.Debugf("Subtract operator")
|
||||
|
||||
return crossFunction(d, context, expressionNode, subtract)
|
||||
}
|
||||
|
||||
func subtract(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||
lhs.Node = unwrapDoc(lhs.Node)
|
||||
rhs.Node = unwrapDoc(rhs.Node)
|
||||
|
||||
lhsNode := lhs.Node
|
||||
|
||||
if lhsNode.Tag == "!!null" {
|
||||
return lhs.CreateChild(nil, rhs.Node), nil
|
||||
}
|
||||
|
||||
target := lhs.CreateChild(nil, &yaml.Node{})
|
||||
|
||||
switch lhsNode.Kind {
|
||||
case yaml.MappingNode:
|
||||
return nil, fmt.Errorf("Maps not yet supported for subtraction")
|
||||
case yaml.SequenceNode:
|
||||
return nil, fmt.Errorf("Sequences not yet supported for subtraction")
|
||||
// target.Node.Kind = yaml.SequenceNode
|
||||
// target.Node.Style = lhsNode.Style
|
||||
// target.Node.Tag = "!!seq"
|
||||
// target.Node.Content = append(lhsNode.Content, toNodes(rhs)...)
|
||||
case yaml.ScalarNode:
|
||||
if rhs.Node.Kind != yaml.ScalarNode {
|
||||
return nil, fmt.Errorf("%v (%v) cannot be added to a %v", rhs.Node.Tag, rhs.Path, lhsNode.Tag)
|
||||
}
|
||||
target.Node.Kind = yaml.ScalarNode
|
||||
target.Node.Style = lhsNode.Style
|
||||
return subtractScalars(target, lhsNode, rhs.Node)
|
||||
}
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func subtractScalars(target *CandidateNode, lhs *yaml.Node, rhs *yaml.Node) (*CandidateNode, error) {
|
||||
|
||||
if lhs.Tag == "!!str" {
|
||||
return nil, fmt.Errorf("strings cannot be subtracted")
|
||||
} else if lhs.Tag == "!!int" && rhs.Tag == "!!int" {
|
||||
lhsNum, err := strconv.Atoi(lhs.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rhsNum, err := strconv.Atoi(rhs.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := lhsNum - rhsNum
|
||||
target.Node.Tag = "!!int"
|
||||
target.Node.Value = fmt.Sprintf("%v", result)
|
||||
} else if (lhs.Tag == "!!int" || lhs.Tag == "!!float") && (rhs.Tag == "!!int" || rhs.Tag == "!!float") {
|
||||
lhsNum, err := strconv.ParseFloat(lhs.Value, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rhsNum, err := strconv.ParseFloat(rhs.Value, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := lhsNum - rhsNum
|
||||
target.Node.Tag = "!!float"
|
||||
target.Node.Value = fmt.Sprintf("%v", result)
|
||||
} else {
|
||||
return nil, fmt.Errorf("%v cannot be added to %v", lhs.Tag, rhs.Tag)
|
||||
}
|
||||
|
||||
return target, nil
|
||||
}
|
50
pkg/yqlib/operator_subtract_test.go
Normal file
50
pkg/yqlib/operator_subtract_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
package yqlib
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var subtractOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
description: "Number subtraction - float",
|
||||
subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",
|
||||
document: `{a: 3, b: 4.5}`,
|
||||
expression: `.a = .a - .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: -1.5, b: 4.5}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Number subtraction - float",
|
||||
subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",
|
||||
document: `{a: 3, b: 4.5}`,
|
||||
expression: `.a = .a - .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: -1.5, b: 4.5}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Number subtraction - int",
|
||||
subdescription: "If both the lhs and rhs are ints then the expression will be calculated with ints.",
|
||||
document: `{a: 3, b: 4}`,
|
||||
expression: `.a = .a - .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: -1, b: 4}\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Decrement numbers",
|
||||
document: `{a: 3, b: 5}`,
|
||||
expression: `.[] -= 1`,
|
||||
expected: []string{
|
||||
"D0, P[], (doc)::{a: 2, b: 4}\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestSubtractOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range subtractOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
documentScenarios(t, "Subtract", subtractOperatorScenarios)
|
||||
}
|
Loading…
Reference in New Issue
Block a user