simple anchors

This commit is contained in:
Mike Farah 2020-10-30 10:56:45 +11:00
parent 4edb3e9021
commit 643f2467ee
8 changed files with 87 additions and 33 deletions

View File

@ -31,6 +31,7 @@ func (n *CandidateNode) UpdateFrom(other *CandidateNode) {
n.UpdateAttributesFrom(other)
n.Node.Content = other.Node.Content
n.Node.Value = other.Node.Value
n.Node.Alias = other.Node.Alias
}
func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) {

View File

@ -49,8 +49,6 @@ var Select = &OperationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler:
var DeleteChild = &OperationType{Type: "DELETE", NumArgs: 2, Precedence: 40, Handler: DeleteChildOperator}
// var Splat = &OperationType{Type: "SPLAT", NumArgs: 0, Precedence: 40, Handler: SplatOperator}
// var Exists = &OperationType{Type: "Length", NumArgs: 2, Precedence: 35}
// filters matches if they have the existing path
@ -59,6 +57,7 @@ type Operation struct {
Value interface{}
StringValue string
CandidateNode *CandidateNode // used for Value Path elements
Preferences interface{}
}
func CreateValueOperation(value interface{}, stringValue string) *Operation {
@ -132,6 +131,8 @@ func NodeToString(node *CandidateNode) string {
tag := value.Tag
if value.Kind == yaml.DocumentNode {
tag = "doc"
} else if value.Kind == yaml.AliasNode {
tag = "alias"
}
return fmt.Sprintf(`D%v, P%v, (%v)::%v`, node.Document, node.Path, tag, buf.String())
}

View File

@ -107,7 +107,7 @@ func applyAssignment(d *dataTreeNavigator, pathIndexToStartFrom int, lhs *Candid
lhsPath := rhs.Path[pathIndexToStartFrom:]
assignmentOp := &Operation{OperationType: AssignAttributes}
if rhs.Node.Kind == yaml.ScalarNode {
if rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode {
assignmentOp.OperationType = Assign
}
rhsOp := &Operation{OperationType: ValueOp, CandidateNode: rhs}

View File

@ -81,6 +81,20 @@ b:
"D0, P[], (!!map)::{a: {c: cat}}\n",
},
},
{
document: `{a: &cat {c: frog}, b: {f: *cat}, c: {g: thongs}}`,
expression: `.c * .b`,
expected: []string{
"D0, P[c], (!!map)::{g: thongs, f: *cat}\n",
},
},
{
document: `{a: {c: &cat frog}, b: {f: *cat}, c: {g: thongs}}`,
expression: `.c * .a`,
expected: []string{
"D0, P[c], (!!map)::{g: thongs, c: frog}\n",
},
},
}
func TestMultiplyOperatorScenarios(t *testing.T) {

View File

@ -2,6 +2,8 @@ package treeops
import (
"container/list"
"gopkg.in/yaml.v3"
)
func RecursiveDescentOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) {
@ -24,14 +26,17 @@ func recursiveDecent(d *dataTreeNavigator, results *list.List, matchMap *list.Li
log.Debugf("Recursive Decent, added %v", NodeToString(candidate))
results.PushBack(candidate)
children, err := Splat(d, nodeToMap(candidate))
if candidate.Node.Kind != yaml.AliasNode {
if err != nil {
return err
}
err = recursiveDecent(d, results, children)
if err != nil {
return err
children, err := Splat(d, nodeToMap(candidate))
if err != nil {
return err
}
err = recursiveDecent(d, results, children)
if err != nil {
return err
}
}
}
return nil

View File

@ -50,6 +50,16 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
"D0, P[2], (!!bool)::true\n",
},
},
{
document: `{a: &cat {c: frog}, b: *cat}`,
expression: `..`,
expected: []string{
"D0, P[], (!!map)::{a: &cat {c: frog}, b: *cat}\n",
"D0, P[a], (!!map)::&cat {c: frog}\n",
"D0, P[a c], (!!str)::frog\n",
"D0, P[b], (alias)::*cat\n",
},
},
}
func TestRecursiveDescentOperatorScenarios(t *testing.T) {

View File

@ -8,8 +8,13 @@ import (
"gopkg.in/yaml.v3"
)
type TraversePreferences struct {
DontFollowAlias bool
}
func Splat(d *dataTreeNavigator, matches *list.List) (*list.List, error) {
splatOperation := &Operation{OperationType: TraversePath, Value: "[]"}
preferences := &TraversePreferences{DontFollowAlias: true}
splatOperation := &Operation{OperationType: TraversePath, Value: "[]", Preferences: preferences}
splatTreeNode := &PathTreeNode{Operation: splatOperation}
return TraversePathOperator(d, matches, splatTreeNode)
}
@ -33,14 +38,20 @@ func TraversePathOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *P
return matchingNodeMap, nil
}
func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, pathNode *Operation) ([]*CandidateNode, error) {
func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, operation *Operation) ([]*CandidateNode, error) {
log.Debug("Traversing %v", NodeToString(matchingNode))
value := matchingNode.Node
if value.Tag == "!!null" && pathNode.Value != "[]" {
followAlias := true
// if operation.Preferences != nil {
// followAlias = !operation.Preferences.(*TraversePreferences).DontFollowAlias
// }
if value.Tag == "!!null" && operation.Value != "[]" {
log.Debugf("Guessing kind")
// we must ahve added this automatically, lets guess what it should be now
switch pathNode.Value.(type) {
switch operation.Value.(type) {
case int, int64:
log.Debugf("probably an array")
value.Kind = yaml.SequenceNode
@ -54,33 +65,24 @@ func traverse(d *dataTreeNavigator, matchingNode *CandidateNode, pathNode *Opera
switch value.Kind {
case yaml.MappingNode:
log.Debug("its a map with %v entries", len(value.Content)/2)
return traverseMap(matchingNode, pathNode)
return traverseMap(matchingNode, operation)
case yaml.SequenceNode:
log.Debug("its a sequence of %v things!", len(value.Content))
return traverseArray(matchingNode, pathNode)
// default:
return traverseArray(matchingNode, operation)
// if head == "+" {
// return n.appendArray(value, head, tail, pathStack)
// } else if len(value.Content) == 0 && head == "**" {
// return n.navigationStrategy.Visit(nodeContext)
// }
// return n.splatArray(value, head, tail, pathStack)
// }
// case yaml.AliasNode:
// log.Debug("its an alias!")
// DebugNode(value.Alias)
// if n.navigationStrategy.FollowAlias(nodeContext) {
// log.Debug("following the alias")
// return n.recurse(value.Alias, head, tail, pathStack)
// }
// return nil
case yaml.AliasNode:
log.Debug("its an alias!")
if followAlias {
matchingNode.Node = matchingNode.Node.Alias
return traverse(d, matchingNode, operation)
}
return []*CandidateNode{matchingNode}, nil
case yaml.DocumentNode:
log.Debug("digging into doc node")
return traverse(d, &CandidateNode{
Node: matchingNode.Node.Content[0],
Document: matchingNode.Document}, pathNode)
Document: matchingNode.Document}, operation)
default:
return nil, nil
}

View File

@ -83,6 +83,27 @@ var traversePathOperatorScenarios = []expressionScenario{
"D0, P[a mad], (!!str)::things\n",
},
},
{
document: `{a: &cat {c: frog}, b: *cat}`,
expression: `.b`,
expected: []string{
"D0, P[b], (alias)::*cat\n",
},
},
{
document: `{a: &cat {c: frog}, b: *cat}`,
expression: `.b.[]`,
expected: []string{
"D0, P[b c], (!!str)::frog\n",
},
},
{
document: `{a: &cat {c: frog}, b: *cat}`,
expression: `.b.c`,
expected: []string{
"D0, P[b c], (!!str)::frog\n",
},
},
}
func TestTraversePathOperatorScenarios(t *testing.T) {