simplified, refactored

This commit is contained in:
Mike Farah 2020-10-20 15:33:20 +11:00
parent 73cf6224f2
commit 4f574efdc4
18 changed files with 848 additions and 981 deletions

View File

@ -48,17 +48,17 @@ func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *orderedmap.OrderedMa
log.Debugf("getMatchingNodes - nothing to do") log.Debugf("getMatchingNodes - nothing to do")
return matchingNodes, nil return matchingNodes, nil
} }
log.Debugf("Processing Path: %v", pathNode.PathElement.toString()) log.Debugf("Processing Op: %v", pathNode.Operation.toString())
if log.IsEnabledFor(logging.DEBUG) { if log.IsEnabledFor(logging.DEBUG) {
for el := matchingNodes.Front(); el != nil; el = el.Next() { for el := matchingNodes.Front(); el != nil; el = el.Next() {
log.Debug(NodeToString(el.Value.(*CandidateNode))) log.Debug(NodeToString(el.Value.(*CandidateNode)))
} }
} }
log.Debug(">>") log.Debug(">>")
handler := pathNode.PathElement.OperationType.Handler handler := pathNode.Operation.OperationType.Handler
if handler != nil { if handler != nil {
return handler(d, matchingNodes, pathNode) return handler(d, matchingNodes, pathNode)
} }
return nil, fmt.Errorf("Unknown operator %v", pathNode.PathElement.OperationType) return nil, fmt.Errorf("Unknown operator %v", pathNode.Operation.OperationType)
} }

File diff suppressed because it is too large Load Diff

View File

@ -36,8 +36,8 @@ var Collect = &OperationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handle
var TraversePath = &OperationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator} var TraversePath = &OperationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
var DocumentFilter = &OperationType{Type: "DOCUMENT_FILTER", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator} var DocumentFilter = &OperationType{Type: "DOCUMENT_FILTER", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator}
var SelfReference = &OperationType{Type: "SELF", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator} var SelfReference = &OperationType{Type: "SELF", NumArgs: 0, Precedence: 50, Handler: SelfOperator}
var ValueOp = &OperationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: TraversePathOperator} var ValueOp = &OperationType{Type: "VALUE", NumArgs: 0, Precedence: 50, Handler: ValueOperator}
var RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator} var RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator}
@ -52,15 +52,38 @@ var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 2, Precedence: 40, Han
// var Exists = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35} // var Exists = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35}
// filters matches if they have the existing path // filters matches if they have the existing path
type PathElement struct { type Operation struct {
OperationType *OperationType OperationType *OperationType
Value interface{} Value interface{}
StringValue string StringValue string
CandidateNode *CandidateNode // used for Value Path elements CandidateNode *CandidateNode // used for Value Path elements
} }
func CreateValueOperation(value interface{}, stringValue string) *Operation {
var node yaml.Node = yaml.Node{Kind: yaml.ScalarNode}
node.Value = stringValue
switch value.(type) {
case float32, float64:
node.Tag = "!!float"
case int, int64, int32:
node.Tag = "!!int"
case bool:
node.Tag = "!!bool"
case string:
node.Tag = "!!str"
}
return &Operation{
OperationType: ValueOp,
Value: value,
StringValue: stringValue,
CandidateNode: &CandidateNode{Node: &node},
}
}
// debugging purposes only // debugging purposes only
func (p *PathElement) toString() string { func (p *Operation) toString() string {
if p.OperationType == TraversePath { if p.OperationType == TraversePath {
return fmt.Sprintf("%v", p.Value) return fmt.Sprintf("%v", p.Value)
} else if p.OperationType == DocumentFilter { } else if p.OperationType == DocumentFilter {

View File

@ -34,7 +34,7 @@ func hasMatch(d *dataTreeNavigator, candidate *CandidateNode, lhs *PathTreeNode,
} }
// TODO = handle other RHS types // TODO = handle other RHS types
return containsMatchingValue(childMatches, rhs.PathElement.StringValue), nil return containsMatchingValue(childMatches, rhs.Operation.StringValue), nil
} }
func containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) bool { func containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) bool {

View File

@ -61,12 +61,12 @@ func multiply(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*Ca
func createTraversalTree(path []interface{}) *PathTreeNode { func createTraversalTree(path []interface{}) *PathTreeNode {
if len(path) == 0 { if len(path) == 0 {
return &PathTreeNode{PathElement: &PathElement{OperationType: SelfReference}} return &PathTreeNode{Operation: &Operation{OperationType: SelfReference}}
} else if len(path) == 1 { } else if len(path) == 1 {
return &PathTreeNode{PathElement: &PathElement{OperationType: TraversePath, Value: path[0], StringValue: fmt.Sprintf("%v", path[0])}} return &PathTreeNode{Operation: &Operation{OperationType: TraversePath, Value: path[0], StringValue: fmt.Sprintf("%v", path[0])}}
} }
return &PathTreeNode{ return &PathTreeNode{
PathElement: &PathElement{OperationType: Pipe}, Operation: &Operation{OperationType: Pipe},
Lhs: createTraversalTree(path[0:1]), Lhs: createTraversalTree(path[0:1]),
Rhs: createTraversalTree(path[1:])} Rhs: createTraversalTree(path[1:])}
@ -77,13 +77,13 @@ func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *Candid
lhsPath := rhs.Path[pathIndexToStartFrom:] lhsPath := rhs.Path[pathIndexToStartFrom:]
assignmentOp := &PathElement{OperationType: AssignAttributes} assignmentOp := &Operation{OperationType: AssignAttributes}
if rhs.Node.Kind == yaml.ScalarNode { if rhs.Node.Kind == yaml.ScalarNode {
assignmentOp.OperationType = Assign assignmentOp.OperationType = Assign
} }
rhsOp := &PathElement{OperationType: ValueOp, CandidateNode: rhs} rhsOp := &Operation{OperationType: ValueOp, CandidateNode: rhs}
assignmentOpNode := &PathTreeNode{PathElement: assignmentOp, Lhs: createTraversalTree(lhsPath), Rhs: &PathTreeNode{PathElement: rhsOp}} assignmentOpNode := &PathTreeNode{Operation: assignmentOp, Lhs: createTraversalTree(lhsPath), Rhs: &PathTreeNode{Operation: rhsOp}}
_, err := d.getMatchingNodes(nodeToMap(lhs), assignmentOpNode) _, err := d.getMatchingNodes(nodeToMap(lhs), assignmentOpNode)

View File

@ -16,8 +16,8 @@ func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *orderedmap.Ordered
} }
func recursiveDecent(d *dataTreeNavigator, results *orderedmap.OrderedMap, matchMap *orderedmap.OrderedMap) error { func recursiveDecent(d *dataTreeNavigator, results *orderedmap.OrderedMap, matchMap *orderedmap.OrderedMap) error {
splatPathElement := &PathElement{OperationType: TraversePath, Value: "[]"} splatOperation := &Operation{OperationType: TraversePath, Value: "[]"}
splatTreeNode := &PathTreeNode{PathElement: splatPathElement} splatTreeNode := &PathTreeNode{Operation: splatOperation}
for el := matchMap.Front(); el != nil; el = el.Next() { for el := matchMap.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)

View File

@ -0,0 +1,7 @@
package treeops
import "github.com/elliotchance/orderedmap"
func SelfOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
return matchMap, nil
}

View File

@ -14,7 +14,7 @@ func TraversePathOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap,
var err error var err error
for el := matchMap.Front(); el != nil; el = el.Next() { for el := matchMap.Front(); el != nil; el = el.Next() {
newNodes, err = traverse(d, el.Value.(*CandidateNode), pathNode.PathElement) newNodes, err = traverse(d, el.Value.(*CandidateNode), pathNode.Operation)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -26,7 +26,7 @@ func TraversePathOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap,
return matchingNodeMap, nil return matchingNodeMap, nil
} }
func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) { func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, pathNode *Operation) ([]*CandidateNode, error) {
log.Debug("Traversing %v", NodeToString(matchingNode)) log.Debug("Traversing %v", NodeToString(matchingNode))
value := matchingNode.Node value := matchingNode.Node
@ -78,11 +78,11 @@ func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, pathNode *PathE
} }
} }
func keyMatches(key *yaml.Node, pathNode *PathElement) bool { func keyMatches(key *yaml.Node, pathNode *Operation) bool {
return pathNode.Value == "[]" || Match(key.Value, pathNode.StringValue) return pathNode.Value == "[]" || Match(key.Value, pathNode.StringValue)
} }
func traverseMap(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) { func traverseMap(candidate *CandidateNode, pathNode *Operation) ([]*CandidateNode, error) {
// value.Content is a concatenated array of key, value, // value.Content is a concatenated array of key, value,
// so keys are in the even indexes, values in odd. // so keys are in the even indexes, values in odd.
// merge aliases are defined first, but we only want to traverse them // merge aliases are defined first, but we only want to traverse them
@ -123,7 +123,7 @@ func traverseMap(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateN
return newMatches, nil return newMatches, nil
} }
func traverseArray(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) { func traverseArray(candidate *CandidateNode, pathNode *Operation) ([]*CandidateNode, error) {
log.Debug("pathNode Value %v", pathNode.Value) log.Debug("pathNode Value %v", pathNode.Value)
if pathNode.Value == "[]" { if pathNode.Value == "[]" {

View File

@ -12,6 +12,60 @@ var traversePathOperatorScenarios = []expressionScenario{
"D0, P[a], (!!map)::{b: apple}\n", "D0, P[a], (!!map)::{b: apple}\n",
}, },
}, },
{
document: `[{b: apple}, {c: banana}]`,
expression: `.[]`,
expected: []string{
"D0, P[0], (!!map)::{b: apple}\n",
"D0, P[1], (!!map)::{c: banana}\n",
},
},
{
document: `{}`,
expression: `.a.b`,
expected: []string{
"D0, P[a b], ()::null\n",
},
},
{
document: `{}`,
expression: `.[1].a`,
expected: []string{
"D0, P[1 a], ()::null\n",
},
},
{
document: `{}`,
expression: `.a.[1]`,
expected: []string{
"D0, P[a 1], ()::null\n",
},
},
{
document: `{a: {cat: apple, mad: things}}`,
expression: `.a."*a*"`,
expected: []string{
"D0, P[a cat], (!!str)::apple\n",
"D0, P[a mad], (!!str)::things\n",
},
},
{
document: `{a: {cat: {b: 3}, mad: {b: 4}, fad: {c: t}}}`,
expression: `.a."*a*".b`,
expected: []string{
"D0, P[a cat b], (!!int)::3\n",
"D0, P[a mad b], (!!int)::4\n",
"D0, P[a fad b], ()::null\n",
},
},
{
document: `{a: {cat: apple, mad: things}}`,
expression: `.a | (.cat, .mad)`,
expected: []string{
"D0, P[a cat], (!!str)::apple\n",
"D0, P[a mad], (!!str)::things\n",
},
},
} }
func TestTraversePathOperatorScenarios(t *testing.T) { func TestTraversePathOperatorScenarios(t *testing.T) {

View File

@ -0,0 +1,7 @@
package treeops
import "github.com/elliotchance/orderedmap"
func ValueOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
return nodeToMap(pathNode.Operation.CandidateNode), nil
}

View File

@ -11,7 +11,8 @@ var valueOperatorScenarios = []expressionScenario{
expected: []string{ expected: []string{
"D0, P[], (!!int)::1\n", "D0, P[], (!!int)::1\n",
}, },
}, { },
{
document: ``, document: ``,
expression: `-1`, expression: `-1`,
expected: []string{ expected: []string{

View File

@ -41,22 +41,22 @@ var pathTests = []struct {
// {`.[].a`, append(make([]interface{}, 0), "[]", "PIPE", "a")}, // {`.[].a`, append(make([]interface{}, 0), "[]", "PIPE", "a")},
{ {
`d0.a`, `d0.a`,
append(make([]interface{}, 0), int64(0), "PIPE", "a"), append(make([]interface{}, 0), "d0", "PIPE", "a"),
append(make([]interface{}, 0), "D0", "a", "PIPE"), append(make([]interface{}, 0), "d0", "a", "PIPE"),
}, },
{ {
`.a | (.[].b == "apple")`, `.a | (.[].b == "apple")`,
append(make([]interface{}, 0), "a", "PIPE", "(", "[]", "PIPE", "b", "EQUALS", "apple", ")"), append(make([]interface{}, 0), "a", "PIPE", "(", "[]", "PIPE", "b", "EQUALS", "apple (string)", ")"),
append(make([]interface{}, 0), "a", "[]", "b", "PIPE", "apple (string)", "EQUALS", "PIPE"), append(make([]interface{}, 0), "a", "[]", "b", "PIPE", "apple (string)", "EQUALS", "PIPE"),
}, },
{ {
`.[] | select(. == "*at")`, `.[] | select(. == "*at")`,
append(make([]interface{}, 0), "[]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at", ")"), append(make([]interface{}, 0), "[]", "PIPE", "SELECT", "(", "SELF", "EQUALS", "*at (string)", ")"),
append(make([]interface{}, 0), "[]", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"), append(make([]interface{}, 0), "[]", "SELF", "*at (string)", "EQUALS", "SELECT", "PIPE"),
}, },
{ {
`[true]`, `[true]`,
append(make([]interface{}, 0), "[", true, "]"), append(make([]interface{}, 0), "[", "true (bool)", "]"),
append(make([]interface{}, 0), "true (bool)", "COLLECT", "PIPE"), append(make([]interface{}, 0), "true (bool)", "COLLECT", "PIPE"),
}, },
@ -91,7 +91,7 @@ func TestPathParsing(t *testing.T) {
} }
var tokenValues []interface{} var tokenValues []interface{}
for _, token := range tokens { for _, token := range tokens {
tokenValues = append(tokenValues, token.Value) tokenValues = append(tokenValues, token.toString())
} }
test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path)) test.AssertResultComplexWithContext(t, tt.expectedTokens, tokenValues, fmt.Sprintf("tokenise: %v", tt.path))

View File

@ -7,7 +7,7 @@ import (
) )
type PathPostFixer interface { type PathPostFixer interface {
ConvertToPostfix([]*Token) ([]*PathElement, error) ConvertToPostfix([]*Token) ([]*Operation, error)
} }
type pathPostFixer struct { type pathPostFixer struct {
@ -17,27 +17,20 @@ func NewPathPostFixer() PathPostFixer {
return &pathPostFixer{} return &pathPostFixer{}
} }
func popOpToResult(opStack []*Token, result []*PathElement) ([]*Token, []*PathElement) { func popOpToResult(opStack []*Token, result []*Operation) ([]*Token, []*Operation) {
var newOp *Token var newOp *Token
opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1] opStack, newOp = opStack[0:len(opStack)-1], opStack[len(opStack)-1]
var pathElement = PathElement{OperationType: newOp.OperationType, Value: newOp.Value, StringValue: newOp.StringValue} return opStack, append(result, newOp.Operation)
if newOp.OperationType == ValueOp {
var candidateNode = BuildCandidateNodeFrom(newOp)
pathElement.CandidateNode = candidateNode
}
return opStack, append(result, &pathElement)
} }
func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement, error) { func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*Operation, error) {
var result []*PathElement var result []*Operation
// surround the whole thing with quotes // surround the whole thing with quotes
var opStack = []*Token{&Token{TokenType: OpenBracket, Value: "("}} var opStack = []*Token{&Token{TokenType: OpenBracket}}
var tokens = append(infixTokens, &Token{TokenType: CloseBracket, Value: ")"}) var tokens = append(infixTokens, &Token{TokenType: CloseBracket})
for _, token := range tokens { for _, token := range tokens {
log.Debugf("postfix processing token %v", token.Value) log.Debugf("postfix processing token %v, %v", token.toString(), token.Operation)
switch token.TokenType { switch token.TokenType {
case OpenBracket, OpenCollect: case OpenBracket, OpenCollect:
opStack = append(opStack, token) opStack = append(opStack, token)
@ -51,8 +44,8 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement,
// now we should have [] as the last element on the opStack, get rid of it // now we should have [] as the last element on the opStack, get rid of it
opStack = opStack[0 : len(opStack)-1] opStack = opStack[0 : len(opStack)-1]
//and append a collect to the opStack //and append a collect to the opStack
opStack = append(opStack, &Token{TokenType: Operation, OperationType: Pipe}) opStack = append(opStack, &Token{TokenType: OperationToken, Operation: &Operation{OperationType: Pipe}})
opStack = append(opStack, &Token{TokenType: Operation, OperationType: Collect}) opStack = append(opStack, &Token{TokenType: OperationToken, Operation: &Operation{OperationType: Collect}})
case CloseBracket: case CloseBracket:
for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != OpenBracket { for len(opStack) > 0 && opStack[len(opStack)-1].TokenType != OpenBracket {
opStack, result = popOpToResult(opStack, result) opStack, result = popOpToResult(opStack, result)
@ -64,9 +57,11 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*Token) ([]*PathElement,
opStack = opStack[0 : len(opStack)-1] opStack = opStack[0 : len(opStack)-1]
default: default:
var currentPrecedence = token.OperationType.Precedence var currentPrecedence = token.Operation.OperationType.Precedence
// pop off higher precedent operators onto the result // pop off higher precedent operators onto the result
for len(opStack) > 0 && opStack[len(opStack)-1].OperationType.Precedence >= currentPrecedence { for len(opStack) > 0 &&
opStack[len(opStack)-1].TokenType == OperationToken &&
opStack[len(opStack)-1].Operation.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

View File

@ -1,6 +1,7 @@
package treeops package treeops
import ( import (
"fmt"
"strconv" "strconv"
lex "github.com/timtadh/lexmachine" lex "github.com/timtadh/lexmachine"
@ -14,7 +15,7 @@ func skip(*lex.Scanner, *machines.Match) (interface{}, error) {
type TokenType uint32 type TokenType uint32
const ( const (
Operation = 1 << iota OperationToken = 1 << iota
OpenBracket OpenBracket
CloseBracket CloseBracket
OpenCollect OpenCollect
@ -23,13 +24,27 @@ const (
type Token struct { type Token struct {
TokenType TokenType TokenType TokenType
OperationType *OperationType Operation *Operation
Value interface{}
StringValue string
CheckForPostTraverse bool // e.g. [1]cat should really be [1].cat CheckForPostTraverse bool // e.g. [1]cat should really be [1].cat
} }
func (t *Token) toString() string {
if t.TokenType == OperationToken {
return t.Operation.toString()
} else if t.TokenType == OpenBracket {
return "("
} else if t.TokenType == CloseBracket {
return ")"
} else if t.TokenType == OpenCollect {
return "["
} else if t.TokenType == CloseCollect {
return "]"
} else {
return fmt.Sprintf("NFI")
}
}
func pathToken(wrapped bool) lex.Action { func pathToken(wrapped 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)
@ -37,13 +52,15 @@ func pathToken(wrapped bool) lex.Action {
if wrapped { if wrapped {
value = unwrap(value) value = unwrap(value)
} }
return &Token{TokenType: Operation, OperationType: TraversePath, Value: value, StringValue: value, CheckForPostTraverse: true}, nil op := &Operation{OperationType: TraversePath, Value: value, StringValue: value}
return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil
} }
} }
func literalPathToken(value string) lex.Action { func literalPathToken(value string) lex.Action {
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
return &Token{TokenType: Operation, OperationType: TraversePath, Value: value, StringValue: value, CheckForPostTraverse: true}, nil op := &Operation{OperationType: TraversePath, Value: value, StringValue: value}
return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil
} }
} }
@ -55,20 +72,22 @@ func documentToken() lex.Action {
if errParsingInt != nil { if errParsingInt != nil {
return nil, errParsingInt return nil, errParsingInt
} }
return &Token{TokenType: Operation, OperationType: DocumentFilter, Value: number, StringValue: numberString, CheckForPostTraverse: true}, nil op := &Operation{OperationType: DocumentFilter, Value: number, StringValue: numberString}
return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil
} }
} }
func opToken(op *OperationType) lex.Action { func opToken(op *OperationType) 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{TokenType: Operation, OperationType: op, Value: op.Type, StringValue: value}, nil op := &Operation{OperationType: op, Value: op.Type, StringValue: value}
return &Token{TokenType: OperationToken, Operation: op}, nil
} }
} }
func literalToken(pType TokenType, literal string, checkForPost bool) lex.Action { func literalToken(pType TokenType, checkForPost 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{TokenType: Operation, OperationType: ValueOp, Value: literal, StringValue: literal, CheckForPostTraverse: checkForPost}, nil return &Token{TokenType: pType, CheckForPostTraverse: checkForPost}, nil
} }
} }
@ -88,7 +107,8 @@ func arrayIndextoken(precedingDot bool) lex.Action {
if errParsingInt != nil { if errParsingInt != nil {
return nil, errParsingInt return nil, errParsingInt
} }
return &Token{TokenType: Operation, OperationType: TraversePath, Value: number, StringValue: numberString, CheckForPostTraverse: true}, nil op := &Operation{OperationType: TraversePath, Value: number, StringValue: numberString}
return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil
} }
} }
@ -99,7 +119,8 @@ func numberValue() lex.Action {
if errParsingInt != nil { if errParsingInt != nil {
return nil, errParsingInt return nil, errParsingInt
} }
return &Token{TokenType: Operation, OperationType: ValueOp, Value: number, StringValue: numberString}, nil
return &Token{TokenType: OperationToken, Operation: CreateValueOperation(number, numberString)}, nil
} }
} }
@ -110,13 +131,13 @@ func floatValue() lex.Action {
if errParsingInt != nil { if errParsingInt != nil {
return nil, errParsingInt return nil, errParsingInt
} }
return &Token{TokenType: Operation, OperationType: ValueOp, Value: number, StringValue: numberString}, nil return &Token{TokenType: OperationToken, Operation: CreateValueOperation(number, numberString)}, nil
} }
} }
func booleanValue(val bool) lex.Action { func booleanValue(val 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{TokenType: Operation, OperationType: ValueOp, Value: val, StringValue: string(m.Bytes)}, nil return &Token{TokenType: OperationToken, Operation: CreateValueOperation(val, string(m.Bytes))}, nil
} }
} }
@ -126,21 +147,22 @@ func stringValue(wrapped bool) lex.Action {
if wrapped { if wrapped {
value = unwrap(value) value = unwrap(value)
} }
return &Token{TokenType: Operation, OperationType: ValueOp, Value: value, StringValue: value}, nil return &Token{TokenType: OperationToken, Operation: CreateValueOperation(value, value)}, nil
} }
} }
func selfToken() lex.Action { func selfToken() lex.Action {
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
return &Token{TokenType: Operation, OperationType: SelfReference}, nil op := &Operation{OperationType: SelfReference}
return &Token{TokenType: OperationToken, Operation: op}, nil
} }
} }
// Creates the lexer object and compiles the NFA. // Creates the lexer object and compiles the NFA.
func initLexer() (*lex.Lexer, error) { func initLexer() (*lex.Lexer, error) {
lexer := lex.NewLexer() lexer := lex.NewLexer()
lexer.Add([]byte(`\(`), literalToken(OpenBracket, "(", false)) lexer.Add([]byte(`\(`), literalToken(OpenBracket, false))
lexer.Add([]byte(`\)`), literalToken(CloseBracket, ")", true)) lexer.Add([]byte(`\)`), literalToken(CloseBracket, true))
lexer.Add([]byte(`\.?\[\]`), literalPathToken("[]")) lexer.Add([]byte(`\.?\[\]`), literalPathToken("[]"))
lexer.Add([]byte(`\.\.`), opToken(RecursiveDescent)) lexer.Add([]byte(`\.\.`), opToken(RecursiveDescent))
@ -180,8 +202,8 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`"[^ "]+"`), stringValue(true)) lexer.Add([]byte(`"[^ "]+"`), stringValue(true))
lexer.Add([]byte(`\[`), literalToken(OpenCollect, "[", false)) lexer.Add([]byte(`\[`), literalToken(OpenCollect, false))
lexer.Add([]byte(`\]`), literalToken(CloseCollect, "]", true)) lexer.Add([]byte(`\]`), literalToken(CloseCollect, true))
lexer.Add([]byte(`\*`), opToken(Multiply)) lexer.Add([]byte(`\*`), opToken(Multiply))
// lexer.Add([]byte(`[^ \,\|\.\[\(\)=]+`), stringValue(false)) // lexer.Add([]byte(`[^ \,\|\.\[\(\)=]+`), stringValue(false))
@ -219,7 +241,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 - %v", token.Value, token.OperationType.Type) log.Debugf("Tokenising %v", token.toString())
tokens = append(tokens, token) tokens = append(tokens, token)
} }
if err != nil { if err != nil {
@ -233,8 +255,10 @@ func (p *pathTokeniser) Tokenise(path string) ([]*Token, error) {
postProcessedTokens = append(postProcessedTokens, token) postProcessedTokens = append(postProcessedTokens, token)
if index != len(tokens)-1 && token.CheckForPostTraverse && if index != len(tokens)-1 && token.CheckForPostTraverse &&
tokens[index+1].OperationType == TraversePath { tokens[index+1].TokenType == OperationToken &&
postProcessedTokens = append(postProcessedTokens, &Token{TokenType: Operation, OperationType: Pipe, Value: "PIPE"}) tokens[index+1].Operation.OperationType == TraversePath {
op := &Operation{OperationType: Pipe, Value: "PIPE"}
postProcessedTokens = append(postProcessedTokens, &Token{TokenType: OperationToken, Operation: op})
} }
} }

View File

@ -6,14 +6,14 @@ var myPathTokeniser = NewPathTokeniser()
var myPathPostfixer = NewPathPostFixer() var myPathPostfixer = NewPathPostFixer()
type PathTreeNode struct { type PathTreeNode struct {
PathElement *PathElement Operation *Operation
Lhs *PathTreeNode Lhs *PathTreeNode
Rhs *PathTreeNode Rhs *PathTreeNode
} }
type PathTreeCreator interface { type PathTreeCreator interface {
ParsePath(path string) (*PathTreeNode, error) ParsePath(path string) (*PathTreeNode, error)
CreatePathTree(postFixPath []*PathElement) (*PathTreeNode, error) CreatePathTree(postFixPath []*Operation) (*PathTreeNode, error)
} }
type pathTreeCreator struct { type pathTreeCreator struct {
@ -28,26 +28,26 @@ func (p *pathTreeCreator) ParsePath(path string) (*PathTreeNode, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
var pathElements []*PathElement var Operations []*Operation
pathElements, err = myPathPostfixer.ConvertToPostfix(tokens) Operations, err = myPathPostfixer.ConvertToPostfix(tokens)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return p.CreatePathTree(pathElements) return p.CreatePathTree(Operations)
} }
func (p *pathTreeCreator) CreatePathTree(postFixPath []*PathElement) (*PathTreeNode, error) { func (p *pathTreeCreator) CreatePathTree(postFixPath []*Operation) (*PathTreeNode, error) {
var stack = make([]*PathTreeNode, 0) var stack = make([]*PathTreeNode, 0)
if len(postFixPath) == 0 { if len(postFixPath) == 0 {
return nil, nil return nil, nil
} }
for _, pathElement := range postFixPath { for _, Operation := range postFixPath {
var newNode = PathTreeNode{PathElement: pathElement} var newNode = PathTreeNode{Operation: Operation}
log.Debugf("pathTree %v ", pathElement.toString()) log.Debugf("pathTree %v ", Operation.toString())
if pathElement.OperationType.NumArgs > 0 { if Operation.OperationType.NumArgs > 0 {
numArgs := pathElement.OperationType.NumArgs numArgs := Operation.OperationType.NumArgs
if numArgs == 1 { if numArgs == 1 {
remaining, rhs := stack[:len(stack)-1], stack[len(stack)-1] remaining, rhs := stack[:len(stack)-1], stack[len(stack)-1]
newNode.Rhs = rhs newNode.Rhs = rhs

View File

@ -1 +1 @@
{a: {b: apple, c: cactus}} {a: {cat: apple, mad: things}}

View File

@ -1,4 +1,6 @@
{ {
"a": [1,2], "a": {
"b": [3,4] "cat": "apple",
"mad": "things"
}
} }

View File

@ -1,20 +0,0 @@
package treeops
import "gopkg.in/yaml.v3"
func BuildCandidateNodeFrom(token *Token) *CandidateNode {
var node yaml.Node = yaml.Node{Kind: yaml.ScalarNode}
node.Value = token.StringValue
switch token.Value.(type) {
case float32, float64:
node.Tag = "!!float"
case int, int64, int32:
node.Tag = "!!int"
case bool:
node.Tag = "!!bool"
case string:
node.Tag = "!!str"
}
return &CandidateNode{Node: &node}
}