This commit is contained in:
Mike Farah 2020-10-21 12:54:58 +11:00
parent 6a698332dd
commit 65e6e492cd
28 changed files with 232 additions and 148 deletions

2
go.mod
View File

@ -1,7 +1,7 @@
module github.com/mikefarah/yq/v3
require (
github.com/elliotchance/orderedmap v1.3.0
github.com/elliotchance/orderedmap v1.3.0 // indirect
github.com/fatih/color v1.9.0
github.com/goccy/go-yaml v1.8.1
github.com/kylelemons/godebug v1.1.0 // indirect

View File

@ -3,7 +3,8 @@ package treeops
import (
"fmt"
"github.com/elliotchance/orderedmap"
"container/list"
"gopkg.in/op/go-logging.v1"
)
@ -24,10 +25,10 @@ func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator {
}
func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error) {
var matchingNodeMap = orderedmap.NewOrderedMap()
var matchingNodeMap = list.New()
for _, n := range matchingNodes {
matchingNodeMap.Set(n.GetKey(), n)
matchingNodeMap.PushBack(n)
}
matchedNodes, err := d.getMatchingNodes(matchingNodeMap, pathNode)
@ -43,7 +44,7 @@ func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*CandidateNode, pat
return values, nil
}
func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
if pathNode == nil {
log.Debugf("getMatchingNodes - nothing to do")
return matchingNodes, nil

View File

@ -2,9 +2,9 @@ package treeops
import (
"bytes"
"container/list"
"fmt"
"github.com/elliotchance/orderedmap"
"gopkg.in/op/go-logging.v1"
"gopkg.in/yaml.v3"
)
@ -22,17 +22,18 @@ var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: OrOpera
var And = &OperationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: AndOperator}
var Union = &OperationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: UnionOperator}
var Intersection = &OperationType{Type: "INTERSECTION", NumArgs: 2, Precedence: 20, Handler: IntersectionOperator}
var Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignOperator}
var AssignAttributes = &OperationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: AssignAttributesOperator}
var Multiply = &OperationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 40, Handler: MultiplyOperator}
var Equals = &OperationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: EqualsOperator}
var CreateMap = &OperationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 40, Handler: CreateMapOperator}
var Pipe = &OperationType{Type: "PIPE", NumArgs: 2, Precedence: 45, Handler: PipeOperator}
var Length = &OperationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: LengthOperator}
var Collect = &OperationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: CollectOperator}
var CollectObject = &OperationType{Type: "COLLECT_OBJECT", NumArgs: 0, Precedence: 50, Handler: CollectObjectOperator}
var TraversePath = &OperationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
var DocumentFilter = &OperationType{Type: "DOCUMENT_FILTER", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
@ -129,7 +130,7 @@ func (l *lib) Get(document int, documentNode *yaml.Node, path string) ([]*Candid
}
//use for debugging only
func NodesToString(collection *orderedmap.OrderedMap) string {
func NodesToString(collection *list.List) string {
if !log.IsEnabledFor(logging.DEBUG) {
return ""
}

View File

@ -0,0 +1,2 @@
package treeops

View File

@ -1,8 +1,8 @@
package treeops
import "github.com/elliotchance/orderedmap"
import "container/list"
func AssignOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func AssignOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
@ -27,7 +27,7 @@ func AssignOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap,
}
// does not update content or values
func AssignAttributesOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func AssignAttributesOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err

View File

@ -1,7 +1,8 @@
package treeops
import (
"github.com/elliotchance/orderedmap"
"container/list"
"gopkg.in/yaml.v3"
)
@ -23,8 +24,8 @@ func isTruthy(c *CandidateNode) (bool, error) {
type boolOp func(bool, bool) bool
func booleanOp(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode, op boolOp) (*orderedmap.OrderedMap, error) {
var results = orderedmap.NewOrderedMap()
func booleanOp(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode, op boolOp) (*list.List, error) {
var results = list.New()
for el := matchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
@ -52,7 +53,7 @@ func booleanOp(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathN
}
boolResult := createBooleanCandidate(lhsCandidate, op(lhsTrue, rhsTrue))
results.Set(boolResult.GetKey(), boolResult)
results.PushBack(boolResult)
}
}
@ -60,14 +61,14 @@ func booleanOp(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathN
return results, nil
}
func OrOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func OrOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- orOp")
return booleanOp(d, matchingNodes, pathNode, func(b1 bool, b2 bool) bool {
return b1 || b2
})
}
func AndOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func AndOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- AndOp")
return booleanOp(d, matchingNodes, pathNode, func(b1 bool, b2 bool) bool {
return b1 && b2

View File

@ -21,6 +21,7 @@ var booleanOperatorScenarios = []expressionScenario{
document: `{a: true, b: false}`,
expression: `.[] or (false, true)`,
expected: []string{
"D0, P[a], (!!bool)::true\n",
"D0, P[a], (!!bool)::true\n",
"D0, P[b], (!!bool)::false\n",
"D0, P[b], (!!bool)::true\n",

View File

@ -1,14 +1,15 @@
package treeops
import (
"github.com/elliotchance/orderedmap"
"container/list"
"gopkg.in/yaml.v3"
)
func CollectOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func CollectOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- collectOperation")
var results = orderedmap.NewOrderedMap()
var results = list.New()
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
@ -26,7 +27,7 @@ func CollectOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, path
}
collectC := &CandidateNode{Node: node, Document: document, Path: path}
results.Set(collectC.GetKey(), collectC)
results.PushBack(collectC)
return results, nil
}

View File

@ -0,0 +1,8 @@
package treeops
import "container/list"
func CollectObjectOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- collectObjectOperation")
return nil, nil
}

View File

@ -0,0 +1,43 @@
package treeops
import (
"container/list"
"gopkg.in/yaml.v3"
)
func CreateMapOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- createMapOperation")
var path []interface{} = nil
var document uint = 0
if matchingNodes.Front() != nil {
sample := matchingNodes.Front().Value.(*CandidateNode)
path = sample.Path
document = sample.Document
}
mapPairs, err := crossFunction(d, matchingNodes, pathNode,
func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
node := yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
log.Debugf("LHS:", lhs.Node.Value)
log.Debugf("RHS:", rhs.Node.Value)
node.Content = []*yaml.Node{
lhs.Node,
rhs.Node,
}
return &CandidateNode{Node: &node, Document: document, Path: path}, nil
})
if err != nil {
return nil, err
}
//wrap up all the pairs into an array
node := yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
for mapPair := mapPairs.Front(); mapPair != nil; mapPair = mapPair.Next() {
mapPairCandidate := mapPair.Value.(*CandidateNode)
log.Debugf("Collecting %v into sequence", NodeToString(mapPairCandidate))
node.Content = append(node.Content, mapPairCandidate.Node)
}
return nodeToMap(&CandidateNode{Node: &node, Document: document, Path: path}), nil
}

View File

@ -0,0 +1,28 @@
package treeops
import (
"testing"
)
var createMapOperatorScenarios = []expressionScenario{
{
document: `{name: Mike, age: 32}`,
expression: `.name: .age`,
expected: []string{
"D0, P[], (!!seq)::- Mike: 32\n",
},
},
{
document: `{name: Mike, pets: [cat, dog]}`,
expression: `.name: .pets[]`,
expected: []string{
"D0, P[], (!!seq)::- Mike: cat\n- Mike: dog\n",
},
},
}
func TestCreateMapOperatorScenarios(t *testing.T) {
for _, tt := range createMapOperatorScenarios {
testScenario(t, &tt)
}
}

View File

@ -1,11 +1,12 @@
package treeops
import (
"github.com/elliotchance/orderedmap"
"container/list"
"gopkg.in/yaml.v3"
)
func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
@ -16,8 +17,8 @@ func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *orderedmap.Ordered
for el := lhs.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
elMap := orderedmap.NewOrderedMap()
elMap.Set(candidate.GetKey(), candidate)
elMap := list.New()
elMap.PushBack(candidate)
nodesToDelete, err := d.getMatchingNodes(elMap, pathNode.Rhs)
log.Debug("nodesToDelete:\n%v", NodesToString(nodesToDelete))
if err != nil {
@ -35,7 +36,7 @@ func DeleteChildOperator(d *dataTreeNavigator, matchingNodes *orderedmap.Ordered
return lhs, nil
}
func deleteFromMap(candidate *CandidateNode, nodesToDelete *orderedmap.OrderedMap) {
func deleteFromMap(candidate *CandidateNode, nodesToDelete *list.List) {
log.Debug("deleteFromMap")
node := candidate.Node
contents := node.Content
@ -50,7 +51,8 @@ func deleteFromMap(candidate *CandidateNode, nodesToDelete *orderedmap.OrderedMa
Document: candidate.Document,
Path: append(candidate.Path, key.Value),
}
_, shouldDelete := nodesToDelete.Get(childCandidate.GetKey())
// _, shouldDelete := nodesToDelete.Get(childCandidate.GetKey())
shouldDelete := true
log.Debugf("shouldDelete %v ? %v", childCandidate.GetKey(), shouldDelete)
@ -61,7 +63,7 @@ func deleteFromMap(candidate *CandidateNode, nodesToDelete *orderedmap.OrderedMa
node.Content = newContents
}
func deleteFromArray(candidate *CandidateNode, nodesToDelete *orderedmap.OrderedMap) {
func deleteFromArray(candidate *CandidateNode, nodesToDelete *list.List) {
log.Debug("deleteFromArray")
node := candidate.Node
contents := node.Content
@ -70,13 +72,14 @@ func deleteFromArray(candidate *CandidateNode, nodesToDelete *orderedmap.Ordered
for index := 0; index < len(contents); index = index + 1 {
value := contents[index]
childCandidate := &CandidateNode{
Node: value,
Document: candidate.Document,
Path: append(candidate.Path, index),
}
// childCandidate := &CandidateNode{
// Node: value,
// Document: candidate.Document,
// Path: append(candidate.Path, index),
// }
_, shouldDelete := nodesToDelete.Get(childCandidate.GetKey())
// _, shouldDelete := nodesToDelete.Get(childCandidate.GetKey())
shouldDelete := true
if !shouldDelete {
newContents = append(newContents, value)
}

View File

@ -1,10 +1,10 @@
package treeops
import (
"github.com/elliotchance/orderedmap"
"container/list"
)
func EqualsOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func EqualsOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- equalsOperation")
return crossFunction(d, matchingNodes, pathNode, isEquals)
}

View File

@ -5,35 +5,30 @@ import (
)
var equalsOperatorScenarios = []expressionScenario{
// {
// document: `[cat,goat,dog]`,
// expression: `(.[] == "*at")`,
// expected: []string{
// "D0, P[], (!!bool)::true\n",
// },
// }, {
// document: `[cat,goat,dog]`,
// expression: `.[] | (. == "*at")`,
// expected: []string{
// "D0, P[0], (!!bool)::true\n",
// "D0, P[1], (!!bool)::true\n",
// "D0, P[2], (!!bool)::false\n",
// },
// }, {
// document: `[3, 4, 5]`,
// expression: `.[] | (. == 4)`,
// expected: []string{
// "D0, P[0], (!!bool)::false\n",
// "D0, P[1], (!!bool)::true\n",
// "D0, P[2], (!!bool)::false\n",
// },
// }, {
// document: `a: { cat: {b: apple, c: whatever}, pat: {b: banana} }`,
// expression: `.a | (.[].b == "apple")`,
// expected: []string{
// "D0, P[a], (!!bool)::true\n",
// },
// },
{
document: `[cat,goat,dog]`,
expression: `.[] | (. == "*at")`,
expected: []string{
"D0, P[0], (!!bool)::true\n",
"D0, P[1], (!!bool)::true\n",
"D0, P[2], (!!bool)::false\n",
},
}, {
document: `[3, 4, 5]`,
expression: `.[] | (. == 4)`,
expected: []string{
"D0, P[0], (!!bool)::false\n",
"D0, P[1], (!!bool)::true\n",
"D0, P[2], (!!bool)::false\n",
},
}, {
document: `a: { cat: {b: apple, c: whatever}, pat: {b: banana} }`,
expression: `.a | (.[].b == "apple")`,
expected: []string{
"D0, P[a cat b], (!!bool)::true\n",
"D0, P[a pat b], (!!bool)::false\n",
},
},
{
document: ``,
expression: `null == null`,
@ -41,13 +36,13 @@ var equalsOperatorScenarios = []expressionScenario{
"D0, P[], (!!bool)::true\n",
},
},
// {
// document: ``,
// expression: `null == ~`,
// expected: []string{
// "D0, P[], (!!bool)::true\n",
// },
// },
{
document: ``,
expression: `null == ~`,
expected: []string{
"D0, P[], (!!bool)::true\n",
},
},
}
func TestEqualOperatorScenarios(t *testing.T) {

View File

@ -3,13 +3,14 @@ package treeops
import (
"fmt"
"github.com/elliotchance/orderedmap"
"container/list"
"gopkg.in/yaml.v3"
)
type CrossFunctionCalculation func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
func crossFunction(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode, calculation CrossFunctionCalculation) (*orderedmap.OrderedMap, error) {
func crossFunction(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode, calculation CrossFunctionCalculation) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
@ -21,7 +22,7 @@ func crossFunction(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, p
return nil, err
}
var results = orderedmap.NewOrderedMap()
var results = list.New()
for el := lhs.Front(); el != nil; el = el.Next() {
lhsCandidate := el.Value.(*CandidateNode)
@ -32,14 +33,14 @@ func crossFunction(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, p
if err != nil {
return nil, err
}
results.Set(resultCandidate.GetKey(), resultCandidate)
results.PushBack(resultCandidate)
}
}
return results, nil
}
func MultiplyOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func MultiplyOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- MultiplyOperator")
return crossFunction(d, matchingNodes, pathNode, multiply)
}
@ -47,7 +48,7 @@ func MultiplyOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap
func multiply(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
if lhs.Node.Kind == yaml.MappingNode && rhs.Node.Kind == yaml.MappingNode ||
(lhs.Node.Kind == yaml.SequenceNode && rhs.Node.Kind == yaml.SequenceNode) {
var results = orderedmap.NewOrderedMap()
var results = list.New()
recursiveDecent(d, results, nodeToMap(rhs))
var pathIndexToStartFrom int = 0

View File

@ -1,10 +1,10 @@
package treeops
import "github.com/elliotchance/orderedmap"
import "container/list"
func NotOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func NotOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- notOperation")
var results = orderedmap.NewOrderedMap()
var results = list.New()
for el := matchMap.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
@ -14,7 +14,7 @@ func NotOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode
return nil, errDecoding
}
result := createBooleanCandidate(candidate, !truthy)
results.Set(result.GetKey(), result)
results.PushBack(result)
}
return results, nil
}

View File

@ -1,11 +1,11 @@
package treeops
import (
"github.com/elliotchance/orderedmap"
"container/list"
)
func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
var results = orderedmap.NewOrderedMap()
func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
var results = list.New()
err := recursiveDecent(d, results, matchMap)
if err != nil {
@ -15,13 +15,13 @@ func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *orderedmap.Ordered
return results, nil
}
func recursiveDecent(d *dataTreeNavigator, results *orderedmap.OrderedMap, matchMap *orderedmap.OrderedMap) error {
func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.List) error {
splatOperation := &Operation{OperationType: TraversePath, Value: "[]"}
splatTreeNode := &PathTreeNode{Operation: splatOperation}
for el := matchMap.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
results.Set(candidate.GetKey(), candidate)
results.PushBack(candidate)
children, err := TraversePathOperator(d, nodeToMap(candidate), splatTreeNode)

View File

@ -1,13 +1,13 @@
package treeops
import (
"github.com/elliotchance/orderedmap"
"container/list"
)
func SelectOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func SelectOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- selectOperation")
var results = orderedmap.NewOrderedMap()
var results = list.New()
for el := matchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
@ -29,7 +29,7 @@ func SelectOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap,
}
if includeResult {
results.Set(candidate.GetKey(), candidate)
results.PushBack(candidate)
}
}
}

View File

@ -1,7 +1,7 @@
package treeops
import "github.com/elliotchance/orderedmap"
import "container/list"
func SelfOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func SelfOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
return matchMap, nil
}

View File

@ -3,13 +3,14 @@ package treeops
import (
"fmt"
"github.com/elliotchance/orderedmap"
"container/list"
"gopkg.in/yaml.v3"
)
func TraversePathOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func TraversePathOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- Traversing")
var matchingNodeMap = orderedmap.NewOrderedMap()
var matchingNodeMap = list.New()
var newNodes []*CandidateNode
var err error
@ -19,7 +20,7 @@ func TraversePathOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap,
return nil, err
}
for _, n := range newNodes {
matchingNodeMap.Set(n.GetKey(), n)
matchingNodeMap.PushBack(n)
}
}
@ -30,7 +31,7 @@ func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, pathNode *Opera
log.Debug("Traversing %v", NodeToString(matchingNode))
value := matchingNode.Node
if value.Kind == 0 {
if value.Tag == "!!null" {
log.Debugf("Guessing kind")
// we must ahve added this automatically, lets guess what it should be now
switch pathNode.Value.(type) {
@ -41,6 +42,7 @@ func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, pathNode *Opera
log.Debugf("probabel a map")
value.Kind = yaml.MappingNode
}
value.Tag = ""
}
switch value.Kind {
@ -110,7 +112,7 @@ func traverseMap(candidate *CandidateNode, pathNode *Operation) ([]*CandidateNod
}
if len(newMatches) == 0 {
//no matches, create one automagically
valueNode := &yaml.Node{Tag: "!!null"}
valueNode := &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"}
node.Content = append(node.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: pathNode.StringValue}, valueNode)
newMatches = append(newMatches, &CandidateNode{
Node: valueNode,
@ -145,7 +147,7 @@ func traverseArray(candidate *CandidateNode, pathNode *Operation) ([]*CandidateN
indexToUse := index
contentLength := int64(len(candidate.Node.Content))
for contentLength <= index {
candidate.Node.Content = append(candidate.Node.Content, &yaml.Node{Tag: "!!null"})
candidate.Node.Content = append(candidate.Node.Content, &yaml.Node{Tag: "!!null", Kind: yaml.ScalarNode, Value: "null"})
contentLength = int64(len(candidate.Node.Content))
}

View File

@ -24,21 +24,21 @@ var traversePathOperatorScenarios = []expressionScenario{
document: `{}`,
expression: `.a.b`,
expected: []string{
"D0, P[a b], ()::null\n",
"D0, P[a b], (!!null)::null\n",
},
},
{
document: `{}`,
expression: `.[1].a`,
expected: []string{
"D0, P[1 a], ()::null\n",
"D0, P[1 a], (!!null)::null\n",
},
},
{
document: `{}`,
expression: `.a.[1]`,
expected: []string{
"D0, P[a 1], ()::null\n",
"D0, P[a 1], (!!null)::null\n",
},
},
{
@ -55,7 +55,7 @@ var traversePathOperatorScenarios = []expressionScenario{
expected: []string{
"D0, P[a cat b], (!!int)::3\n",
"D0, P[a mad b], (!!int)::4\n",
"D0, P[a fad b], ()::null\n",
"D0, P[a fad b], (!!null)::null\n",
},
},
{
@ -72,7 +72,7 @@ var traversePathOperatorScenarios = []expressionScenario{
expected: []string{
"D0, P[a cat], (!!str)::apple\n",
"D0, P[a mad], (!!str)::things\n",
"D0, P[a fad], ()::null\n",
"D0, P[a fad], (!!null)::null\n",
},
},
{

View File

@ -1,8 +1,8 @@
package treeops
import "github.com/elliotchance/orderedmap"
import "container/list"
func UnionOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func UnionOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
@ -13,7 +13,7 @@ func UnionOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, p
}
for el := rhs.Front(); el != nil; el = el.Next() {
node := el.Value.(*CandidateNode)
lhs.Set(node.GetKey(), node)
lhs.PushBack(node)
}
return lhs, nil
}

View File

@ -1,8 +1,8 @@
package treeops
import "github.com/elliotchance/orderedmap"
import "container/list"
func ValueOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func ValueOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debug("value = %v", pathNode.Operation.CandidateNode.Node.Value)
return nodeToMap(pathNode.Operation.CandidateNode), nil
}

View File

@ -1,15 +1,15 @@
package treeops
import (
"container/list"
"fmt"
"github.com/elliotchance/orderedmap"
"gopkg.in/yaml.v3"
)
type OperatorHandler func(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error)
type OperatorHandler func(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error)
func PipeOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func PipeOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
@ -26,34 +26,15 @@ func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode {
return &CandidateNode{Node: node, Document: owner.Document, Path: owner.Path}
}
func nodeToMap(candidate *CandidateNode) *orderedmap.OrderedMap {
elMap := orderedmap.NewOrderedMap()
elMap.Set(candidate.GetKey(), candidate)
func nodeToMap(candidate *CandidateNode) *list.List {
elMap := list.New()
elMap.PushBack(candidate)
return elMap
}
func IntersectionOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
rhs, err := d.getMatchingNodes(matchingNodes, pathNode.Rhs)
if err != nil {
return nil, err
}
var matchingNodeMap = orderedmap.NewOrderedMap()
for el := lhs.Front(); el != nil; el = el.Next() {
_, exists := rhs.Get(el.Key)
if exists {
matchingNodeMap.Set(el.Key, el.Value)
}
}
return matchingNodeMap, nil
}
func LengthOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
func LengthOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("-- lengthOperation")
var results = orderedmap.NewOrderedMap()
var results = list.New()
for el := matchMap.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
@ -71,7 +52,7 @@ func LengthOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathN
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", length), Tag: "!!int"}
lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path}
results.Set(candidate.GetKey(), lengthCand)
results.PushBack(lengthCand)
}
return results, nil

View File

@ -59,6 +59,16 @@ var pathTests = []struct {
append(make([]interface{}, 0), "[", "true (bool)", "]"),
append(make([]interface{}, 0), "true (bool)", "COLLECT", "PIPE"),
},
{
`"mike": .a`,
append(make([]interface{}, 0), "mike (string)", "CREATE_MAP", "a"),
append(make([]interface{}, 0), "mike (string)", "a", "CREATE_MAP"),
},
{
`.a: "mike"`,
append(make([]interface{}, 0), "a", "CREATE_MAP", "mike (string)"),
append(make([]interface{}, 0), "a", "mike (string)", "CREATE_MAP"),
},
// {".animals | .==cat", append(make([]interface{}, 0), "animals", "TRAVERSE", "SELF", "EQUALS", "cat")},
// {".animals | (. == cat)", append(make([]interface{}, 0), "animals", "TRAVERSE", "(", "SELF", "EQUALS", "cat", ")")},

View File

@ -20,6 +20,8 @@ const (
CloseBracket
OpenCollect
CloseCollect
OpenCollectObject
CloseCollectObject
)
type Token struct {
@ -40,6 +42,10 @@ func (t *Token) toString() string {
return "["
} else if t.TokenType == CloseCollect {
return "]"
} else if t.TokenType == OpenCollectObject {
return "{"
} else if t.TokenType == CloseCollectObject {
return "}"
} else {
return fmt.Sprintf("NFI")
}
@ -174,6 +180,7 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`\.\.`), opToken(RecursiveDescent))
lexer.Add([]byte(`,`), opToken(Union))
lexer.Add([]byte(`:\s*`), opToken(CreateMap))
lexer.Add([]byte(`length`), opToken(Length))
lexer.Add([]byte(`select`), opToken(Select))
lexer.Add([]byte(`or`), opToken(Or))
@ -195,7 +202,7 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`d[0-9]+`), documentToken()) // $0
lexer.Add([]byte(`\."[^ "]+"`), pathToken(true))
lexer.Add([]byte(`\.[^ \[\],\|\.\[\(\)=]+`), pathToken(false))
lexer.Add([]byte(`\.[^ \}\{\:\[\],\|\.\[\(\)=]+`), pathToken(false))
lexer.Add([]byte(`\.`), selfToken())
lexer.Add([]byte(`\|`), opToken(Pipe))
@ -214,6 +221,8 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`\[`), literalToken(OpenCollect, false))
lexer.Add([]byte(`\]`), literalToken(CloseCollect, true))
lexer.Add([]byte(`\{`), literalToken(OpenCollectObject, false))
lexer.Add([]byte(`\}`), literalToken(CloseCollectObject, true))
lexer.Add([]byte(`\*`), opToken(Multiply))
// lexer.Add([]byte(`[^ \,\|\.\[\(\)=]+`), stringValue(false))

View File

@ -1 +1 @@
{a: {also: [1]}, b: {also: me}}
{name: Mike, pets: [cat, dog]}

View File

@ -1,10 +1,7 @@
{
"a": {
"also": [
1
]
},
"b": {
"also": "me"
}
"name": "Mike",
"pets": [
"cat",
"dog"
]
}