diff --git a/pkg/yqlib/operator_add.go b/pkg/yqlib/operator_add.go index 9e5b17ed..a0ca9711 100644 --- a/pkg/yqlib/operator_add.go +++ b/pkg/yqlib/operator_add.go @@ -39,7 +39,7 @@ func toNodes(candidate *CandidateNode) []*yaml.Node { func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("Add operator") - return crossFunction(d, context, expressionNode, add) + return crossFunction(d, context, expressionNode, add, false) } func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { diff --git a/pkg/yqlib/operator_alternative.go b/pkg/yqlib/operator_alternative.go index 3b310325..85ae2670 100644 --- a/pkg/yqlib/operator_alternative.go +++ b/pkg/yqlib/operator_alternative.go @@ -1,14 +1,14 @@ package yqlib -// corssFunction no matches -// can boolean use crossfunction - func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- alternative") - return crossFunction(d, context, expressionNode, alternativeFunc) + return crossFunction(d, context, expressionNode, alternativeFunc, true) } func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { + if lhs == nil { + return rhs, nil + } lhs.Node = unwrapDoc(lhs.Node) rhs.Node = unwrapDoc(rhs.Node) log.Debugf("Alternative LHS: %v", lhs.Node.Tag) diff --git a/pkg/yqlib/operator_alternative_test.go b/pkg/yqlib/operator_alternative_test.go index bb4b3ca6..b76c19d2 100644 --- a/pkg/yqlib/operator_alternative_test.go +++ b/pkg/yqlib/operator_alternative_test.go @@ -13,6 +13,14 @@ var alternativeOperatorScenarios = []expressionScenario{ "D0, P[a], (!!str)::bridge\n", }, }, + { + expression: `select(tag == "seq") // "cat"`, + skipDoc: true, + document: `a: frog`, + expected: []string{ + "D0, P[], (!!str)::cat\n", + }, + }, { description: "LHS is not defined", expression: `.a // "hello"`, diff --git a/pkg/yqlib/operator_booleans.go b/pkg/yqlib/operator_booleans.go index af689ab0..084e5f74 100644 --- a/pkg/yqlib/operator_booleans.go +++ b/pkg/yqlib/operator_booleans.go @@ -49,7 +49,7 @@ func orOperator(d *dataTreeNavigator, context Context, expressionNode *Expressio return crossFunction(d, context, expressionNode, performBoolOp( func(b1 bool, b2 bool) bool { return b1 || b2 - })) + }), false) } func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { @@ -57,7 +57,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) } func notOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { diff --git a/pkg/yqlib/operator_create_map.go b/pkg/yqlib/operator_create_map.go index dda93c46..cac23356 100644 --- a/pkg/yqlib/operator_create_map.go +++ b/pkg/yqlib/operator_create_map.go @@ -62,7 +62,7 @@ func sequenceFor(d *dataTreeNavigator, context Context, matchingNode *CandidateN } return &CandidateNode{Node: &node, Document: document, Path: path}, nil - }) + }, false) if err != nil { return nil, err diff --git a/pkg/yqlib/operator_equals.go b/pkg/yqlib/operator_equals.go index 2f444284..1edbccc4 100644 --- a/pkg/yqlib/operator_equals.go +++ b/pkg/yqlib/operator_equals.go @@ -4,7 +4,7 @@ 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)) + return crossFunction(d, context, expressionNode, isEquals(false), false) } func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { @@ -29,5 +29,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)) + return crossFunction(d, context, expressionNode, isEquals(true), false) } diff --git a/pkg/yqlib/operator_multiply.go b/pkg/yqlib/operator_multiply.go index eb892698..2cd94f5f 100644 --- a/pkg/yqlib/operator_multiply.go +++ b/pkg/yqlib/operator_multiply.go @@ -17,7 +17,7 @@ type multiplyPreferences struct { func multiplyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- MultiplyOperator") - return crossFunction(d, context, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences))) + return crossFunction(d, context, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences)), false) } func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { diff --git a/pkg/yqlib/operator_subtract.go b/pkg/yqlib/operator_subtract.go index a5ef6965..cb779896 100644 --- a/pkg/yqlib/operator_subtract.go +++ b/pkg/yqlib/operator_subtract.go @@ -25,7 +25,7 @@ func subtractAssignOperator(d *dataTreeNavigator, context Context, expressionNod func subtractOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("Subtract operator") - return crossFunction(d, context, expressionNode, subtract) + return crossFunction(d, context, expressionNode, subtract, false) } func subtract(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { diff --git a/pkg/yqlib/operators.go b/pkg/yqlib/operators.go index 798ebe70..0a0e25dc 100644 --- a/pkg/yqlib/operators.go +++ b/pkg/yqlib/operators.go @@ -24,7 +24,20 @@ func emptyOperator(d *dataTreeNavigator, context Context, expressionNode *Expres type crossFunctionCalculation func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) -func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (Context, error) { +func resultsForRhs(d *dataTreeNavigator, context Context, lhsCandidate *CandidateNode, rhs Context, calculation crossFunctionCalculation, results *list.List) error { + for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() { + log.Debugf("Applying calc") + rhsCandidate := rightEl.Value.(*CandidateNode) + resultCandidate, err := calculation(d, context, lhsCandidate, rhsCandidate) + if err != nil { + return err + } + results.PushBack(resultCandidate) + } + return nil +} + +func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation, calcWhenEmpty bool) (Context, error) { var results = list.New() lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { @@ -38,24 +51,33 @@ func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *Expressi return Context{}, err } - for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { - lhsCandidate := el.Value.(*CandidateNode) - - for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() { - log.Debugf("Applying calc") - rhsCandidate := rightEl.Value.(*CandidateNode) - resultCandidate, err := calculation(d, context, lhsCandidate, rhsCandidate) + if calcWhenEmpty && lhs.MatchingNodes.Len() == 0 { + if rhs.MatchingNodes.Len() == 0 { + resultCandidate, err := calculation(d, context, nil, nil) if err != nil { return Context{}, err } results.PushBack(resultCandidate) } + err := resultsForRhs(d, context, nil, rhs, calculation, results) + if err != nil { + return Context{}, err + } + } + + for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { + lhsCandidate := el.Value.(*CandidateNode) + + err := resultsForRhs(d, context, lhsCandidate, rhs, calculation, results) + if err != nil { + return Context{}, err + } } return context.ChildContext(results), nil } -func crossFunction(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (Context, error) { +func crossFunction(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation, calcWhenEmpty bool) (Context, error) { var results = list.New() var evaluateAllTogether = true @@ -66,11 +88,11 @@ func crossFunction(d *dataTreeNavigator, context Context, expressionNode *Expres } } if evaluateAllTogether { - return doCrossFunc(d, context, expressionNode, calculation) + return doCrossFunc(d, context, expressionNode, calculation, calcWhenEmpty) } for matchEl := context.MatchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() { - innerResults, err := doCrossFunc(d, context.SingleChildContext(matchEl.Value.(*CandidateNode)), expressionNode, calculation) + innerResults, err := doCrossFunc(d, context.SingleChildContext(matchEl.Value.(*CandidateNode)), expressionNode, calculation, calcWhenEmpty) if err != nil { return Context{}, err }