mirror of
https://github.com/mikefarah/yq.git
synced 2025-01-24 22:58:10 +00:00
wip
This commit is contained in:
parent
4bc98776a6
commit
73cf6224f2
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type dataTreeNavigator struct {
|
type dataTreeNavigator struct {
|
||||||
leafTraverser LeafTraverser
|
navigationPrefs NavigationPrefs
|
||||||
}
|
}
|
||||||
|
|
||||||
type NavigationPrefs struct {
|
type NavigationPrefs struct {
|
||||||
@ -20,27 +20,7 @@ type DataTreeNavigator interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator {
|
func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator {
|
||||||
leafTraverser := NewLeafTraverser(navigationPrefs)
|
return &dataTreeNavigator{navigationPrefs}
|
||||||
return &dataTreeNavigator{leafTraverser}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataTreeNavigator) traverse(matchMap *orderedmap.OrderedMap, pathNode *PathElement) (*orderedmap.OrderedMap, error) {
|
|
||||||
log.Debugf("-- Traversing")
|
|
||||||
var matchingNodeMap = orderedmap.NewOrderedMap()
|
|
||||||
var newNodes []*CandidateNode
|
|
||||||
var err error
|
|
||||||
|
|
||||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
|
||||||
newNodes, err = d.leafTraverser.Traverse(el.Value.(*CandidateNode), pathNode)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, n := range newNodes {
|
|
||||||
matchingNodeMap.Set(n.GetKey(), n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchingNodeMap, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error) {
|
func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error) {
|
||||||
@ -75,19 +55,10 @@ func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *orderedmap.OrderedMa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Debug(">>")
|
log.Debug(">>")
|
||||||
|
|
||||||
if pathNode.PathElement.PathElementType == SelfReference {
|
|
||||||
return matchingNodes, nil
|
|
||||||
} else if pathNode.PathElement.PathElementType == PathKey {
|
|
||||||
return d.traverse(matchingNodes, pathNode.PathElement)
|
|
||||||
} else if pathNode.PathElement.PathElementType == Value {
|
|
||||||
return nodeToMap(pathNode.PathElement.CandidateNode), nil
|
|
||||||
} else {
|
|
||||||
handler := pathNode.PathElement.OperationType.Handler
|
handler := pathNode.PathElement.OperationType.Handler
|
||||||
if handler != nil {
|
if handler != nil {
|
||||||
return handler(d, matchingNodes, pathNode)
|
return handler(d, matchingNodes, pathNode)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("Unknown operator %v", pathNode.PathElement.OperationType)
|
return nil, fmt.Errorf("Unknown operator %v", pathNode.PathElement.OperationType)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,6 @@ import (
|
|||||||
|
|
||||||
var log = logging.MustGetLogger("yq-treeops")
|
var log = logging.MustGetLogger("yq-treeops")
|
||||||
|
|
||||||
type PathElementType uint32
|
|
||||||
|
|
||||||
const (
|
|
||||||
PathKey PathElementType = 1 << iota
|
|
||||||
DocumentKey
|
|
||||||
Operation
|
|
||||||
SelfReference
|
|
||||||
OpenBracket
|
|
||||||
CloseBracket
|
|
||||||
OpenCollect
|
|
||||||
CloseCollect
|
|
||||||
Value // e.g. string, int
|
|
||||||
)
|
|
||||||
|
|
||||||
type OperationType struct {
|
type OperationType struct {
|
||||||
Type string
|
Type string
|
||||||
NumArgs uint // number of arguments to the op
|
NumArgs uint // number of arguments to the op
|
||||||
@ -32,8 +18,6 @@ type OperationType struct {
|
|||||||
Handler OperatorHandler
|
Handler OperatorHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
var None = &OperationType{Type: "NONE", NumArgs: 0, Precedence: 0}
|
|
||||||
|
|
||||||
var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: OrOperator}
|
var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: OrOperator}
|
||||||
var And = &OperationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: AndOperator}
|
var And = &OperationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: AndOperator}
|
||||||
|
|
||||||
@ -49,6 +33,12 @@ var Pipe = &OperationType{Type: "PIPE", NumArgs: 2, Precedence: 45, Handler: Pip
|
|||||||
|
|
||||||
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}
|
var Collect = &OperationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: CollectOperator}
|
||||||
|
var TraversePath = &OperationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
|
||||||
|
|
||||||
|
var DocumentFilter = &OperationType{Type: "DOCUMENT_FILTER", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
|
||||||
|
var SelfReference = &OperationType{Type: "SELF", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
|
||||||
|
var ValueOp = &OperationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
|
||||||
|
|
||||||
var RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator}
|
var RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator}
|
||||||
|
|
||||||
// not sure yet
|
// not sure yet
|
||||||
@ -63,7 +53,6 @@ var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 2, Precedence: 40, Han
|
|||||||
// filters matches if they have the existing path
|
// filters matches if they have the existing path
|
||||||
|
|
||||||
type PathElement struct {
|
type PathElement struct {
|
||||||
PathElementType PathElementType
|
|
||||||
OperationType *OperationType
|
OperationType *OperationType
|
||||||
Value interface{}
|
Value interface{}
|
||||||
StringValue string
|
StringValue string
|
||||||
@ -72,22 +61,17 @@ type PathElement struct {
|
|||||||
|
|
||||||
// debugging purposes only
|
// debugging purposes only
|
||||||
func (p *PathElement) toString() string {
|
func (p *PathElement) toString() string {
|
||||||
var result string = ``
|
if p.OperationType == TraversePath {
|
||||||
switch p.PathElementType {
|
return fmt.Sprintf("%v", p.Value)
|
||||||
case PathKey:
|
} else if p.OperationType == DocumentFilter {
|
||||||
result = result + fmt.Sprintf("%v", p.Value)
|
return fmt.Sprintf("d%v", p.Value)
|
||||||
case DocumentKey:
|
} else if p.OperationType == SelfReference {
|
||||||
result = result + fmt.Sprintf("D%v", p.Value)
|
return "SELF"
|
||||||
case SelfReference:
|
} else if p.OperationType == ValueOp {
|
||||||
result = result + fmt.Sprintf("SELF")
|
return fmt.Sprintf("%v (%T)", p.Value, p.Value)
|
||||||
case Operation:
|
} else {
|
||||||
result = result + fmt.Sprintf("%v", p.OperationType.Type)
|
return fmt.Sprintf("%v", p.OperationType.Type)
|
||||||
case Value:
|
|
||||||
result = result + fmt.Sprintf("%v (%T)", p.Value, p.Value)
|
|
||||||
default:
|
|
||||||
result = result + "I HAVENT GOT A STRATEGY"
|
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type YqTreeLib interface {
|
type YqTreeLib interface {
|
||||||
|
@ -61,12 +61,12 @@ func multiply(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*Ca
|
|||||||
|
|
||||||
func createTraversalTree(path []interface{}) *PathTreeNode {
|
func createTraversalTree(path []interface{}) *PathTreeNode {
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
return &PathTreeNode{PathElement: &PathElement{PathElementType: SelfReference}}
|
return &PathTreeNode{PathElement: &PathElement{OperationType: SelfReference}}
|
||||||
} else if len(path) == 1 {
|
} else if len(path) == 1 {
|
||||||
return &PathTreeNode{PathElement: &PathElement{PathElementType: PathKey, Value: path[0], StringValue: fmt.Sprintf("%v", path[0])}}
|
return &PathTreeNode{PathElement: &PathElement{OperationType: TraversePath, Value: path[0], StringValue: fmt.Sprintf("%v", path[0])}}
|
||||||
}
|
}
|
||||||
return &PathTreeNode{
|
return &PathTreeNode{
|
||||||
PathElement: &PathElement{PathElementType: Operation, OperationType: Pipe},
|
PathElement: &PathElement{OperationType: Pipe},
|
||||||
Lhs: createTraversalTree(path[0:1]),
|
Lhs: createTraversalTree(path[0:1]),
|
||||||
Rhs: createTraversalTree(path[1:])}
|
Rhs: createTraversalTree(path[1:])}
|
||||||
|
|
||||||
@ -77,11 +77,11 @@ func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *Candid
|
|||||||
|
|
||||||
lhsPath := rhs.Path[pathIndexToStartFrom:]
|
lhsPath := rhs.Path[pathIndexToStartFrom:]
|
||||||
|
|
||||||
assignmentOp := &PathElement{PathElementType: Operation, OperationType: AssignAttributes}
|
assignmentOp := &PathElement{OperationType: AssignAttributes}
|
||||||
if rhs.Node.Kind == yaml.ScalarNode {
|
if rhs.Node.Kind == yaml.ScalarNode {
|
||||||
assignmentOp.OperationType = Assign
|
assignmentOp.OperationType = Assign
|
||||||
}
|
}
|
||||||
rhsOp := &PathElement{PathElementType: Value, CandidateNode: rhs}
|
rhsOp := &PathElement{OperationType: ValueOp, CandidateNode: rhs}
|
||||||
|
|
||||||
assignmentOpNode := &PathTreeNode{PathElement: assignmentOp, Lhs: createTraversalTree(lhsPath), Rhs: &PathTreeNode{PathElement: rhsOp}}
|
assignmentOpNode := &PathTreeNode{PathElement: assignmentOp, Lhs: createTraversalTree(lhsPath), Rhs: &PathTreeNode{PathElement: rhsOp}}
|
||||||
|
|
||||||
|
@ -16,11 +16,14 @@ func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *orderedmap.Ordered
|
|||||||
}
|
}
|
||||||
|
|
||||||
func recursiveDecent(d *dataTreeNavigator, results *orderedmap.OrderedMap, matchMap *orderedmap.OrderedMap) error {
|
func recursiveDecent(d *dataTreeNavigator, results *orderedmap.OrderedMap, matchMap *orderedmap.OrderedMap) error {
|
||||||
|
splatPathElement := &PathElement{OperationType: TraversePath, Value: "[]"}
|
||||||
|
splatTreeNode := &PathTreeNode{PathElement: splatPathElement}
|
||||||
|
|
||||||
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)
|
||||||
results.Set(candidate.GetKey(), candidate)
|
results.Set(candidate.GetKey(), candidate)
|
||||||
|
|
||||||
children, err := d.traverse(nodeToMap(candidate), &PathElement{PathElementType: PathKey, Value: "[]"})
|
children, err := TraversePathOperator(d, nodeToMap(candidate), splatTreeNode)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -3,26 +3,86 @@ package treeops
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/elliotchance/orderedmap"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type traverser struct {
|
func TraversePathOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
|
||||||
prefs NavigationPrefs
|
log.Debugf("-- Traversing")
|
||||||
|
var matchingNodeMap = orderedmap.NewOrderedMap()
|
||||||
|
var newNodes []*CandidateNode
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for el := matchMap.Front(); el != nil; el = el.Next() {
|
||||||
|
newNodes, err = traverse(d, el.Value.(*CandidateNode), pathNode.PathElement)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, n := range newNodes {
|
||||||
|
matchingNodeMap.Set(n.GetKey(), n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingNodeMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type LeafTraverser interface {
|
func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) {
|
||||||
Traverse(matchingNode *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error)
|
log.Debug("Traversing %v", NodeToString(matchingNode))
|
||||||
|
value := matchingNode.Node
|
||||||
|
|
||||||
|
if value.Kind == 0 {
|
||||||
|
log.Debugf("Guessing kind")
|
||||||
|
// we must ahve added this automatically, lets guess what it should be now
|
||||||
|
switch pathNode.Value.(type) {
|
||||||
|
case int, int64:
|
||||||
|
log.Debugf("probably an array")
|
||||||
|
value.Kind = yaml.SequenceNode
|
||||||
|
default:
|
||||||
|
log.Debugf("probabel a map")
|
||||||
|
value.Kind = yaml.MappingNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value.Kind {
|
||||||
|
case yaml.MappingNode:
|
||||||
|
log.Debug("its a map with %v entries", len(value.Content)/2)
|
||||||
|
return traverseMap(matchingNode, pathNode)
|
||||||
|
|
||||||
|
case yaml.SequenceNode:
|
||||||
|
log.Debug("its a sequence of %v things!", len(value.Content))
|
||||||
|
return traverseArray(matchingNode, pathNode)
|
||||||
|
// default:
|
||||||
|
|
||||||
|
// if head == "+" {
|
||||||
|
// return n.appendArray(value, head, tail, pathStack)
|
||||||
|
// } else if len(value.Content) == 0 && head == "**" {
|
||||||
|
// return n.navigationStrategy.Visit(nodeContext)
|
||||||
|
// }
|
||||||
|
// return n.splatArray(value, head, tail, pathStack)
|
||||||
|
// }
|
||||||
|
// case yaml.AliasNode:
|
||||||
|
// log.Debug("its an alias!")
|
||||||
|
// DebugNode(value.Alias)
|
||||||
|
// if n.navigationStrategy.FollowAlias(nodeContext) {
|
||||||
|
// log.Debug("following the alias")
|
||||||
|
// return n.recurse(value.Alias, head, tail, pathStack)
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
case yaml.DocumentNode:
|
||||||
|
log.Debug("digging into doc node")
|
||||||
|
return traverse(d, &CandidateNode{
|
||||||
|
Node: matchingNode.Node.Content[0],
|
||||||
|
Document: matchingNode.Document}, pathNode)
|
||||||
|
default:
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLeafTraverser(navigationPrefs NavigationPrefs) LeafTraverser {
|
func keyMatches(key *yaml.Node, pathNode *PathElement) bool {
|
||||||
return &traverser{navigationPrefs}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *traverser) keyMatches(key *yaml.Node, pathNode *PathElement) bool {
|
|
||||||
return pathNode.Value == "[]" || Match(key.Value, pathNode.StringValue)
|
return pathNode.Value == "[]" || Match(key.Value, pathNode.StringValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *traverser) traverseMap(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) {
|
func traverseMap(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) {
|
||||||
// value.Content is a concatenated array of key, value,
|
// value.Content is a concatenated array of key, value,
|
||||||
// so keys are in the even indexes, values in odd.
|
// so keys are in the even indexes, values in odd.
|
||||||
// merge aliases are defined first, but we only want to traverse them
|
// merge aliases are defined first, but we only want to traverse them
|
||||||
@ -39,7 +99,7 @@ func (t *traverser) traverseMap(candidate *CandidateNode, pathNode *PathElement)
|
|||||||
value := contents[index+1]
|
value := contents[index+1]
|
||||||
|
|
||||||
log.Debug("checking %v (%v)", key.Value, key.Tag)
|
log.Debug("checking %v (%v)", key.Value, key.Tag)
|
||||||
if t.keyMatches(key, pathNode) {
|
if keyMatches(key, pathNode) {
|
||||||
log.Debug("MATCHED")
|
log.Debug("MATCHED")
|
||||||
newMatches = append(newMatches, &CandidateNode{
|
newMatches = append(newMatches, &CandidateNode{
|
||||||
Node: value,
|
Node: value,
|
||||||
@ -63,7 +123,7 @@ func (t *traverser) traverseMap(candidate *CandidateNode, pathNode *PathElement)
|
|||||||
return newMatches, nil
|
return newMatches, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *traverser) traverseArray(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) {
|
func traverseArray(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) {
|
||||||
log.Debug("pathNode Value %v", pathNode.Value)
|
log.Debug("pathNode Value %v", pathNode.Value)
|
||||||
if pathNode.Value == "[]" {
|
if pathNode.Value == "[]" {
|
||||||
|
|
||||||
@ -104,55 +164,3 @@ func (t *traverser) traverseArray(candidate *CandidateNode, pathNode *PathElemen
|
|||||||
}}, nil
|
}}, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *traverser) Traverse(matchingNode *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) {
|
|
||||||
log.Debug("Traversing %v", NodeToString(matchingNode))
|
|
||||||
value := matchingNode.Node
|
|
||||||
|
|
||||||
if value.Kind == 0 {
|
|
||||||
log.Debugf("Guessing kind")
|
|
||||||
// we must ahve added this automatically, lets guess what it should be now
|
|
||||||
switch pathNode.Value.(type) {
|
|
||||||
case int, int64:
|
|
||||||
log.Debugf("probably an array")
|
|
||||||
value.Kind = yaml.SequenceNode
|
|
||||||
default:
|
|
||||||
log.Debugf("probabel a map")
|
|
||||||
value.Kind = yaml.MappingNode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch value.Kind {
|
|
||||||
case yaml.MappingNode:
|
|
||||||
log.Debug("its a map with %v entries", len(value.Content)/2)
|
|
||||||
return t.traverseMap(matchingNode, pathNode)
|
|
||||||
|
|
||||||
case yaml.SequenceNode:
|
|
||||||
log.Debug("its a sequence of %v things!", len(value.Content))
|
|
||||||
return t.traverseArray(matchingNode, pathNode)
|
|
||||||
// default:
|
|
||||||
|
|
||||||
// if head == "+" {
|
|
||||||
// return n.appendArray(value, head, tail, pathStack)
|
|
||||||
// } else if len(value.Content) == 0 && head == "**" {
|
|
||||||
// return n.navigationStrategy.Visit(nodeContext)
|
|
||||||
// }
|
|
||||||
// return n.splatArray(value, head, tail, pathStack)
|
|
||||||
// }
|
|
||||||
// case yaml.AliasNode:
|
|
||||||
// log.Debug("its an alias!")
|
|
||||||
// DebugNode(value.Alias)
|
|
||||||
// if n.navigationStrategy.FollowAlias(nodeContext) {
|
|
||||||
// log.Debug("following the alias")
|
|
||||||
// return n.recurse(value.Alias, head, tail, pathStack)
|
|
||||||
// }
|
|
||||||
// return nil
|
|
||||||
case yaml.DocumentNode:
|
|
||||||
log.Debug("digging into doc node")
|
|
||||||
return t.Traverse(&CandidateNode{
|
|
||||||
Node: matchingNode.Node.Content[0],
|
|
||||||
Document: matchingNode.Document}, pathNode)
|
|
||||||
default:
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
21
pkg/yqlib/treeops/operator_traverse_path_test.go
Normal file
21
pkg/yqlib/treeops/operator_traverse_path_test.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package treeops
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var traversePathOperatorScenarios = []expressionScenario{
|
||||||
|
{
|
||||||
|
document: `{a: {b: apple}}`,
|
||||||
|
expression: `.a`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a], (!!map)::{b: apple}\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTraversePathOperatorScenarios(t *testing.T) {
|
||||||
|
for _, tt := range traversePathOperatorScenarios {
|
||||||
|
testScenario(t, &tt)
|
||||||
|
}
|
||||||
|
}
|
@ -81,6 +81,7 @@ var pathTests = []struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tokeniser = NewPathTokeniser()
|
var tokeniser = NewPathTokeniser()
|
||||||
|
var postFixer = NewPathPostFixer()
|
||||||
|
|
||||||
func TestPathParsing(t *testing.T) {
|
func TestPathParsing(t *testing.T) {
|
||||||
for _, tt := range pathTests {
|
for _, tt := range pathTests {
|
||||||
|
@ -18,32 +18,31 @@ func NewPathPostFixer() PathPostFixer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func popOpToResult(opStack []*Token, result []*PathElement) ([]*Token, []*PathElement) {
|
func popOpToResult(opStack []*Token, result []*PathElement) ([]*Token, []*PathElement) {
|
||||||
var operatorToPushToPostFix *Token
|
var newOp *Token
|
||||||
opStack, operatorToPushToPostFix = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
|
opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
|
||||||
var pathElement = PathElement{PathElementType: Operation, OperationType: operatorToPushToPostFix.OperationType}
|
var pathElement = PathElement{OperationType: newOp.OperationType, Value: newOp.Value, StringValue: newOp.StringValue}
|
||||||
|
|
||||||
|
if newOp.OperationType == ValueOp {
|
||||||
|
var candidateNode = BuildCandidateNodeFrom(newOp)
|
||||||
|
pathElement.CandidateNode = candidateNode
|
||||||
|
}
|
||||||
|
|
||||||
return opStack, append(result, &pathElement)
|
return opStack, append(result, &pathElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
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, Value: "("}}
|
var opStack = []*Token{&Token{TokenType: OpenBracket, Value: "("}}
|
||||||
var tokens = append(infixTokens, &Token{PathElementType: CloseBracket, OperationType: None, Value: ")"})
|
var tokens = append(infixTokens, &Token{TokenType: CloseBracket, Value: ")"})
|
||||||
|
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
log.Debugf("postfix processing token %v", token.Value)
|
log.Debugf("postfix processing token %v", token.Value)
|
||||||
switch token.PathElementType {
|
switch token.TokenType {
|
||||||
case Value:
|
|
||||||
var candidateNode = BuildCandidateNodeFrom(token)
|
|
||||||
var pathElement = PathElement{PathElementType: token.PathElementType, Value: token.Value, StringValue: token.StringValue, CandidateNode: candidateNode}
|
|
||||||
result = append(result, &pathElement)
|
|
||||||
case PathKey, SelfReference, DocumentKey:
|
|
||||||
var pathElement = PathElement{PathElementType: token.PathElementType, Value: token.Value, StringValue: token.StringValue}
|
|
||||||
result = append(result, &pathElement)
|
|
||||||
case OpenBracket, OpenCollect:
|
case OpenBracket, OpenCollect:
|
||||||
opStack = append(opStack, token)
|
opStack = append(opStack, token)
|
||||||
case CloseCollect:
|
case CloseCollect:
|
||||||
for len(opStack) > 0 && opStack[len(opStack)-1].PathElementType != OpenCollect {
|
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != OpenCollect {
|
||||||
opStack, result = popOpToResult(opStack, result)
|
opStack, result = popOpToResult(opStack, result)
|
||||||
}
|
}
|
||||||
if len(opStack) == 0 {
|
if len(opStack) == 0 {
|
||||||
@ -52,10 +51,10 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement,
|
|||||||
// 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{TokenType: Operation, OperationType: Pipe})
|
||||||
opStack = append(opStack, &Token{PathElementType: Operation, OperationType: Collect})
|
opStack = append(opStack, &Token{TokenType: 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].TokenType != OpenBracket {
|
||||||
opStack, result = popOpToResult(opStack, result)
|
opStack, result = popOpToResult(opStack, result)
|
||||||
}
|
}
|
||||||
if len(opStack) == 0 {
|
if len(opStack) == 0 {
|
||||||
|
@ -1,355 +0,0 @@
|
|||||||
package treeops
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/mikefarah/yq/v3/test"
|
|
||||||
)
|
|
||||||
|
|
||||||
// var tokeniser = NewPathTokeniser()
|
|
||||||
var postFixer = NewPathPostFixer()
|
|
||||||
|
|
||||||
func testExpression(expression string) (string, error) {
|
|
||||||
tokens, err := tokeniser.Tokenise(expression)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
results, errorP := postFixer.ConvertToPostfix(tokens)
|
|
||||||
if errorP != nil {
|
|
||||||
return "", errorP
|
|
||||||
}
|
|
||||||
formatted := ""
|
|
||||||
for _, path := range results {
|
|
||||||
formatted = formatted + path.toString() + ", "
|
|
||||||
}
|
|
||||||
return formatted, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestPostFixTraverseBar(t *testing.T) {
|
|
||||||
var infix = ".animals | [.]"
|
|
||||||
var expectedOutput = `PathKey - animals
|
|
||||||
--------
|
|
||||||
SELF
|
|
||||||
--------
|
|
||||||
Operation - COLLECT
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixPipeEquals(t *testing.T) {
|
|
||||||
var infix = `.animals | (. == "cat") `
|
|
||||||
var expectedOutput = `PathKey - animals
|
|
||||||
--------
|
|
||||||
SELF
|
|
||||||
--------
|
|
||||||
Value - cat (string)
|
|
||||||
--------
|
|
||||||
Operation - EQUALS
|
|
||||||
--------
|
|
||||||
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
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixLength(t *testing.T) {
|
|
||||||
var infix = ".a | length"
|
|
||||||
var expectedOutput = `PathKey - a
|
|
||||||
--------
|
|
||||||
Operation - LENGTH
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixSimpleExample(t *testing.T) {
|
|
||||||
var infix = ".a"
|
|
||||||
var expectedOutput = `PathKey - a
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixSimplePathExample(t *testing.T) {
|
|
||||||
var infix = ".apples.bananas*.cat"
|
|
||||||
var expectedOutput = `PathKey - apples
|
|
||||||
--------
|
|
||||||
PathKey - bananas*
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
PathKey - cat
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixSimpleAssign(t *testing.T) {
|
|
||||||
var infix = ".a.b |= \"frog\""
|
|
||||||
var expectedOutput = `PathKey - a
|
|
||||||
--------
|
|
||||||
PathKey - b
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
Value - frog (string)
|
|
||||||
--------
|
|
||||||
Operation - ASSIGN
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixSimplePathNumbersExample(t *testing.T) {
|
|
||||||
var infix = ".apples[0].cat"
|
|
||||||
var expectedOutput = `PathKey - apples
|
|
||||||
--------
|
|
||||||
PathKey - 0
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
PathKey - cat
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixSimplePathSplatArrayExample(t *testing.T) {
|
|
||||||
var infix = ".apples[].cat"
|
|
||||||
var expectedOutput = `PathKey - apples
|
|
||||||
--------
|
|
||||||
PathKey - []
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
PathKey - cat
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixOrExample(t *testing.T) {
|
|
||||||
var infix = ".a, .b"
|
|
||||||
var expectedOutput = `PathKey - a
|
|
||||||
--------
|
|
||||||
PathKey - b
|
|
||||||
--------
|
|
||||||
Operation - OR
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixEqualsNumberExample(t *testing.T) {
|
|
||||||
var infix = ".animal == 3"
|
|
||||||
var expectedOutput = `PathKey - animal
|
|
||||||
--------
|
|
||||||
Value - 3 (int64)
|
|
||||||
--------
|
|
||||||
Operation - EQUALS
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixOrWithEqualsExample(t *testing.T) {
|
|
||||||
var infix = ".a==\"thing\", .b==.thongs"
|
|
||||||
var expectedOutput = `PathKey - a
|
|
||||||
--------
|
|
||||||
Value - thing (string)
|
|
||||||
--------
|
|
||||||
Operation - EQUALS
|
|
||||||
--------
|
|
||||||
PathKey - b
|
|
||||||
--------
|
|
||||||
PathKey - thongs
|
|
||||||
--------
|
|
||||||
Operation - EQUALS
|
|
||||||
--------
|
|
||||||
Operation - OR
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostFixOrWithEqualsPathExample(t *testing.T) {
|
|
||||||
var infix = ".apples.monkeys==\"thing\", .bogs.bobos==true"
|
|
||||||
var expectedOutput = `PathKey - apples
|
|
||||||
--------
|
|
||||||
PathKey - monkeys
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
Value - thing (string)
|
|
||||||
--------
|
|
||||||
Operation - EQUALS
|
|
||||||
--------
|
|
||||||
PathKey - bogs
|
|
||||||
--------
|
|
||||||
PathKey - bobos
|
|
||||||
--------
|
|
||||||
Operation - PIPE
|
|
||||||
--------
|
|
||||||
Value - true (bool)
|
|
||||||
--------
|
|
||||||
Operation - EQUALS
|
|
||||||
--------
|
|
||||||
Operation - OR
|
|
||||||
--------
|
|
||||||
`
|
|
||||||
|
|
||||||
actual, err := testExpression(infix)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
test.AssertResultComplex(t, expectedOutput, actual)
|
|
||||||
}
|
|
@ -11,12 +11,21 @@ func skip(*lex.Scanner, *machines.Match) (interface{}, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TokenType uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
Operation = 1 << iota
|
||||||
|
OpenBracket
|
||||||
|
CloseBracket
|
||||||
|
OpenCollect
|
||||||
|
CloseCollect
|
||||||
|
)
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
PathElementType PathElementType
|
TokenType TokenType
|
||||||
OperationType *OperationType
|
OperationType *OperationType
|
||||||
Value interface{}
|
Value interface{}
|
||||||
StringValue string
|
StringValue string
|
||||||
PrefixSelf bool
|
|
||||||
|
|
||||||
CheckForPostTraverse bool // e.g. [1]cat should really be [1].cat
|
CheckForPostTraverse bool // e.g. [1]cat should really be [1].cat
|
||||||
}
|
}
|
||||||
@ -28,7 +37,13 @@ func pathToken(wrapped bool) lex.Action {
|
|||||||
if wrapped {
|
if wrapped {
|
||||||
value = unwrap(value)
|
value = unwrap(value)
|
||||||
}
|
}
|
||||||
return &Token{PathElementType: PathKey, OperationType: None, Value: value, StringValue: value, CheckForPostTraverse: true}, nil
|
return &Token{TokenType: Operation, OperationType: TraversePath, Value: value, StringValue: value, CheckForPostTraverse: true}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func literalPathToken(value string) lex.Action {
|
||||||
|
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
|
||||||
|
return &Token{TokenType: Operation, OperationType: TraversePath, Value: value, StringValue: value, CheckForPostTraverse: true}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,20 +55,20 @@ func documentToken() lex.Action {
|
|||||||
if errParsingInt != nil {
|
if errParsingInt != nil {
|
||||||
return nil, errParsingInt
|
return nil, errParsingInt
|
||||||
}
|
}
|
||||||
return &Token{PathElementType: DocumentKey, OperationType: None, Value: number, StringValue: numberString, CheckForPostTraverse: true}, nil
|
return &Token{TokenType: Operation, OperationType: DocumentFilter, Value: number, StringValue: numberString, CheckForPostTraverse: true}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func opToken(op *OperationType) 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}, nil
|
return &Token{TokenType: Operation, OperationType: op, Value: op.Type, StringValue: value}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func literalToken(pType PathElementType, literal string, checkForPost bool) lex.Action {
|
func literalToken(pType TokenType, 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, CheckForPostTraverse: checkForPost}, nil
|
return &Token{TokenType: Operation, OperationType: ValueOp, Value: literal, StringValue: literal, CheckForPostTraverse: checkForPost}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +88,7 @@ func arrayIndextoken(precedingDot bool) lex.Action {
|
|||||||
if errParsingInt != nil {
|
if errParsingInt != nil {
|
||||||
return nil, errParsingInt
|
return nil, errParsingInt
|
||||||
}
|
}
|
||||||
return &Token{PathElementType: PathKey, OperationType: None, Value: number, StringValue: numberString, CheckForPostTraverse: true}, nil
|
return &Token{TokenType: Operation, OperationType: TraversePath, Value: number, StringValue: numberString, CheckForPostTraverse: true}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +99,7 @@ func numberValue() lex.Action {
|
|||||||
if errParsingInt != nil {
|
if errParsingInt != nil {
|
||||||
return nil, errParsingInt
|
return nil, errParsingInt
|
||||||
}
|
}
|
||||||
return &Token{PathElementType: Value, OperationType: None, Value: number, StringValue: numberString}, nil
|
return &Token{TokenType: Operation, OperationType: ValueOp, Value: number, StringValue: numberString}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,13 +110,13 @@ func floatValue() lex.Action {
|
|||||||
if errParsingInt != nil {
|
if errParsingInt != nil {
|
||||||
return nil, errParsingInt
|
return nil, errParsingInt
|
||||||
}
|
}
|
||||||
return &Token{PathElementType: Value, OperationType: None, Value: number, StringValue: numberString}, nil
|
return &Token{TokenType: Operation, OperationType: ValueOp, Value: number, StringValue: numberString}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func booleanValue(val bool) lex.Action {
|
func booleanValue(val 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: Value, OperationType: None, Value: val, StringValue: string(m.Bytes)}, nil
|
return &Token{TokenType: Operation, OperationType: ValueOp, Value: val, StringValue: string(m.Bytes)}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,13 +126,13 @@ func stringValue(wrapped bool) lex.Action {
|
|||||||
if wrapped {
|
if wrapped {
|
||||||
value = unwrap(value)
|
value = unwrap(value)
|
||||||
}
|
}
|
||||||
return &Token{PathElementType: Value, OperationType: None, Value: value, StringValue: value}, nil
|
return &Token{TokenType: Operation, OperationType: ValueOp, Value: value, StringValue: value}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func selfToken() lex.Action {
|
func selfToken() 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: SelfReference, OperationType: None, Value: "SELF", StringValue: "SELF"}, nil
|
return &Token{TokenType: Operation, OperationType: SelfReference}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,7 +142,7 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`\(`), literalToken(OpenBracket, "(", false))
|
lexer.Add([]byte(`\(`), literalToken(OpenBracket, "(", false))
|
||||||
lexer.Add([]byte(`\)`), literalToken(CloseBracket, ")", true))
|
lexer.Add([]byte(`\)`), literalToken(CloseBracket, ")", true))
|
||||||
|
|
||||||
lexer.Add([]byte(`\.?\[\]`), literalToken(PathKey, "[]", true))
|
lexer.Add([]byte(`\.?\[\]`), literalPathToken("[]"))
|
||||||
lexer.Add([]byte(`\.\.`), opToken(RecursiveDescent))
|
lexer.Add([]byte(`\.\.`), opToken(RecursiveDescent))
|
||||||
|
|
||||||
lexer.Add([]byte(`,`), opToken(Union))
|
lexer.Add([]byte(`,`), opToken(Union))
|
||||||
@ -218,8 +233,8 @@ func (p *pathTokeniser) Tokenise(path string) ([]*Token, error) {
|
|||||||
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].OperationType == TraversePath {
|
||||||
postProcessedTokens = append(postProcessedTokens, &Token{PathElementType: Operation, OperationType: Pipe, Value: "PIPE"})
|
postProcessedTokens = append(postProcessedTokens, &Token{TokenType: Operation, OperationType: Pipe, Value: "PIPE"})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ 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}
|
||||||
log.Debugf("pathTree %v ", pathElement.toString())
|
log.Debugf("pathTree %v ", pathElement.toString())
|
||||||
if pathElement.PathElementType == Operation && pathElement.OperationType.NumArgs > 0 {
|
if pathElement.OperationType.NumArgs > 0 {
|
||||||
numArgs := pathElement.OperationType.NumArgs
|
numArgs := pathElement.OperationType.NumArgs
|
||||||
if numArgs == 1 {
|
if numArgs == 1 {
|
||||||
remaining, rhs := stack[:len(stack)-1], stack[len(stack)-1]
|
remaining, rhs := stack[:len(stack)-1], stack[len(stack)-1]
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
- snapcraft
|
- snapcraft
|
||||||
- will auto create a candidate, test it works then promote
|
- will auto create a candidate, test it works then promote
|
||||||
- see https://build.snapcraft.io/user/mikefarah/yq
|
|
||||||
|
|
||||||
sudo snap remove yq
|
sudo snap remove yq
|
||||||
sudo snap install --edge yq
|
sudo snap install --edge yq
|
||||||
|
Loading…
Reference in New Issue
Block a user