2020-11-03 23:48:43 +00:00
|
|
|
package yqlib
|
2020-10-19 05:14:29 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2021-01-18 02:28:40 +00:00
|
|
|
"strconv"
|
2020-10-19 05:14:29 +00:00
|
|
|
|
2020-10-21 01:54:58 +00:00
|
|
|
"container/list"
|
|
|
|
|
2020-11-20 11:57:32 +00:00
|
|
|
yaml "gopkg.in/yaml.v3"
|
2020-10-19 05:14:29 +00:00
|
|
|
)
|
|
|
|
|
2021-01-11 06:13:48 +00:00
|
|
|
type crossFunctionCalculation func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
|
2020-10-20 05:27:30 +00:00
|
|
|
|
2021-01-17 23:15:31 +00:00
|
|
|
func doCrossFunc(d *dataTreeNavigator, contextList *list.List, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (*list.List, error) {
|
|
|
|
var results = list.New()
|
|
|
|
lhs, err := d.GetMatchingNodes(contextList, expressionNode.Lhs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
log.Debugf("crossFunction LHS len: %v", lhs.Len())
|
|
|
|
|
|
|
|
rhs, err := d.GetMatchingNodes(contextList, expressionNode.Rhs)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for el := lhs.Front(); el != nil; el = el.Next() {
|
|
|
|
lhsCandidate := el.Value.(*CandidateNode)
|
|
|
|
|
|
|
|
for rightEl := rhs.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
|
|
|
log.Debugf("Applying calc")
|
|
|
|
rhsCandidate := rightEl.Value.(*CandidateNode)
|
|
|
|
resultCandidate, err := calculation(d, lhsCandidate, rhsCandidate)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
results.PushBack(resultCandidate)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return results, nil
|
|
|
|
}
|
2020-10-19 05:14:29 +00:00
|
|
|
|
2021-01-17 23:15:31 +00:00
|
|
|
func crossFunction(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (*list.List, error) {
|
2021-01-16 03:09:49 +00:00
|
|
|
var results = list.New()
|
2021-01-18 02:28:40 +00:00
|
|
|
|
|
|
|
var evaluateAllTogether = true
|
2021-01-16 03:09:49 +00:00
|
|
|
for matchEl := matchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
|
2021-01-18 02:28:40 +00:00
|
|
|
evaluateAllTogether = evaluateAllTogether && matchEl.Value.(*CandidateNode).EvaluateTogether
|
|
|
|
if !evaluateAllTogether {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if evaluateAllTogether {
|
|
|
|
return doCrossFunc(d, matchingNodes, expressionNode, calculation)
|
|
|
|
}
|
2020-10-19 05:14:29 +00:00
|
|
|
|
2021-01-18 02:28:40 +00:00
|
|
|
for matchEl := matchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() {
|
|
|
|
contextList := nodeToMap(matchEl.Value.(*CandidateNode))
|
2021-01-17 23:15:31 +00:00
|
|
|
innerResults, err := doCrossFunc(d, contextList, expressionNode, calculation)
|
2021-01-16 03:09:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-17 23:15:31 +00:00
|
|
|
results.PushBackList(innerResults)
|
2020-10-19 05:14:29 +00:00
|
|
|
}
|
2021-01-16 03:09:49 +00:00
|
|
|
|
2020-10-20 05:27:30 +00:00
|
|
|
return results, nil
|
|
|
|
}
|
|
|
|
|
2021-01-11 06:13:48 +00:00
|
|
|
type multiplyPreferences struct {
|
2021-01-13 05:54:28 +00:00
|
|
|
AppendArrays bool
|
|
|
|
TraversePrefs traversePreferences
|
2020-11-27 23:41:09 +00:00
|
|
|
}
|
|
|
|
|
2021-01-12 23:18:53 +00:00
|
|
|
func multiplyOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) {
|
2020-10-20 05:27:30 +00:00
|
|
|
log.Debugf("-- MultiplyOperator")
|
2021-01-13 05:59:01 +00:00
|
|
|
return crossFunction(d, matchingNodes, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences)))
|
2020-10-19 05:14:29 +00:00
|
|
|
}
|
|
|
|
|
2021-01-13 05:59:01 +00:00
|
|
|
func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
2020-11-27 23:41:09 +00:00
|
|
|
return func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
2021-01-12 23:00:51 +00:00
|
|
|
lhs.Node = unwrapDoc(lhs.Node)
|
|
|
|
rhs.Node = unwrapDoc(rhs.Node)
|
2020-11-27 23:41:09 +00:00
|
|
|
log.Debugf("Multipling LHS: %v", lhs.Node.Tag)
|
|
|
|
log.Debugf("- RHS: %v", rhs.Node.Tag)
|
2020-10-27 05:45:16 +00:00
|
|
|
|
2020-11-27 23:41:09 +00:00
|
|
|
if lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode ||
|
|
|
|
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
|
|
|
|
|
2021-01-12 08:36:28 +00:00
|
|
|
var newBlank = lhs.CreateChild(nil, &yaml.Node{})
|
2021-01-13 05:59:01 +00:00
|
|
|
var newThing, err = mergeObjects(d, newBlank, lhs, multiplyPreferences{})
|
2020-11-27 23:41:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-01-13 05:54:28 +00:00
|
|
|
return mergeObjects(d, newThing, rhs, preferences)
|
2021-01-18 02:28:40 +00:00
|
|
|
} else if lhs.Node.Tag == "!!int" && rhs.Node.Tag == "!!int" {
|
|
|
|
return multiplyIntegers(lhs, rhs)
|
2020-11-27 23:41:09 +00:00
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("Cannot multiply %v with %v", lhs.Node.Tag, rhs.Node.Tag)
|
2020-10-19 05:14:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-18 02:28:40 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-01-13 05:59:01 +00:00
|
|
|
func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) {
|
2021-01-13 05:54:28 +00:00
|
|
|
shouldAppendArrays := preferences.AppendArrays
|
2020-10-28 02:00:26 +00:00
|
|
|
var results = list.New()
|
2020-11-27 23:41:09 +00:00
|
|
|
|
|
|
|
// shouldn't recurse arrays if appending
|
2021-01-13 06:00:03 +00:00
|
|
|
prefs := recursiveDescentPreferences{RecurseArray: !shouldAppendArrays,
|
2021-01-13 05:54:28 +00:00
|
|
|
TraversePreferences: traversePreferences{DontFollowAlias: true}}
|
2020-12-28 00:24:42 +00:00
|
|
|
err := recursiveDecent(d, results, nodeToMap(rhs), prefs)
|
2020-11-13 03:07:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-10-28 02:00:26 +00:00
|
|
|
|
|
|
|
var pathIndexToStartFrom int = 0
|
|
|
|
if results.Front() != nil {
|
|
|
|
pathIndexToStartFrom = len(results.Front().Value.(*CandidateNode).Path)
|
|
|
|
}
|
|
|
|
|
|
|
|
for el := results.Front(); el != nil; el = el.Next() {
|
2021-01-13 05:54:28 +00:00
|
|
|
err := applyAssignment(d, pathIndexToStartFrom, lhs, el.Value.(*CandidateNode), preferences)
|
2020-10-28 02:00:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return lhs, nil
|
|
|
|
}
|
|
|
|
|
2021-01-13 05:59:01 +00:00
|
|
|
func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error {
|
2021-01-13 05:54:28 +00:00
|
|
|
shouldAppendArrays := preferences.AppendArrays
|
2020-10-19 05:14:29 +00:00
|
|
|
log.Debugf("merge - applyAssignment lhs %v, rhs: %v", NodeToString(lhs), NodeToString(rhs))
|
|
|
|
|
|
|
|
lhsPath := rhs.Path[pathIndexToStartFrom:]
|
|
|
|
|
2021-01-11 06:13:48 +00:00
|
|
|
assignmentOp := &Operation{OperationType: assignAttributesOpType}
|
2020-10-29 23:56:45 +00:00
|
|
|
if rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode {
|
2021-01-11 06:13:48 +00:00
|
|
|
assignmentOp.OperationType = assignOpType
|
2021-01-06 09:22:50 +00:00
|
|
|
assignmentOp.UpdateAssign = false
|
2020-11-27 23:41:09 +00:00
|
|
|
} else if shouldAppendArrays && rhs.Node.Kind == yaml.SequenceNode {
|
2021-01-11 06:13:48 +00:00
|
|
|
assignmentOp.OperationType = addAssignOpType
|
2020-10-19 05:14:29 +00:00
|
|
|
}
|
2021-01-11 06:13:48 +00:00
|
|
|
rhsOp := &Operation{OperationType: valueOpType, CandidateNode: rhs}
|
2020-10-19 05:14:29 +00:00
|
|
|
|
2021-01-13 05:54:28 +00:00
|
|
|
assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath, preferences.TraversePrefs), Rhs: &ExpressionNode{Operation: rhsOp}}
|
2020-10-19 05:14:29 +00:00
|
|
|
|
2020-10-27 05:45:16 +00:00
|
|
|
_, err := d.GetMatchingNodes(nodeToMap(lhs), assignmentOpNode)
|
2020-10-19 05:14:29 +00:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|