diff --git a/pkg/yqlib/data_tree_navigator.go b/pkg/yqlib/data_tree_navigator.go index 203a45e..b67ea40 100644 --- a/pkg/yqlib/data_tree_navigator.go +++ b/pkg/yqlib/data_tree_navigator.go @@ -1,23 +1,73 @@ package yqlib -// import yaml "gopkg.in/yaml.v3" +type dataTreeNavigator struct { +} -// type NodeLeafContext struct { -// Node *yaml.Node -// Head interface{} -// PathStack []interface{} -// } +type DataTreeNavigator interface { + GetMatchingNodes(matchingNodes []*NodeContext, pathNode *PathTreeNode) ([]*NodeContext, error) +} -// func newNodeLeafContext(node *yaml.Node, head interface{}, tailpathStack []interface{}) NodeLeafContext { -// newPathStack := make([]interface{}, len(pathStack)) -// copy(newPathStack, pathStack) -// return NodeContext{ -// Node: node, -// Head: head, -// PathStack: newPathStack, -// } -// } +func NewTreeNavigator() DataTreeNavigator { + return &dataTreeNavigator{} +} -// type DataTreeNavigator interface { -// Traverse(value *NodeLeafContext) -// } +func (d *dataTreeNavigator) traverseSingle(matchingNode *NodeContext, pathNode *PathElement) ([]*NodeContext, error) { + var value = matchingNode.Node + // match all for splat + // match all and recurse for deep + // etc and so forth + +} + +func (d *dataTreeNavigator) traverse(matchingNodes []*NodeContext, pathNode *PathElement) ([]*NodeContext, error) { + var newMatchingNodes = make([]*NodeContext, 0) + var newNodes []*NodeContext + var err error + for _, node := range matchingNodes { + + newNodes, err = d.traverseSingle(node, pathNode) + if err != nil { + return nil, err + } + newMatchingNodes = append(newMatchingNodes, newNodes...) + } + + return newMatchingNodes, nil +} + +func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*NodeContext, pathNode *PathTreeNode) ([]*NodeContext, error) { + if pathNode.PathElement.PathElementType == PathKey || pathNode.PathElement.PathElementType == ArrayIndex { + return d.traverse(matchingNodes, pathNode.PathElement) + } else { + var lhs, rhs []*NodeContext + var err error + switch pathNode.PathElement.OperationType { + case Traverse: + lhs, err = d.GetMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + return d.GetMatchingNodes(lhs, pathNode.Rhs) + case Or, And: + lhs, err = d.GetMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + rhs, err = d.GetMatchingNodes(matchingNodes, pathNode.Rhs) + if err != nil { + return nil, err + } + return d.setFunction(pathNode.PathElement, lhs, rhs), nil + case Equals: + lhs, err = d.GetMatchingNodes(matchingNodes, pathNode.Lhs) + if err != nil { + return nil, err + } + return d.findMatchingValues(lhs, pathNode.Rhs) + case EqualsSelf: + return d.findMatchingValues(matchingNodes, pathNode.Rhs) + } + + } + +} diff --git a/pkg/yqlib/path_postfix.go b/pkg/yqlib/path_postfix.go index 631076b..30de129 100644 --- a/pkg/yqlib/path_postfix.go +++ b/pkg/yqlib/path_postfix.go @@ -32,7 +32,6 @@ type PathElement struct { PathElementType PathElementType OperationType OperationType Value interface{} - Finished bool } // debugging purposes only @@ -109,14 +108,6 @@ func popOpToResult(opStack []*lex.Token, result []*PathElement) ([]*lex.Token, [ return opStack, append(result, &pathElement) } -func finishPathKey(result []*PathElement) { - if len(result) > 0 { - //need to mark PathKey elements as finished so we - //stop appending PathKeys as children - result[len(result)-1].Finished = true - } -} - func (p *pathPostFixer) ConvertToPostfix(infixTokens []*lex.Token) ([]*PathElement, error) { var result []*PathElement // surround the whole thing with quotes @@ -130,7 +121,6 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*lex.Token) ([]*PathEleme result = append(result, &pathElement) case TokenIds["("]: opStack = append(opStack, token) - finishPathKey(result) case TokenIds["OR_OPERATOR"], TokenIds["AND_OPERATOR"], TokenIds["EQUALS_OPERATOR"], TokenIds["EQUALS_SELF_OPERATOR"], TokenIds["TRAVERSE_OPERATOR"]: var currentPrecedence = precedenceMap[token.Type] // pop off higher precedent operators onto the result @@ -139,7 +129,6 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*lex.Token) ([]*PathEleme } // add this operator to the opStack opStack = append(opStack, token) - finishPathKey(result) case TokenIds[")"]: for len(opStack) > 0 && opStack[len(opStack)-1].Type != TokenIds["("] { opStack, result = popOpToResult(opStack, result) @@ -149,7 +138,6 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*lex.Token) ([]*PathEleme } // now we should have ( as the last element on the opStack, get rid of it opStack = opStack[0 : len(opStack)-1] - finishPathKey(result) } } return result, nil diff --git a/pkg/yqlib/path_tree.go b/pkg/yqlib/path_tree.go index cf0b1ea..070ee89 100644 --- a/pkg/yqlib/path_tree.go +++ b/pkg/yqlib/path_tree.go @@ -1,27 +1,39 @@ package yqlib -import lex "github.com/timtadh/lexmachine" - -func parseTree(tokens []*lex.Token, currentElement *PathElement, allElements []*PathElement) []*PathElement { - currentToken, remainingTokens := tokens[0], tokens[1:] - - switch currentToken.Type { - case TokenIds["PATH_KEY"]: - currentElement.PathElementType = PathKey - currentElement.OperationType = None - currentElement.Value = currentToken.Value - } - - if len(remainingTokens) == 0 { - return append(allElements, currentElement) - } - return parseTree(remainingTokens, &PathElement{}, append(allElements, currentElement)) +import "fmt" +type PathTreeNode struct { + PathElement *PathElement + Lhs *PathTreeNode + Rhs *PathTreeNode } -func ParseTree(tokens []*lex.Token) []*PathElement { - if len(tokens) == 0 { - return make([]*PathElement, 0) - } - return parseTree(tokens, &PathElement{}, make([]*PathElement, 0)) +type PathTreeCreator interface { + CreatePathTree([]*PathElement) (*PathTreeNode, error) +} + +type pathTreeCreator struct { +} + +func NewPathTreeCreator() PathTreeCreator { + return &pathTreeCreator{} +} + +func (p *pathTreeCreator) CreatePathTree(postFixPath []*PathElement) (*PathTreeNode, error) { + var stack = make([]*PathTreeNode, 0) + + for _, pathElement := range postFixPath { + var newNode = PathTreeNode{PathElement: pathElement} + if pathElement.PathElementType == Operation { + remaining, lhs, rhs := stack[:len(stack)-2], stack[len(stack)-2], stack[len(stack)-1] + newNode.Lhs = lhs + newNode.Rhs = rhs + stack = remaining + } + stack = append(stack, &newNode) + } + if len(stack) != 1 { + return nil, fmt.Errorf("expected stack to have 1 thing but its %v", stack) + } + return stack[0], nil }