mirror of
				https://github.com/taigrr/yq
				synced 2025-01-18 04:53:17 -08:00 
			
		
		
		
	refactoring, fixing
This commit is contained in:
		
							parent
							
								
									59296b7d12
								
							
						
					
					
						commit
						60511f5f92
					
				| @ -15,7 +15,7 @@ type CandidateNode struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (n *CandidateNode) GetKey() string { | func (n *CandidateNode) GetKey() string { | ||||||
| 	return fmt.Sprintf("%v - %v", n.Document, n.Path) | 	return fmt.Sprintf("%v - %v - %v", n.Document, n.Path, n.Node.Value) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // updates this candidate from the given candidate node | // updates this candidate from the given candidate node | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 
 | 
 | ||||||
| 	"github.com/elliotchance/orderedmap" | 	"github.com/elliotchance/orderedmap" | ||||||
|  | 	"gopkg.in/op/go-logging.v1" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type dataTreeNavigator struct { | type dataTreeNavigator struct { | ||||||
| @ -68,6 +69,13 @@ func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *orderedmap.OrderedMa | |||||||
| 		return matchingNodes, nil | 		return matchingNodes, nil | ||||||
| 	} | 	} | ||||||
| 	log.Debugf("Processing Path: %v", pathNode.PathElement.toString()) | 	log.Debugf("Processing Path: %v", pathNode.PathElement.toString()) | ||||||
|  | 	if log.IsEnabledFor(logging.DEBUG) { | ||||||
|  | 		for el := matchingNodes.Front(); el != nil; el = el.Next() { | ||||||
|  | 			log.Debug(NodeToString(el.Value.(*CandidateNode))) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	log.Debug(">>") | ||||||
|  | 
 | ||||||
| 	if pathNode.PathElement.PathElementType == SelfReference { | 	if pathNode.PathElement.PathElementType == SelfReference { | ||||||
| 		return matchingNodes, nil | 		return matchingNodes, nil | ||||||
| 	} else if pathNode.PathElement.PathElementType == PathKey { | 	} else if pathNode.PathElement.PathElementType == PathKey { | ||||||
|  | |||||||
| @ -12,6 +12,9 @@ var treeNavigator = NewDataTreeNavigator(NavigationPrefs{}) | |||||||
| var treeCreator = NewPathTreeCreator() | var treeCreator = NewPathTreeCreator() | ||||||
| 
 | 
 | ||||||
| func readDoc(t *testing.T, content string) []*CandidateNode { | func readDoc(t *testing.T, content string) []*CandidateNode { | ||||||
|  | 	if content == "" { | ||||||
|  | 		return []*CandidateNode{} | ||||||
|  | 	} | ||||||
| 	decoder := yaml.NewDecoder(strings.NewReader(content)) | 	decoder := yaml.NewDecoder(strings.NewReader(content)) | ||||||
| 	var dataBucket yaml.Node | 	var dataBucket yaml.Node | ||||||
| 	err := decoder.Decode(&dataBucket) | 	err := decoder.Decode(&dataBucket) | ||||||
| @ -21,10 +24,10 @@ func readDoc(t *testing.T, content string) []*CandidateNode { | |||||||
| 	return []*CandidateNode{&CandidateNode{Node: dataBucket.Content[0], Document: 0}} | 	return []*CandidateNode{&CandidateNode{Node: dataBucket.Content[0], Document: 0}} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func resultsToString(results []*CandidateNode) string { | func resultsToString(results []*CandidateNode) []string { | ||||||
| 	var pretty string = "" | 	var pretty []string = make([]string, 0) | ||||||
| 	for _, n := range results { | 	for _, n := range results { | ||||||
| 		pretty = pretty + "\n" + NodeToString(n) | 		pretty = append(pretty, NodeToString(n)) | ||||||
| 	} | 	} | ||||||
| 	return pretty | 	return pretty | ||||||
| } | } | ||||||
| @ -1052,242 +1055,3 @@ func TestDataTreeNavigatorAnd(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	test.AssertResult(t, expected, resultsToString(results)) | 	test.AssertResult(t, expected, resultsToString(results)) | ||||||
| } | } | ||||||
| 
 |  | ||||||
| func TestDataTreeNavigatorEqualsSimple(t *testing.T) { |  | ||||||
| 
 |  | ||||||
| 	nodes := readDoc(t, `a:  |  | ||||||
|   cat: {b: apple, c: yes} |  | ||||||
|   pat: {b: banana} |  | ||||||
| `) |  | ||||||
| 
 |  | ||||||
| 	path, errPath := treeCreator.ParsePath(".a | (.[].b == \"apple\")") |  | ||||||
| 	if errPath != nil { |  | ||||||
| 		t.Error(errPath) |  | ||||||
| 	} |  | ||||||
| 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) |  | ||||||
| 
 |  | ||||||
| 	if errNav != nil { |  | ||||||
| 		t.Error(errNav) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	expected := ` |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [a cat] |  | ||||||
|   Tag: !!map, Kind: MappingNode, Anchor:  |  | ||||||
|   {b: apple, c: yes} |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	test.AssertResult(t, expected, resultsToString(results)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestDataTreeNavigatorEqualsSelf(t *testing.T) { |  | ||||||
| 
 |  | ||||||
| 	nodes := readDoc(t, `a: frog |  | ||||||
| b: cat |  | ||||||
| c: frog`) |  | ||||||
| 
 |  | ||||||
| 	path, errPath := treeCreator.ParsePath("(a or b).(. == frog)") |  | ||||||
| 	if errPath != nil { |  | ||||||
| 		t.Error(errPath) |  | ||||||
| 	} |  | ||||||
| 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) |  | ||||||
| 
 |  | ||||||
| 	if errNav != nil { |  | ||||||
| 		t.Error(errNav) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	expected := ` |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [a] |  | ||||||
|   Tag: !!str, Kind: ScalarNode, Anchor:  |  | ||||||
|   frog |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	test.AssertResult(t, expected, resultsToString(results)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestDataTreeNavigatorEqualsNested(t *testing.T) { |  | ||||||
| 
 |  | ||||||
| 	nodes := readDoc(t, `a: {t: frog} |  | ||||||
| b: {t: cat} |  | ||||||
| c: {t: frog}`) |  | ||||||
| 
 |  | ||||||
| 	path, errPath := treeCreator.ParsePath("(t == frog)") |  | ||||||
| 	if errPath != nil { |  | ||||||
| 		t.Error(errPath) |  | ||||||
| 	} |  | ||||||
| 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) |  | ||||||
| 
 |  | ||||||
| 	if errNav != nil { |  | ||||||
| 		t.Error(errNav) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	expected := ` |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [a] |  | ||||||
|   Tag: !!map, Kind: MappingNode, Anchor:  |  | ||||||
|   {t: frog} |  | ||||||
| 
 |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [c] |  | ||||||
|   Tag: !!map, Kind: MappingNode, Anchor:  |  | ||||||
|   {t: frog} |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	test.AssertResult(t, expected, resultsToString(results)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestDataTreeNavigatorArrayEqualsSelf(t *testing.T) { |  | ||||||
| 
 |  | ||||||
| 	nodes := readDoc(t, `- cat |  | ||||||
| - dog |  | ||||||
| - frog`) |  | ||||||
| 
 |  | ||||||
| 	path, errPath := treeCreator.ParsePath("(. == *og)") |  | ||||||
| 	if errPath != nil { |  | ||||||
| 		t.Error(errPath) |  | ||||||
| 	} |  | ||||||
| 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) |  | ||||||
| 
 |  | ||||||
| 	if errNav != nil { |  | ||||||
| 		t.Error(errNav) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	expected := ` |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [1] |  | ||||||
|   Tag: !!str, Kind: ScalarNode, Anchor:  |  | ||||||
|   dog |  | ||||||
| 
 |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [2] |  | ||||||
|   Tag: !!str, Kind: ScalarNode, Anchor:  |  | ||||||
|   frog |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	test.AssertResult(t, expected, resultsToString(results)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestDataTreeNavigatorArrayEqualsSelfSplatFirst(t *testing.T) { |  | ||||||
| 
 |  | ||||||
| 	nodes := readDoc(t, `- cat |  | ||||||
| - dog |  | ||||||
| - frog`) |  | ||||||
| 
 |  | ||||||
| 	path, errPath := treeCreator.ParsePath("*(. == *og)") |  | ||||||
| 	if errPath != nil { |  | ||||||
| 		t.Error(errPath) |  | ||||||
| 	} |  | ||||||
| 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) |  | ||||||
| 
 |  | ||||||
| 	if errNav != nil { |  | ||||||
| 		t.Error(errNav) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	expected := ` |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [1] |  | ||||||
|   Tag: !!str, Kind: ScalarNode, Anchor:  |  | ||||||
|   dog |  | ||||||
| 
 |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [2] |  | ||||||
|   Tag: !!str, Kind: ScalarNode, Anchor:  |  | ||||||
|   frog |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	test.AssertResult(t, expected, resultsToString(results)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestDataTreeNavigatorArrayEquals(t *testing.T) { |  | ||||||
| 
 |  | ||||||
| 	nodes := readDoc(t, `- { b: apple, animal: rabbit } |  | ||||||
| - { b: banana, animal: cat } |  | ||||||
| - { b: corn, animal: dog }`) |  | ||||||
| 
 |  | ||||||
| 	path, errPath := treeCreator.ParsePath("(b == apple or animal == dog)") |  | ||||||
| 	if errPath != nil { |  | ||||||
| 		t.Error(errPath) |  | ||||||
| 	} |  | ||||||
| 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) |  | ||||||
| 
 |  | ||||||
| 	if errNav != nil { |  | ||||||
| 		t.Error(errNav) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	expected := ` |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [0] |  | ||||||
|   Tag: !!map, Kind: MappingNode, Anchor:  |  | ||||||
|   {b: apple, animal: rabbit} |  | ||||||
| 
 |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [2] |  | ||||||
|   Tag: !!map, Kind: MappingNode, Anchor:  |  | ||||||
|   {b: corn, animal: dog} |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	test.AssertResult(t, expected, resultsToString(results)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestDataTreeNavigatorArrayEqualsDeep(t *testing.T) { |  | ||||||
| 
 |  | ||||||
| 	nodes := readDoc(t, `apples: |  | ||||||
|   - { b: apple, animal: {legs: 2} } |  | ||||||
|   - { b: banana, animal: {legs: 4} } |  | ||||||
|   - { b: corn, animal: {legs: 6} } |  | ||||||
| `) |  | ||||||
| 
 |  | ||||||
| 	path, errPath := treeCreator.ParsePath("apples(animal.legs == 4)") |  | ||||||
| 	if errPath != nil { |  | ||||||
| 		t.Error(errPath) |  | ||||||
| 	} |  | ||||||
| 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) |  | ||||||
| 
 |  | ||||||
| 	if errNav != nil { |  | ||||||
| 		t.Error(errNav) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	expected := ` |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [apples 1] |  | ||||||
|   Tag: !!map, Kind: MappingNode, Anchor:  |  | ||||||
|   {b: banana, animal: {legs: 4}} |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	test.AssertResult(t, expected, resultsToString(results)) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func xTestDataTreeNavigatorEqualsTrickey(t *testing.T) { |  | ||||||
| 
 |  | ||||||
| 	nodes := readDoc(t, `a:  |  | ||||||
|   cat: {b: apso, c: {d : yes}} |  | ||||||
|   pat: {b: apple, c: {d : no}} |  | ||||||
|   sat: {b: apsy, c: {d : yes}} |  | ||||||
|   fat: {b: apple} |  | ||||||
| `) |  | ||||||
| 
 |  | ||||||
| 	path, errPath := treeCreator.ParsePath(".a(.b == ap* and .c.d == yes)") |  | ||||||
| 	if errPath != nil { |  | ||||||
| 		t.Error(errPath) |  | ||||||
| 	} |  | ||||||
| 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) |  | ||||||
| 
 |  | ||||||
| 	if errNav != nil { |  | ||||||
| 		t.Error(errNav) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	expected := ` |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [a cat] |  | ||||||
|   Tag: !!map, Kind: MappingNode, Anchor:  |  | ||||||
|   {b: apso, c: {d: yes}} |  | ||||||
| 
 |  | ||||||
| -- Node -- |  | ||||||
|   Document 0, path: [a sat] |  | ||||||
|   Tag: !!map, Kind: MappingNode, Anchor:  |  | ||||||
|   {b: apsy, c: {d: yes}} |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	test.AssertResult(t, expected, resultsToString(results)) |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ func NewLeafTraverser(navigationPrefs NavigationPrefs) LeafTraverser { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *traverser) keyMatches(key *yaml.Node, pathNode *PathElement) bool { | func (t *traverser) keyMatches(key *yaml.Node, pathNode *PathElement) bool { | ||||||
| 	return Match(key.Value, pathNode.StringValue) | 	return pathNode.Value == "[]" || Match(key.Value, pathNode.StringValue) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *traverser) traverseMap(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) { | func (t *traverser) traverseMap(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) { | ||||||
|  | |||||||
| @ -33,19 +33,24 @@ type OperationType struct { | |||||||
| 
 | 
 | ||||||
| var None = &OperationType{Type: "NONE", NumArgs: 0, Precedence: 0} | var None = &OperationType{Type: "NONE", NumArgs: 0, Precedence: 0} | ||||||
| 
 | 
 | ||||||
| var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 10, Handler: UnionOperator} | var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: OrOperator} | ||||||
| var And = &OperationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: IntersectionOperator} | 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 Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignOperator} | ||||||
| var Equals = &OperationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: EqualsOperator} | var Equals = &OperationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: EqualsOperator} | ||||||
| var Pipe = &OperationType{Type: "PIPE", NumArgs: 2, Precedence: 45, Handler: PipeOperator} | var Pipe = &OperationType{Type: "PIPE", NumArgs: 2, Precedence: 45, Handler: PipeOperator} | ||||||
| 
 | 
 | ||||||
| var Length = &OperationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: LengthOperator} | var Length = &OperationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: LengthOperator} | ||||||
|  | var Collect = &OperationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: CollectOperator} | ||||||
| 
 | 
 | ||||||
| // not sure yet | // not sure yet | ||||||
| 
 | 
 | ||||||
|  | var Select = &OperationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: SelectOperator} | ||||||
|  | 
 | ||||||
| var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 2, Precedence: 40, Handler: DeleteChildOperator} | var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 2, Precedence: 40, Handler: DeleteChildOperator} | ||||||
| var Collect = &OperationType{Type: "COLLECT", NumArgs: 1, Precedence: 40, Handler: CollectOperator} |  | ||||||
| 
 | 
 | ||||||
| // var Splat = &OperationType{Type: "SPLAT", NumArgs: 0, Precedence: 40, Handler: SplatOperator} | // var Splat = &OperationType{Type: "SPLAT", NumArgs: 0, Precedence: 40, Handler: SplatOperator} | ||||||
| 
 | 
 | ||||||
| @ -64,13 +69,13 @@ func (p *PathElement) toString() string { | |||||||
| 	var result string = `` | 	var result string = `` | ||||||
| 	switch p.PathElementType { | 	switch p.PathElementType { | ||||||
| 	case PathKey: | 	case PathKey: | ||||||
| 		result = result + fmt.Sprintf("PathKey - %v", p.Value) | 		result = result + fmt.Sprintf("%v", p.Value) | ||||||
| 	case SelfReference: | 	case SelfReference: | ||||||
| 		result = result + fmt.Sprintf("SELF") | 		result = result + fmt.Sprintf("SELF") | ||||||
| 	case Operation: | 	case Operation: | ||||||
| 		result = result + fmt.Sprintf("Operation - %v", p.OperationType.Type) | 		result = result + fmt.Sprintf("%v", p.OperationType.Type) | ||||||
| 	case Value: | 	case Value: | ||||||
| 		result = result + fmt.Sprintf("Value - %v (%T)", p.Value, p.Value) | 		result = result + fmt.Sprintf("%v (%T)", p.Value, p.Value) | ||||||
| 	default: | 	default: | ||||||
| 		result = result + "I HAVENT GOT A STRATEGY" | 		result = result + "I HAVENT GOT A STRATEGY" | ||||||
| 	} | 	} | ||||||
| @ -124,7 +129,7 @@ func NodeToString(node *CandidateNode) string { | |||||||
| 	} | 	} | ||||||
| 	value := node.Node | 	value := node.Node | ||||||
| 	if value == nil { | 	if value == nil { | ||||||
| 		return "-- node is nil --" | 		return "-- nil --" | ||||||
| 	} | 	} | ||||||
| 	buf := new(bytes.Buffer) | 	buf := new(bytes.Buffer) | ||||||
| 	encoder := yaml.NewEncoder(buf) | 	encoder := yaml.NewEncoder(buf) | ||||||
| @ -133,10 +138,7 @@ func NodeToString(node *CandidateNode) string { | |||||||
| 		log.Error("Error debugging node, %v", errorEncoding.Error()) | 		log.Error("Error debugging node, %v", errorEncoding.Error()) | ||||||
| 	} | 	} | ||||||
| 	encoder.Close() | 	encoder.Close() | ||||||
| 	return fmt.Sprintf(`-- Node -- | 	return fmt.Sprintf(`D%v, P%v, (%v)::%v`, node.Document, node.Path, value.Tag, buf.String()) | ||||||
|   Document %v, path: %v |  | ||||||
|   Tag: %v, Kind: %v, Anchor: %v |  | ||||||
|   %v`, node.Document, node.Path, value.Tag, KindString(value.Kind), value.Anchor, buf.String()) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func KindString(kind yaml.Kind) string { | func KindString(kind yaml.Kind) string { | ||||||
|  | |||||||
							
								
								
									
										72
									
								
								pkg/yqlib/treeops/operator_booleans.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								pkg/yqlib/treeops/operator_booleans.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/elliotchance/orderedmap" | ||||||
|  | 	"gopkg.in/yaml.v3" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func isTruthy(c *CandidateNode) (bool, error) { | ||||||
|  | 	node := c.Node | ||||||
|  | 	value := true | ||||||
|  | 	if node.Kind == yaml.ScalarNode && node.Tag == "!!bool" { | ||||||
|  | 		errDecoding := node.Decode(&value) | ||||||
|  | 		if errDecoding != nil { | ||||||
|  | 			return false, errDecoding | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 	return value, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type boolOp func(bool, bool) bool | ||||||
|  | 
 | ||||||
|  | func booleanOp(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode, op boolOp) (*orderedmap.OrderedMap, error) { | ||||||
|  | 	var results = orderedmap.NewOrderedMap() | ||||||
|  | 
 | ||||||
|  | 	for el := matchingNodes.Front(); el != nil; el = el.Next() { | ||||||
|  | 		candidate := el.Value.(*CandidateNode) | ||||||
|  | 		lhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Lhs) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for lhsChild := lhs.Front(); lhsChild != nil; lhsChild = lhsChild.Next() { | ||||||
|  | 			lhsCandidate := lhsChild.Value.(*CandidateNode) | ||||||
|  | 			lhsTrue, errDecoding := isTruthy(lhsCandidate) | ||||||
|  | 			if errDecoding != nil { | ||||||
|  | 				return nil, errDecoding | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			for rhsChild := rhs.Front(); rhsChild != nil; rhsChild = rhsChild.Next() { | ||||||
|  | 				rhsCandidate := rhsChild.Value.(*CandidateNode) | ||||||
|  | 				rhsTrue, errDecoding := isTruthy(rhsCandidate) | ||||||
|  | 				if errDecoding != nil { | ||||||
|  | 					return nil, errDecoding | ||||||
|  | 				} | ||||||
|  | 				boolResult := createBooleanCandidate(lhsCandidate, op(lhsTrue, rhsTrue)) | ||||||
|  | 
 | ||||||
|  | 				results.Set(boolResult.GetKey(), boolResult) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 	return results, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func OrOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, 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) { | ||||||
|  | 	log.Debugf("-- AndOp") | ||||||
|  | 	return booleanOp(d, matchingNodes, pathNode, func(b1 bool, b2 bool) bool { | ||||||
|  | 		return b1 && b2 | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								pkg/yqlib/treeops/operator_booleans_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								pkg/yqlib/treeops/operator_booleans_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var booleanOperatorScenarios = []expressionScenario{ | ||||||
|  | 	{ | ||||||
|  | 		document:   `{}`, | ||||||
|  | 		expression: `true or false`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[], (!!bool)::true\n", | ||||||
|  | 		}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `{}`, | ||||||
|  | 		expression: `false or false`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[], (!!bool)::false\n", | ||||||
|  | 		}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `{a: true, b: false}`, | ||||||
|  | 		expression: `.[] or (false, true)`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[a], (!!bool)::true\n", | ||||||
|  | 			"D0, P[b], (!!bool)::false\n", | ||||||
|  | 			"D0, P[b], (!!bool)::true\n", | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestBooleanOperatorScenarios(t *testing.T) { | ||||||
|  | 	for _, tt := range booleanOperatorScenarios { | ||||||
|  | 		testScenario(t, &tt) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								pkg/yqlib/treeops/operator_collect.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								pkg/yqlib/treeops/operator_collect.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/elliotchance/orderedmap" | ||||||
|  | 	"gopkg.in/yaml.v3" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func CollectOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | ||||||
|  | 	log.Debugf("-- collectOperation") | ||||||
|  | 
 | ||||||
|  | 	var results = orderedmap.NewOrderedMap() | ||||||
|  | 
 | ||||||
|  | 	node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} | ||||||
|  | 
 | ||||||
|  | 	var document uint = 0 | ||||||
|  | 	var path []interface{} | ||||||
|  | 
 | ||||||
|  | 	for el := matchMap.Front(); el != nil; el = el.Next() { | ||||||
|  | 		candidate := el.Value.(*CandidateNode) | ||||||
|  | 		log.Debugf("Collecting %v", NodeToString(candidate)) | ||||||
|  | 		if path == nil && candidate.Path != nil && len(candidate.Path) > 1 { | ||||||
|  | 			path = candidate.Path[:len(candidate.Path)-1] | ||||||
|  | 			document = candidate.Document | ||||||
|  | 		} | ||||||
|  | 		node.Content = append(node.Content, candidate.Node) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	collectC := &CandidateNode{Node: node, Document: document, Path: path} | ||||||
|  | 	results.Set(collectC.GetKey(), collectC) | ||||||
|  | 
 | ||||||
|  | 	return results, nil | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								pkg/yqlib/treeops/operator_collect_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								pkg/yqlib/treeops/operator_collect_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var collectOperatorScenarios = []expressionScenario{ | ||||||
|  | 	{ | ||||||
|  | 		document:   `{}`, | ||||||
|  | 		expression: `["cat"]`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[], (!!seq)::- cat\n", | ||||||
|  | 		}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `{}`, | ||||||
|  | 		expression: `["cat", "dog"]`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[], (!!seq)::- cat\n- dog\n", | ||||||
|  | 		}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `{}`, | ||||||
|  | 		expression: `1 | collect`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[], (!!seq)::- 1\n", | ||||||
|  | 		}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `[1,2,3]`, | ||||||
|  | 		expression: `[.[]]`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[], (!!seq)::- 1\n- 2\n- 3\n", | ||||||
|  | 		}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `a: {b: [1,2,3]}`, | ||||||
|  | 		expression: `[.a.b[]]`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[a b], (!!seq)::- 1\n- 2\n- 3\n", | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCollectOperatorScenarios(t *testing.T) { | ||||||
|  | 	for _, tt := range collectOperatorScenarios { | ||||||
|  | 		testScenario(t, &tt) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -2,7 +2,6 @@ package treeops | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/elliotchance/orderedmap" | 	"github.com/elliotchance/orderedmap" | ||||||
| 	"gopkg.in/yaml.v3" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func EqualsOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | func EqualsOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | ||||||
| @ -18,15 +17,8 @@ func EqualsOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathN | |||||||
| 			return nil, errInChild | 			return nil, errInChild | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		matchString := "true" | 		equalsCandidate := createBooleanCandidate(candidate, matches) | ||||||
| 		if !matches { | 		results.Set(equalsCandidate.GetKey(), equalsCandidate) | ||||||
| 			matchString = "false" |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		node := &yaml.Node{Kind: yaml.ScalarNode, Value: matchString, Tag: "!!bool"} |  | ||||||
| 		lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} |  | ||||||
| 		results.Set(candidate.GetKey(), lengthCand) |  | ||||||
| 
 |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return results, nil | 	return results, nil | ||||||
							
								
								
									
										43
									
								
								pkg/yqlib/treeops/operator_equals_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								pkg/yqlib/treeops/operator_equals_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 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", | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestEqualOperatorScenarios(t *testing.T) { | ||||||
|  | 	for _, tt := range equalsOperatorScenarios { | ||||||
|  | 		testScenario(t, &tt) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								pkg/yqlib/treeops/operator_select.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								pkg/yqlib/treeops/operator_select.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/elliotchance/orderedmap" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func SelectOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | ||||||
|  | 
 | ||||||
|  | 	log.Debugf("-- selectOperation") | ||||||
|  | 	var results = orderedmap.NewOrderedMap() | ||||||
|  | 
 | ||||||
|  | 	for el := matchingNodes.Front(); el != nil; el = el.Next() { | ||||||
|  | 		candidate := el.Value.(*CandidateNode) | ||||||
|  | 
 | ||||||
|  | 		rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs) | ||||||
|  | 
 | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// grab the first value | ||||||
|  | 		first := rhs.Front() | ||||||
|  | 
 | ||||||
|  | 		if first != nil { | ||||||
|  | 			result := first.Value.(*CandidateNode) | ||||||
|  | 			includeResult, errDecoding := isTruthy(result) | ||||||
|  | 			if errDecoding != nil { | ||||||
|  | 				return nil, errDecoding | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if includeResult { | ||||||
|  | 				results.Set(candidate.GetKey(), candidate) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return results, nil | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								pkg/yqlib/treeops/operator_select_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								pkg/yqlib/treeops/operator_select_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var selectOperatorScenarios = []expressionScenario{ | ||||||
|  | 	{ | ||||||
|  | 		document:   `[cat,goat,dog]`, | ||||||
|  | 		expression: `.[] | select(. == "*at")`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[0], (!!str)::cat\n", | ||||||
|  | 			"D0, P[1], (!!str)::goat\n", | ||||||
|  | 		}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `[hot, fot, dog]`, | ||||||
|  | 		expression: `.[] | select(. == "*at")`, | ||||||
|  | 		expected:   []string{}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `a: [cat,goat,dog]`, | ||||||
|  | 		expression: `.a[] | select(. == "*at")`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[a 0], (!!str)::cat\n", | ||||||
|  | 			"D0, P[a 1], (!!str)::goat\n"}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `a: { things: cat, bob: goat, horse: dog }`, | ||||||
|  | 		expression: `.a[] | select(. == "*at")`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[a things], (!!str)::cat\n", | ||||||
|  | 			"D0, P[a bob], (!!str)::goat\n"}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `a: { things: {include: true}, notMe: {include: false}, andMe: {include: fold} }`, | ||||||
|  | 		expression: `.a[] | select(.include)`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[a things], (!!map)::{include: true}\n", | ||||||
|  | 			"D0, P[a andMe], (!!map)::{include: fold}\n", | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestSelectOperatorScenarios(t *testing.T) { | ||||||
|  | 	for _, tt := range selectOperatorScenarios { | ||||||
|  | 		testScenario(t, &tt) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								pkg/yqlib/treeops/operator_union.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								pkg/yqlib/treeops/operator_union.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import "github.com/elliotchance/orderedmap" | ||||||
|  | 
 | ||||||
|  | func UnionOperator(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 | ||||||
|  | 	} | ||||||
|  | 	for el := rhs.Front(); el != nil; el = el.Next() { | ||||||
|  | 		node := el.Value.(*CandidateNode) | ||||||
|  | 		lhs.Set(node.GetKey(), node) | ||||||
|  | 	} | ||||||
|  | 	return lhs, nil | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								pkg/yqlib/treeops/operator_union_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								pkg/yqlib/treeops/operator_union_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,31 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var unionOperatorScenarios = []expressionScenario{ | ||||||
|  | 	{ | ||||||
|  | 		document:   `{}`, | ||||||
|  | 		expression: `"cat", "dog"`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[], (!!str)::cat\n", | ||||||
|  | 			"D0, P[], (!!str)::dog\n", | ||||||
|  | 		}, | ||||||
|  | 	}, { | ||||||
|  | 		document:   `{a: frog}`, | ||||||
|  | 		expression: `1, true, "cat", .a`, | ||||||
|  | 		expected: []string{ | ||||||
|  | 			"D0, P[], (!!int)::1\n", | ||||||
|  | 			"D0, P[], (!!bool)::true\n", | ||||||
|  | 			"D0, P[], (!!str)::cat\n", | ||||||
|  | 			"D0, P[a], (!!str)::frog\n", | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestUnionOperatorScenarios(t *testing.T) { | ||||||
|  | 	for _, tt := range unionOperatorScenarios { | ||||||
|  | 		testScenario(t, &tt) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -17,6 +17,15 @@ func PipeOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pa | |||||||
| 	return d.getMatchingNodes(lhs, pathNode.Rhs) | 	return d.getMatchingNodes(lhs, pathNode.Rhs) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func createBooleanCandidate(owner *CandidateNode, value bool) *CandidateNode { | ||||||
|  | 	valString := "true" | ||||||
|  | 	if !value { | ||||||
|  | 		valString = "false" | ||||||
|  | 	} | ||||||
|  | 	node := &yaml.Node{Kind: yaml.ScalarNode, Value: valString, Tag: "!!bool"} | ||||||
|  | 	return &CandidateNode{Node: node, Document: owner.Document, Path: owner.Path} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func nodeToMap(candidate *CandidateNode) *orderedmap.OrderedMap { | func nodeToMap(candidate *CandidateNode) *orderedmap.OrderedMap { | ||||||
| 	elMap := orderedmap.NewOrderedMap() | 	elMap := orderedmap.NewOrderedMap() | ||||||
| 	elMap.Set(candidate.GetKey(), candidate) | 	elMap.Set(candidate.GetKey(), candidate) | ||||||
| @ -47,22 +56,6 @@ func AssignOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, | |||||||
| 	return lhs, nil | 	return lhs, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func UnionOperator(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 |  | ||||||
| 	} |  | ||||||
| 	for el := rhs.Front(); el != nil; el = el.Next() { |  | ||||||
| 		node := el.Value.(*CandidateNode) |  | ||||||
| 		lhs.Set(node.GetKey(), node) |  | ||||||
| 	} |  | ||||||
| 	return lhs, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func IntersectionOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | func IntersectionOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | ||||||
| 	lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) | 	lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -82,16 +75,6 @@ func IntersectionOperator(d *dataTreeNavigator, matchingNodes *orderedmap.Ordere | |||||||
| 	return matchingNodeMap, nil | 	return matchingNodeMap, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func splatNode(d *dataTreeNavigator, candidate *CandidateNode) (*orderedmap.OrderedMap, error) { |  | ||||||
| 	//need to splat matching nodes, then search through them |  | ||||||
| 	splatter := &PathTreeNode{PathElement: &PathElement{ |  | ||||||
| 		PathElementType: PathKey, |  | ||||||
| 		Value:           "*", |  | ||||||
| 		StringValue:     "*", |  | ||||||
| 	}} |  | ||||||
| 	return d.getMatchingNodes(nodeToMap(candidate), splatter) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func LengthOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | func LengthOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | ||||||
| 	log.Debugf("-- lengthOperation") | 	log.Debugf("-- lengthOperation") | ||||||
| 	var results = orderedmap.NewOrderedMap() | 	var results = orderedmap.NewOrderedMap() | ||||||
| @ -117,29 +100,3 @@ func LengthOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathN | |||||||
| 
 | 
 | ||||||
| 	return results, nil | 	return results, nil | ||||||
| } | } | ||||||
| 
 |  | ||||||
| func CollectOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { |  | ||||||
| 	log.Debugf("-- collectOperation") |  | ||||||
| 
 |  | ||||||
| 	var results = orderedmap.NewOrderedMap() |  | ||||||
| 
 |  | ||||||
| 	node := &yaml.Node{Kind: yaml.SequenceNode} |  | ||||||
| 
 |  | ||||||
| 	var document uint = 0 |  | ||||||
| 	var path []interface{} |  | ||||||
| 
 |  | ||||||
| 	for el := matchMap.Front(); el != nil; el = el.Next() { |  | ||||||
| 		candidate := el.Value.(*CandidateNode) |  | ||||||
| 		if path == nil && candidate.Path != nil { |  | ||||||
| 			path = candidate.Path |  | ||||||
| 			document = candidate.Document |  | ||||||
| 		} |  | ||||||
| 		node.Content = append(node.Content, candidate.Node) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	collectC := &CandidateNode{Node: node, Document: document, Path: path} |  | ||||||
| 	results.Set(collectC.GetKey(), collectC) |  | ||||||
| 
 |  | ||||||
| 	return results, nil |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								pkg/yqlib/treeops/operators_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								pkg/yqlib/treeops/operators_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/mikefarah/yq/v3/test" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type expressionScenario struct { | ||||||
|  | 	document   string | ||||||
|  | 	expression string | ||||||
|  | 	expected   []string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func testScenario(t *testing.T, s *expressionScenario) { | ||||||
|  | 
 | ||||||
|  | 	nodes := readDoc(t, s.document) | ||||||
|  | 	path, errPath := treeCreator.ParsePath(s.expression) | ||||||
|  | 	if errPath != nil { | ||||||
|  | 		t.Error(errPath) | ||||||
|  | 	} | ||||||
|  | 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) | ||||||
|  | 
 | ||||||
|  | 	if errNav != nil { | ||||||
|  | 		t.Error(errNav) | ||||||
|  | 	} | ||||||
|  | 	test.AssertResultComplexWithContext(t, s.expected, resultsToString(results), s.expression) | ||||||
|  | } | ||||||
| @ -1,14 +1,16 @@ | |||||||
| package treeops | package treeops | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/mikefarah/yq/v3/test" | 	"github.com/mikefarah/yq/v3/test" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var tokeniserTests = []struct { | var pathTests = []struct { | ||||||
| 	path            string | 	path            string | ||||||
| 	expectedTokens  []interface{} | 	expectedTokens  []interface{} | ||||||
|  | 	expectedPostFix []interface{} | ||||||
| }{ // TODO: Ensure ALL documented examples have tests! sheesh | }{ // TODO: Ensure ALL documented examples have tests! sheesh | ||||||
| 	// {"len(.)", append(make([]interface{}, 0), "LENGTH", "(", "SELF", ")")}, | 	// {"len(.)", append(make([]interface{}, 0), "LENGTH", "(", "SELF", ")")}, | ||||||
| 	// {"\"len\"(.)", append(make([]interface{}, 0), "len", "TRAVERSE", "(", "SELF", ")")}, | 	// {"\"len\"(.)", append(make([]interface{}, 0), "len", "TRAVERSE", "(", "SELF", ")")}, | ||||||
| @ -37,7 +39,21 @@ var tokeniserTests = []struct { | |||||||
| 	// {`."[a", ."b]"`, append(make([]interface{}, 0), "[a", "OR", "b]")}, | 	// {`."[a", ."b]"`, append(make([]interface{}, 0), "[a", "OR", "b]")}, | ||||||
| 	// {`.a[]`, append(make([]interface{}, 0), "a", "PIPE", "[]")}, | 	// {`.a[]`, append(make([]interface{}, 0), "a", "PIPE", "[]")}, | ||||||
| 	// {`.[].a`, append(make([]interface{}, 0), "[]", "PIPE", "a")}, | 	// {`.[].a`, append(make([]interface{}, 0), "[]", "PIPE", "a")}, | ||||||
| 	{`.a | (.[].b == "apple")`, append(make([]interface{}, 0), "a", "PIPE", "(", "[]", "PIPE", "b", "EQUALS", "apple", ")")}, | 	{ | ||||||
|  | 		`.a | (.[].b == "apple")`, | ||||||
|  | 		append(make([]interface{}, 0), "a", "PIPE", "(", "[]", "PIPE", "b", "EQUALS", "apple", ")"), | ||||||
|  | 		append(make([]interface{}, 0), "a", "[]", "b", "PIPE", "apple (string)", "EQUALS", "PIPE"), | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		`.[] | select(. == "*at")`, | ||||||
|  | 		append(make([]interface{}, 0), "[]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at", ")"), | ||||||
|  | 		append(make([]interface{}, 0), "[]", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"), | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		`[true]`, | ||||||
|  | 		append(make([]interface{}, 0), "[", true, "]"), | ||||||
|  | 		append(make([]interface{}, 0), "true (bool)", "COLLECT"), | ||||||
|  | 	}, | ||||||
| 
 | 
 | ||||||
| 	// {".animals | .==cat", append(make([]interface{}, 0), "animals", "TRAVERSE", "SELF", "EQUALS", "cat")}, | 	// {".animals | .==cat", append(make([]interface{}, 0), "animals", "TRAVERSE", "SELF", "EQUALS", "cat")}, | ||||||
| 	// {".animals | (. == cat)", append(make([]interface{}, 0), "animals", "TRAVERSE", "(", "SELF", "EQUALS", "cat", ")")}, | 	// {".animals | (. == cat)", append(make([]interface{}, 0), "animals", "TRAVERSE", "(", "SELF", "EQUALS", "cat", ")")}, | ||||||
| @ -61,8 +77,8 @@ var tokeniserTests = []struct { | |||||||
| 
 | 
 | ||||||
| var tokeniser = NewPathTokeniser() | var tokeniser = NewPathTokeniser() | ||||||
| 
 | 
 | ||||||
| func TestTokeniser(t *testing.T) { | func TestPathParsing(t *testing.T) { | ||||||
| 	for _, tt := range tokeniserTests { | 	for _, tt := range pathTests { | ||||||
| 		tokens, err := tokeniser.Tokenise(tt.path) | 		tokens, err := tokeniser.Tokenise(tt.path) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Error(tt.path, err) | 			t.Error(tt.path, err) | ||||||
| @ -71,6 +87,20 @@ func TestTokeniser(t *testing.T) { | |||||||
| 		for _, token := range tokens { | 		for _, token := range tokens { | ||||||
| 			tokenValues = append(tokenValues, token.Value) | 			tokenValues = append(tokenValues, token.Value) | ||||||
| 		} | 		} | ||||||
| 		test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, tt.path) | 		test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path)) | ||||||
|  | 
 | ||||||
|  | 		results, errorP := postFixer.ConvertToPostfix(tokens) | ||||||
|  | 
 | ||||||
|  | 		var readableResults []interface{} | ||||||
|  | 		for _, token := range results { | ||||||
|  | 			readableResults = append(readableResults, token.toString()) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if errorP != nil { | ||||||
|  | 			t.Error(tt.path, err) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		test.AssertResultComplexWithContext(t, tt.expectedPostFix, readableResults, fmt.Sprintf("postfix: %v", tt.path)) | ||||||
|  | 
 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -2,6 +2,8 @@ package treeops | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 
 | ||||||
|  | 	"gopkg.in/op/go-logging.v1" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type PathPostFixer interface { | type PathPostFixer interface { | ||||||
| @ -43,9 +45,10 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement, | |||||||
| 			if len(opStack) == 0 { | 			if len(opStack) == 0 { | ||||||
| 				return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket") | 				return nil, errors.New("Bad path expression, got close collect brackets without matching opening bracket") | ||||||
| 			} | 			} | ||||||
| 			// now we should have ( as the last element on the opStack, get rid of it | 			// now we should have [] as the last element on the opStack, get rid of it | ||||||
| 			opStack = opStack[0 : len(opStack)-1] | 			opStack = opStack[0 : len(opStack)-1] | ||||||
| 			//and append a collect to the opStack | 			//and append a collect to the opStack | ||||||
|  | 			opStack = append(opStack, &Token{PathElementType: Operation, OperationType: Pipe}) | ||||||
| 			opStack = append(opStack, &Token{PathElementType: Operation, OperationType: Collect}) | 			opStack = append(opStack, &Token{PathElementType: Operation, OperationType: Collect}) | ||||||
| 		case CloseBracket: | 		case CloseBracket: | ||||||
| 			for len(opStack) > 0 && opStack[len(opStack)-1].PathElementType != OpenBracket { | 			for len(opStack) > 0 && opStack[len(opStack)-1].PathElementType != OpenBracket { | ||||||
| @ -67,5 +70,13 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement, | |||||||
| 			opStack = append(opStack, token) | 			opStack = append(opStack, token) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if log.IsEnabledFor(logging.DEBUG) { | ||||||
|  | 		log.Debugf("PostFix Result:") | ||||||
|  | 		for _, token := range result { | ||||||
|  | 			log.Debugf("> %v", token.toString()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return result, nil | 	return result, nil | ||||||
| } | } | ||||||
|  | |||||||
| @ -20,11 +20,12 @@ func testExpression(expression string) (string, error) { | |||||||
| 	} | 	} | ||||||
| 	formatted := "" | 	formatted := "" | ||||||
| 	for _, path := range results { | 	for _, path := range results { | ||||||
| 		formatted = formatted + path.toString() + "\n--------\n" | 		formatted = formatted + path.toString() + ", " | ||||||
| 	} | 	} | ||||||
| 	return formatted, nil | 	return formatted, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| func TestPostFixTraverseBar(t *testing.T) { | func TestPostFixTraverseBar(t *testing.T) { | ||||||
| 	var infix = ".animals | [.]" | 	var infix = ".animals | [.]" | ||||||
| 	var expectedOutput = `PathKey - animals | 	var expectedOutput = `PathKey - animals | ||||||
|  | |||||||
| @ -107,9 +107,12 @@ func initLexer() (*lex.Lexer, error) { | |||||||
| 	lexer.Add([]byte(`\.?\[\]`), literalToken(PathKey, "[]", true)) | 	lexer.Add([]byte(`\.?\[\]`), literalToken(PathKey, "[]", true)) | ||||||
| 	lexer.Add([]byte(`\.\.`), literalToken(PathKey, "..", true)) | 	lexer.Add([]byte(`\.\.`), literalToken(PathKey, "..", true)) | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`,`), opToken(Or)) | 	lexer.Add([]byte(`,`), opToken(Union)) | ||||||
| 	lexer.Add([]byte(`length`), opToken(Length)) | 	lexer.Add([]byte(`length`), opToken(Length)) | ||||||
| 	lexer.Add([]byte(`([Cc][Oo][Ll][Ll][Ee][Cc][Tt])`), opToken(Collect)) | 	lexer.Add([]byte(`select`), opToken(Select)) | ||||||
|  | 	lexer.Add([]byte(`or`), opToken(Or)) | ||||||
|  | 	lexer.Add([]byte(`and`), opToken(And)) | ||||||
|  | 	lexer.Add([]byte(`collect`), opToken(Collect)) | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`\s*==\s*`), opToken(Equals)) | 	lexer.Add([]byte(`\s*==\s*`), opToken(Equals)) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -52,7 +52,7 @@ func (p *pathTreeCreator) CreatePathTree(postFixPath []*PathElement) (*PathTreeN | |||||||
| 				remaining, rhs := stack[:len(stack)-1], stack[len(stack)-1] | 				remaining, rhs := stack[:len(stack)-1], stack[len(stack)-1] | ||||||
| 				newNode.Rhs = rhs | 				newNode.Rhs = rhs | ||||||
| 				stack = remaining | 				stack = remaining | ||||||
| 			} else { | 			} else if numArgs == 2 { | ||||||
| 				remaining, lhs, rhs := stack[:len(stack)-2], stack[len(stack)-2], stack[len(stack)-1] | 				remaining, lhs, rhs := stack[:len(stack)-2], stack[len(stack)-2], stack[len(stack)-1] | ||||||
| 				newNode.Lhs = lhs | 				newNode.Lhs = lhs | ||||||
| 				newNode.Rhs = rhs | 				newNode.Rhs = rhs | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user