Fixed cross-function combinatorial bug

This commit is contained in:
Mike Farah 2021-01-18 13:28:40 +11:00
parent 76bd1896e9
commit 6c14a80991
2 changed files with 48 additions and 20 deletions

View File

@ -2,6 +2,7 @@ package yqlib
import ( import (
"fmt" "fmt"
"strconv"
"container/list" "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) { func crossFunction(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (*list.List, error) {
var results = list.New() 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() { for matchEl := matchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
contextList := nodeToMap(matchEl.Value.(*CandidateNode)) contextList := nodeToMap(matchEl.Value.(*CandidateNode))
innerResults, err := doCrossFunc(d, contextList, expressionNode, calculation) innerResults, err := doCrossFunc(d, contextList, expressionNode, calculation)
if err != nil { if err != nil {
return nil, err return nil, err
} }
results.PushBackList(innerResults) results.PushBackList(innerResults)
} }
return results, nil return results, nil
@ -85,14 +94,31 @@ func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, lhs *C
return nil, err return nil, err
} }
return mergeObjects(d, newThing, rhs, preferences) 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) 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) { func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) {
shouldAppendArrays := preferences.AppendArrays shouldAppendArrays := preferences.AppendArrays
var results = list.New() var results = list.New()

View File

@ -4,16 +4,6 @@ import (
"testing" "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{ var multiplyOperatorScenarios = []expressionScenario{
{ {
skipDoc: true, skipDoc: true,
@ -25,11 +15,23 @@ var multiplyOperatorScenarios = []expressionScenario{
}, },
{ {
skipDoc: true, skipDoc: true,
document: `a: {also: [1]}`, document: `{a: 2, b: 5}`,
document2: `b: {also: me}`, document2: `{a: 3, b: 10}`,
expression: `.a * .b`, expression: `.a * .b`,
expected: []string{ 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",
}, },
}, },
{ {