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
```
## 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) {
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
lhs.Node = unwrapDoc(lhs.Node)
rhs.Node = unwrapDoc(rhs.Node)
owner := lhs
lhsTrue, errDecoding := isTruthy(lhs)
if errDecoding != nil {
return nil, errDecoding
if lhs == nil && rhs == nil {
owner = &CandidateNode{}
} else if lhs == nil {
owner = rhs
}
rhsTrue, errDecoding := isTruthy(rhs)
if errDecoding != nil {
return nil, errDecoding
}
var errDecoding error
lhsTrue := false
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")
return crossFunction(d, context, expressionNode, performBoolOp(
func(b1 bool, b2 bool) bool {
log.Debugf("-- peformingOrOp with %v and %v", b1, b2)
return b1 || b2
}), false)
}), true)
}
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(
func(b1 bool, b2 bool) bool {
return b1 && b2
}), false)
}), true)
}
func notOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {

View File

@ -12,6 +12,22 @@ var booleanOperatorScenarios = []expressionScenario{
"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",
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) {
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) {
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
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)
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) {
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",
},
},
{
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) {

View File

@ -14,6 +14,16 @@ var selectOperatorScenarios = []expressionScenario{
"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,
document: `[{animal: cat, legs: {cool: true}}, {animal: fish}]`,