diff --git a/pkg/yqlib/treeops/data_tree_navigator.go b/pkg/yqlib/treeops/data_tree_navigator.go index 5fb47369..1a33c1e8 100644 --- a/pkg/yqlib/treeops/data_tree_navigator.go +++ b/pkg/yqlib/treeops/data_tree_navigator.go @@ -7,8 +7,7 @@ import ( ) type dataTreeNavigator struct { - leafTraverser LeafTraverser - operatorHandlers map[OperationType]OperatorHandler + leafTraverser LeafTraverser } type NavigationPrefs struct { @@ -21,16 +20,7 @@ type DataTreeNavigator interface { func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator { leafTraverser := NewLeafTraverser(navigationPrefs) - operatorHandlers := make(map[OperationType]OperatorHandler) - - operatorHandlers[Traverse] = TraverseOperator - operatorHandlers[Equals] = EqualsOperator - operatorHandlers[Or] = UnionOperator - operatorHandlers[And] = IntersectionOperator - operatorHandlers[Assign] = AssignOperator - operatorHandlers[DeleteChild] = DeleteChildOperator - - return &dataTreeNavigator{leafTraverser, operatorHandlers} + return &dataTreeNavigator{leafTraverser} } func (d *dataTreeNavigator) traverse(matchMap *orderedmap.OrderedMap, pathNode *PathElement) (*orderedmap.OrderedMap, error) { @@ -79,7 +69,7 @@ func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *orderedmap.OrderedMa } else if pathNode.PathElement.PathElementType == PathKey || pathNode.PathElement.PathElementType == ArrayIndex { return d.traverse(matchingNodes, pathNode.PathElement) } else { - handler := d.operatorHandlers[pathNode.PathElement.OperationType] + handler := pathNode.PathElement.OperationType.Handler if handler != nil { return handler(d, matchingNodes, pathNode) } diff --git a/pkg/yqlib/treeops/data_tree_navigator_test.go b/pkg/yqlib/treeops/data_tree_navigator_test.go index 0753eae5..ddead453 100644 --- a/pkg/yqlib/treeops/data_tree_navigator_test.go +++ b/pkg/yqlib/treeops/data_tree_navigator_test.go @@ -220,7 +220,7 @@ func TestDataTreeNavigatorDeleteViaSelf(t *testing.T) { - sdfsd - apple`) - path, errPath := treeCreator.ParsePath("(. .- .)") + path, errPath := treeCreator.ParsePath(". .- (. == apple)") if errPath != nil { t.Error(errPath) } @@ -378,7 +378,7 @@ func TestDataTreeNavigatorSimpleAssignByFind(t *testing.T) { nodes := readDoc(t, `a: b: apple`) - path, errPath := treeCreator.ParsePath("(b == apple) := frog)") + path, errPath := treeCreator.ParsePath("a(. == apple) := frog") if errPath != nil { t.Error(errPath) } diff --git a/pkg/yqlib/treeops/lib.go b/pkg/yqlib/treeops/lib.go index 2f068c47..663871bc 100644 --- a/pkg/yqlib/treeops/lib.go +++ b/pkg/yqlib/treeops/lib.go @@ -21,6 +21,57 @@ func (n *CandidateNode) getKey() string { return fmt.Sprintf("%v - %v", n.Document, n.Path) } +type PathElementType uint32 + +const ( + PathKey PathElementType = 1 << iota + ArrayIndex + Operation + SelfReference + OpenBracket + CloseBracket +) + +type OperationType struct { + Type string + NumArgs uint // number of arguments to the op + Precedence uint + Handler OperatorHandler +} + +var None = &OperationType{Type: "NONE", NumArgs: 0, Precedence: 0} +var Traverse = &OperationType{Type: "TRAVERSE", NumArgs: 2, Precedence: 40, Handler: TraverseOperator} +var Or = &OperationType{Type: "OR", NumArgs: 2, Precedence: 10, Handler: UnionOperator} +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: 35, Handler: AssignOperator} +var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 2, Precedence: 30, Handler: DeleteChildOperator} + +// var Length = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35} + +type PathElement struct { + PathElementType PathElementType + OperationType *OperationType + Value interface{} + StringValue string +} + +// debugging purposes only +func (p *PathElement) toString() string { + var result string = `` + switch p.PathElementType { + case PathKey: + result = result + fmt.Sprintf("PathKey - '%v'\n", p.Value) + case ArrayIndex: + result = result + fmt.Sprintf("ArrayIndex - '%v'\n", p.Value) + case SelfReference: + result = result + fmt.Sprintf("SELF\n") + case Operation: + result = result + fmt.Sprintf("Operation - %v\n", p.OperationType.Type) + } + return result +} + type YqTreeLib interface { Get(rootNode *yaml.Node, path string) ([]*CandidateNode, error) // GetForMerge(rootNode *yaml.Node, path string, arrayMergeStrategy ArrayMergeStrategy) ([]*NodeContext, error) diff --git a/pkg/yqlib/treeops/operators.go b/pkg/yqlib/treeops/operators.go index c2de7180..e8ebd272 100644 --- a/pkg/yqlib/treeops/operators.go +++ b/pkg/yqlib/treeops/operators.go @@ -129,7 +129,7 @@ func containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) for el := matchMap.Front(); el != nil; el = el.Next() { node := el.Value.(*CandidateNode) - log.Debugf("-- compating %v to %v", node.Node.Value, valuePattern) + log.Debugf("-- comparing %v to %v", node.Node.Value, valuePattern) if Match(node.Node.Value, valuePattern) { return true } diff --git a/pkg/yqlib/treeops/path_postfix.go b/pkg/yqlib/treeops/path_postfix.go index 2d8f1081..40419959 100644 --- a/pkg/yqlib/treeops/path_postfix.go +++ b/pkg/yqlib/treeops/path_postfix.go @@ -2,78 +2,8 @@ package treeops import ( "errors" - "fmt" ) -var precedenceMap map[int]int - -type PathElementType uint32 - -const ( - PathKey PathElementType = 1 << iota - ArrayIndex - Operation - SelfReference - OpenBracket - CloseBracket -) - -type OperationType uint32 - -const ( - None OperationType = 1 << iota - Traverse - Or - And - Equals - Assign - DeleteChild -) - -type PathElement struct { - PathElementType PathElementType - OperationType OperationType - Value interface{} - StringValue string -} - -// debugging purposes only -func (p *PathElement) toString() string { - var result string = `` - switch p.PathElementType { - case PathKey: - result = result + fmt.Sprintf("PathKey - '%v'\n", p.Value) - case ArrayIndex: - result = result + fmt.Sprintf("ArrayIndex - '%v'\n", p.Value) - case SelfReference: - result = result + fmt.Sprintf("SELF\n") - case Operation: - result = result + "Operation - " - switch p.OperationType { - case Or: - result = result + "OR\n" - case And: - result = result + "AND\n" - case Equals: - result = result + "EQUALS\n" - case Assign: - result = result + "ASSIGN\n" - case Traverse: - result = result + "TRAVERSE\n" - case DeleteChild: - result = result + "DELETE CHILD\n" - - } - - } - return result -} - -func createOperationPathElement(opToken *Token) PathElement { - var pathElement = PathElement{PathElementType: Operation, OperationType: opToken.OperationType} - return pathElement -} - type PathPostFixer interface { ConvertToPostfix([]*Token) ([]*PathElement, error) } @@ -88,15 +18,15 @@ func NewPathPostFixer() PathPostFixer { func popOpToResult(opStack []*Token, result []*PathElement) ([]*Token, []*PathElement) { var operatorToPushToPostFix *Token opStack, operatorToPushToPostFix = opStack[0:len(opStack)-1], opStack[len(opStack)-1] - var pathElement = createOperationPathElement(operatorToPushToPostFix) + var pathElement = PathElement{PathElementType: Operation, OperationType: operatorToPushToPostFix.OperationType} return opStack, append(result, &pathElement) } func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement, error) { var result []*PathElement // surround the whole thing with quotes - var opStack = []*Token{&Token{PathElementType: OpenBracket}} - var tokens = append(infixTokens, &Token{PathElementType: CloseBracket}) + var opStack = []*Token{&Token{PathElementType: OpenBracket, OperationType: None}} + var tokens = append(infixTokens, &Token{PathElementType: CloseBracket, OperationType: None}) for _, token := range tokens { switch token.PathElementType { @@ -117,9 +47,9 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement, opStack = opStack[0 : len(opStack)-1] default: - var currentPrecedence = p.precendenceOf(token) + var currentPrecedence = token.OperationType.Precedence // pop off higher precedent operators onto the result - for len(opStack) > 0 && p.precendenceOf(opStack[len(opStack)-1]) >= currentPrecedence { + for len(opStack) > 0 && opStack[len(opStack)-1].OperationType.Precedence >= currentPrecedence { opStack, result = popOpToResult(opStack, result) } // add this operator to the opStack @@ -128,19 +58,3 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement, } return result, nil } - -func (p *pathPostFixer) precendenceOf(token *Token) int { - switch token.OperationType { - case Or: - return 10 - case And: - return 20 - case Equals, DeleteChild: - return 30 - case Assign: - return 35 - case Traverse: - return 40 - } - return 0 -} diff --git a/pkg/yqlib/treeops/path_tokeniser.go b/pkg/yqlib/treeops/path_tokeniser.go index 57bca21c..8d6241ae 100644 --- a/pkg/yqlib/treeops/path_tokeniser.go +++ b/pkg/yqlib/treeops/path_tokeniser.go @@ -13,7 +13,7 @@ func skip(*lex.Scanner, *machines.Match) (interface{}, error) { type Token struct { PathElementType PathElementType - OperationType OperationType + OperationType *OperationType Value interface{} StringValue string PrefixSelf bool @@ -21,7 +21,6 @@ type Token struct { CheckForPreTraverse bool // this token can sometimes have the traverse '.' missing in frnot of it // 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 { @@ -34,7 +33,7 @@ func pathToken(wrapped bool) lex.Action { } } -func opToken(op OperationType, againstSelf bool) lex.Action { +func opToken(op *OperationType, againstSelf bool) lex.Action { return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { value := string(m.Bytes) return &Token{PathElementType: Operation, OperationType: op, Value: value, StringValue: value, PrefixSelf: againstSelf}, nil @@ -43,7 +42,7 @@ func opToken(op OperationType, againstSelf bool) lex.Action { func literalToken(pType PathElementType, literal string, checkForPre bool, checkForPost bool, againstSelf bool) lex.Action { return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { - return &Token{PathElementType: pType, Value: literal, StringValue: literal, CheckForPreTraverse: checkForPre, CheckForPostTraverse: checkForPost, PrefixSelf: againstSelf}, nil + return &Token{PathElementType: pType, OperationType: None, Value: literal, StringValue: literal, CheckForPreTraverse: checkForPre, CheckForPostTraverse: checkForPost, PrefixSelf: againstSelf}, nil } } @@ -61,7 +60,7 @@ func arrayIndextoken(wrapped bool, checkForPre bool, checkForPost bool) lex.Acti if errParsingInt != nil { return nil, errParsingInt } - return &Token{PathElementType: ArrayIndex, Value: number, StringValue: numberString, CheckForPreTraverse: checkForPre, CheckForPostTraverse: checkForPost}, nil + return &Token{PathElementType: ArrayIndex, OperationType: None, Value: number, StringValue: numberString, CheckForPreTraverse: checkForPre, CheckForPostTraverse: checkForPost}, nil } } @@ -130,7 +129,7 @@ func (p *pathTokeniser) Tokenise(path string) ([]*Token, error) { if tok != nil { token := tok.(*Token) - log.Debugf("Tokenising %v", token.Value) + log.Debugf("Tokenising %v - %v", token.Value, token.OperationType.Type) tokens = append(tokens, token) } if err != nil {