diff --git a/examples/data1.yaml b/examples/data1.yaml index 6b87c47f..1467d89f 100644 --- a/examples/data1.yaml +++ b/examples/data1.yaml @@ -1,4 +1,6 @@ -a: simple # just the best -b: [1, 2] -c: - test: 1 +a: + key1: "value1" + key2: 2.6 + ab: + key1: 6 + key2: "h" \ No newline at end of file diff --git a/pkg/yqlib/all_at_once_evaluator.go b/pkg/yqlib/all_at_once_evaluator.go index 7de4a0b0..cfb936b7 100644 --- a/pkg/yqlib/all_at_once_evaluator.go +++ b/pkg/yqlib/all_at_once_evaluator.go @@ -39,7 +39,11 @@ func (e *allAtOnceEvaluator) EvaluateCandidateNodes(expression string, inputCand if err != nil { return nil, err } - return e.treeNavigator.GetMatchingNodes(inputCandidates, node) + context, err := e.treeNavigator.GetMatchingNodes(Context{MatchingNodes: inputCandidates}, node) + if err != nil { + return nil, err + } + return context.MatchingNodes, nil } func (e *allAtOnceEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer) error { diff --git a/pkg/yqlib/context.go b/pkg/yqlib/context.go new file mode 100644 index 00000000..a6693c5f --- /dev/null +++ b/pkg/yqlib/context.go @@ -0,0 +1,18 @@ +package yqlib + +import "container/list" + +type Context struct { + MatchingNodes *list.List + Variables map[string]*list.List +} + +func (n *Context) SingleChildContext(candidate *CandidateNode) Context { + elMap := list.New() + elMap.PushBack(candidate) + return Context{MatchingNodes: elMap, Variables: n.Variables} +} + +func (n *Context) ChildContext(results *list.List) Context { + return Context{MatchingNodes: results, Variables: n.Variables} +} diff --git a/pkg/yqlib/data_tree_navigator.go b/pkg/yqlib/data_tree_navigator.go index e9b43506..13659eaa 100644 --- a/pkg/yqlib/data_tree_navigator.go +++ b/pkg/yqlib/data_tree_navigator.go @@ -3,16 +3,14 @@ package yqlib import ( "fmt" - "container/list" - logging "gopkg.in/op/go-logging.v1" ) type DataTreeNavigator interface { - // given a list of CandidateEntities and a expressionNode, - // this will process the list against the given expressionNode and return - // a new list of matching candidates - GetMatchingNodes(matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) + // given the context and a expressionNode, + // this will process the against the given expressionNode and return + // a new context of matching candidates + GetMatchingNodes(context Context, expressionNode *ExpressionNode) (Context, error) } type dataTreeNavigator struct { @@ -22,22 +20,22 @@ func NewDataTreeNavigator() DataTreeNavigator { return &dataTreeNavigator{} } -func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func (d *dataTreeNavigator) GetMatchingNodes(context Context, expressionNode *ExpressionNode) (Context, error) { if expressionNode == nil { log.Debugf("getMatchingNodes - nothing to do") - return matchingNodes, nil + return context, nil } log.Debugf("Processing Op: %v", expressionNode.Operation.toString()) if log.IsEnabledFor(logging.DEBUG) { - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { log.Debug(NodeToString(el.Value.(*CandidateNode))) } } log.Debug(">>") handler := expressionNode.Operation.OperationType.Handler if handler != nil { - return handler(d, matchingNodes, expressionNode) + return handler(d, context, expressionNode) } - return nil, fmt.Errorf("Unknown operator %v", expressionNode.Operation.OperationType) + return Context{}, fmt.Errorf("Unknown operator %v", expressionNode.Operation.OperationType) } diff --git a/pkg/yqlib/operator_add.go b/pkg/yqlib/operator_add.go index 90eafb0f..b263d7d8 100644 --- a/pkg/yqlib/operator_add.go +++ b/pkg/yqlib/operator_add.go @@ -3,7 +3,6 @@ package yqlib import ( "fmt" - "container/list" "strconv" yaml "gopkg.in/yaml.v3" @@ -15,12 +14,12 @@ func createAddOp(lhs *ExpressionNode, rhs *ExpressionNode) *ExpressionNode { Rhs: rhs} } -func addAssignOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func addAssignOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { assignmentOp := &Operation{OperationType: assignOpType} assignmentOp.UpdateAssign = false assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: expressionNode.Lhs, Rhs: createAddOp(expressionNode.Lhs, expressionNode.Rhs)} - return d.GetMatchingNodes(matchingNodes, assignmentOpNode) + return d.GetMatchingNodes(context, assignmentOpNode) } func toNodes(candidate *CandidateNode) []*yaml.Node { @@ -37,13 +36,13 @@ func toNodes(candidate *CandidateNode) []*yaml.Node { } -func addOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("Add operator") - return crossFunction(d, matchingNodes, expressionNode, add) + return crossFunction(d, context, expressionNode, add) } -func add(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { +func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { lhs.Node = unwrapDoc(lhs.Node) rhs.Node = unwrapDoc(rhs.Node) diff --git a/pkg/yqlib/operator_alternative.go b/pkg/yqlib/operator_alternative.go index 0823c8c5..3b310325 100644 --- a/pkg/yqlib/operator_alternative.go +++ b/pkg/yqlib/operator_alternative.go @@ -1,18 +1,14 @@ package yqlib -import ( - "container/list" -) - // corssFunction no matches // can boolean use crossfunction -func alternativeOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- alternative") - return crossFunction(d, matchingNodes, expressionNode, alternativeFunc) + return crossFunction(d, context, expressionNode, alternativeFunc) } -func alternativeFunc(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { +func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { lhs.Node = unwrapDoc(lhs.Node) rhs.Node = unwrapDoc(rhs.Node) log.Debugf("Alternative LHS: %v", lhs.Node.Tag) diff --git a/pkg/yqlib/operator_anchors_aliases.go b/pkg/yqlib/operator_anchors_aliases.go index da84869e..c1566bcb 100644 --- a/pkg/yqlib/operator_anchors_aliases.go +++ b/pkg/yqlib/operator_anchors_aliases.go @@ -6,146 +6,146 @@ import ( yaml "gopkg.in/yaml.v3" ) -func assignAliasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("AssignAlias operator!") aliasName := "" if !expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - aliasName = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + aliasName = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } } - lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs) + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { - return nil, err + return Context{}, err } - for el := lhs.Front(); el != nil; el = el.Next() { + for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) log.Debugf("Setting aliasName : %v", candidate.GetKey()) if expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - aliasName = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + aliasName = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } } candidate.Node.Kind = yaml.AliasNode candidate.Node.Value = aliasName } - return matchingNodes, nil + return context, nil } -func getAliasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func getAliasOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("GetAlias operator!") var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Node.Value, Tag: "!!str"} result := candidate.CreateChild(nil, node) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } -func assignAnchorOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func assignAnchorOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("AssignAnchor operator!") anchorName := "" if !expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - anchorName = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + anchorName = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } } - lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs) + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { - return nil, err + return Context{}, err } - for el := lhs.Front(); el != nil; el = el.Next() { + for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) log.Debugf("Setting anchorName of : %v", candidate.GetKey()) if expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - anchorName = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + anchorName = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } } candidate.Node.Anchor = anchorName } - return matchingNodes, nil + return context, nil } -func getAnchorOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func getAnchorOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("GetAnchor operator!") var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) anchor := candidate.Node.Anchor node := &yaml.Node{Kind: yaml.ScalarNode, Value: anchor, Tag: "!!str"} result := candidate.CreateChild(nil, node) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } -func explodeOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func explodeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- ExplodeOperation") - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) - rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - for childEl := rhs.Front(); childEl != nil; childEl = childEl.Next() { - err = explodeNode(childEl.Value.(*CandidateNode).Node) + for childEl := rhs.MatchingNodes.Front(); childEl != nil; childEl = childEl.Next() { + err = explodeNode(childEl.Value.(*CandidateNode).Node, context) if err != nil { - return nil, err + return Context{}, err } } } - return matchMap, nil + return context, nil } -func explodeNode(node *yaml.Node) error { +func explodeNode(node *yaml.Node, context Context) error { node.Anchor = "" switch node.Kind { case yaml.SequenceNode, yaml.DocumentNode: for index, contentNode := range node.Content { log.Debugf("exploding index %v", index) - errorInContent := explodeNode(contentNode) + errorInContent := explodeNode(contentNode, context) if errorInContent != nil { return errorInContent } @@ -169,7 +169,7 @@ func explodeNode(node *yaml.Node) error { valueNode := node.Content[index+1] log.Debugf("traversing %v", keyNode.Value) if keyNode.Value != "<<" { - err := overrideEntry(node, keyNode, valueNode, index, newContent) + err := overrideEntry(node, keyNode, valueNode, index, context.ChildContext(newContent)) if err != nil { return err } @@ -178,14 +178,14 @@ func explodeNode(node *yaml.Node) error { log.Debugf("an alias merge list!") for index := 0; index < len(valueNode.Content); index = index + 1 { aliasNode := valueNode.Content[index] - err := applyAlias(node, aliasNode.Alias, index, newContent) + err := applyAlias(node, aliasNode.Alias, index, context.ChildContext(newContent)) if err != nil { return err } } } else { log.Debugf("an alias merge!") - err := applyAlias(node, valueNode.Alias, index, newContent) + err := applyAlias(node, valueNode.Alias, index, context.ChildContext(newContent)) if err != nil { return err } @@ -205,7 +205,7 @@ func explodeNode(node *yaml.Node) error { } } -func applyAlias(node *yaml.Node, alias *yaml.Node, aliasIndex int, newContent *list.List) error { +func applyAlias(node *yaml.Node, alias *yaml.Node, aliasIndex int, newContent Context) error { if alias == nil { return nil } @@ -221,15 +221,15 @@ func applyAlias(node *yaml.Node, alias *yaml.Node, aliasIndex int, newContent *l return nil } -func overrideEntry(node *yaml.Node, key *yaml.Node, value *yaml.Node, startIndex int, newContent *list.List) error { +func overrideEntry(node *yaml.Node, key *yaml.Node, value *yaml.Node, startIndex int, newContent Context) error { - err := explodeNode(value) + err := explodeNode(value, newContent) if err != nil { return err } - for newEl := newContent.Front(); newEl != nil; newEl = newEl.Next() { + for newEl := newContent.MatchingNodes.Front(); newEl != nil; newEl = newEl.Next() { valueEl := newEl.Next() // move forward twice keyNode := newEl.Value.(*yaml.Node) log.Debugf("checking new content %v:%v", keyNode.Value, valueEl.Value.(*yaml.Node).Value) @@ -250,12 +250,12 @@ func overrideEntry(node *yaml.Node, key *yaml.Node, value *yaml.Node, startIndex } } - err = explodeNode(key) + err = explodeNode(key, newContent) if err != nil { return err } log.Debugf("adding %v:%v", key.Value, value.Value) - newContent.PushBack(key) - newContent.PushBack(value) + newContent.MatchingNodes.PushBack(key) + newContent.MatchingNodes.PushBack(value) return nil } diff --git a/pkg/yqlib/operator_assign.go b/pkg/yqlib/operator_assign.go index 2d6bc407..85744f5c 100644 --- a/pkg/yqlib/operator_assign.go +++ b/pkg/yqlib/operator_assign.go @@ -1,30 +1,28 @@ package yqlib -import "container/list" - -func assignUpdateOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { - lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs) +func assignUpdateOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { - return nil, err + return Context{}, err } - var rhs *list.List + var rhs Context if !expressionNode.Operation.UpdateAssign { - rhs, err = d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) + rhs, err = d.GetMatchingNodes(context, expressionNode.Rhs) } - for el := lhs.Front(); el != nil; el = el.Next() { + for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) if expressionNode.Operation.UpdateAssign { - rhs, err = d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err = d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) } if err != nil { - return nil, err + return Context{}, err } // grab the first value - first := rhs.Front() + first := rhs.MatchingNodes.Front() if first != nil { rhsCandidate := first.Value.(*CandidateNode) @@ -33,30 +31,30 @@ func assignUpdateOperator(d *dataTreeNavigator, matchingNodes *list.List, expres } } - return matchingNodes, nil + return context, nil } // does not update content or values -func assignAttributesOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { - lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs) +func assignAttributesOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { - return nil, err + return Context{}, err } - for el := lhs.Front(); el != nil; el = el.Next() { + for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) - rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } // grab the first value - first := rhs.Front() + first := rhs.MatchingNodes.Front() if first != nil { candidate.UpdateAttributesFrom(first.Value.(*CandidateNode)) } } - return matchingNodes, nil + return context, nil } diff --git a/pkg/yqlib/operator_booleans.go b/pkg/yqlib/operator_booleans.go index 123007f3..af689ab0 100644 --- a/pkg/yqlib/operator_booleans.go +++ b/pkg/yqlib/operator_booleans.go @@ -25,8 +25,8 @@ func isTruthy(c *CandidateNode) (bool, error) { type boolOp func(bool, bool) bool -func performBoolOp(op boolOp) func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { - return func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { +func performBoolOp(op boolOp) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { + return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { lhs.Node = unwrapDoc(lhs.Node) rhs.Node = unwrapDoc(rhs.Node) @@ -44,35 +44,35 @@ func performBoolOp(op boolOp) func(d *dataTreeNavigator, lhs *CandidateNode, rhs } } -func orOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func orOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- orOp") - return crossFunction(d, matchingNodes, expressionNode, performBoolOp( + return crossFunction(d, context, expressionNode, performBoolOp( func(b1 bool, b2 bool) bool { return b1 || b2 })) } -func andOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- AndOp") - return crossFunction(d, matchingNodes, expressionNode, performBoolOp( + return crossFunction(d, context, expressionNode, performBoolOp( func(b1 bool, b2 bool) bool { return b1 && b2 })) } -func notOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func notOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- notOperation") var results = list.New() - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) log.Debug("notOperation checking %v", candidate) truthy, errDecoding := isTruthy(candidate) if errDecoding != nil { - return nil, errDecoding + return Context{}, errDecoding } result := createBooleanCandidate(candidate, !truthy) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_collect.go b/pkg/yqlib/operator_collect.go index 1b6ad5be..3dcab637 100644 --- a/pkg/yqlib/operator_collect.go +++ b/pkg/yqlib/operator_collect.go @@ -6,21 +6,21 @@ import ( yaml "gopkg.in/yaml.v3" ) -func collectOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func collectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- collectOperation") - if matchMap.Len() == 0 { + if context.MatchingNodes.Len() == 0 { node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq", Value: "[]"} candidate := &CandidateNode{Node: node} - return nodeToMap(candidate), nil + return context.SingleChildContext(candidate), nil } var results = list.New() node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} var collectC *CandidateNode - if matchMap.Front() != nil { - collectC = matchMap.Front().Value.(*CandidateNode).CreateChild(nil, node) + if context.MatchingNodes.Front() != nil { + collectC = context.MatchingNodes.Front().Value.(*CandidateNode).CreateChild(nil, node) if len(collectC.Path) > 0 { collectC.Path = collectC.Path[:len(collectC.Path)-1] } @@ -28,7 +28,7 @@ func collectOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode * collectC = &CandidateNode{Node: node} } - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) log.Debugf("Collecting %v", NodeToString(candidate)) node.Content = append(node.Content, unwrapDoc(candidate.Node)) @@ -36,5 +36,5 @@ func collectOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode * results.PushBack(collectC) - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_collect_object.go b/pkg/yqlib/operator_collect_object.go index ecd81669..4aa5f63b 100644 --- a/pkg/yqlib/operator_collect_object.go +++ b/pkg/yqlib/operator_collect_object.go @@ -17,22 +17,22 @@ import ( ... */ -func collectObjectOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func collectObjectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- collectObjectOperation") - if matchMap.Len() == 0 { + if context.MatchingNodes.Len() == 0 { node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Value: "{}"} candidate := &CandidateNode{Node: node} - return nodeToMap(candidate), nil + return context.SingleChildContext(candidate), nil } - first := matchMap.Front().Value.(*CandidateNode) + first := context.MatchingNodes.Front().Value.(*CandidateNode) var rotated []*list.List = make([]*list.List, len(first.Node.Content)) for i := 0; i < len(first.Node.Content); i++ { rotated[i] = list.New() } - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidateNode := el.Value.(*CandidateNode) for i := 0; i < len(first.Node.Content); i++ { rotated[i].PushBack(candidateNode.CreateChild(i, candidateNode.Node.Content[i])) @@ -41,59 +41,59 @@ func collectObjectOperator(d *dataTreeNavigator, matchMap *list.List, expression newObject := list.New() for i := 0; i < len(first.Node.Content); i++ { - additions, err := collect(d, list.New(), rotated[i]) + additions, err := collect(d, context.ChildContext(list.New()), rotated[i]) if err != nil { - return nil, err + return Context{}, err } - newObject.PushBackList(additions) + newObject.PushBackList(additions.MatchingNodes) } - return newObject, nil + return context.ChildContext(newObject), nil } -func collect(d *dataTreeNavigator, aggregate *list.List, remainingMatches *list.List) (*list.List, error) { +func collect(d *dataTreeNavigator, context Context, remainingMatches *list.List) (Context, error) { if remainingMatches.Len() == 0 { - return aggregate, nil + return context, nil } candidate := remainingMatches.Remove(remainingMatches.Front()).(*CandidateNode) - splatted, err := splat(d, nodeToMap(candidate), + splatted, err := splat(d, context.SingleChildContext(candidate), traversePreferences{DontFollowAlias: true, IncludeMapKeys: false}) - for splatEl := splatted.Front(); splatEl != nil; splatEl = splatEl.Next() { + for splatEl := splatted.MatchingNodes.Front(); splatEl != nil; splatEl = splatEl.Next() { splatEl.Value.(*CandidateNode).Path = nil } if err != nil { - return nil, err + return Context{}, err } - if aggregate.Len() == 0 { + if context.MatchingNodes.Len() == 0 { return collect(d, splatted, remainingMatches) } newAgg := list.New() - for el := aggregate.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { aggCandidate := el.Value.(*CandidateNode) - for splatEl := splatted.Front(); splatEl != nil; splatEl = splatEl.Next() { + for splatEl := splatted.MatchingNodes.Front(); splatEl != nil; splatEl = splatEl.Next() { splatCandidate := splatEl.Value.(*CandidateNode) newCandidate, err := aggCandidate.Copy() if err != nil { - return nil, err + return Context{}, err } newCandidate.Path = nil - newCandidate, err = multiply(multiplyPreferences{AppendArrays: false})(d, newCandidate, splatCandidate) + newCandidate, err = multiply(multiplyPreferences{AppendArrays: false})(d, context, newCandidate, splatCandidate) if err != nil { - return nil, err + return Context{}, err } newAgg.PushBack(newCandidate) } } - return collect(d, newAgg, remainingMatches) + return collect(d, context.ChildContext(newAgg), remainingMatches) } diff --git a/pkg/yqlib/operator_comments.go b/pkg/yqlib/operator_comments.go index b66cba59..5eebf60c 100644 --- a/pkg/yqlib/operator_comments.go +++ b/pkg/yqlib/operator_comments.go @@ -13,41 +13,41 @@ type commentOpPreferences struct { FootComment bool } -func assignCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func assignCommentsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("AssignComments operator!") - lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs) + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { - return nil, err + return Context{}, err } preferences := expressionNode.Operation.Preferences.(commentOpPreferences) comment := "" if !expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - comment = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + comment = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } } - for el := lhs.Front(); el != nil; el = el.Next() { + for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) if expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - comment = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + comment = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } } @@ -63,15 +63,15 @@ func assignCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, expr } } - return matchingNodes, nil + return context, nil } -func getCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func getCommentsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { preferences := expressionNode.Operation.Preferences.(commentOpPreferences) log.Debugf("GetComments operator!") var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) comment := "" if preferences.LineComment { @@ -87,5 +87,5 @@ func getCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, express result := candidate.CreateChild(nil, node) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_create_map.go b/pkg/yqlib/operator_create_map.go index cd83922f..dda93c46 100644 --- a/pkg/yqlib/operator_create_map.go +++ b/pkg/yqlib/operator_create_map.go @@ -6,7 +6,7 @@ import ( "gopkg.in/yaml.v3" ) -func createMapOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func createMapOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- createMapOperation") //each matchingNodes entry should turn into a sequence of keys to create. @@ -18,29 +18,29 @@ func createMapOperator(d *dataTreeNavigator, matchingNodes *list.List, expressio sequences := list.New() - if matchingNodes.Len() > 0 { + if context.MatchingNodes.Len() > 0 { - for matchingNodeEl := matchingNodes.Front(); matchingNodeEl != nil; matchingNodeEl = matchingNodeEl.Next() { + for matchingNodeEl := context.MatchingNodes.Front(); matchingNodeEl != nil; matchingNodeEl = matchingNodeEl.Next() { matchingNode := matchingNodeEl.Value.(*CandidateNode) - sequenceNode, err := sequenceFor(d, matchingNode, expressionNode) + sequenceNode, err := sequenceFor(d, context, matchingNode, expressionNode) if err != nil { - return nil, err + return Context{}, err } sequences.PushBack(sequenceNode) } } else { - sequenceNode, err := sequenceFor(d, nil, expressionNode) + sequenceNode, err := sequenceFor(d, context, nil, expressionNode) if err != nil { - return nil, err + return Context{}, err } sequences.PushBack(sequenceNode) } - return nodeToMap(&CandidateNode{Node: listToNodeSeq(sequences), Document: document, Path: path}), nil + return context.SingleChildContext(&CandidateNode{Node: listToNodeSeq(sequences), Document: document, Path: path}), nil } -func sequenceFor(d *dataTreeNavigator, matchingNode *CandidateNode, expressionNode *ExpressionNode) (*CandidateNode, error) { +func sequenceFor(d *dataTreeNavigator, context Context, matchingNode *CandidateNode, expressionNode *ExpressionNode) (*CandidateNode, error) { var path []interface{} var document uint = 0 var matches = list.New() @@ -48,11 +48,11 @@ func sequenceFor(d *dataTreeNavigator, matchingNode *CandidateNode, expressionNo if matchingNode != nil { path = matchingNode.Path document = matchingNode.Document - matches = nodeToMap(matchingNode) + matches.PushBack(matchingNode) } - mapPairs, err := crossFunction(d, matches, expressionNode, - func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { + mapPairs, err := crossFunction(d, context.ChildContext(matches), expressionNode, + func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { node := yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"} log.Debugf("LHS:", NodeToString(lhs)) log.Debugf("RHS:", NodeToString(rhs)) @@ -67,7 +67,7 @@ func sequenceFor(d *dataTreeNavigator, matchingNode *CandidateNode, expressionNo if err != nil { return nil, err } - innerList := listToNodeSeq(mapPairs) + innerList := listToNodeSeq(mapPairs.MatchingNodes) innerList.Style = yaml.FlowStyle return &CandidateNode{Node: innerList, Document: document, Path: path}, nil } diff --git a/pkg/yqlib/operator_delete.go b/pkg/yqlib/operator_delete.go index c84039e6..abda4cd2 100644 --- a/pkg/yqlib/operator_delete.go +++ b/pkg/yqlib/operator_delete.go @@ -1,21 +1,20 @@ package yqlib import ( - "container/list" "fmt" yaml "gopkg.in/yaml.v3" ) -func deleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { - nodesToDelete, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) + nodesToDelete, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - for el := nodesToDelete.Front(); el != nil; el = el.Next() { + for el := nodesToDelete.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) deleteImmediateChildOp := &Operation{ @@ -28,26 +27,26 @@ func deleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, express Rhs: createTraversalTree(candidate.Path[0:len(candidate.Path)-1], traversePreferences{}), } - _, err := d.GetMatchingNodes(matchingNodes, deleteImmediateChildOpNode) + _, err := d.GetMatchingNodes(context, deleteImmediateChildOpNode) if err != nil { - return nil, err + return Context{}, err } } - return matchingNodes, nil + return context, nil } -func deleteImmediateChildOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { - parents, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) +func deleteImmediateChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { + parents, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } childPath := expressionNode.Operation.Value log.Debug("childPath to remove %v", childPath) - for el := parents.Front(); el != nil; el = el.Next() { + for el := parents.MatchingNodes.Front(); el != nil; el = el.Next() { parent := el.Value.(*CandidateNode) parentNode := unwrapDoc(parent.Node) if parentNode.Kind == yaml.MappingNode { @@ -55,11 +54,11 @@ func deleteImmediateChildOperator(d *dataTreeNavigator, matchingNodes *list.List } else if parentNode.Kind == yaml.SequenceNode { deleteFromArray(parent, childPath) } else { - return nil, fmt.Errorf("Cannot delete nodes from parent of tag %v", parentNode.Tag) + return Context{}, fmt.Errorf("Cannot delete nodes from parent of tag %v", parentNode.Tag) } } - return matchingNodes, nil + return context, nil } func deleteFromMap(candidate *CandidateNode, childPath interface{}) { diff --git a/pkg/yqlib/operator_document_index.go b/pkg/yqlib/operator_document_index.go index 2647800c..58aa0312 100644 --- a/pkg/yqlib/operator_document_index.go +++ b/pkg/yqlib/operator_document_index.go @@ -7,14 +7,14 @@ import ( "gopkg.in/yaml.v3" ) -func getDocumentIndexOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func getDocumentIndexOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Document), Tag: "!!int"} scalar := candidate.CreateChild(nil, node) results.PushBack(scalar) } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_env.go b/pkg/yqlib/operator_env.go index 5089f480..f6c5eebc 100644 --- a/pkg/yqlib/operator_env.go +++ b/pkg/yqlib/operator_env.go @@ -1,7 +1,6 @@ package yqlib import ( - "container/list" "fmt" "os" "strings" @@ -13,7 +12,7 @@ type envOpPreferences struct { StringValue bool } -func envOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func envOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { envName := expressionNode.Operation.CandidateNode.Node.Value log.Debug("EnvOperator, env name:", envName) @@ -29,13 +28,13 @@ func envOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *Expr Value: rawValue, } } else if rawValue == "" { - return nil, fmt.Errorf("Value for env variable '%v' not provided in env()", envName) + return Context{}, fmt.Errorf("Value for env variable '%v' not provided in env()", envName) } else { var dataBucket yaml.Node decoder := yaml.NewDecoder(strings.NewReader(rawValue)) errorReading := decoder.Decode(&dataBucket) if errorReading != nil { - return nil, errorReading + return Context{}, errorReading } //first node is a doc node = unwrapDoc(&dataBucket) @@ -46,5 +45,5 @@ func envOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *Expr target := &CandidateNode{Node: node} - return nodeToMap(target), nil + return context.SingleChildContext(target), nil } diff --git a/pkg/yqlib/operator_equals.go b/pkg/yqlib/operator_equals.go index 0c1ddb29..2882c314 100644 --- a/pkg/yqlib/operator_equals.go +++ b/pkg/yqlib/operator_equals.go @@ -1,15 +1,11 @@ package yqlib -import ( - "container/list" -) - -func equalsOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- equalsOperation") - return crossFunction(d, matchingNodes, expressionNode, isEquals) + return crossFunction(d, context, expressionNode, isEquals) } -func isEquals(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { +func isEquals(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { value := false lhsNode := unwrapDoc(lhs.Node) diff --git a/pkg/yqlib/operator_file.go b/pkg/yqlib/operator_file.go index 06ad9469..d4eeeb1e 100644 --- a/pkg/yqlib/operator_file.go +++ b/pkg/yqlib/operator_file.go @@ -7,32 +7,32 @@ import ( yaml "gopkg.in/yaml.v3" ) -func getFilenameOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func getFilenameOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("GetFilename") var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: candidate.Filename, Tag: "!!str"} result := candidate.CreateChild(nil, node) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } -func getFileIndexOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func getFileIndexOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("GetFileIndex") var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.FileIndex), Tag: "!!int"} result := candidate.CreateChild(nil, node) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_has.go b/pkg/yqlib/operator_has.go index cc433a74..21d36327 100644 --- a/pkg/yqlib/operator_has.go +++ b/pkg/yqlib/operator_has.go @@ -7,20 +7,20 @@ import ( yaml "gopkg.in/yaml.v3" ) -func hasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func hasOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- hasOperation") var results = list.New() - rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) - wanted := rhs.Front().Value.(*CandidateNode).Node + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) + wanted := rhs.MatchingNodes.Front().Value.(*CandidateNode).Node wantedKey := wanted.Value if err != nil { - return nil, err + return Context{}, err } - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) // grab the first value @@ -41,7 +41,7 @@ func hasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode if wanted.Tag == "!!int" { var number, errParsingInt = strconv.ParseInt(wantedKey, 10, 64) // nolint if errParsingInt != nil { - return nil, errParsingInt + return Context{}, errParsingInt } candidateHasKey = int64(len(contents)) > number } @@ -50,5 +50,5 @@ func hasOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode results.PushBack(createBooleanCandidate(candidate, false)) } } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_keys.go b/pkg/yqlib/operator_keys.go index b778355f..d2fe78ea 100644 --- a/pkg/yqlib/operator_keys.go +++ b/pkg/yqlib/operator_keys.go @@ -7,12 +7,12 @@ import ( "gopkg.in/yaml.v3" ) -func keysOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func keysOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- keysOperator") var results = list.New() - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := unwrapDoc(candidate.Node) var targetNode *yaml.Node @@ -21,14 +21,14 @@ func keysOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *Exp } else if node.Kind == yaml.SequenceNode { targetNode = getIndicies(node) } else { - return nil, fmt.Errorf("Cannot get keys of %v, keys only works for maps and arrays", node.Tag) + return Context{}, fmt.Errorf("Cannot get keys of %v, keys only works for maps and arrays", node.Tag) } result := candidate.CreateChild(nil, targetNode) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } func getMapKeys(node *yaml.Node) *yaml.Node { diff --git a/pkg/yqlib/operator_length.go b/pkg/yqlib/operator_length.go index 70888d2e..07ab0c9b 100644 --- a/pkg/yqlib/operator_length.go +++ b/pkg/yqlib/operator_length.go @@ -7,11 +7,11 @@ import ( yaml "gopkg.in/yaml.v3" ) -func lengthOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func lengthOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- lengthOperation") var results = list.New() - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) targetNode := unwrapDoc(candidate.Node) var length int @@ -35,5 +35,5 @@ func lengthOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *E results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_multiply.go b/pkg/yqlib/operator_multiply.go index 7194acc9..ec86c015 100644 --- a/pkg/yqlib/operator_multiply.go +++ b/pkg/yqlib/operator_multiply.go @@ -9,77 +9,18 @@ import ( yaml "gopkg.in/yaml.v3" ) -type crossFunctionCalculation func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) - -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 -} - -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 -} - type multiplyPreferences struct { AppendArrays bool TraversePrefs traversePreferences } -func multiplyOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func multiplyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- MultiplyOperator") - return crossFunction(d, matchingNodes, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences))) + return crossFunction(d, context, expressionNode, multiply(expressionNode.Operation.Preferences.(multiplyPreferences))) } -func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { - return func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { +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) { lhs.Node = unwrapDoc(lhs.Node) rhs.Node = unwrapDoc(rhs.Node) log.Debugf("Multipling LHS: %v", lhs.Node.Tag) @@ -89,11 +30,11 @@ func multiply(preferences multiplyPreferences) func(d *dataTreeNavigator, lhs *C (lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) { var newBlank = lhs.CreateChild(nil, &yaml.Node{}) - var newThing, err = mergeObjects(d, newBlank, lhs, multiplyPreferences{}) + var newThing, err = mergeObjects(d, context, newBlank, lhs, multiplyPreferences{}) if err != nil { return nil, err } - return mergeObjects(d, newThing, rhs, preferences) + return mergeObjects(d, context, newThing, rhs, preferences) } else if lhs.Node.Tag == "!!int" && rhs.Node.Tag == "!!int" { return multiplyIntegers(lhs, rhs) } @@ -119,14 +60,14 @@ func multiplyIntegers(lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, e return target, nil } -func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) { +func mergeObjects(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) (*CandidateNode, error) { shouldAppendArrays := preferences.AppendArrays var results = list.New() // shouldn't recurse arrays if appending prefs := recursiveDescentPreferences{RecurseArray: !shouldAppendArrays, TraversePreferences: traversePreferences{DontFollowAlias: true}} - err := recursiveDecent(d, results, nodeToMap(rhs), prefs) + err := recursiveDecent(d, results, context.SingleChildContext(rhs), prefs) if err != nil { return nil, err } @@ -137,7 +78,7 @@ func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode, } for el := results.Front(); el != nil; el = el.Next() { - err := applyAssignment(d, pathIndexToStartFrom, lhs, el.Value.(*CandidateNode), preferences) + err := applyAssignment(d, context, pathIndexToStartFrom, lhs, el.Value.(*CandidateNode), preferences) if err != nil { return nil, err } @@ -145,7 +86,7 @@ func mergeObjects(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode, return lhs, nil } -func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error { +func applyAssignment(d *dataTreeNavigator, context Context, pathIndexToStartFrom int, lhs *CandidateNode, rhs *CandidateNode, preferences multiplyPreferences) error { shouldAppendArrays := preferences.AppendArrays log.Debugf("merge - applyAssignment lhs %v, rhs: %v", NodeToString(lhs), NodeToString(rhs)) @@ -162,7 +103,7 @@ func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *Candid assignmentOpNode := &ExpressionNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath, preferences.TraversePrefs), Rhs: &ExpressionNode{Operation: rhsOp}} - _, err := d.GetMatchingNodes(nodeToMap(lhs), assignmentOpNode) + _, err := d.GetMatchingNodes(context.SingleChildContext(lhs), assignmentOpNode) return err } diff --git a/pkg/yqlib/operator_path.go b/pkg/yqlib/operator_path.go index 677d9b14..a9828b43 100644 --- a/pkg/yqlib/operator_path.go +++ b/pkg/yqlib/operator_path.go @@ -16,12 +16,12 @@ func createPathNodeFor(pathElement interface{}) *yaml.Node { } } -func getPathOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func getPathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("GetPath") var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} @@ -35,5 +35,5 @@ func getPathOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionN results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_pipe.go b/pkg/yqlib/operator_pipe.go index d922fcb8..2e023521 100644 --- a/pkg/yqlib/operator_pipe.go +++ b/pkg/yqlib/operator_pipe.go @@ -1,11 +1,18 @@ package yqlib -import "container/list" +func pipeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { -func pipeOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { - lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs) + //lhs may update the variable context, we should pass that into the RHS + // BUT we still return the original context back (see jq) + // https://stedolan.github.io/jq/manual/#Variable/SymbolicBindingOperator:...as$identifier|... + + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { - return nil, err + return Context{}, err } - return d.GetMatchingNodes(lhs, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(lhs, expressionNode.Rhs) + if err != nil { + return Context{}, err + } + return context.ChildContext(rhs.MatchingNodes), nil } diff --git a/pkg/yqlib/operator_recursive_descent.go b/pkg/yqlib/operator_recursive_descent.go index ac08eaa1..71095540 100644 --- a/pkg/yqlib/operator_recursive_descent.go +++ b/pkg/yqlib/operator_recursive_descent.go @@ -11,20 +11,20 @@ type recursiveDescentPreferences struct { RecurseArray bool } -func recursiveDescentOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func recursiveDescentOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { var results = list.New() preferences := expressionNode.Operation.Preferences.(recursiveDescentPreferences) - err := recursiveDecent(d, results, matchMap, preferences) + err := recursiveDecent(d, results, context, preferences) if err != nil { - return nil, err + return Context{}, err } - return results, nil + return context.ChildContext(results), nil } -func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.List, preferences recursiveDescentPreferences) error { - for el := matchMap.Front(); el != nil; el = el.Next() { +func recursiveDecent(d *dataTreeNavigator, results *list.List, context Context, preferences recursiveDescentPreferences) error { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) candidate.Node = unwrapDoc(candidate.Node) @@ -35,7 +35,7 @@ func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.Li if candidate.Node.Kind != yaml.AliasNode && len(candidate.Node.Content) > 0 && (preferences.RecurseArray || candidate.Node.Kind != yaml.SequenceNode) { - children, err := splat(d, nodeToMap(candidate), preferences.TraversePreferences) + children, err := splat(d, context.SingleChildContext(candidate), preferences.TraversePreferences) if err != nil { return err diff --git a/pkg/yqlib/operator_select.go b/pkg/yqlib/operator_select.go index 9a29f892..ce5036ba 100644 --- a/pkg/yqlib/operator_select.go +++ b/pkg/yqlib/operator_select.go @@ -4,28 +4,28 @@ import ( "container/list" ) -func selectOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func selectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- selectOperation") var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) - rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } // grab the first value - first := rhs.Front() + first := rhs.MatchingNodes.Front() if first != nil { result := first.Value.(*CandidateNode) includeResult, errDecoding := isTruthy(result) if errDecoding != nil { - return nil, errDecoding + return Context{}, errDecoding } if includeResult { @@ -33,5 +33,5 @@ func selectOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNo } } } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_self.go b/pkg/yqlib/operator_self.go index a18a68d6..232f7d7d 100644 --- a/pkg/yqlib/operator_self.go +++ b/pkg/yqlib/operator_self.go @@ -1,7 +1,5 @@ package yqlib -import "container/list" - -func selfOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { - return matchMap, nil +func selfOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { + return context, nil } diff --git a/pkg/yqlib/operator_sort_keys.go b/pkg/yqlib/operator_sort_keys.go index 58199495..e5908be0 100644 --- a/pkg/yqlib/operator_sort_keys.go +++ b/pkg/yqlib/operator_sort_keys.go @@ -1,33 +1,32 @@ package yqlib import ( - "container/list" "sort" yaml "gopkg.in/yaml.v3" ) -func sortKeysOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func sortKeysOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) - rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - for childEl := rhs.Front(); childEl != nil; childEl = childEl.Next() { + for childEl := rhs.MatchingNodes.Front(); childEl != nil; childEl = childEl.Next() { node := unwrapDoc(childEl.Value.(*CandidateNode).Node) if node.Kind == yaml.MappingNode { sortKeys(node) } if err != nil { - return nil, err + return Context{}, err } } } - return matchingNodes, nil + return context, nil } func sortKeys(node *yaml.Node) { diff --git a/pkg/yqlib/operator_split_document.go b/pkg/yqlib/operator_split_document.go index 851fa7c0..6cabcd23 100644 --- a/pkg/yqlib/operator_split_document.go +++ b/pkg/yqlib/operator_split_document.go @@ -1,18 +1,14 @@ package yqlib -import ( - "container/list" -) - -func splitDocumentOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func splitDocumentOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- splitDocumentOperator") var index uint = 0 - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) candidate.Document = index index = index + 1 } - return matchMap, nil + return context, nil } diff --git a/pkg/yqlib/operator_strings.go b/pkg/yqlib/operator_strings.go index 479e3395..8a0c5365 100644 --- a/pkg/yqlib/operator_strings.go +++ b/pkg/yqlib/operator_strings.go @@ -8,32 +8,32 @@ import ( "gopkg.in/yaml.v3" ) -func joinStringOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func joinStringOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- joinStringOperator") joinStr := "" - rhs, err := d.GetMatchingNodes(matchMap, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - joinStr = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + joinStr = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } var results = list.New() - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := unwrapDoc(candidate.Node) if node.Kind != yaml.SequenceNode { - return nil, fmt.Errorf("Cannot join with %v, can only join arrays of scalars", node.Tag) + return Context{}, fmt.Errorf("Cannot join with %v, can only join arrays of scalars", node.Tag) } targetNode := join(node.Content, joinStr) result := candidate.CreateChild(nil, targetNode) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } func join(content []*yaml.Node, joinStr string) *yaml.Node { @@ -49,35 +49,35 @@ func join(content []*yaml.Node, joinStr string) *yaml.Node { return &yaml.Node{Kind: yaml.ScalarNode, Value: strings.Join(stringsToJoin, joinStr), Tag: "!!str"} } -func splitStringOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func splitStringOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- splitStringOperator") splitStr := "" - rhs, err := d.GetMatchingNodes(matchMap, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - splitStr = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + splitStr = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } var results = list.New() - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := unwrapDoc(candidate.Node) if node.Tag == "!!null" { continue } if node.Tag != "!!str" { - return nil, fmt.Errorf("Cannot split %v, can only split strings", node.Tag) + return Context{}, fmt.Errorf("Cannot split %v, can only split strings", node.Tag) } targetNode := split(node.Value, splitStr) result := candidate.CreateChild(nil, targetNode) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } func split(value string, spltStr string) *yaml.Node { diff --git a/pkg/yqlib/operator_style.go b/pkg/yqlib/operator_style.go index 96408c91..693f344f 100644 --- a/pkg/yqlib/operator_style.go +++ b/pkg/yqlib/operator_style.go @@ -26,43 +26,43 @@ func parseStyle(customStyle string) (yaml.Style, error) { return 0, nil } -func assignStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("AssignStyleOperator: %v") var style yaml.Style if !expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - style, err = parseStyle(rhs.Front().Value.(*CandidateNode).Node.Value) + if rhs.MatchingNodes.Front() != nil { + style, err = parseStyle(rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value) if err != nil { - return nil, err + return Context{}, err } } } - lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs) + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { - return nil, err + return Context{}, err } - for el := lhs.Front(); el != nil; el = el.Next() { + for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) log.Debugf("Setting style of : %v", candidate.GetKey()) if expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - style, err = parseStyle(rhs.Front().Value.(*CandidateNode).Node.Value) + if rhs.MatchingNodes.Front() != nil { + style, err = parseStyle(rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value) if err != nil { - return nil, err + return Context{}, err } } } @@ -70,15 +70,15 @@ func assignStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, express candidate.Node.Style = style } - return matchingNodes, nil + return context, nil } -func getStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func getStyleOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("GetStyleOperator") var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) var style string switch candidate.Node.Style { @@ -104,5 +104,5 @@ func getStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, expression results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_tag.go b/pkg/yqlib/operator_tag.go index a3c4b84f..60653509 100644 --- a/pkg/yqlib/operator_tag.go +++ b/pkg/yqlib/operator_tag.go @@ -6,58 +6,58 @@ import ( yaml "gopkg.in/yaml.v3" ) -func assignTagOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("AssignTagOperator: %v") tag := "" if !expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - tag = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + tag = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } } - lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs) + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { - return nil, err + return Context{}, err } - for el := lhs.Front(); el != nil; el = el.Next() { + for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) log.Debugf("Setting tag of : %v", candidate.GetKey()) if expressionNode.Operation.UpdateAssign { - rhs, err := d.GetMatchingNodes(nodeToMap(candidate), expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - if rhs.Front() != nil { - tag = rhs.Front().Value.(*CandidateNode).Node.Value + if rhs.MatchingNodes.Front() != nil { + tag = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Value } } unwrapDoc(candidate.Node).Tag = tag } - return matchingNodes, nil + return context, nil } -func getTagOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func getTagOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("GetTagOperator") var results = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) node := &yaml.Node{Kind: yaml.ScalarNode, Value: unwrapDoc(candidate.Node).Tag, Tag: "!!str"} result := candidate.CreateChild(nil, node) results.PushBack(result) } - return results, nil + return context.ChildContext(results), nil } diff --git a/pkg/yqlib/operator_traverse_path.go b/pkg/yqlib/operator_traverse_path.go index d63b6cec..20754b60 100644 --- a/pkg/yqlib/operator_traverse_path.go +++ b/pkg/yqlib/operator_traverse_path.go @@ -15,23 +15,23 @@ type traversePreferences struct { DontAutoCreate bool // by default, we automatically create entries on the fly. } -func splat(d *dataTreeNavigator, matches *list.List, prefs traversePreferences) (*list.List, error) { - return traverseNodesWithArrayIndices(matches, make([]*yaml.Node, 0), prefs) +func splat(d *dataTreeNavigator, context Context, prefs traversePreferences) (Context, error) { + return traverseNodesWithArrayIndices(context, make([]*yaml.Node, 0), prefs) } -func traversePathOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func traversePathOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("-- Traversing") - var matchingNodeMap = list.New() + var matches = list.New() - for el := matchMap.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { newNodes, err := traverse(d, el.Value.(*CandidateNode), expressionNode.Operation) if err != nil { - return nil, err + return Context{}, err } - matchingNodeMap.PushBackList(newNodes) + matches.PushBackList(newNodes) } - return matchingNodeMap, nil + return context.ChildContext(matches), nil } func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, operation *Operation) (*list.List, error) { @@ -74,30 +74,30 @@ func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, operation *Oper } } -func traverseArrayOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { // rhs is a collect expression that will yield indexes to retreive of the arrays - rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - var indicesToTraverse = rhs.Front().Value.(*CandidateNode).Node.Content - return traverseNodesWithArrayIndices(matchingNodes, indicesToTraverse, traversePreferences{}) + var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Content + return traverseNodesWithArrayIndices(context, indicesToTraverse, traversePreferences{}) } -func traverseNodesWithArrayIndices(matchingNodes *list.List, indicesToTraverse []*yaml.Node, prefs traversePreferences) (*list.List, error) { +func traverseNodesWithArrayIndices(context Context, indicesToTraverse []*yaml.Node, prefs traversePreferences) (Context, error) { var matchingNodeMap = list.New() - for el := matchingNodes.Front(); el != nil; el = el.Next() { + for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) newNodes, err := traverseArrayIndices(candidate, indicesToTraverse, prefs) if err != nil { - return nil, err + return Context{}, err } matchingNodeMap.PushBackList(newNodes) } - return matchingNodeMap, nil + return context.ChildContext(matchingNodeMap), nil } func traverseArrayIndices(matchingNode *CandidateNode, indicesToTraverse []*yaml.Node, prefs traversePreferences) (*list.List, error) { // call this if doc / alias like the other traverse diff --git a/pkg/yqlib/operator_union.go b/pkg/yqlib/operator_union.go index b95c986f..cb64d140 100644 --- a/pkg/yqlib/operator_union.go +++ b/pkg/yqlib/operator_union.go @@ -1,19 +1,17 @@ package yqlib -import "container/list" - -func unionOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { - lhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Lhs) +func unionOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) if err != nil { - return nil, err + return Context{}, err } - rhs, err := d.GetMatchingNodes(matchingNodes, expressionNode.Rhs) + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) if err != nil { - return nil, err + return Context{}, err } - for el := rhs.Front(); el != nil; el = el.Next() { + for el := rhs.MatchingNodes.Front(); el != nil; el = el.Next() { node := el.Value.(*CandidateNode) - lhs.PushBack(node) + lhs.MatchingNodes.PushBack(node) } return lhs, nil } diff --git a/pkg/yqlib/operator_value.go b/pkg/yqlib/operator_value.go index f30459a0..6c70b1f7 100644 --- a/pkg/yqlib/operator_value.go +++ b/pkg/yqlib/operator_value.go @@ -1,8 +1,6 @@ package yqlib -import "container/list" - -func valueOperator(d *dataTreeNavigator, matchMap *list.List, expressionNode *ExpressionNode) (*list.List, error) { +func valueOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debug("value = %v", expressionNode.Operation.CandidateNode.Node.Value) - return nodeToMap(expressionNode.Operation.CandidateNode), nil + return context.SingleChildContext(expressionNode.Operation.CandidateNode), nil } diff --git a/pkg/yqlib/operators.go b/pkg/yqlib/operators.go index 37d4b1eb..08e8ad45 100644 --- a/pkg/yqlib/operators.go +++ b/pkg/yqlib/operators.go @@ -7,7 +7,7 @@ import ( "gopkg.in/yaml.v3" ) -type operatorHandler func(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) +type operatorHandler func(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) func unwrapDoc(node *yaml.Node) *yaml.Node { if node.Kind == yaml.DocumentNode { @@ -16,8 +16,67 @@ func unwrapDoc(node *yaml.Node) *yaml.Node { return node } -func emptyOperator(d *dataTreeNavigator, matchingNodes *list.List, expressionNode *ExpressionNode) (*list.List, error) { - return list.New(), nil +func emptyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { + context.MatchingNodes = list.New() + return context, nil +} + +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) { + var results = list.New() + lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs) + if err != nil { + return Context{}, err + } + log.Debugf("crossFunction LHS len: %v", lhs.MatchingNodes.Len()) + + rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) + + if err != nil { + 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 err != nil { + return Context{}, err + } + results.PushBack(resultCandidate) + } + + } + return context.ChildContext(results), nil +} + +func crossFunction(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, calculation crossFunctionCalculation) (Context, error) { + var results = list.New() + + var evaluateAllTogether = true + for matchEl := context.MatchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() { + evaluateAllTogether = evaluateAllTogether && matchEl.Value.(*CandidateNode).EvaluateTogether + if !evaluateAllTogether { + break + } + } + if evaluateAllTogether { + return doCrossFunc(d, context, expressionNode, calculation) + } + + for matchEl := context.MatchingNodes.Front(); matchEl != nil; matchEl = matchEl.Next() { + innerResults, err := doCrossFunc(d, context.SingleChildContext(matchEl.Value.(*CandidateNode)), expressionNode, calculation) + if err != nil { + return Context{}, err + } + results.PushBackList(innerResults.MatchingNodes) + } + + return context.ChildContext(results), nil } func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode { @@ -29,12 +88,6 @@ func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode { return owner.CreateChild(nil, node) } -func nodeToMap(candidate *CandidateNode) *list.List { - elMap := list.New() - elMap.PushBack(candidate) - return elMap -} - func createTraversalTree(path []interface{}, traversePrefs traversePreferences) *ExpressionNode { if len(path) == 0 { return &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}} diff --git a/pkg/yqlib/operators_test.go b/pkg/yqlib/operators_test.go index 71019c15..cd5cbbee 100644 --- a/pkg/yqlib/operators_test.go +++ b/pkg/yqlib/operators_test.go @@ -27,7 +27,6 @@ type expressionScenario struct { } func testScenario(t *testing.T, s *expressionScenario) { - var results *list.List var err error node, err := NewExpressionParser().ParseExpression(s.expression) @@ -66,13 +65,13 @@ func testScenario(t *testing.T, s *expressionScenario) { os.Setenv("myenv", s.environmentVariable) } - results, err = NewDataTreeNavigator().GetMatchingNodes(inputs, node) + context, err := NewDataTreeNavigator().GetMatchingNodes(Context{MatchingNodes: inputs}, node) if err != nil { t.Error(fmt.Errorf("%v: %v", err, s.expression)) return } - test.AssertResultComplexWithContext(t, s.expected, resultsToString(results), fmt.Sprintf("exp: %v\ndoc: %v", s.expression, s.document)) + test.AssertResultComplexWithContext(t, s.expected, resultsToString(context.MatchingNodes), fmt.Sprintf("exp: %v\ndoc: %v", s.expression, s.document)) } func resultsToString(results *list.List) []string { @@ -252,12 +251,12 @@ func documentOutput(t *testing.T, w *bufio.Writer, s expressionScenario, formatt } - results, err := NewDataTreeNavigator().GetMatchingNodes(inputs, node) + context, err := NewDataTreeNavigator().GetMatchingNodes(Context{MatchingNodes: inputs}, node) if err != nil { t.Error(err, s.expression) } - err = printer.PrintResults(results) + err = printer.PrintResults(context.MatchingNodes) if err != nil { t.Error(err, s.expression) } diff --git a/pkg/yqlib/printer.go b/pkg/yqlib/printer.go index 082c02dd..361e3023 100644 --- a/pkg/yqlib/printer.go +++ b/pkg/yqlib/printer.go @@ -74,14 +74,14 @@ func (p *resultsPrinter) safelyFlush(writer *bufio.Writer) { func (p *resultsPrinter) PrintResults(matchingNodes *list.List) error { log.Debug("PrintResults for %v matches", matchingNodes.Len()) - var err error if p.outputToJSON { explodeOp := Operation{OperationType: explodeOpType} explodeNode := ExpressionNode{Operation: &explodeOp} - matchingNodes, err = p.treeNavigator.GetMatchingNodes(matchingNodes, &explodeNode) + context, err := p.treeNavigator.GetMatchingNodes(Context{MatchingNodes: matchingNodes}, &explodeNode) if err != nil { return err } + matchingNodes = context.MatchingNodes } bufferedWriter := bufio.NewWriter(p.writer) diff --git a/pkg/yqlib/printer_test.go b/pkg/yqlib/printer_test.go index aaafb448..fdb51736 100644 --- a/pkg/yqlib/printer_test.go +++ b/pkg/yqlib/printer_test.go @@ -3,6 +3,7 @@ package yqlib import ( "bufio" "bytes" + "container/list" "strings" "testing" @@ -16,6 +17,12 @@ a: apple a: coconut ` +func nodeToList(candidate *CandidateNode) *list.List { + elMap := list.New() + elMap.PushBack(candidate) + return elMap +} + func TestPrinterMultipleDocsInSequence(t *testing.T) { var output bytes.Buffer var writer = bufio.NewWriter(&output) @@ -27,13 +34,13 @@ func TestPrinterMultipleDocsInSequence(t *testing.T) { } el := inputs.Front() - sample1 := nodeToMap(el.Value.(*CandidateNode)) + sample1 := nodeToList(el.Value.(*CandidateNode)) el = el.Next() - sample2 := nodeToMap(el.Value.(*CandidateNode)) + sample2 := nodeToList(el.Value.(*CandidateNode)) el = el.Next() - sample3 := nodeToMap(el.Value.(*CandidateNode)) + sample3 := nodeToList(el.Value.(*CandidateNode)) err = printer.PrintResults(sample1) if err != nil { @@ -68,19 +75,19 @@ func TestPrinterMultipleFilesInSequence(t *testing.T) { elNode := el.Value.(*CandidateNode) elNode.Document = 0 elNode.FileIndex = 0 - sample1 := nodeToMap(elNode) + sample1 := nodeToList(elNode) el = el.Next() elNode = el.Value.(*CandidateNode) elNode.Document = 0 elNode.FileIndex = 1 - sample2 := nodeToMap(elNode) + sample2 := nodeToList(elNode) el = el.Next() elNode = el.Value.(*CandidateNode) elNode.Document = 0 elNode.FileIndex = 2 - sample3 := nodeToMap(elNode) + sample3 := nodeToList(elNode) err = printer.PrintResults(sample1) if err != nil { diff --git a/pkg/yqlib/stream_evaluator.go b/pkg/yqlib/stream_evaluator.go index 1fc1e0c1..093c2777 100644 --- a/pkg/yqlib/stream_evaluator.go +++ b/pkg/yqlib/stream_evaluator.go @@ -41,11 +41,11 @@ func (s *streamEvaluator) EvaluateNew(expression string, printer Printer) error inputList := list.New() inputList.PushBack(candidateNode) - matches, errorParsing := s.treeNavigator.GetMatchingNodes(inputList, node) + result, errorParsing := s.treeNavigator.GetMatchingNodes(Context{MatchingNodes: inputList}, node) if errorParsing != nil { return errorParsing } - return printer.PrintResults(matches) + return printer.PrintResults(result.MatchingNodes) } func (s *streamEvaluator) EvaluateFiles(expression string, filenames []string, printer Printer) error { @@ -97,11 +97,11 @@ func (s *streamEvaluator) Evaluate(filename string, reader io.Reader, node *Expr inputList := list.New() inputList.PushBack(candidateNode) - matches, errorParsing := s.treeNavigator.GetMatchingNodes(inputList, node) + result, errorParsing := s.treeNavigator.GetMatchingNodes(Context{MatchingNodes: inputList}, node) if errorParsing != nil { return errorParsing } - err := printer.PrintResults(matches) + err := printer.PrintResults(result.MatchingNodes) if err != nil { return err }