mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
more
This commit is contained in:
parent
49615f5581
commit
6a698332dd
@ -38,6 +38,7 @@ var TraversePath = &OperationType{Type: "TRAVERSE_PATH", NumArgs: 0, Precedence:
|
||||
var DocumentFilter = &OperationType{Type: "DOCUMENT_FILTER", 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: ValueOperator}
|
||||
var Not = &OperationType{Type: "NOT", NumArgs: 0, Precedence: 50, Handler: NotOperator}
|
||||
|
||||
var RecursiveDescent = &OperationType{Type: "RECURSIVE_DESCENT", NumArgs: 0, Precedence: 50, Handler: RecursiveDescentOperator}
|
||||
|
||||
|
@ -8,6 +8,9 @@ import (
|
||||
func isTruthy(c *CandidateNode) (bool, error) {
|
||||
node := c.Node
|
||||
value := true
|
||||
if node.Tag == "!!null" {
|
||||
return false, nil
|
||||
}
|
||||
if node.Kind == yaml.ScalarNode && node.Tag == "!!bool" {
|
||||
errDecoding := node.Decode(&value)
|
||||
if errDecoding != nil {
|
||||
|
@ -4,50 +4,19 @@ import (
|
||||
"github.com/elliotchance/orderedmap"
|
||||
)
|
||||
|
||||
func EqualsOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
|
||||
func EqualsOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
|
||||
log.Debugf("-- equalsOperation")
|
||||
var results = orderedmap.NewOrderedMap()
|
||||
|
||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
||||
candidate := el.Value.(*CandidateNode)
|
||||
log.Debug("equalsOperation checking %v", candidate)
|
||||
|
||||
matches, errInChild := hasMatch(d, candidate, pathNode.Lhs, pathNode.Rhs)
|
||||
if errInChild != nil {
|
||||
return nil, errInChild
|
||||
return crossFunction(d, matchingNodes, pathNode, isEquals)
|
||||
}
|
||||
|
||||
equalsCandidate := createBooleanCandidate(candidate, matches)
|
||||
results.Set(equalsCandidate.GetKey(), equalsCandidate)
|
||||
}
|
||||
func isEquals(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||
value := false
|
||||
|
||||
return results, nil
|
||||
if lhs.Node.Tag == "!!null" {
|
||||
value = (rhs.Node.Tag == "!!null")
|
||||
} else {
|
||||
value = Match(lhs.Node.Value, rhs.Node.Value)
|
||||
}
|
||||
|
||||
func hasMatch(d *dataTreeNavigator, candidate *CandidateNode, lhs *PathTreeNode, rhs *PathTreeNode) (bool, error) {
|
||||
childMap := orderedmap.NewOrderedMap()
|
||||
childMap.Set(candidate.GetKey(), candidate)
|
||||
childMatches, errChild := d.getMatchingNodes(childMap, lhs)
|
||||
log.Debug("got the LHS")
|
||||
if errChild != nil {
|
||||
return false, errChild
|
||||
}
|
||||
|
||||
// TODO = handle other RHS types
|
||||
return containsMatchingValue(childMatches, rhs.Operation.StringValue), nil
|
||||
}
|
||||
|
||||
func containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) bool {
|
||||
log.Debugf("-- findMatchingValues")
|
||||
|
||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
||||
node := el.Value.(*CandidateNode)
|
||||
log.Debugf("-- comparing %v to %v", node.Node.Value, valuePattern)
|
||||
if Match(node.Node.Value, valuePattern) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
log.Debugf("-- done findMatchingValues")
|
||||
|
||||
return false
|
||||
log.Debugf("%v == %v ? %v", NodeToString(lhs), NodeToString(rhs), value)
|
||||
return createBooleanCandidate(lhs, value), nil
|
||||
}
|
||||
|
@ -5,35 +5,49 @@ import (
|
||||
)
|
||||
|
||||
var equalsOperatorScenarios = []expressionScenario{
|
||||
// {
|
||||
// document: `[cat,goat,dog]`,
|
||||
// expression: `(.[] == "*at")`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!bool)::true\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `[cat,goat,dog]`,
|
||||
// expression: `.[] | (. == "*at")`,
|
||||
// expected: []string{
|
||||
// "D0, P[0], (!!bool)::true\n",
|
||||
// "D0, P[1], (!!bool)::true\n",
|
||||
// "D0, P[2], (!!bool)::false\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `[3, 4, 5]`,
|
||||
// expression: `.[] | (. == 4)`,
|
||||
// expected: []string{
|
||||
// "D0, P[0], (!!bool)::false\n",
|
||||
// "D0, P[1], (!!bool)::true\n",
|
||||
// "D0, P[2], (!!bool)::false\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `a: { cat: {b: apple, c: whatever}, pat: {b: banana} }`,
|
||||
// expression: `.a | (.[].b == "apple")`,
|
||||
// expected: []string{
|
||||
// "D0, P[a], (!!bool)::true\n",
|
||||
// },
|
||||
// },
|
||||
{
|
||||
document: `[cat,goat,dog]`,
|
||||
expression: `(.[] == "*at")`,
|
||||
document: ``,
|
||||
expression: `null == null`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::true\n",
|
||||
},
|
||||
}, {
|
||||
document: `[cat,goat,dog]`,
|
||||
expression: `.[] | (. == "*at")`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!bool)::true\n",
|
||||
"D0, P[1], (!!bool)::true\n",
|
||||
"D0, P[2], (!!bool)::false\n",
|
||||
},
|
||||
}, {
|
||||
document: `[3, 4, 5]`,
|
||||
expression: `.[] | (. == 4)`,
|
||||
expected: []string{
|
||||
"D0, P[0], (!!bool)::false\n",
|
||||
"D0, P[1], (!!bool)::true\n",
|
||||
"D0, P[2], (!!bool)::false\n",
|
||||
},
|
||||
}, {
|
||||
document: `a: { cat: {b: apple, c: whatever}, pat: {b: banana} }`,
|
||||
expression: `.a | (.[].b == "apple")`,
|
||||
expected: []string{
|
||||
"D0, P[a], (!!bool)::true\n",
|
||||
},
|
||||
},
|
||||
// {
|
||||
// document: ``,
|
||||
// expression: `null == ~`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!bool)::true\n",
|
||||
// },
|
||||
// },
|
||||
}
|
||||
|
||||
func TestEqualOperatorScenarios(t *testing.T) {
|
||||
|
@ -7,7 +7,9 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func MultiplyOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
|
||||
type CrossFunctionCalculation func(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
|
||||
|
||||
func crossFunction(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode, calculation CrossFunctionCalculation) (*orderedmap.OrderedMap, error) {
|
||||
lhs, err := d.getMatchingNodes(matchingNodes, pathNode.Lhs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -26,7 +28,7 @@ func MultiplyOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap
|
||||
|
||||
for rightEl := rhs.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
||||
rhsCandidate := rightEl.Value.(*CandidateNode)
|
||||
resultCandidate, err := multiply(d, lhsCandidate, rhsCandidate)
|
||||
resultCandidate, err := calculation(d, lhsCandidate, rhsCandidate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -34,7 +36,12 @@ func MultiplyOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap
|
||||
}
|
||||
|
||||
}
|
||||
return matchingNodes, nil
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func MultiplyOperator(d *dataTreeNavigator, matchingNodes *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
|
||||
log.Debugf("-- MultiplyOperator")
|
||||
return crossFunction(d, matchingNodes, pathNode, multiply)
|
||||
}
|
||||
|
||||
func multiply(d *dataTreeNavigator, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||
|
@ -6,67 +6,67 @@ import (
|
||||
|
||||
var multiplyOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: `{a: {also: [1]}, b: {also: me}}`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
|
||||
},
|
||||
}, {
|
||||
document: `{a: {also: me}, b: {also: [1]}}`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n",
|
||||
},
|
||||
}, {
|
||||
document: `{a: {also: me}, b: {also: {g: wizz}}}`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n",
|
||||
},
|
||||
}, {
|
||||
document: `{a: {also: {g: wizz}}, b: {also: me}}`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
|
||||
},
|
||||
}, {
|
||||
document: `{a: {also: {g: wizz}}, b: {also: [1]}}`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n",
|
||||
},
|
||||
}, {
|
||||
document: `{a: {also: [1]}, b: {also: {g: wizz}}}`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n",
|
||||
},
|
||||
}, {
|
||||
document: `{a: {things: great}, b: {also: me}}`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n",
|
||||
},
|
||||
}, {
|
||||
document: `a: {things: great}
|
||||
b:
|
||||
also: "me"
|
||||
`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
`D0, P[], (!!map)::a:
|
||||
things: great
|
||||
also: "me"
|
||||
b:
|
||||
also: "me"
|
||||
`,
|
||||
},
|
||||
}, {
|
||||
document: `{a: [1,2,3], b: [3,4,5]}`,
|
||||
expression: `.a * .b`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!map)::{a: [3, 4, 5], b: [3, 4, 5]}\n",
|
||||
},
|
||||
// document: `{a: {also: [1]}, b: {also: me}}`,
|
||||
// expression: `.a * .b`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `{a: {also: me}, b: {also: [1]}}`,
|
||||
// expression: `.a * .b`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `{a: {also: me}, b: {also: {g: wizz}}}`,
|
||||
// expression: `.a * .b`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `{a: {also: {g: wizz}}, b: {also: me}}`,
|
||||
// expression: `.a * .b`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!map)::{a: {also: me}, b: {also: me}}\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `{a: {also: {g: wizz}}, b: {also: [1]}}`,
|
||||
// expression: `.a * .b`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!map)::{a: {also: [1]}, b: {also: [1]}}\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `{a: {also: [1]}, b: {also: {g: wizz}}}`,
|
||||
// expression: `.a * .b`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `{a: {things: great}, b: {also: me}}`,
|
||||
// expression: `.a * .b`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!map)::{a: {things: great, also: me}, b: {also: me}}\n",
|
||||
// },
|
||||
// }, {
|
||||
// document: `a: {things: great}
|
||||
// b:
|
||||
// also: "me"
|
||||
// `,
|
||||
// expression: `(.a * .b)`,
|
||||
// expected: []string{
|
||||
// `D0, P[], (!!map)::a:
|
||||
// things: great
|
||||
// also: "me"
|
||||
// b:
|
||||
// also: "me"
|
||||
// `,
|
||||
// },
|
||||
// }, {
|
||||
// document: `{a: [1,2,3], b: [3,4,5]}`,
|
||||
// expression: `.a * .b`,
|
||||
// expected: []string{
|
||||
// "D0, P[], (!!map)::{a: [3, 4, 5], b: [3, 4, 5]}\n",
|
||||
// },
|
||||
},
|
||||
}
|
||||
|
||||
|
20
pkg/yqlib/treeops/operator_not.go
Normal file
20
pkg/yqlib/treeops/operator_not.go
Normal file
@ -0,0 +1,20 @@
|
||||
package treeops
|
||||
|
||||
import "github.com/elliotchance/orderedmap"
|
||||
|
||||
func NotOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
|
||||
log.Debugf("-- notOperation")
|
||||
var results = orderedmap.NewOrderedMap()
|
||||
|
||||
for el := matchMap.Front(); el != nil; el = el.Next() {
|
||||
candidate := el.Value.(*CandidateNode)
|
||||
log.Debug("notOperation checking %v", candidate)
|
||||
truthy, errDecoding := isTruthy(candidate)
|
||||
if errDecoding != nil {
|
||||
return nil, errDecoding
|
||||
}
|
||||
result := createBooleanCandidate(candidate, !truthy)
|
||||
results.Set(result.GetKey(), result)
|
||||
}
|
||||
return results, nil
|
||||
}
|
56
pkg/yqlib/treeops/operator_not_test.go
Normal file
56
pkg/yqlib/treeops/operator_not_test.go
Normal file
@ -0,0 +1,56 @@
|
||||
package treeops
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var notOperatorScenarios = []expressionScenario{
|
||||
{
|
||||
document: `cat`,
|
||||
expression: `. | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `1`,
|
||||
expression: `. | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `0`,
|
||||
expression: `. | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `~`,
|
||||
expression: `. | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::true\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `false`,
|
||||
expression: `. | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::true\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `true`,
|
||||
expression: `. | not`,
|
||||
expected: []string{
|
||||
"D0, P[], (!!bool)::false\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestNotOperatorScenarios(t *testing.T) {
|
||||
for _, tt := range notOperatorScenarios {
|
||||
testScenario(t, &tt)
|
||||
}
|
||||
}
|
@ -110,7 +110,7 @@ func traverseMap(candidate *CandidateNode, pathNode *Operation) ([]*CandidateNod
|
||||
}
|
||||
if len(newMatches) == 0 {
|
||||
//no matches, create one automagically
|
||||
valueNode := &yaml.Node{}
|
||||
valueNode := &yaml.Node{Tag: "!!null"}
|
||||
node.Content = append(node.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: pathNode.StringValue}, valueNode)
|
||||
newMatches = append(newMatches, &CandidateNode{
|
||||
Node: valueNode,
|
||||
@ -145,7 +145,7 @@ func traverseArray(candidate *CandidateNode, pathNode *Operation) ([]*CandidateN
|
||||
indexToUse := index
|
||||
contentLength := int64(len(candidate.Node.Content))
|
||||
for contentLength <= index {
|
||||
candidate.Node.Content = append(candidate.Node.Content, &yaml.Node{})
|
||||
candidate.Node.Content = append(candidate.Node.Content, &yaml.Node{Tag: "!!null"})
|
||||
contentLength = int64(len(candidate.Node.Content))
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,23 @@ var traversePathOperatorScenarios = []expressionScenario{
|
||||
"D0, P[a mad], (!!str)::things\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: {cat: apple, mad: things}}`,
|
||||
expression: `.a | (.cat, .mad, .fad)`,
|
||||
expected: []string{
|
||||
"D0, P[a cat], (!!str)::apple\n",
|
||||
"D0, P[a mad], (!!str)::things\n",
|
||||
"D0, P[a fad], ()::null\n",
|
||||
},
|
||||
},
|
||||
{
|
||||
document: `{a: {cat: apple, mad: things}}`,
|
||||
expression: `.a | (.cat, .mad, .fad) | select( (. == null) | not)`,
|
||||
expected: []string{
|
||||
"D0, P[a cat], (!!str)::apple\n",
|
||||
"D0, P[a mad], (!!str)::things\n",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestTraversePathOperatorScenarios(t *testing.T) {
|
||||
|
@ -3,5 +3,6 @@ package treeops
|
||||
import "github.com/elliotchance/orderedmap"
|
||||
|
||||
func ValueOperator(d *dataTreeNavigator, matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) {
|
||||
log.Debug("value = %v", pathNode.Operation.CandidateNode.Node.Value)
|
||||
return nodeToMap(pathNode.Operation.CandidateNode), nil
|
||||
}
|
||||
|
@ -177,6 +177,7 @@ func initLexer() (*lex.Lexer, error) {
|
||||
lexer.Add([]byte(`length`), opToken(Length))
|
||||
lexer.Add([]byte(`select`), opToken(Select))
|
||||
lexer.Add([]byte(`or`), opToken(Or))
|
||||
lexer.Add([]byte(`not`), opToken(Not))
|
||||
// lexer.Add([]byte(`and`), opToken())
|
||||
lexer.Add([]byte(`collect`), opToken(Collect))
|
||||
|
||||
|
@ -1 +1 @@
|
||||
{a: {cat: apple, mad: things}}
|
||||
{a: {also: [1]}, b: {also: me}}
|
@ -1,6 +1,10 @@
|
||||
{
|
||||
"a": {
|
||||
"cat": "apple",
|
||||
"mad": "things"
|
||||
"also": [
|
||||
1
|
||||
]
|
||||
},
|
||||
"b": {
|
||||
"also": "me"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user