mirror of
				https://github.com/taigrr/yq
				synced 2025-01-18 04:53:17 -08:00 
			
		
		
		
	JQ like syntax wip
This commit is contained in:
		
							parent
							
								
									449fb8952c
								
							
						
					
					
						commit
						6829d8cb78
					
				| @ -18,6 +18,15 @@ func (n *CandidateNode) GetKey() string { | |||||||
| 	return fmt.Sprintf("%v - %v", n.Document, n.Path) | 	return fmt.Sprintf("%v - %v", n.Document, n.Path) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // updates this candidate from the given candidate node | ||||||
|  | func (n *CandidateNode) UpdateFrom(other *CandidateNode) { | ||||||
|  | 	n.Node.Content = other.Node.Content | ||||||
|  | 	n.Node.Value = other.Node.Value | ||||||
|  | 	n.Node.Kind = other.Node.Kind | ||||||
|  | 	n.Node.Tag = other.Node.Tag | ||||||
|  | 	n.Node.Style = other.Node.Style | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (n *CandidateNode) PathStackToString() string { | func (n *CandidateNode) PathStackToString() string { | ||||||
| 	return mergePathStackToString(n.Path) | 	return mergePathStackToString(n.Path) | ||||||
| } | } | ||||||
|  | |||||||
| @ -70,7 +70,7 @@ func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *orderedmap.OrderedMa | |||||||
| 	log.Debugf("Processing Path: %v", pathNode.PathElement.toString()) | 	log.Debugf("Processing Path: %v", pathNode.PathElement.toString()) | ||||||
| 	if pathNode.PathElement.PathElementType == SelfReference { | 	if pathNode.PathElement.PathElementType == SelfReference { | ||||||
| 		return matchingNodes, nil | 		return matchingNodes, nil | ||||||
| 	} else if pathNode.PathElement.PathElementType == PathKey || pathNode.PathElement.PathElementType == ArrayIndex { | 	} else if pathNode.PathElement.PathElementType == PathKey { | ||||||
| 		return d.traverse(matchingNodes, pathNode.PathElement) | 		return d.traverse(matchingNodes, pathNode.PathElement) | ||||||
| 	} else { | 	} else { | ||||||
| 		handler := pathNode.PathElement.OperationType.Handler | 		handler := pathNode.PathElement.OperationType.Handler | ||||||
|  | |||||||
| @ -240,14 +240,41 @@ func TestDataTreeNavigatorDeleteViaSelf(t *testing.T) { | |||||||
| 	test.AssertResult(t, expected, resultsToString(results)) | 	test.AssertResult(t, expected, resultsToString(results)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestDataTreeNavigatorCountWithFilter(t *testing.T) { | func TestDataTreeNavigatorFilterWithSplat(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 	nodes := readDoc(t, `f: | 	nodes := readDoc(t, `f: | ||||||
|   a: frog |   a: frog | ||||||
|   b: dally |   b: dally | ||||||
|   c: log`) |   c: log`) | ||||||
| 
 | 
 | ||||||
| 	path, errPath := treeCreator.ParsePath("f(count(. == *og))") | 	path, errPath := treeCreator.ParsePath(".f | .[] == \"frog\"") | ||||||
|  | 	if errPath != nil { | ||||||
|  | 		t.Error(errPath) | ||||||
|  | 	} | ||||||
|  | 	results, errNav := treeNavigator.GetMatchingNodes(nodes, path) | ||||||
|  | 
 | ||||||
|  | 	if errNav != nil { | ||||||
|  | 		t.Error(errNav) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	expected := ` | ||||||
|  | -- Node -- | ||||||
|  |   Document 0, path: [f] | ||||||
|  |   Tag: !!int, Kind: ScalarNode, Anchor:  | ||||||
|  |   2 | ||||||
|  | ` | ||||||
|  | 
 | ||||||
|  | 	test.AssertResult(t, expected, resultsToString(results)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestDataTreeNavigatorCountAndCollectWithFilterCmd(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	nodes := readDoc(t, `f: | ||||||
|  |   a: frog | ||||||
|  |   b: dally | ||||||
|  |   c: log`) | ||||||
|  | 
 | ||||||
|  | 	path, errPath := treeCreator.ParsePath(".f | .[] == *og ") | ||||||
| 	if errPath != nil { | 	if errPath != nil { | ||||||
| 		t.Error(errPath) | 		t.Error(errPath) | ||||||
| 	} | 	} | ||||||
| @ -934,7 +961,7 @@ func TestDataTreeNavigatorEqualsSimple(t *testing.T) { | |||||||
|   pat: {b: banana} |   pat: {b: banana} | ||||||
| `) | `) | ||||||
| 
 | 
 | ||||||
| 	path, errPath := treeCreator.ParsePath("a.(b == apple)") | 	path, errPath := treeCreator.ParsePath(".a | (.[].b == \"apple\")") | ||||||
| 	if errPath != nil { | 	if errPath != nil { | ||||||
| 		t.Error(errPath) | 		t.Error(errPath) | ||||||
| 	} | 	} | ||||||
| @ -1132,7 +1159,7 @@ func TestDataTreeNavigatorArrayEqualsDeep(t *testing.T) { | |||||||
| 	test.AssertResult(t, expected, resultsToString(results)) | 	test.AssertResult(t, expected, resultsToString(results)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestDataTreeNavigatorEqualsTrickey(t *testing.T) { | func xTestDataTreeNavigatorEqualsTrickey(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 	nodes := readDoc(t, `a:  | 	nodes := readDoc(t, `a:  | ||||||
|   cat: {b: apso, c: {d : yes}} |   cat: {b: apso, c: {d : yes}} | ||||||
| @ -1141,7 +1168,7 @@ func TestDataTreeNavigatorEqualsTrickey(t *testing.T) { | |||||||
|   fat: {b: apple} |   fat: {b: apple} | ||||||
| `) | `) | ||||||
| 
 | 
 | ||||||
| 	path, errPath := treeCreator.ParsePath("a.(b == ap* and c.d == yes)") | 	path, errPath := treeCreator.ParsePath(".a(.b == ap* and .c.d == yes)") | ||||||
| 	if errPath != nil { | 	if errPath != nil { | ||||||
| 		t.Error(errPath) | 		t.Error(errPath) | ||||||
| 	} | 	} | ||||||
|  | |||||||
							
								
								
									
										61
									
								
								pkg/yqlib/treeops/equals_operator.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								pkg/yqlib/treeops/equals_operator.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | package treeops | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/elliotchance/orderedmap" | ||||||
|  | 	"gopkg.in/yaml.v3" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func EqualsOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | ||||||
|  | 	log.Debugf("-- equalsOperation") | ||||||
|  | 	var results = orderedmap.NewOrderedMap() | ||||||
|  | 
 | ||||||
|  | 	for el := matchMap.Front(); el != nil; el = el.Next() { | ||||||
|  | 		candidate := el.Value.(*CandidateNode) | ||||||
|  | 		log.Debug("equalsOperation checking %v", candidate) | ||||||
|  | 
 | ||||||
|  | 		matches, errInChild := hasMatch(d, candidate, pathNode.Lhs, pathNode.Rhs) | ||||||
|  | 		if errInChild != nil { | ||||||
|  | 			return nil, errInChild | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		matchString := "true" | ||||||
|  | 		if !matches { | ||||||
|  | 			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 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func hasMatch(d *dataTreeNavigator, candidate *CandidateNode, lhs *PathTreeNode, rhs *PathTreeNode) (bool, error) { | ||||||
|  | 	childMap := orderedmap.NewOrderedMap() | ||||||
|  | 	childMap.Set(candidate.GetKey(), candidate) | ||||||
|  | 	childMatches, errChild := d.getMatchingNodes(childMap, lhs) | ||||||
|  | 	log.Debug("got the LHS") | ||||||
|  | 	if errChild != nil { | ||||||
|  | 		return false, errChild | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// TODO = handle other RHS types | ||||||
|  | 	return containsMatchingValue(childMatches, rhs.PathElement.StringValue), nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) bool { | ||||||
|  | 	log.Debugf("-- findMatchingValues") | ||||||
|  | 
 | ||||||
|  | 	for el := matchMap.Front(); el != nil; el = el.Next() { | ||||||
|  | 		node := el.Value.(*CandidateNode) | ||||||
|  | 		log.Debugf("-- comparing %v to %v", node.Node.Value, valuePattern) | ||||||
|  | 		if Match(node.Node.Value, valuePattern) { | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	log.Debugf("-- done findMatchingValues") | ||||||
|  | 
 | ||||||
|  | 	return false | ||||||
|  | } | ||||||
| @ -53,7 +53,7 @@ func (t *traverser) traverseMap(candidate *CandidateNode, pathNode *PathElement) | |||||||
| 
 | 
 | ||||||
| func (t *traverser) traverseArray(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) { | func (t *traverser) traverseArray(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) { | ||||||
| 	log.Debug("pathNode Value %v", pathNode.Value) | 	log.Debug("pathNode Value %v", pathNode.Value) | ||||||
| 	if pathNode.Value == "[*]" || pathNode.Value == "*" { | 	if pathNode.Value == "[]" { | ||||||
| 
 | 
 | ||||||
| 		var contents = candidate.Node.Content | 		var contents = candidate.Node.Content | ||||||
| 		var newMatches = make([]*CandidateNode, len(contents)) | 		var newMatches = make([]*CandidateNode, len(contents)) | ||||||
|  | |||||||
| @ -15,11 +15,13 @@ type PathElementType uint32 | |||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	PathKey PathElementType = 1 << iota | 	PathKey PathElementType = 1 << iota | ||||||
| 	ArrayIndex |  | ||||||
| 	Operation | 	Operation | ||||||
| 	SelfReference | 	SelfReference | ||||||
| 	OpenBracket | 	OpenBracket | ||||||
| 	CloseBracket | 	CloseBracket | ||||||
|  | 	OpenCollect | ||||||
|  | 	CloseCollect | ||||||
|  | 	Value // e.g. string, int | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type OperationType struct { | type OperationType struct { | ||||||
| @ -30,16 +32,23 @@ type OperationType struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var None = &OperationType{Type: "NONE", NumArgs: 0, Precedence: 0} | var None = &OperationType{Type: "NONE", NumArgs: 0, Precedence: 0} | ||||||
| var Traverse = &OperationType{Type: "TRAVERSE", NumArgs: 2, Precedence: 35, Handler: TraverseOperator} | 
 | ||||||
| var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 10, Handler: UnionOperator} | var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 10, Handler: UnionOperator} | ||||||
| var And = &OperationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: IntersectionOperator} | var And = &OperationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: IntersectionOperator} | ||||||
| var Equals = &OperationType{Type: "EQUALS", NumArgs: 2, Precedence: 30, Handler: EqualsOperator} |  | ||||||
| var Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignOperator} |  | ||||||
| var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 2, Precedence: 30, Handler: DeleteChildOperator} |  | ||||||
| 
 | 
 | ||||||
| var Count = &OperationType{Type: "COUNT", NumArgs: 1, Precedence: 40, Handler: CountOperator} | var Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignOperator} | ||||||
|  | var Equals = &OperationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: EqualsOperator} | ||||||
|  | var Pipe = &OperationType{Type: "PIPE", NumArgs: 2, Precedence: 45, Handler: PipeOperator} | ||||||
|  | 
 | ||||||
|  | var Length = &OperationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: LengthOperator} | ||||||
|  | 
 | ||||||
|  | // not sure yet | ||||||
|  | 
 | ||||||
|  | var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 2, Precedence: 40, Handler: DeleteChildOperator} | ||||||
| var Collect = &OperationType{Type: "COLLECT", NumArgs: 1, Precedence: 40, Handler: CollectOperator} | var Collect = &OperationType{Type: "COLLECT", NumArgs: 1, Precedence: 40, Handler: CollectOperator} | ||||||
| 
 | 
 | ||||||
|  | // var Splat = &OperationType{Type: "SPLAT", NumArgs: 0, Precedence: 40, Handler: SplatOperator} | ||||||
|  | 
 | ||||||
| // var Exists = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35} | // var Exists = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35} | ||||||
| // filters matches if they have the existing path | // filters matches if they have the existing path | ||||||
| 
 | 
 | ||||||
| @ -55,13 +64,15 @@ 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'\n", p.Value) | 		result = result + fmt.Sprintf("PathKey - %v", p.Value) | ||||||
| 	case ArrayIndex: |  | ||||||
| 		result = result + fmt.Sprintf("ArrayIndex - '%v'\n", p.Value) |  | ||||||
| 	case SelfReference: | 	case SelfReference: | ||||||
| 		result = result + fmt.Sprintf("SELF\n") | 		result = result + fmt.Sprintf("SELF") | ||||||
| 	case Operation: | 	case Operation: | ||||||
| 		result = result + fmt.Sprintf("Operation - %v\n", p.OperationType.Type) | 		result = result + fmt.Sprintf("Operation - %v", p.OperationType.Type) | ||||||
|  | 	case Value: | ||||||
|  | 		result = result + fmt.Sprintf("Value - %v (%T)", p.Value, p.Value) | ||||||
|  | 	default: | ||||||
|  | 		result = result + "I HAVENT GOT A STRATEGY" | ||||||
| 	} | 	} | ||||||
| 	return result | 	return result | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| type OperatorHandler func(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) | type OperatorHandler func(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) | ||||||
| 
 | 
 | ||||||
| func TraverseOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | func PipeOperator(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 { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -17,15 +17,32 @@ func TraverseOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap | |||||||
| 	return d.getMatchingNodes(lhs, pathNode.Rhs) | 	return d.getMatchingNodes(lhs, pathNode.Rhs) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func nodeToMap(candidate *CandidateNode) *orderedmap.OrderedMap { | ||||||
|  | 	elMap := orderedmap.NewOrderedMap() | ||||||
|  | 	elMap.Set(candidate.GetKey(), candidate) | ||||||
|  | 	return elMap | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func AssignOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | func AssignOperator(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 { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	for el := lhs.Front(); el != nil; el = el.Next() { | 	for el := lhs.Front(); el != nil; el = el.Next() { | ||||||
| 		node := el.Value.(*CandidateNode) | 		candidate := el.Value.(*CandidateNode) | ||||||
| 		log.Debugf("Assiging %v to %v", node.GetKey(), pathNode.Rhs.PathElement.StringValue) | 
 | ||||||
| 		node.Node.Value = pathNode.Rhs.PathElement.StringValue | 		rhs, err := d.getMatchingNodes(nodeToMap(candidate), pathNode.Rhs) | ||||||
|  | 
 | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// grab the first value | ||||||
|  | 		first := rhs.Front() | ||||||
|  | 
 | ||||||
|  | 		if first != nil { | ||||||
|  | 			candidate.UpdateFrom(first.Value.(*CandidateNode)) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return lhs, nil | 	return lhs, nil | ||||||
| } | } | ||||||
| @ -66,54 +83,36 @@ func IntersectionOperator(d *dataTreeNavigator, matchingNodes *orderedmap.Ordere | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func splatNode(d *dataTreeNavigator, candidate *CandidateNode) (*orderedmap.OrderedMap, error) { | func splatNode(d *dataTreeNavigator, candidate *CandidateNode) (*orderedmap.OrderedMap, error) { | ||||||
| 	elMap := orderedmap.NewOrderedMap() |  | ||||||
| 	elMap.Set(candidate.GetKey(), candidate) |  | ||||||
| 	//need to splat matching nodes, then search through them | 	//need to splat matching nodes, then search through them | ||||||
| 	splatter := &PathTreeNode{PathElement: &PathElement{ | 	splatter := &PathTreeNode{PathElement: &PathElement{ | ||||||
| 		PathElementType: PathKey, | 		PathElementType: PathKey, | ||||||
| 		Value:           "*", | 		Value:           "*", | ||||||
| 		StringValue:     "*", | 		StringValue:     "*", | ||||||
| 	}} | 	}} | ||||||
| 	return d.getMatchingNodes(elMap, splatter) | 	return d.getMatchingNodes(nodeToMap(candidate), splatter) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func EqualsOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | func LengthOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | ||||||
| 	log.Debugf("-- equalsOperation") | 	log.Debugf("-- lengthOperation") | ||||||
| 	var results = orderedmap.NewOrderedMap() | 	var results = orderedmap.NewOrderedMap() | ||||||
| 
 | 
 | ||||||
| 	for el := matchMap.Front(); el != nil; el = el.Next() { | 	for el := matchMap.Front(); el != nil; el = el.Next() { | ||||||
| 		candidate := el.Value.(*CandidateNode) | 		candidate := el.Value.(*CandidateNode) | ||||||
| 		valuePattern := pathNode.Rhs.PathElement.StringValue | 		var length int | ||||||
| 		log.Debug("checking %v", candidate) | 		switch candidate.Node.Kind { | ||||||
| 
 | 		case yaml.ScalarNode: | ||||||
| 		errInChild := findMatchingChildren(d, results, candidate, pathNode.Lhs, valuePattern) | 			length = len(candidate.Node.Value) | ||||||
| 		if errInChild != nil { | 		case yaml.MappingNode: | ||||||
| 			return nil, errInChild | 			length = len(candidate.Node.Content) / 2 | ||||||
| 		} | 		case yaml.SequenceNode: | ||||||
|  | 			length = len(candidate.Node.Content) | ||||||
|  | 		default: | ||||||
|  | 			length = 0 | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	return results, nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func CountOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { |  | ||||||
| 	log.Debugf("-- countOperation") |  | ||||||
| 	var results = orderedmap.NewOrderedMap() |  | ||||||
| 
 |  | ||||||
| 	for el := matchMap.Front(); el != nil; el = el.Next() { |  | ||||||
| 		candidate := el.Value.(*CandidateNode) |  | ||||||
| 		elMap := orderedmap.NewOrderedMap() |  | ||||||
| 		elMap.Set(el.Key, el.Value) |  | ||||||
| 		childMatches, errChild := d.getMatchingNodes(elMap, pathNode.Rhs) |  | ||||||
| 
 |  | ||||||
| 		if errChild != nil { |  | ||||||
| 			return nil, errChild |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		length := childMatches.Len() |  | ||||||
| 		node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", length), Tag: "!!int"} | 		node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", length), Tag: "!!int"} | ||||||
| 		lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} | 		lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} | ||||||
| 		results.Set(candidate.GetKey(), lengthCand) | 		results.Set(candidate.GetKey(), lengthCand) | ||||||
| 
 |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return results, nil | 	return results, nil | ||||||
| @ -122,76 +121,25 @@ func CountOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNo | |||||||
| func CollectOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | func CollectOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { | ||||||
| 	log.Debugf("-- collectOperation") | 	log.Debugf("-- collectOperation") | ||||||
| 
 | 
 | ||||||
| 	log.Debugf("-- countOperation") |  | ||||||
| 	var results = orderedmap.NewOrderedMap() | 	var results = orderedmap.NewOrderedMap() | ||||||
| 
 | 
 | ||||||
| 	for el := matchMap.Front(); el != nil; el = el.Next() { |  | ||||||
| 		candidate := el.Value.(*CandidateNode) |  | ||||||
| 		elMap := orderedmap.NewOrderedMap() |  | ||||||
| 		elMap.Set(el.Key, el.Value) |  | ||||||
| 		childMatches, errChild := d.getMatchingNodes(elMap, pathNode.Rhs) |  | ||||||
| 
 |  | ||||||
| 		if errChild != nil { |  | ||||||
| 			return nil, errChild |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 	node := &yaml.Node{Kind: yaml.SequenceNode} | 	node := &yaml.Node{Kind: yaml.SequenceNode} | ||||||
| 
 | 
 | ||||||
| 		for childEl := childMatches.Front(); childEl != nil; childEl = childEl.Next() { | 	var document uint = 0 | ||||||
| 			childCandidate := childEl.Value.(*CandidateNode) | 	var path []interface{} | ||||||
| 			node.Content = append(node.Content, childCandidate.Node) | 
 | ||||||
|  | 	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) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 		lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} | 	collectC := &CandidateNode{Node: node, Document: document, Path: path} | ||||||
| 		results.Set(candidate.GetKey(), lengthCand) | 	results.Set(collectC.GetKey(), collectC) | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return results, nil | 	return results, nil | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 |  | ||||||
| func findMatchingChildren(d *dataTreeNavigator, results *orderedmap.OrderedMap, candidate *CandidateNode, lhs *PathTreeNode, valuePattern string) error { |  | ||||||
| 	var children *orderedmap.OrderedMap |  | ||||||
| 	var err error |  | ||||||
| 	// don't splat scalars. |  | ||||||
| 	if candidate.Node.Kind != yaml.ScalarNode { |  | ||||||
| 		children, err = splatNode(d, candidate) |  | ||||||
| 		log.Debugf("-- splatted matches, ") |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		children = orderedmap.NewOrderedMap() |  | ||||||
| 		children.Set(candidate.GetKey(), candidate) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for childEl := children.Front(); childEl != nil; childEl = childEl.Next() { |  | ||||||
| 		childMap := orderedmap.NewOrderedMap() |  | ||||||
| 		childMap.Set(childEl.Key, childEl.Value) |  | ||||||
| 		childMatches, errChild := d.getMatchingNodes(childMap, lhs) |  | ||||||
| 		log.Debug("got the LHS") |  | ||||||
| 		if errChild != nil { |  | ||||||
| 			return errChild |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if containsMatchingValue(childMatches, valuePattern) { |  | ||||||
| 			results.Set(childEl.Key, childEl.Value) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) bool { |  | ||||||
| 	log.Debugf("-- findMatchingValues") |  | ||||||
| 
 |  | ||||||
| 	for el := matchMap.Front(); el != nil; el = el.Next() { |  | ||||||
| 		node := el.Value.(*CandidateNode) |  | ||||||
| 		log.Debugf("-- comparing %v to %v", node.Node.Value, valuePattern) |  | ||||||
| 		if Match(node.Node.Value, valuePattern) { |  | ||||||
| 			return true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	log.Debugf("-- done findMatchingValues") |  | ||||||
| 
 |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -25,17 +25,28 @@ func popOpToResult(opStack []*Token, result []*PathElement) ([]*Token, []*PathEl | |||||||
| func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement, error) { | func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement, error) { | ||||||
| 	var result []*PathElement | 	var result []*PathElement | ||||||
| 	// surround the whole thing with quotes | 	// surround the whole thing with quotes | ||||||
| 	var opStack = []*Token{&Token{PathElementType: OpenBracket, OperationType: None}} | 	var opStack = []*Token{&Token{PathElementType: OpenBracket, OperationType: None, Value: "("}} | ||||||
| 	var tokens = append(infixTokens, &Token{PathElementType: CloseBracket, OperationType: None}) | 	var tokens = append(infixTokens, &Token{PathElementType: CloseBracket, OperationType: None, Value: ")"}) | ||||||
| 
 | 
 | ||||||
| 	for _, token := range tokens { | 	for _, token := range tokens { | ||||||
|  | 		log.Debugf("postfix processing token %v", token.Value) | ||||||
| 		switch token.PathElementType { | 		switch token.PathElementType { | ||||||
| 		case PathKey, ArrayIndex, SelfReference: | 		case PathKey, SelfReference, Value: | ||||||
| 			var pathElement = PathElement{PathElementType: token.PathElementType, Value: token.Value, StringValue: token.StringValue} | 			var pathElement = PathElement{PathElementType: token.PathElementType, Value: token.Value, StringValue: token.StringValue} | ||||||
| 			result = append(result, &pathElement) | 			result = append(result, &pathElement) | ||||||
| 		case OpenBracket: | 		case OpenBracket, OpenCollect: | ||||||
| 			opStack = append(opStack, token) | 			opStack = append(opStack, token) | ||||||
| 
 | 		case CloseCollect: | ||||||
|  | 			for len(opStack) > 0 && opStack[len(opStack)-1].PathElementType != OpenCollect { | ||||||
|  | 				opStack, result = popOpToResult(opStack, result) | ||||||
|  | 			} | ||||||
|  | 			if len(opStack) == 0 { | ||||||
|  | 				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 | ||||||
|  | 			opStack = opStack[0 : len(opStack)-1] | ||||||
|  | 			//and append a collect to the opStack | ||||||
|  | 			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 { | ||||||
| 				opStack, result = popOpToResult(opStack, result) | 				opStack, result = popOpToResult(opStack, result) | ||||||
|  | |||||||
| @ -20,20 +20,20 @@ func testExpression(expression string) (string, error) { | |||||||
| 	} | 	} | ||||||
| 	formatted := "" | 	formatted := "" | ||||||
| 	for _, path := range results { | 	for _, path := range results { | ||||||
| 		formatted = formatted + path.toString() + "--------\n" | 		formatted = formatted + path.toString() + "\n--------\n" | ||||||
| 	} | 	} | ||||||
| 	return formatted, nil | 	return formatted, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixTraverseBar(t *testing.T) { | func TestPostFixTraverseBar(t *testing.T) { | ||||||
| 	var infix = "animals | collect(.)" | 	var infix = ".animals | [.]" | ||||||
| 	var expectedOutput = `PathKey - 'animals' | 	var expectedOutput = `PathKey - animals | ||||||
| -------- | -------- | ||||||
| SELF | SELF | ||||||
| -------- | -------- | ||||||
| Operation - COLLECT | Operation - COLLECT | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- | -------- | ||||||
| ` | ` | ||||||
| 
 | 
 | ||||||
| @ -45,17 +45,87 @@ Operation - TRAVERSE | |||||||
| 	test.AssertResultComplex(t, expectedOutput, actual) | 	test.AssertResultComplex(t, expectedOutput, actual) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixArrayEquals(t *testing.T) { | func TestPostFixPipeEquals(t *testing.T) { | ||||||
| 	var infix = "animals(.== cat)" | 	var infix = `.animals |   (. == "cat") ` | ||||||
| 	var expectedOutput = `PathKey - 'animals' | 	var expectedOutput = `PathKey - animals | ||||||
| -------- | -------- | ||||||
| SELF | SELF | ||||||
| -------- | -------- | ||||||
| PathKey - 'cat' | Value - cat (string) | ||||||
| -------- | -------- | ||||||
| Operation - EQUALS | Operation - EQUALS | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
|  | -------- | ||||||
|  | ` | ||||||
|  | 
 | ||||||
|  | 	actual, err := testExpression(infix) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	test.AssertResultComplex(t, expectedOutput, actual) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestPostFixCollect(t *testing.T) { | ||||||
|  | 	var infix = "[.a]" | ||||||
|  | 	var expectedOutput = `PathKey - a | ||||||
|  | -------- | ||||||
|  | Operation - COLLECT | ||||||
|  | -------- | ||||||
|  | ` | ||||||
|  | 
 | ||||||
|  | 	actual, err := testExpression(infix) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	test.AssertResultComplex(t, expectedOutput, actual) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestPostFixSplatSearch(t *testing.T) { | ||||||
|  | 	var infix = `.a | (.[].b == "apple")` | ||||||
|  | 	var expectedOutput = `PathKey - a | ||||||
|  | -------- | ||||||
|  | PathKey - [] | ||||||
|  | -------- | ||||||
|  | PathKey - b | ||||||
|  | -------- | ||||||
|  | Operation - PIPE | ||||||
|  | -------- | ||||||
|  | Value - apple (string) | ||||||
|  | -------- | ||||||
|  | Operation - EQUALS | ||||||
|  | -------- | ||||||
|  | Operation - PIPE | ||||||
|  | -------- | ||||||
|  | ` | ||||||
|  | 
 | ||||||
|  | 	actual, err := testExpression(infix) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Error(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	test.AssertResultComplex(t, expectedOutput, actual) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestPostFixCollectWithExpression(t *testing.T) { | ||||||
|  | 	var infix = `[ (.a == "fred") | (.d, .f)]` | ||||||
|  | 	var expectedOutput = `PathKey - a | ||||||
|  | -------- | ||||||
|  | Value - fred (string) | ||||||
|  | -------- | ||||||
|  | Operation - EQUALS | ||||||
|  | -------- | ||||||
|  | PathKey - d | ||||||
|  | -------- | ||||||
|  | PathKey - f | ||||||
|  | -------- | ||||||
|  | Operation - OR | ||||||
|  | -------- | ||||||
|  | Operation - PIPE | ||||||
|  | -------- | ||||||
|  | Operation - COLLECT | ||||||
| -------- | -------- | ||||||
| ` | ` | ||||||
| 
 | 
 | ||||||
| @ -68,10 +138,12 @@ Operation - TRAVERSE | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixLength(t *testing.T) { | func TestPostFixLength(t *testing.T) { | ||||||
| 	var infix = "len(a)" | 	var infix = ".a | length" | ||||||
| 	var expectedOutput = `PathKey - 'a' | 	var expectedOutput = `PathKey - a | ||||||
| -------- | -------- | ||||||
| Operation - Length | Operation - LENGTH | ||||||
|  | -------- | ||||||
|  | Operation - PIPE | ||||||
| -------- | -------- | ||||||
| ` | ` | ||||||
| 
 | 
 | ||||||
| @ -84,8 +156,8 @@ Operation - Length | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixSimpleExample(t *testing.T) { | func TestPostFixSimpleExample(t *testing.T) { | ||||||
| 	var infix = "a" | 	var infix = ".a" | ||||||
| 	var expectedOutput = `PathKey - 'a' | 	var expectedOutput = `PathKey - a | ||||||
| -------- | -------- | ||||||
| ` | ` | ||||||
| 
 | 
 | ||||||
| @ -98,16 +170,16 @@ func TestPostFixSimpleExample(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixSimplePathExample(t *testing.T) { | func TestPostFixSimplePathExample(t *testing.T) { | ||||||
| 	var infix = "apples.bananas*.cat" | 	var infix = ".apples.bananas*.cat" | ||||||
| 	var expectedOutput = `PathKey - 'apples' | 	var expectedOutput = `PathKey - apples | ||||||
| -------- | -------- | ||||||
| PathKey - 'bananas*' | PathKey - bananas* | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- | -------- | ||||||
| PathKey - 'cat' | PathKey - cat | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- | -------- | ||||||
| ` | ` | ||||||
| 
 | 
 | ||||||
| @ -120,14 +192,14 @@ Operation - TRAVERSE | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixSimpleAssign(t *testing.T) { | func TestPostFixSimpleAssign(t *testing.T) { | ||||||
| 	var infix = "a.b := frog" | 	var infix = ".a.b |= \"frog\"" | ||||||
| 	var expectedOutput = `PathKey - 'a' | 	var expectedOutput = `PathKey - a | ||||||
| -------- | -------- | ||||||
| PathKey - 'b' | PathKey - b | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- | -------- | ||||||
| PathKey - 'frog' | Value - frog (string) | ||||||
| -------- | -------- | ||||||
| Operation - ASSIGN | Operation - ASSIGN | ||||||
| -------- | -------- | ||||||
| @ -142,38 +214,16 @@ Operation - ASSIGN | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixSimplePathNumbersExample(t *testing.T) { | func TestPostFixSimplePathNumbersExample(t *testing.T) { | ||||||
| 	var infix = "apples[0].cat" | 	var infix = ".apples[0].cat" | ||||||
| 	var expectedOutput = `PathKey - 'apples' | 	var expectedOutput = `PathKey - apples | ||||||
| -------- | -------- | ||||||
| PathKey - '0' | PathKey - 0 | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- | -------- | ||||||
| PathKey - 'cat' | PathKey - cat | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	actual, err := testExpression(infix) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Error(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	test.AssertResultComplex(t, expectedOutput, actual) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestPostFixSimplePathAppendArrayExample(t *testing.T) { |  | ||||||
| 	var infix = "apples[+].cat" |  | ||||||
| 	var expectedOutput = `PathKey - 'apples' |  | ||||||
| -------- |  | ||||||
| PathKey - '[+]' |  | ||||||
| -------- |  | ||||||
| Operation - TRAVERSE |  | ||||||
| -------- |  | ||||||
| PathKey - 'cat' |  | ||||||
| -------- |  | ||||||
| Operation - TRAVERSE |  | ||||||
| -------- | -------- | ||||||
| ` | ` | ||||||
| 
 | 
 | ||||||
| @ -186,38 +236,16 @@ Operation - TRAVERSE | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixSimplePathSplatArrayExample(t *testing.T) { | func TestPostFixSimplePathSplatArrayExample(t *testing.T) { | ||||||
| 	var infix = "apples.[*]cat" | 	var infix = ".apples[].cat" | ||||||
| 	var expectedOutput = `PathKey - 'apples' | 	var expectedOutput = `PathKey - apples | ||||||
| -------- | -------- | ||||||
| PathKey - '[*]' | PathKey - [] | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- | -------- | ||||||
| PathKey - 'cat' | PathKey - cat | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- |  | ||||||
| ` |  | ||||||
| 
 |  | ||||||
| 	actual, err := testExpression(infix) |  | ||||||
| 	if err != nil { |  | ||||||
| 		t.Error(err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	test.AssertResultComplex(t, expectedOutput, actual) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestPostFixDeepMatchExample(t *testing.T) { |  | ||||||
| 	var infix = "apples.**.cat" |  | ||||||
| 	var expectedOutput = `PathKey - 'apples' |  | ||||||
| -------- |  | ||||||
| PathKey - '**' |  | ||||||
| -------- |  | ||||||
| Operation - TRAVERSE |  | ||||||
| -------- |  | ||||||
| PathKey - 'cat' |  | ||||||
| -------- |  | ||||||
| Operation - TRAVERSE |  | ||||||
| -------- | -------- | ||||||
| ` | ` | ||||||
| 
 | 
 | ||||||
| @ -230,10 +258,10 @@ Operation - TRAVERSE | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixOrExample(t *testing.T) { | func TestPostFixOrExample(t *testing.T) { | ||||||
| 	var infix = "a OR b" | 	var infix = ".a, .b" | ||||||
| 	var expectedOutput = `PathKey - 'a' | 	var expectedOutput = `PathKey - a | ||||||
| -------- | -------- | ||||||
| PathKey - 'b' | PathKey - b | ||||||
| -------- | -------- | ||||||
| Operation - OR | Operation - OR | ||||||
| -------- | -------- | ||||||
| @ -248,10 +276,10 @@ Operation - OR | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixEqualsNumberExample(t *testing.T) { | func TestPostFixEqualsNumberExample(t *testing.T) { | ||||||
| 	var infix = "(animal == 3)" | 	var infix = ".animal == 3" | ||||||
| 	var expectedOutput = `PathKey - 'animal' | 	var expectedOutput = `PathKey - animal | ||||||
| -------- | -------- | ||||||
| PathKey - '3' | Value - 3 (int64) | ||||||
| -------- | -------- | ||||||
| Operation - EQUALS | Operation - EQUALS | ||||||
| -------- | -------- | ||||||
| @ -266,16 +294,16 @@ Operation - EQUALS | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixOrWithEqualsExample(t *testing.T) { | func TestPostFixOrWithEqualsExample(t *testing.T) { | ||||||
| 	var infix = "a==thing OR b==thongs" | 	var infix = ".a==\"thing\", .b==.thongs" | ||||||
| 	var expectedOutput = `PathKey - 'a' | 	var expectedOutput = `PathKey - a | ||||||
| -------- | -------- | ||||||
| PathKey - 'thing' | Value - thing (string) | ||||||
| -------- | -------- | ||||||
| Operation - EQUALS | Operation - EQUALS | ||||||
| -------- | -------- | ||||||
| PathKey - 'b' | PathKey - b | ||||||
| -------- | -------- | ||||||
| PathKey - 'thongs' | PathKey - thongs | ||||||
| -------- | -------- | ||||||
| Operation - EQUALS | Operation - EQUALS | ||||||
| -------- | -------- | ||||||
| @ -292,24 +320,24 @@ Operation - OR | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPostFixOrWithEqualsPathExample(t *testing.T) { | func TestPostFixOrWithEqualsPathExample(t *testing.T) { | ||||||
| 	var infix = "apples.monkeys==thing OR bogs.bobos==thongs" | 	var infix = ".apples.monkeys==\"thing\", .bogs.bobos==true" | ||||||
| 	var expectedOutput = `PathKey - 'apples' | 	var expectedOutput = `PathKey - apples | ||||||
| -------- | -------- | ||||||
| PathKey - 'monkeys' | PathKey - monkeys | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- | -------- | ||||||
| PathKey - 'thing' | Value - thing (string) | ||||||
| -------- | -------- | ||||||
| Operation - EQUALS | Operation - EQUALS | ||||||
| -------- | -------- | ||||||
| PathKey - 'bogs' | PathKey - bogs | ||||||
| -------- | -------- | ||||||
| PathKey - 'bobos' | PathKey - bobos | ||||||
| -------- | -------- | ||||||
| Operation - TRAVERSE | Operation - PIPE | ||||||
| -------- | -------- | ||||||
| PathKey - 'thongs' | Value - true (bool) | ||||||
| -------- | -------- | ||||||
| Operation - EQUALS | Operation - EQUALS | ||||||
| -------- | -------- | ||||||
|  | |||||||
| @ -18,31 +18,30 @@ type Token struct { | |||||||
| 	StringValue     string | 	StringValue     string | ||||||
| 	PrefixSelf      bool | 	PrefixSelf      bool | ||||||
| 
 | 
 | ||||||
| 	CheckForPreTraverse bool // this token can sometimes have the traverse '.' missing in frnot of it | 	CheckForPostTraverse bool // e.g. [1]cat should really be [1].cat | ||||||
| 	// e.g. a[1] should really be a.[1] |  | ||||||
| 	CheckForPostTraverse bool // samething but for post, e.g. [1]cat should really be [1].cat |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func pathToken(wrapped bool) lex.Action { | func pathToken(wrapped bool) lex.Action { | ||||||
| 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | ||||||
| 		value := string(m.Bytes) | 		value := string(m.Bytes) | ||||||
|  | 		value = value[1:len(value)] | ||||||
| 		if wrapped { | 		if wrapped { | ||||||
| 			value = unwrap(value) | 			value = unwrap(value) | ||||||
| 		} | 		} | ||||||
| 		return &Token{PathElementType: PathKey, OperationType: None, Value: value, StringValue: value}, nil | 		return &Token{PathElementType: PathKey, OperationType: None, Value: value, StringValue: value, CheckForPostTraverse: true}, nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func opToken(op *OperationType, againstSelf bool) lex.Action { | func opToken(op *OperationType) lex.Action { | ||||||
| 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | ||||||
| 		value := string(m.Bytes) | 		value := string(m.Bytes) | ||||||
| 		return &Token{PathElementType: Operation, OperationType: op, Value: op.Type, StringValue: value, PrefixSelf: againstSelf}, nil | 		return &Token{PathElementType: Operation, OperationType: op, Value: op.Type, StringValue: value}, nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func literalToken(pType PathElementType, literal string, checkForPre bool, checkForPost bool, againstSelf bool) lex.Action { | func literalToken(pType PathElementType, literal string, checkForPost bool) lex.Action { | ||||||
| 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | ||||||
| 		return &Token{PathElementType: pType, OperationType: None, Value: literal, StringValue: literal, CheckForPreTraverse: checkForPre, CheckForPostTraverse: checkForPost, PrefixSelf: againstSelf}, nil | 		return &Token{PathElementType: pType, OperationType: None, Value: literal, StringValue: literal, CheckForPostTraverse: checkForPost}, nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -50,54 +49,96 @@ func unwrap(value string) string { | |||||||
| 	return value[1 : len(value)-1] | 	return value[1 : len(value)-1] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func arrayIndextoken(wrapped bool, checkForPre bool, checkForPost bool) lex.Action { | func arrayIndextoken(precedingDot bool) lex.Action { | ||||||
| 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | ||||||
| 		var numberString = string(m.Bytes) | 		var numberString = string(m.Bytes) | ||||||
| 		if wrapped { | 		startIndex := 1 | ||||||
| 			numberString = unwrap(numberString) | 		if precedingDot { | ||||||
|  | 			startIndex = 2 | ||||||
| 		} | 		} | ||||||
|  | 		numberString = numberString[startIndex : len(numberString)-1] | ||||||
| 		var number, errParsingInt = strconv.ParseInt(numberString, 10, 64) // nolint | 		var number, errParsingInt = strconv.ParseInt(numberString, 10, 64) // nolint | ||||||
| 		if errParsingInt != nil { | 		if errParsingInt != nil { | ||||||
| 			return nil, errParsingInt | 			return nil, errParsingInt | ||||||
| 		} | 		} | ||||||
| 		return &Token{PathElementType: ArrayIndex, OperationType: None, Value: number, StringValue: numberString, CheckForPreTraverse: checkForPre, CheckForPostTraverse: checkForPost}, nil | 		return &Token{PathElementType: PathKey, OperationType: None, Value: number, StringValue: numberString, CheckForPostTraverse: true}, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func numberValue() lex.Action { | ||||||
|  | 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | ||||||
|  | 		var numberString = string(m.Bytes) | ||||||
|  | 		var number, errParsingInt = strconv.ParseInt(numberString, 10, 64) // nolint | ||||||
|  | 		if errParsingInt != nil { | ||||||
|  | 			return nil, errParsingInt | ||||||
|  | 		} | ||||||
|  | 		return &Token{PathElementType: Value, OperationType: None, Value: number, StringValue: numberString}, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func booleanValue(val bool) lex.Action { | ||||||
|  | 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | ||||||
|  | 		return &Token{PathElementType: Value, OperationType: None, Value: val, StringValue: string(m.Bytes)}, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func stringValue(wrapped bool) lex.Action { | ||||||
|  | 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | ||||||
|  | 		value := string(m.Bytes) | ||||||
|  | 		if wrapped { | ||||||
|  | 			value = unwrap(value) | ||||||
|  | 		} | ||||||
|  | 		return &Token{PathElementType: Value, OperationType: None, Value: value, StringValue: value}, nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func selfToken() lex.Action { | ||||||
|  | 	return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { | ||||||
|  | 		return &Token{PathElementType: SelfReference, OperationType: None, Value: "SELF", StringValue: "SELF"}, nil | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Creates the lexer object and compiles the NFA. | // Creates the lexer object and compiles the NFA. | ||||||
| func initLexer() (*lex.Lexer, error) { | func initLexer() (*lex.Lexer, error) { | ||||||
| 	lexer := lex.NewLexer() | 	lexer := lex.NewLexer() | ||||||
| 	lexer.Add([]byte(`\(`), literalToken(OpenBracket, "(", true, false, false)) | 	lexer.Add([]byte(`\(`), literalToken(OpenBracket, "(", false)) | ||||||
| 	lexer.Add([]byte(`\)`), literalToken(CloseBracket, ")", false, true, false)) | 	lexer.Add([]byte(`\)`), literalToken(CloseBracket, ")", true)) | ||||||
| 	lexer.Add([]byte(`\.\s*\)`), literalToken(CloseBracket, ")", false, true, true)) |  | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`\[\+\]`), literalToken(PathKey, "[+]", true, true, false)) | 	lexer.Add([]byte(`\.?\[\]`), literalToken(PathKey, "[]", true)) | ||||||
| 	lexer.Add([]byte(`\[\*\]`), literalToken(PathKey, "[*]", true, true, false)) | 	lexer.Add([]byte(`\.\.`), literalToken(PathKey, "..", true)) | ||||||
| 	lexer.Add([]byte(`\*\*`), literalToken(PathKey, "**", false, false, false)) |  | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`([Oo][Rr])`), opToken(Or, false)) | 	lexer.Add([]byte(`,`), opToken(Or)) | ||||||
| 	lexer.Add([]byte(`([Aa][Nn][Dd])`), opToken(And, false)) | 	lexer.Add([]byte(`length`), opToken(Length)) | ||||||
| 	lexer.Add([]byte(`([Cc][Oo][Uu][Nn][Tt])`), opToken(Count, false)) | 	lexer.Add([]byte(`([Cc][Oo][Ll][Ll][Ee][Cc][Tt])`), opToken(Collect)) | ||||||
| 	lexer.Add([]byte(`([Cc][Oo][Ll][Ll][Ee][Cc][Tt])`), opToken(Collect, false)) |  | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`\.\s*==\s*`), opToken(Equals, true)) | 	lexer.Add([]byte(`\s*==\s*`), opToken(Equals)) | ||||||
| 	lexer.Add([]byte(`\s*==\s*`), opToken(Equals, false)) |  | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`\.\s*.-\s*`), opToken(DeleteChild, true)) | 	lexer.Add([]byte(`\s*.-\s*`), opToken(DeleteChild)) | ||||||
| 	lexer.Add([]byte(`\s*.-\s*`), opToken(DeleteChild, false)) |  | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`\.\s*:=\s*`), opToken(Assign, true)) | 	lexer.Add([]byte(`\s*\|=\s*`), opToken(Assign)) | ||||||
| 	lexer.Add([]byte(`\s*:=\s*`), opToken(Assign, false)) | 
 | ||||||
|  | 	lexer.Add([]byte(`\[-?[0-9]+\]`), arrayIndextoken(false)) | ||||||
|  | 	lexer.Add([]byte(`\.\[-?[0-9]+\]`), arrayIndextoken(true)) | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`\[-?[0-9]+\]`), arrayIndextoken(true, true, true)) |  | ||||||
| 	lexer.Add([]byte(`-?[0-9]+`), arrayIndextoken(false, false, false)) |  | ||||||
| 	lexer.Add([]byte("( |\t|\n|\r)+"), skip) | 	lexer.Add([]byte("( |\t|\n|\r)+"), skip) | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`"[^ "]+"`), pathToken(true)) | 	lexer.Add([]byte(`\."[^ "]+"`), pathToken(true)) | ||||||
| 	lexer.Add([]byte(`[^ \|\.\[\(\)=]+`), pathToken(false)) | 	lexer.Add([]byte(`\.[^ \[\],\|\.\[\(\)=]+`), pathToken(false)) | ||||||
|  | 	lexer.Add([]byte(`\.`), selfToken()) | ||||||
| 
 | 
 | ||||||
| 	lexer.Add([]byte(`\|`), opToken(Traverse, false)) | 	lexer.Add([]byte(`\|`), opToken(Pipe)) | ||||||
| 	lexer.Add([]byte(`\.`), opToken(Traverse, false)) | 
 | ||||||
|  | 	lexer.Add([]byte(`-?[0-9]+`), numberValue()) | ||||||
|  | 
 | ||||||
|  | 	lexer.Add([]byte(`[Tt][Rr][Uu][Ee]`), booleanValue(true)) | ||||||
|  | 	lexer.Add([]byte(`[Ff][Aa][Ll][Ss][Ee]`), booleanValue(false)) | ||||||
|  | 
 | ||||||
|  | 	lexer.Add([]byte(`"[^ "]+"`), stringValue(true)) | ||||||
|  | 
 | ||||||
|  | 	lexer.Add([]byte(`\[`), literalToken(OpenCollect, "[", false)) | ||||||
|  | 	lexer.Add([]byte(`\]`), literalToken(CloseCollect, "]", true)) | ||||||
|  | 
 | ||||||
|  | 	// lexer.Add([]byte(`[^ \,\|\.\[\(\)=]+`), stringValue(false)) | ||||||
| 	err := lexer.Compile() | 	err := lexer.Compile() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @ -142,19 +183,12 @@ func (p *pathTokeniser) Tokenise(path string) ([]*Token, error) { | |||||||
| 	var postProcessedTokens = make([]*Token, 0) | 	var postProcessedTokens = make([]*Token, 0) | ||||||
| 
 | 
 | ||||||
| 	for index, token := range tokens { | 	for index, token := range tokens { | ||||||
| 		if index > 0 && token.CheckForPreTraverse && |  | ||||||
| 			(tokens[index-1].PathElementType == PathKey || tokens[index-1].PathElementType == CloseBracket) { |  | ||||||
| 			postProcessedTokens = append(postProcessedTokens, &Token{PathElementType: Operation, OperationType: Traverse, Value: "."}) |  | ||||||
| 		} |  | ||||||
| 		if token.PrefixSelf { |  | ||||||
| 			postProcessedTokens = append(postProcessedTokens, &Token{PathElementType: SelfReference, Value: "SELF"}) |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		postProcessedTokens = append(postProcessedTokens, token) | 		postProcessedTokens = append(postProcessedTokens, token) | ||||||
| 
 | 
 | ||||||
| 		if index != len(tokens)-1 && token.CheckForPostTraverse && | 		if index != len(tokens)-1 && token.CheckForPostTraverse && | ||||||
| 			tokens[index+1].PathElementType == PathKey { | 			tokens[index+1].PathElementType == PathKey { | ||||||
| 			postProcessedTokens = append(postProcessedTokens, &Token{PathElementType: Operation, OperationType: Traverse, Value: "."}) | 			postProcessedTokens = append(postProcessedTokens, &Token{PathElementType: Operation, OperationType: Pipe, Value: "PIPE"}) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -10,50 +10,53 @@ var tokeniserTests = []struct { | |||||||
| 	path           string | 	path           string | ||||||
| 	expectedTokens []interface{} | 	expectedTokens []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", ".", "(", "SELF", ")")}, | 	// {"\"len\"(.)", append(make([]interface{}, 0), "len", "TRAVERSE", "(", "SELF", ")")}, | ||||||
| 	// {"a OR (b OR c)", append(make([]interface{}, 0), "a", "OR", "(", "b", "OR", "c", ")")}, | 	// {".a OR (.b OR .c)", append(make([]interface{}, 0), "a", "OR", "(", "b", "OR", "c", ")")}, | ||||||
| 	// {"a OR (b OR c)", append(make([]interface{}, 0), "a", "OR", "(", "b", "OR", "c", ")")}, | 	// {"a OR (b OR c)", append(make([]interface{}, 0), "a", "OR", "(", "b", "OR", "c", ")")}, | ||||||
| 	// {"a .- (b OR c)", append(make([]interface{}, 0), "a", " .- ", "(", "b", "OR", "c", ")")}, | 	// {"a .- (b OR c)", append(make([]interface{}, 0), "a", " .- ", "(", "b", "OR", "c", ")")}, | ||||||
| 	// {"(animal==3)", append(make([]interface{}, 0), "(", "animal", "==", int64(3), ")")}, | 	// {"(animal==3)", append(make([]interface{}, 0), "(", "animal", "==", int64(3), ")")}, | ||||||
| 	// {"(animal==f3)", append(make([]interface{}, 0), "(", "animal", "==", "f3", ")")}, | 	// {"(animal==f3)", append(make([]interface{}, 0), "(", "animal", "==", "f3", ")")}, | ||||||
| 	// {"apples.BANANAS", append(make([]interface{}, 0), "apples", ".", "BANANAS")}, | 	// {"apples.BANANAS", append(make([]interface{}, 0), "apples", "TRAVERSE", "BANANAS")}, | ||||||
| 	// {"appl*.BANA*", append(make([]interface{}, 0), "appl*", ".", "BANA*")}, | 	// {"appl*.BANA*", append(make([]interface{}, 0), "appl*", "TRAVERSE", "BANA*")}, | ||||||
| 	// {"a.b.**", append(make([]interface{}, 0), "a", ".", "b", ".", "**")}, | 	// {"a.b.**", append(make([]interface{}, 0), "a", "TRAVERSE", "b", "TRAVERSE", "**")}, | ||||||
| 	// {"a.\"=\".frog", append(make([]interface{}, 0), "a", ".", "=", ".", "frog")}, | 	// {"a.\"=\".frog", append(make([]interface{}, 0), "a", "TRAVERSE", "=", "TRAVERSE", "frog")}, | ||||||
| 	// {"a.b.*", append(make([]interface{}, 0), "a", ".", "b", ".", "*")}, | 	// {"a.b.*", append(make([]interface{}, 0), "a", "TRAVERSE", "b", "TRAVERSE", "*")}, | ||||||
| 	// {"a.b.thin*", append(make([]interface{}, 0), "a", ".", "b", ".", "thin*")}, | 	// {"a.b.thin*", append(make([]interface{}, 0), "a", "TRAVERSE", "b", "TRAVERSE", "thin*")}, | ||||||
| 	// {"a.b[0]", append(make([]interface{}, 0), "a", ".", "b", ".", int64(0))}, | 	// {".a.b.[0]", append(make([]interface{}, 0), "a", "TRAVERSE", "b", "TRAVERSE", int64(0))}, | ||||||
| 	// {"a.b.[0]", append(make([]interface{}, 0), "a", ".", "b", ".", int64(0))}, | 	// {".a.b.[]", append(make([]interface{}, 0), "a", "TRAVERSE", "b", "TRAVERSE", "[]")}, | ||||||
| 	// {"a.b[*]", append(make([]interface{}, 0), "a", ".", "b", ".", "[*]")}, | 	// {".a.b.[+]", append(make([]interface{}, 0), "a", "TRAVERSE", "b", "TRAVERSE", "[+]")}, | ||||||
| 	// {"a.b.[*]", append(make([]interface{}, 0), "a", ".", "b", ".", "[*]")}, | 	// {".a.b.[-12]", append(make([]interface{}, 0), "a", "TRAVERSE", "b", "TRAVERSE", int64(-12))}, | ||||||
| 	// {"a.b[+]", append(make([]interface{}, 0), "a", ".", "b", ".", "[+]")}, | 	// {".a.b.0", append(make([]interface{}, 0), "a", "TRAVERSE", "b", "TRAVERSE", "0")}, | ||||||
| 	// {"a.b.[+]", append(make([]interface{}, 0), "a", ".", "b", ".", "[+]")}, | 	// {".a", append(make([]interface{}, 0), "a")}, | ||||||
| 	// {"a.b[-12]", append(make([]interface{}, 0), "a", ".", "b", ".", int64(-12))}, | 	// {".\"a.b\".c", append(make([]interface{}, 0), "a.b", "TRAVERSE", "c")}, | ||||||
| 	// {"a.b.0", append(make([]interface{}, 0), "a", ".", "b", ".", int64(0))}, | 	// {`.b."foo.bar"`, append(make([]interface{}, 0), "b", "TRAVERSE", "foo.bar")}, | ||||||
| 	// // {"a.b.-12", append(make([]interface{}, 0), "a", ".", "b", ".", int64(-12))}, | 	// {`f | . == *og | length`, append(make([]interface{}, 0), "f", "TRAVERSE", "SELF", "EQUALS", "*og", "TRAVERSE", "LENGTH")}, | ||||||
| 	// {"a", append(make([]interface{}, 0), "a")}, | 	// {`.a, .b`, append(make([]interface{}, 0), "a", "OR", "b")}, | ||||||
| 	// {"\"a.b\".c", append(make([]interface{}, 0), "a.b", ".", "c")}, | 	// {`[.a, .b]`, append(make([]interface{}, 0), "[", "a", "OR", "b", "]")}, | ||||||
| 	// {`b."foo.bar"`, append(make([]interface{}, 0), "b", ".", "foo.bar")}, | 	// {`."[a", ."b]"`, append(make([]interface{}, 0), "[a", "OR", "b]")}, | ||||||
| 	// {"animals(.==cat)", append(make([]interface{}, 0), "animals", ".", "(", "SELF", ".==", "cat", ")")}, | 	// {`.a[]`, append(make([]interface{}, 0), "a", "PIPE", "[]")}, | ||||||
| 	// {"animals.(.==cat)", append(make([]interface{}, 0), "animals", ".", "(", "SELF", ".==", "cat", ")")}, | 	// {`.[].a`, append(make([]interface{}, 0), "[]", "PIPE", "a")}, | ||||||
| 	// {"animals(. == cat)", append(make([]interface{}, 0), "animals", ".", "(", "SELF", ". == ", "cat", ")")}, | 	{`.a | (.[].b == "apple")`, append(make([]interface{}, 0), "a", "PIPE", "(", "[]", "PIPE", "b", "EQUALS", "apple", ")")}, | ||||||
| 	// {"animals(.==c*)", append(make([]interface{}, 0), "animals", ".", "(", "SELF", ".==", "c*", ")")}, | 
 | ||||||
| 	// {"animals(a.b==c*)", append(make([]interface{}, 0), "animals", ".", "(", "a", ".", "b", "==", "c*", ")")}, | 	// {".animals | .==cat", append(make([]interface{}, 0), "animals", "TRAVERSE", "SELF", "EQUALS", "cat")}, | ||||||
| 	// {"animals.(a.b==c*)", append(make([]interface{}, 0), "animals", ".", "(", "a", ".", "b", "==", "c*", ")")}, | 	// {".animals | (. == cat)", append(make([]interface{}, 0), "animals", "TRAVERSE", "(", "SELF", "EQUALS", "cat", ")")}, | ||||||
| 	// {"(a.b==c*).animals", append(make([]interface{}, 0), "(", "a", ".", "b", "==", "c*", ")", ".", "animals")}, | 	// {".animals | (.==c*)", append(make([]interface{}, 0), "animals", "TRAVERSE", "(", "SELF", "EQUALS", "c*", ")")}, | ||||||
| 	// {"(a.b==c*)animals", append(make([]interface{}, 0), "(", "a", ".", "b", "==", "c*", ")", ".", "animals")}, | 	// {"animals(a.b==c*)", append(make([]interface{}, 0), "animals", "TRAVERSE", "(", "a", "TRAVERSE", "b", "==", "c*", ")")}, | ||||||
| 	// {"[1].a.d", append(make([]interface{}, 0), int64(1), ".", "a", ".", "d")}, | 	// {"animals.(a.b==c*)", append(make([]interface{}, 0), "animals", "TRAVERSE", "(", "a", "TRAVERSE", "b", "==", "c*", ")")}, | ||||||
| 	// {"[1]a.d", append(make([]interface{}, 0), int64(1), ".", "a", ".", "d")}, | 	// {"(a.b==c*).animals", append(make([]interface{}, 0), "(", "a", "TRAVERSE", "b", "==", "c*", ")", "TRAVERSE", "animals")}, | ||||||
| 	// {"a[0]c", append(make([]interface{}, 0), "a", ".", int64(0), ".", "c")}, | 	// {"(a.b==c*)animals", append(make([]interface{}, 0), "(", "a", "TRAVERSE", "b", "==", "c*", ")", "TRAVERSE", "animals")}, | ||||||
| 	// {"a.[0].c", append(make([]interface{}, 0), "a", ".", int64(0), ".", "c")}, | 	// {"[1].a.d", append(make([]interface{}, 0), int64(1), "TRAVERSE", "a", "TRAVERSE", "d")}, | ||||||
|  | 	// {"[1]a.d", append(make([]interface{}, 0), int64(1), "TRAVERSE", "a", "TRAVERSE", "d")}, | ||||||
|  | 	// {"a[0]c", append(make([]interface{}, 0), "a", "TRAVERSE", int64(0), "TRAVERSE", "c")}, | ||||||
|  | 	// {"a.[0].c", append(make([]interface{}, 0), "a", "TRAVERSE", int64(0), "TRAVERSE", "c")}, | ||||||
| 	// {"[0]", append(make([]interface{}, 0), int64(0))}, | 	// {"[0]", append(make([]interface{}, 0), int64(0))}, | ||||||
| 	// {"0", append(make([]interface{}, 0), int64(0))}, | 	// {"0", append(make([]interface{}, 0), int64(0))}, | ||||||
| 	// {"a.b[+]c", append(make([]interface{}, 0), "a", ".", "b", ".", "[+]", ".", "c")}, | 	// {"a.b[+]c", append(make([]interface{}, 0), "a", "TRAVERSE", "b", "TRAVERSE", "[+]", "TRAVERSE", "c")}, | ||||||
| 	// {"a.cool(s.d.f == cool)", append(make([]interface{}, 0), "a", ".", "cool", ".", "(", "s", ".", "d", ".", "f", " == ", "cool", ")")}, | 	// {"a.cool(s.d.f == cool)", append(make([]interface{}, 0), "a", "TRAVERSE", "cool", "TRAVERSE", "(", "s", "TRAVERSE", "d", "TRAVERSE", "f", " == ", "cool", ")")}, | ||||||
| 	// {"a.cool.(s.d.f==cool OR t.b.h==frog).caterpillar", append(make([]interface{}, 0), "a", ".", "cool", ".", "(", "s", ".", "d", ".", "f", "==", "cool", "OR", "t", ".", "b", ".", "h", "==", "frog", ")", ".", "caterpillar")}, | 	// {"a.cool.(s.d.f==cool OR t.b.h==frog).caterpillar", append(make([]interface{}, 0), "a", "TRAVERSE", "cool", "TRAVERSE", "(", "s", "TRAVERSE", "d", "TRAVERSE", "f", "==", "cool", "OR", "t", "TRAVERSE", "b", "TRAVERSE", "h", "==", "frog", ")", "TRAVERSE", "caterpillar")}, | ||||||
| 	// {"a.cool(s.d.f==cool and t.b.h==frog)*", append(make([]interface{}, 0), "a", ".", "cool", ".", "(", "s", ".", "d", ".", "f", "==", "cool", "and", "t", ".", "b", ".", "h", "==", "frog", ")", ".", "*")}, | 	// {"a.cool(s.d.f==cool and t.b.h==frog)*", append(make([]interface{}, 0), "a", "TRAVERSE", "cool", "TRAVERSE", "(", "s", "TRAVERSE", "d", "TRAVERSE", "f", "==", "cool", "and", "t", "TRAVERSE", "b", "TRAVERSE", "h", "==", "frog", ")", "TRAVERSE", "*")}, | ||||||
| 	// {"a.cool(s.d.f==cool and t.b.h==frog).th*", append(make([]interface{}, 0), "a", ".", "cool", ".", "(", "s", ".", "d", ".", "f", "==", "cool", "and", "t", ".", "b", ".", "h", "==", "frog", ")", ".", "th*")}, | 	// {"a.cool(s.d.f==cool and t.b.h==frog).th*", append(make([]interface{}, 0), "a", "TRAVERSE", "cool", "TRAVERSE", "(", "s", "TRAVERSE", "d", "TRAVERSE", "f", "==", "cool", "and", "t", "TRAVERSE", "b", "TRAVERSE", "h", "==", "frog", ")", "TRAVERSE", "th*")}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var tokeniser = NewPathTokeniser() | var tokeniser = NewPathTokeniser() | ||||||
| @ -68,6 +71,6 @@ func TestTokeniser(t *testing.T) { | |||||||
| 		for _, token := range tokens { | 		for _, token := range tokens { | ||||||
| 			tokenValues = append(tokenValues, token.Value) | 			tokenValues = append(tokenValues, token.Value) | ||||||
| 		} | 		} | ||||||
| 		test.AssertResultComplex(t, tt.expectedTokens, tokenValues) | 		test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, tt.path) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -45,12 +45,10 @@ func (p *pathTreeCreator) CreatePathTree(postFixPath []*PathElement) (*PathTreeN | |||||||
| 
 | 
 | ||||||
| 	for _, pathElement := range postFixPath { | 	for _, pathElement := range postFixPath { | ||||||
| 		var newNode = PathTreeNode{PathElement: pathElement} | 		var newNode = PathTreeNode{PathElement: pathElement} | ||||||
| 		if pathElement.PathElementType == Operation { | 		log.Debugf("pathTree %v ", pathElement.toString()) | ||||||
|  | 		if pathElement.PathElementType == Operation && pathElement.OperationType.NumArgs > 0 { | ||||||
| 			numArgs := pathElement.OperationType.NumArgs | 			numArgs := pathElement.OperationType.NumArgs | ||||||
| 			if numArgs == 0 { | 			if numArgs == 1 { | ||||||
| 				remaining := stack[:len(stack)-1] |  | ||||||
| 				stack = remaining |  | ||||||
| 			} else if numArgs == 1 { |  | ||||||
| 				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 | ||||||
|  | |||||||
| @ -54,6 +54,14 @@ func AssertResultComplex(t *testing.T, expectedValue interface{}, actualValue in | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func AssertResultComplexWithContext(t *testing.T, expectedValue interface{}, actualValue interface{}, context interface{}) { | ||||||
|  | 	t.Helper() | ||||||
|  | 	if !reflect.DeepEqual(expectedValue, actualValue) { | ||||||
|  | 		t.Error(context) | ||||||
|  | 		t.Error("\nExpected <", expectedValue, ">\nbut got  <", actualValue, ">", fmt.Sprintf("%T", actualValue)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func AssertResultWithContext(t *testing.T, expectedValue interface{}, actualValue interface{}, context interface{}) { | func AssertResultWithContext(t *testing.T, expectedValue interface{}, actualValue interface{}, context interface{}) { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
| 	if expectedValue != actualValue { | 	if expectedValue != actualValue { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user