diff --git a/pkg/yqlib/operator_multiply.go b/pkg/yqlib/operator_multiply.go index 98919632..7194acc9 100644 --- a/pkg/yqlib/operator_multiply.go +++ b/pkg/yqlib/operator_multiply.go @@ -2,6 +2,7 @@ package yqlib import ( "fmt" + "strconv" "container/list" @@ -43,17 +44,25 @@ func doCrossFunc(d *dataTreeNavigator, contextList *list.List, expressionNode *E func crossFunction(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (*list.List, error) { var results = list.New() + + var evaluateAllTogether = true + for matchEl := matchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() { + evaluateAllTogether = evaluateAllTogether && matchEl.Value.(*CandidateNode).EvaluateTogether + if !evaluateAllTogether { + break + } + } + if evaluateAllTogether { + return doCrossFunc(d, matchingNodes, expressionNode, calculation) + } + for matchEl := matchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() { contextList := nodeToMap(matchEl.Value.(*CandidateNode)) - innerResults, err := doCrossFunc(d, contextList, expressionNode, calculation) - if err != nil { return nil, err } - results.PushBackList(innerResults) - } return results, nil @@ -85,14 +94,31 @@ func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, lhs *C return nil, err } return mergeObjects(d, newThing, rhs, preferences) + } else if lhs.Node.Tag == "!!int" && rhs.Node.Tag == "!!int" { + return multiplyIntegers(lhs, rhs) } - // else if lhs.Node.Tag == "!!int" && rhs.Node.Tag == "!!int" { - // return lhs.CreateChild(nil, &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: "12"}), nil - // } return nil, fmt.Errorf("Cannot multiply %v with %v", lhs.Node.Tag, rhs.Node.Tag) } } +func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { + target := lhs.CreateChild(nil, &yaml.Node{}) + target.Node.Kind = yaml.ScalarNode + target.Node.Style = lhs.Node.Style + target.Node.Tag = "!!int" + + lhsNum, err := strconv.Atoi(lhs.Node.Value) + if err != nil { + return nil, err + } + rhsNum, err := strconv.Atoi(rhs.Node.Value) + if err != nil { + return nil, err + } + target.Node.Value = fmt.Sprintf("%v", lhsNum*rhsNum) + return target, nil +} + func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) { shouldAppendArrays := preferences.AppendArrays var results = list.New() diff --git a/pkg/yqlib/operator_multiply_test.go b/pkg/yqlib/operator_multiply_test.go index 09b466d8..1794cec1 100644 --- a/pkg/yqlib/operator_multiply_test.go +++ b/pkg/yqlib/operator_multiply_test.go @@ -4,16 +4,6 @@ import ( "testing" ) -// crossfunction: -// normal single document, treat each match separately: eval -// multiple documents, how do you do things between docs??? eval-all -// alternative -// collect and filter matches according to doc before running expression -// d0a d1a d0b d1b -// run it for (d0a d1a) x (d0b d1b) - noting that we dont do (d0a x d1a) nor (d0b d1b) -// I think this will work for eval-all correctly then.. - -//alternative, like jq, eval-all puts all docs in an single array. var multiplyOperatorScenarios = []expressionScenario{ { skipDoc: true, @@ -25,11 +15,23 @@ var multiplyOperatorScenarios = []expressionScenario{ }, { skipDoc: true, - document: `a: {also: [1]}`, - document2: `b: {also: me}`, + document: `{a: 2, b: 5}`, + document2: `{a: 3, b: 10}`, expression: `.a * .b`, expected: []string{ - "D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n", + "D0, P[a], (!!int)::10\n", + "D0, P[a], (!!int)::20\n", + "D0, P[a], (!!int)::15\n", + "D0, P[a], (!!int)::30\n", + }, + }, + { + skipDoc: true, + document: `{a: 2}`, + document2: `{b: 10}`, + expression: `select(fi ==0) * select(fi==1)`, + expected: []string{ + "D0, P[], (!!map)::{a: 2, b: 10}\n", }, }, {