mirror of
https://github.com/mikefarah/yq.git
synced 2024-11-13 22:38:04 +00:00
ops first class
This commit is contained in:
parent
7c4cf72468
commit
288aec942c
@ -7,8 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type dataTreeNavigator struct {
|
type dataTreeNavigator struct {
|
||||||
leafTraverser LeafTraverser
|
leafTraverser LeafTraverser
|
||||||
operatorHandlers map[OperationType]OperatorHandler
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NavigationPrefs struct {
|
type NavigationPrefs struct {
|
||||||
@ -21,16 +20,7 @@ type DataTreeNavigator interface {
|
|||||||
|
|
||||||
func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator {
|
func NewDataTreeNavigator(navigationPrefs NavigationPrefs) DataTreeNavigator {
|
||||||
leafTraverser := NewLeafTraverser(navigationPrefs)
|
leafTraverser := NewLeafTraverser(navigationPrefs)
|
||||||
operatorHandlers := make(map[OperationType]OperatorHandler)
|
return &dataTreeNavigator{leafTraverser}
|
||||||
|
|
||||||
operatorHandlers[Traverse] = TraverseOperator
|
|
||||||
operatorHandlers[Equals] = EqualsOperator
|
|
||||||
operatorHandlers[Or] = UnionOperator
|
|
||||||
operatorHandlers[And] = IntersectionOperator
|
|
||||||
operatorHandlers[Assign] = AssignOperator
|
|
||||||
operatorHandlers[DeleteChild] = DeleteChildOperator
|
|
||||||
|
|
||||||
return &dataTreeNavigator{leafTraverser, operatorHandlers}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dataTreeNavigator) traverse(matchMap *orderedmap.OrderedMap, pathNode *PathElement) (*orderedmap.OrderedMap, error) {
|
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 {
|
} else if pathNode.PathElement.PathElementType == PathKey || pathNode.PathElement.PathElementType == ArrayIndex {
|
||||||
return d.traverse(matchingNodes, pathNode.PathElement)
|
return d.traverse(matchingNodes, pathNode.PathElement)
|
||||||
} else {
|
} else {
|
||||||
handler := d.operatorHandlers[pathNode.PathElement.OperationType]
|
handler := pathNode.PathElement.OperationType.Handler
|
||||||
if handler != nil {
|
if handler != nil {
|
||||||
return handler(d, matchingNodes, pathNode)
|
return handler(d, matchingNodes, pathNode)
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ func TestDataTreeNavigatorDeleteViaSelf(t *testing.T) {
|
|||||||
- sdfsd
|
- sdfsd
|
||||||
- apple`)
|
- apple`)
|
||||||
|
|
||||||
path, errPath := treeCreator.ParsePath("(. .- .)")
|
path, errPath := treeCreator.ParsePath(". .- (. == apple)")
|
||||||
if errPath != nil {
|
if errPath != nil {
|
||||||
t.Error(errPath)
|
t.Error(errPath)
|
||||||
}
|
}
|
||||||
@ -378,7 +378,7 @@ func TestDataTreeNavigatorSimpleAssignByFind(t *testing.T) {
|
|||||||
nodes := readDoc(t, `a:
|
nodes := readDoc(t, `a:
|
||||||
b: apple`)
|
b: apple`)
|
||||||
|
|
||||||
path, errPath := treeCreator.ParsePath("(b == apple) := frog)")
|
path, errPath := treeCreator.ParsePath("a(. == apple) := frog")
|
||||||
if errPath != nil {
|
if errPath != nil {
|
||||||
t.Error(errPath)
|
t.Error(errPath)
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,57 @@ func (n *CandidateNode) getKey() string {
|
|||||||
return fmt.Sprintf("%v - %v", n.Document, n.Path)
|
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 {
|
type YqTreeLib interface {
|
||||||
Get(rootNode *yaml.Node, path string) ([]*CandidateNode, error)
|
Get(rootNode *yaml.Node, path string) ([]*CandidateNode, error)
|
||||||
// GetForMerge(rootNode *yaml.Node, path string, arrayMergeStrategy ArrayMergeStrategy) ([]*NodeContext, 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() {
|
for el := matchMap.Front(); el != nil; el = el.Next() {
|
||||||
node := el.Value.(*CandidateNode)
|
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) {
|
if Match(node.Node.Value, valuePattern) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -2,78 +2,8 @@ package treeops
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"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 {
|
type PathPostFixer interface {
|
||||||
ConvertToPostfix([]*Token) ([]*PathElement, error)
|
ConvertToPostfix([]*Token) ([]*PathElement, error)
|
||||||
}
|
}
|
||||||
@ -88,15 +18,15 @@ func NewPathPostFixer() PathPostFixer {
|
|||||||
func popOpToResult(opStack []*Token, result []*PathElement) ([]*Token, []*PathElement) {
|
func popOpToResult(opStack []*Token, result []*PathElement) ([]*Token, []*PathElement) {
|
||||||
var operatorToPushToPostFix *Token
|
var operatorToPushToPostFix *Token
|
||||||
opStack, operatorToPushToPostFix = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
|
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)
|
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}}
|
var opStack = []*Token{&Token{PathElementType: OpenBracket, OperationType: None}}
|
||||||
var tokens = append(infixTokens, &Token{PathElementType: CloseBracket})
|
var tokens = append(infixTokens, &Token{PathElementType: CloseBracket, OperationType: None})
|
||||||
|
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
switch token.PathElementType {
|
switch token.PathElementType {
|
||||||
@ -117,9 +47,9 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement,
|
|||||||
opStack = opStack[0 : len(opStack)-1]
|
opStack = opStack[0 : len(opStack)-1]
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var currentPrecedence = p.precendenceOf(token)
|
var currentPrecedence = token.OperationType.Precedence
|
||||||
// pop off higher precedent operators onto the result
|
// 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)
|
opStack, result = popOpToResult(opStack, result)
|
||||||
}
|
}
|
||||||
// add this operator to the opStack
|
// add this operator to the opStack
|
||||||
@ -128,19 +58,3 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement,
|
|||||||
}
|
}
|
||||||
return result, nil
|
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 {
|
type Token struct {
|
||||||
PathElementType PathElementType
|
PathElementType PathElementType
|
||||||
OperationType OperationType
|
OperationType *OperationType
|
||||||
Value interface{}
|
Value interface{}
|
||||||
StringValue string
|
StringValue string
|
||||||
PrefixSelf bool
|
PrefixSelf bool
|
||||||
@ -21,7 +21,6 @@ type Token struct {
|
|||||||
CheckForPreTraverse bool // this token can sometimes have the traverse '.' missing in frnot of it
|
CheckForPreTraverse bool // this token can sometimes have the traverse '.' missing in frnot of it
|
||||||
// e.g. a[1] should really be a.[1]
|
// e.g. a[1] should really be a.[1]
|
||||||
CheckForPostTraverse bool // samething but for post, e.g. [1]cat should really be [1].cat
|
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 {
|
||||||
@ -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) {
|
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: value, StringValue: value, PrefixSelf: againstSelf}, nil
|
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 {
|
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 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 {
|
if errParsingInt != nil {
|
||||||
return nil, errParsingInt
|
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 {
|
if tok != nil {
|
||||||
token := tok.(*Token)
|
token := tok.(*Token)
|
||||||
log.Debugf("Tokenising %v", token.Value)
|
log.Debugf("Tokenising %v - %v", token.Value, token.OperationType.Type)
|
||||||
tokens = append(tokens, token)
|
tokens = append(tokens, token)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user