mirror of
https://github.com/mikefarah/yq.git
synced 2024-11-12 13:48:06 +00:00
ops first class
This commit is contained in:
parent
7c4cf72468
commit
288aec942c
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user