Fixed boolean op with empty context issue

This commit is contained in:
Mike Farah 2021-05-09 12:44:05 +10:00
parent 25d0787011
commit 37f3e21970
6 changed files with 111 additions and 14 deletions

View File

@ -93,3 +93,31 @@ will output
true true
``` ```
## Non exisitant key doesn't equal a value
Given a sample.yml file of:
```yaml
a: frog
```
then
```bash
yq eval 'select(.b != "thing")' sample.yml
```
will output
```yaml
a: frog
```
## Two non existant keys are equal
Given a sample.yml file of:
```yaml
a: frog
```
then
```bash
yq eval 'select(.b == .c)' sample.yml
```
will output
```yaml
a: frog
```

View File

@ -27,20 +27,37 @@ type boolOp func(bool, bool) bool
func performBoolOp(op boolOp) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func performBoolOp(op boolOp) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
lhs.Node = unwrapDoc(lhs.Node) owner := lhs
rhs.Node = unwrapDoc(rhs.Node)
lhsTrue, errDecoding := isTruthy(lhs) if lhs == nil && rhs == nil {
if errDecoding != nil { owner = &CandidateNode{}
return nil, errDecoding } else if lhs == nil {
owner = rhs
} }
rhsTrue, errDecoding := isTruthy(rhs) var errDecoding error
if errDecoding != nil { lhsTrue := false
return nil, errDecoding if lhs != nil {
} lhs.Node = unwrapDoc(lhs.Node)
lhsTrue, errDecoding = isTruthy(lhs)
return createBooleanCandidate(lhs, op(lhsTrue, rhsTrue)), nil if errDecoding != nil {
return nil, errDecoding
}
}
log.Debugf("-- lhsTrue", lhsTrue)
rhsTrue := false
if rhs != nil {
rhs.Node = unwrapDoc(rhs.Node)
rhsTrue, errDecoding = isTruthy(rhs)
if errDecoding != nil {
return nil, errDecoding
}
}
log.Debugf("-- rhsTrue", rhsTrue)
return createBooleanCandidate(owner, op(lhsTrue, rhsTrue)), nil
} }
} }
@ -48,8 +65,9 @@ func orOperator(d *dataTreeNavigator, context Context, expressionNode *Expressio
log.Debugf("-- orOp") log.Debugf("-- orOp")
return crossFunction(d, context, expressionNode, performBoolOp( return crossFunction(d, context, expressionNode, performBoolOp(
func(b1 bool, b2 bool) bool { func(b1 bool, b2 bool) bool {
log.Debugf("-- peformingOrOp with %v and %v", b1, b2)
return b1 || b2 return b1 || b2
}), false) }), true)
} }
func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
@ -57,7 +75,7 @@ func andOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
return crossFunction(d, context, expressionNode, performBoolOp( return crossFunction(d, context, expressionNode, performBoolOp(
func(b1 bool, b2 bool) bool { func(b1 bool, b2 bool) bool {
return b1 && b2 return b1 && b2
}), false) }), true)
} }
func notOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func notOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {

View File

@ -12,6 +12,22 @@ var booleanOperatorScenarios = []expressionScenario{
"D0, P[], (!!bool)::true\n", "D0, P[], (!!bool)::true\n",
}, },
}, },
{
skipDoc: true,
document: "b: hi",
expression: `select(.a or .b)`,
expected: []string{
"D0, P[], (doc)::b: hi\n",
},
},
{
skipDoc: true,
document: "b: hi",
expression: `select((.a and .b) | not)`,
expected: []string{
"D0, P[], (doc)::b: hi\n",
},
},
{ {
description: "AND example", description: "AND example",
expression: `true and false`, expression: `true and false`,

View File

@ -4,13 +4,22 @@ import "gopkg.in/yaml.v3"
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- equalsOperation") log.Debugf("-- equalsOperation")
return crossFunction(d, context, expressionNode, isEquals(false), false) return crossFunction(d, context, expressionNode, isEquals(false), true)
} }
func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
value := false value := false
if lhs == nil && rhs == nil {
owner := &CandidateNode{}
return createBooleanCandidate(owner, !flip), nil
} else if lhs == nil {
return createBooleanCandidate(rhs, flip), nil
} else if rhs == nil {
return createBooleanCandidate(lhs, flip), nil
}
lhsNode := unwrapDoc(lhs.Node) lhsNode := unwrapDoc(lhs.Node)
rhsNode := unwrapDoc(rhs.Node) rhsNode := unwrapDoc(rhs.Node)
@ -29,5 +38,5 @@ func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *Candid
func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("-- equalsOperation") log.Debugf("-- equalsOperation")
return crossFunction(d, context, expressionNode, isEquals(true), false) return crossFunction(d, context, expressionNode, isEquals(true), true)
} }

View File

@ -87,6 +87,22 @@ var equalsOperatorScenarios = []expressionScenario{
"D0, P[], (!!bool)::true\n", "D0, P[], (!!bool)::true\n",
}, },
}, },
{
description: "Non exisitant key doesn't equal a value",
document: "a: frog",
expression: `select(.b != "thing")`,
expected: []string{
"D0, P[], (doc)::a: frog\n",
},
},
{
description: "Two non existant keys are equal",
document: "a: frog",
expression: `select(.b == .c)`,
expected: []string{
"D0, P[], (doc)::a: frog\n",
},
},
} }
func TestEqualOperatorScenarios(t *testing.T) { func TestEqualOperatorScenarios(t *testing.T) {

View File

@ -14,6 +14,16 @@ var selectOperatorScenarios = []expressionScenario{
"D0, P[1], (!!str)::goat\n", "D0, P[1], (!!str)::goat\n",
}, },
}, },
{
skipDoc: true,
document: "a: hello",
document2: "b: world",
expression: `select(.a == "hello" or .b == "world")`,
expected: []string{
"D0, P[], (doc)::a: hello\n",
"D0, P[], (doc)::b: world\n",
},
},
{ {
skipDoc: true, skipDoc: true,
document: `[{animal: cat, legs: {cool: true}}, {animal: fish}]`, document: `[{animal: cat, legs: {cool: true}}, {animal: fish}]`,