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.UpdateAttributesFrom(other)
n.Node.Content = other.Node.Content n.Node.Content = other.Node.Content
n.Node.Value = other.Node.Value n.Node.Value = other.Node.Value
n.Node.Alias = other.Node.Alias
} }
func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode) { 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 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} // 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
@ -59,6 +57,7 @@ type Operation struct {
Value interface{} Value interface{}
StringValue string StringValue string
CandidateNode *CandidateNode // used for Value Path elements CandidateNode *CandidateNode // used for Value Path elements
Preferences interface{}
} }
func CreateValueOperation(value interface{}, stringValue string) *Operation { func CreateValueOperation(value interface{}, stringValue string) *Operation {
@ -132,6 +131,8 @@ func NodeToString(node *CandidateNode) string {
tag := value.Tag tag := value.Tag
if value.Kind == yaml.DocumentNode { if value.Kind == yaml.DocumentNode {
tag = "doc" 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()) 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:] lhsPath := rhs.Path[pathIndexToStartFrom:]
assignmentOp := &Operation{OperationType: AssignAttributes} assignmentOp := &Operation{OperationType: AssignAttributes}
if rhs.Node.Kind == yaml.ScalarNode { if rhs.Node.Kind == yaml.ScalarNode || rhs.Node.Kind == yaml.AliasNode {
assignmentOp.OperationType = Assign assignmentOp.OperationType = Assign
} }
rhsOp := &Operation{OperationType: ValueOp, CandidateNode: rhs} rhsOp := &Operation{OperationType: ValueOp, CandidateNode: rhs}

View File

@ -81,6 +81,20 @@ b:
"D0, P[], (!!map)::{a: {c: cat}}\n", "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) { func TestMultiplyOperatorScenarios(t *testing.T) {

View File

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

View File

@ -50,6 +50,16 @@ var recursiveDescentOperatorScenarios = []expressionScenario{
"D0, P[2], (!!bool)::true\n", "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) { func TestRecursiveDescentOperatorScenarios(t *testing.T) {

View File

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

View File

@ -83,6 +83,27 @@ var traversePathOperatorScenarios = []expressionScenario{
"D0, P[a mad], (!!str)::things\n", "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) { func TestTraversePathOperatorScenarios(t *testing.T) {