ops first class

This commit is contained in:
Mike Farah 2020-10-12 10:44:33 +11:00
parent 7c4cf72468
commit 288aec942c
6 changed files with 67 additions and 113 deletions

View File

@ -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)
} }

View File

@ -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)
} }

View File

@ -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)

View File

@ -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
} }

View File

@ -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
}

View File

@ -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 {