mirror of
https://github.com/taigrr/yq
synced 2025-01-18 04:53:17 -08: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(`\*[\+|\?d]*`), multiplyWithPrefs())
|
||||||
lexer.Add([]byte(`\+`), opToken(addOpType))
|
lexer.Add([]byte(`\+`), opToken(addOpType))
|
||||||
lexer.Add([]byte(`\+=`), opToken(addAssignOpType))
|
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(`\$[a-zA-Z_-0-9]+`), getVariableOpToken())
|
||||||
lexer.Add([]byte(`as`), opToken(assignVariableOpType))
|
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 assignOpType = &operationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: assignUpdateOperator}
|
||||||
var addAssignOpType = &operationType{Type: "ADD_ASSIGN", NumArgs: 2, Precedence: 40, Handler: addAssignOperator}
|
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 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}
|
||||||
@ -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 multiplyOpType = &operationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 42, Handler: multiplyOperator}
|
||||||
var addOpType = &operationType{Type: "ADD", NumArgs: 2, Precedence: 42, Handler: addOperator}
|
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 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}
|
||||||
|
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…
x
Reference in New Issue
Block a user