2020-11-03 23:48:43 +00:00
|
|
|
package yqlib
|
2020-10-19 05:14:29 +00:00
|
|
|
|
|
|
|
import (
|
2021-11-26 09:24:21 +00:00
|
|
|
"container/list"
|
2020-10-19 05:14:29 +00:00
|
|
|
"fmt"
|
2021-01-18 02:28:40 +00:00
|
|
|
"strconv"
|
2022-01-22 02:47:22 +00:00
|
|
|
"strings"
|
2020-10-19 05:14:29 +00:00
|
|
|
)
|
|
|
|
|
2021-01-11 06:13:48 +00:00
|
|
|
type multiplyPreferences struct {
|
2021-02-18 00:16:54 +00:00
|
|
|
AppendArrays bool
|
|
|
|
DeepMergeArrays bool
|
|
|
|
TraversePrefs traversePreferences
|
2022-01-15 04:48:34 +00:00
|
|
|
AssignPrefs assignPreferences
|
2020-11-27 23:41:09 +00:00
|
|
|
}
|
|
|
|
|
2022-01-22 05:40:17 +00:00
|
|
|
func createMultiplyOp(prefs interface{}) func(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode {
|
|
|
|
return func(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode {
|
|
|
|
return &ExpressionNode{Operation: &Operation{OperationType: multiplyOpType, Preferences: prefs},
|
2022-02-07 00:55:55 +00:00
|
|
|
LHS: lhs,
|
|
|
|
RHS: rhs}
|
2022-01-22 05:40:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func multiplyAssignOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
|
|
var multiplyPrefs = expressionNode.Operation.Preferences
|
|
|
|
|
|
|
|
return compoundAssignFunction(d, context, expressionNode, createMultiplyOp(multiplyPrefs))
|
|
|
|
}
|
|
|
|
|
2021-02-02 07:17:59 +00:00
|
|
|
func multiplyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
2020-10-20 05:27:30 +00:00
|
|
|
log.Debugf("-- MultiplyOperator")
|
2021-04-13 00:42:20 +00:00
|
|
|
return crossFunction(d, context, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences)), false)
|
2020-10-19 05:14:29 +00:00
|
|
|
}
|
|
|
|
|
2021-11-13 23:51:18 +00:00
|
|
|
func getComments(lhs *CandidateNode, rhs *CandidateNode) (leadingContent string, headComment string, footComment string) {
|
|
|
|
leadingContent = rhs.LeadingContent
|
2023-04-11 02:39:22 +00:00
|
|
|
headComment = rhs.HeadComment
|
|
|
|
footComment = rhs.FootComment
|
|
|
|
if lhs.HeadComment != "" || lhs.LeadingContent != "" {
|
|
|
|
headComment = lhs.HeadComment
|
2021-11-13 23:51:18 +00:00
|
|
|
leadingContent = lhs.LeadingContent
|
2021-08-16 23:54:12 +00:00
|
|
|
}
|
|
|
|
|
2023-04-11 02:39:22 +00:00
|
|
|
if lhs.FootComment != "" {
|
|
|
|
footComment = lhs.FootComment
|
2021-08-16 23:54:12 +00:00
|
|
|
}
|
2021-11-13 23:51:18 +00:00
|
|
|
return leadingContent, headComment, footComment
|
2021-08-16 23:54:12 +00:00
|
|
|
}
|
|
|
|
|
2021-02-02 07:17:59 +00:00
|
|
|
func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
|
|
|
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
2021-08-16 23:54:12 +00:00
|
|
|
// need to do this before unWrapping the potential document node
|
2021-11-13 23:51:18 +00:00
|
|
|
leadingContent, headComment, footComment := getComments(lhs, rhs)
|
2023-04-11 02:39:22 +00:00
|
|
|
lhs = lhs.unwrapDocument()
|
|
|
|
rhs = rhs.unwrapDocument()
|
2023-04-17 06:20:00 +00:00
|
|
|
log.Debugf("Multiplying LHS: %v", NodeToString(lhs))
|
|
|
|
log.Debugf("- RHS: %v", NodeToString(rhs))
|
2020-10-27 05:45:16 +00:00
|
|
|
|
2023-04-11 02:39:22 +00:00
|
|
|
if rhs.Tag == "!!null" {
|
|
|
|
return lhs.Copy(), nil
|
2023-01-10 02:48:57 +00:00
|
|
|
}
|
|
|
|
|
2023-04-11 02:39:22 +00:00
|
|
|
if (lhs.Kind == MappingNode && rhs.Kind == MappingNode) ||
|
|
|
|
(lhs.Tag == "!!null" && rhs.Kind == MappingNode) ||
|
|
|
|
(lhs.Kind == SequenceNode && rhs.Kind == SequenceNode) ||
|
|
|
|
(lhs.Tag == "!!null" && rhs.Kind == SequenceNode) {
|
2023-04-15 05:24:40 +00:00
|
|
|
|
|
|
|
var newBlank = lhs.Copy()
|
|
|
|
|
2021-11-13 23:51:18 +00:00
|
|
|
newBlank.LeadingContent = leadingContent
|
2023-04-11 02:39:22 +00:00
|
|
|
newBlank.HeadComment = headComment
|
|
|
|
newBlank.FootComment = footComment
|
2021-10-11 03:46:46 +00:00
|
|
|
|
2023-04-15 05:24:40 +00:00
|
|
|
return mergeObjects(d, context.WritableClone(), newBlank, rhs, preferences)
|
2020-11-27 23:41:09 +00:00
|
|
|
}
|
2022-01-22 02:47:22 +00:00
|
|
|
return multiplyScalars(lhs, rhs)
|
2020-10-19 05:14:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-22 02:47:22 +00:00
|
|
|
func multiplyScalars(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
2023-04-11 02:39:22 +00:00
|
|
|
lhsTag := lhs.Tag
|
|
|
|
rhsTag := rhs.guessTagFromCustomType()
|
2022-01-22 02:47:22 +00:00
|
|
|
lhsIsCustom := false
|
|
|
|
if !strings.HasPrefix(lhsTag, "!!") {
|
|
|
|
// custom tag - we have to have a guess
|
2023-04-11 02:39:22 +00:00
|
|
|
lhsTag = lhs.guessTagFromCustomType()
|
2022-01-22 02:47:22 +00:00
|
|
|
lhsIsCustom = true
|
|
|
|
}
|
|
|
|
|
|
|
|
if lhsTag == "!!int" && rhsTag == "!!int" {
|
|
|
|
return multiplyIntegers(lhs, rhs)
|
|
|
|
} else if (lhsTag == "!!int" || lhsTag == "!!float") && (rhsTag == "!!int" || rhsTag == "!!float") {
|
|
|
|
return multiplyFloats(lhs, rhs, lhsIsCustom)
|
|
|
|
}
|
2023-04-17 06:20:00 +00:00
|
|
|
return nil, fmt.Errorf("cannot multiply %v with %v", lhs.Tag, rhs.Tag)
|
2022-01-22 02:47:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func multiplyFloats(lhs *CandidateNode, rhs *CandidateNode, lhsIsCustom bool) (*CandidateNode, error) {
|
2023-04-11 02:39:22 +00:00
|
|
|
target := lhs.CopyWithoutContent()
|
|
|
|
target.Kind = ScalarNode
|
|
|
|
target.Style = lhs.Style
|
2022-01-22 02:47:22 +00:00
|
|
|
if lhsIsCustom {
|
2023-04-11 02:39:22 +00:00
|
|
|
target.Tag = lhs.Tag
|
2022-01-22 02:47:22 +00:00
|
|
|
} else {
|
2023-04-11 02:39:22 +00:00
|
|
|
target.Tag = "!!float"
|
2022-01-22 02:47:22 +00:00
|
|
|
}
|
2021-02-18 00:16:54 +00:00
|
|
|
|
2023-04-11 02:39:22 +00:00
|
|
|
lhsNum, err := strconv.ParseFloat(lhs.Value, 64)
|
2021-02-18 00:16:54 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-04-11 02:39:22 +00:00
|
|
|
rhsNum, err := strconv.ParseFloat(rhs.Value, 64)
|
2021-02-18 00:16:54 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-04-11 02:39:22 +00:00
|
|
|
target.Value = fmt.Sprintf("%v", lhsNum*rhsNum)
|
2021-02-18 00:16:54 +00:00
|
|
|
return target, nil
|
|
|
|
}
|
|
|
|
|
2021-01-18 02:28:40 +00:00
|
|
|
func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
2023-04-11 02:39:22 +00:00
|
|
|
target := lhs.CopyWithoutContent()
|
|
|
|
target.Kind = ScalarNode
|
|
|
|
target.Style = lhs.Style
|
|
|
|
target.Tag = lhs.Tag
|
2021-01-18 02:28:40 +00:00
|
|
|
|
2023-04-11 02:39:22 +00:00
|
|
|
format, lhsNum, err := parseInt64(lhs.Value)
|
2021-01-18 02:28:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-04-11 02:39:22 +00:00
|
|
|
_, rhsNum, err := parseInt64(rhs.Value)
|
2021-01-18 02:28:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-04-11 02:39:22 +00:00
|
|
|
target.Value = fmt.Sprintf(format, lhsNum*rhsNum)
|
2021-01-18 02:28:40 +00:00
|
|
|
return target, nil
|
|
|
|
}
|
|
|
|
|
2021-02-02 07:17:59 +00:00
|
|
|
func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) {
|
2020-10-28 02:00:26 +00:00
|
|
|
var results = list.New()
|
2020-11-27 23:41:09 +00:00
|
|
|
|
2021-07-22 10:57:47 +00:00
|
|
|
// only need to recurse the array if we are doing a deep merge
|
|
|
|
prefs := recursiveDescentPreferences{RecurseArray: preferences.DeepMergeArrays,
|
2021-02-08 02:58:46 +00:00
|
|
|
TraversePreferences: traversePreferences{DontFollowAlias: true, IncludeMapKeys: true}}
|
2021-07-22 10:57:47 +00:00
|
|
|
log.Debugf("merge - preferences.DeepMergeArrays %v", preferences.DeepMergeArrays)
|
|
|
|
log.Debugf("merge - preferences.AppendArrays %v", preferences.AppendArrays)
|
2021-12-20 22:30:08 +00:00
|
|
|
err := recursiveDecent(results, context.SingleChildContext(rhs), prefs)
|
2020-11-13 03:07:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-10-28 02:00:26 +00:00
|
|
|
|
2021-12-20 22:30:08 +00:00
|
|
|
var pathIndexToStartFrom int
|
2020-10-28 02:00:26 +00:00
|
|
|
if results.Front() != nil {
|
2023-04-11 02:39:22 +00:00
|
|
|
pathIndexToStartFrom = len(results.Front().Value.(*CandidateNode).GetPath())
|
2023-04-17 06:20:00 +00:00
|
|
|
log.Debugf("pathIndexToStartFrom: %v", pathIndexToStartFrom)
|
2020-10-28 02:00:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for el := results.Front(); el != nil; el = el.Next() {
|
2021-02-08 02:58:46 +00:00
|
|
|
candidate := el.Value.(*CandidateNode)
|
2023-04-17 06:20:00 +00:00
|
|
|
|
|
|
|
log.Debugf("*** going to applied assignment to LHS: %v with RHS: %v", NodeToString(lhs), NodeToString(candidate))
|
|
|
|
|
2023-04-11 02:39:22 +00:00
|
|
|
if candidate.Tag == "!!merge" {
|
2021-02-08 02:58:46 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
err := applyAssignment(d, context, pathIndexToStartFrom, lhs, candidate, preferences)
|
2020-10-28 02:00:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-04-17 06:20:00 +00:00
|
|
|
|
|
|
|
log.Debugf("*** applied assignment to LHS: %v", NodeToString(lhs))
|
2020-10-28 02:00:26 +00:00
|
|
|
}
|
|
|
|
return lhs, nil
|
|
|
|
}
|
|
|
|
|
2021-02-02 07:17:59 +00:00
|
|
|
func applyAssignment(d *dataTreeNavigator, context Context, 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
|
|
|
|
2023-04-11 02:39:22 +00:00
|
|
|
lhsPath := rhs.GetPath()[pathIndexToStartFrom:]
|
2021-07-22 10:57:47 +00:00
|
|
|
log.Debugf("merge - lhsPath %v", lhsPath)
|
2020-10-19 05:14:29 +00:00
|
|
|
|
2022-01-15 04:48:34 +00:00
|
|
|
assignmentOp := &Operation{OperationType: assignAttributesOpType, Preferences: preferences.AssignPrefs}
|
2023-04-11 02:39:22 +00:00
|
|
|
if shouldAppendArrays && rhs.Kind == SequenceNode {
|
2021-02-18 00:16:54 +00:00
|
|
|
assignmentOp.OperationType = addAssignOpType
|
2021-07-22 10:57:47 +00:00
|
|
|
log.Debugf("merge - assignmentOp.OperationType = addAssignOpType")
|
2023-04-11 02:39:22 +00:00
|
|
|
} else if !preferences.DeepMergeArrays && rhs.Kind == SequenceNode ||
|
|
|
|
(rhs.Kind == ScalarNode || rhs.Kind == AliasNode) {
|
2021-01-11 06:13:48 +00:00
|
|
|
assignmentOp.OperationType = assignOpType
|
2021-01-06 09:22:50 +00:00
|
|
|
assignmentOp.UpdateAssign = false
|
2023-04-11 02:39:22 +00:00
|
|
|
log.Debugf("merge - rhs.Kind == SequenceNode: %v", rhs.Kind == SequenceNode)
|
|
|
|
log.Debugf("merge - rhs.Kind == ScalarNode: %v", rhs.Kind == ScalarNode)
|
|
|
|
log.Debugf("merge - rhs.Kind == AliasNode: %v", rhs.Kind == AliasNode)
|
2021-07-22 10:57:47 +00:00
|
|
|
log.Debugf("merge - assignmentOp.OperationType = assignOpType, no updateassign")
|
|
|
|
} else {
|
|
|
|
log.Debugf("merge - assignmentOp := &Operation{OperationType: assignAttributesOpType}")
|
2020-10-19 05:14:29 +00:00
|
|
|
}
|
2023-01-12 04:11:45 +00:00
|
|
|
rhsOp := &Operation{OperationType: referenceOpType, CandidateNode: rhs}
|
2020-10-19 05:14:29 +00:00
|
|
|
|
2022-01-15 04:48:34 +00:00
|
|
|
assignmentOpNode := &ExpressionNode{
|
|
|
|
Operation: assignmentOp,
|
2022-02-07 00:55:55 +00:00
|
|
|
LHS: createTraversalTree(lhsPath, preferences.TraversePrefs, rhs.IsMapKey),
|
|
|
|
RHS: &ExpressionNode{Operation: rhsOp},
|
2022-01-15 04:48:34 +00:00
|
|
|
}
|
2020-10-19 05:14:29 +00:00
|
|
|
|
2021-02-02 07:17:59 +00:00
|
|
|
_, err := d.GetMatchingNodes(context.SingleChildContext(lhs), assignmentOpNode)
|
2020-10-19 05:14:29 +00:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|