mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
simplified, refactored
This commit is contained in:
parent
73cf6224f2
commit
4f574efdc4
@ -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
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
7
pkg/yqlib/treeops/operator_self.go
Normal file
7
pkg/yqlib/treeops/operator_self.go
Normal 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
|
||||||
|
}
|
@ -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 == "[]" {
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
7
pkg/yqlib/treeops/operator_value.go
Normal file
7
pkg/yqlib/treeops/operator_value.go
Normal 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
|
||||||
|
}
|
@ -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{
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -1 +1 @@
|
|||||||
{a: {b: apple, c: cactus}}
|
{a: {cat: apple, mad: things}}
|
@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
"a": [1,2],
|
"a": {
|
||||||
"b": [3,4]
|
"cat": "apple",
|
||||||
|
"mad": "things"
|
||||||
|
}
|
||||||
}
|
}
|
@ -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}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user