mirror of
https://github.com/mikefarah/yq.git
synced 2024-12-19 20:19:04 +00:00
Added optional traverse flag
This commit is contained in:
parent
cc08afc435
commit
bb3ffd40b5
@ -32,6 +32,21 @@ b: apple
|
|||||||
c: banana
|
c: banana
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Optional Splat
|
||||||
|
Just like splat, but won't error if you run it against scalars
|
||||||
|
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
cat
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.[]' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
```
|
||||||
|
|
||||||
## Special characters
|
## Special characters
|
||||||
Use quotes with brackets around path elements with special characters
|
Use quotes with brackets around path elements with special characters
|
||||||
|
|
||||||
|
@ -55,7 +55,15 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
|
|||||||
log.Debugf("deleteing open bracket from opstack")
|
log.Debugf("deleteing open bracket from opstack")
|
||||||
|
|
||||||
//and append a collect to the opStack
|
//and append a collect to the opStack
|
||||||
result = append(result, &Operation{OperationType: collectOperator})
|
// hack - see if there's the optional traverse flag
|
||||||
|
// on the close op - move it to the collect op.
|
||||||
|
// allows for .["cat"]?
|
||||||
|
prefs := traversePreferences{}
|
||||||
|
closeTokenMatch := string(currentToken.Match.Bytes)
|
||||||
|
if closeTokenMatch[len(closeTokenMatch)-1:] == "?" {
|
||||||
|
prefs.OptionalTraverse = true
|
||||||
|
}
|
||||||
|
result = append(result, &Operation{OperationType: collectOperator, Preferences: prefs})
|
||||||
log.Debugf("put collect onto the result")
|
log.Debugf("put collect onto the result")
|
||||||
result = append(result, &Operation{OperationType: shortPipeOpType})
|
result = append(result, &Operation{OperationType: shortPipeOpType})
|
||||||
log.Debugf("put shortpipe onto the result")
|
log.Debugf("put shortpipe onto the result")
|
||||||
|
@ -62,6 +62,11 @@ var pathTests = []struct {
|
|||||||
append(make([]interface{}, 0), "b", "TRAVERSE_ARRAY", "[", "a", "]"),
|
append(make([]interface{}, 0), "b", "TRAVERSE_ARRAY", "[", "a", "]"),
|
||||||
append(make([]interface{}, 0), "b", "a", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
append(make([]interface{}, 0), "b", "a", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`.b[.a]?`,
|
||||||
|
append(make([]interface{}, 0), "b", "TRAVERSE_ARRAY", "[", "a", "]"),
|
||||||
|
append(make([]interface{}, 0), "b", "a", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
`.[]`,
|
`.[]`,
|
||||||
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
||||||
@ -72,6 +77,11 @@ var pathTests = []struct {
|
|||||||
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
||||||
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`.a[]?`,
|
||||||
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
||||||
|
append(make([]interface{}, 0), "a", "EMPTY", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
`.a.[]`,
|
`.a.[]`,
|
||||||
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "EMPTY", "]"),
|
||||||
@ -82,6 +92,11 @@ var pathTests = []struct {
|
|||||||
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
||||||
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`.a[0]?`,
|
||||||
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
||||||
|
append(make([]interface{}, 0), "a", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
`.a.[0]`,
|
`.a.[0]`,
|
||||||
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
append(make([]interface{}, 0), "a", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
||||||
|
@ -29,8 +29,9 @@ const (
|
|||||||
type token struct {
|
type token struct {
|
||||||
TokenType tokenType
|
TokenType tokenType
|
||||||
Operation *Operation
|
Operation *Operation
|
||||||
AssignOperation *Operation // e.g. tag (GetTag) op becomes AssignTag if '=' follows it
|
AssignOperation *Operation // e.g. tag (GetTag) op becomes AssignTag if '=' follows it
|
||||||
CheckForPostTraverse bool // e.g. [1]cat should really be [1].cat
|
CheckForPostTraverse bool // e.g. [1]cat should really be [1].cat
|
||||||
|
Match *machines.Match // match that created this token
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +146,7 @@ func assignAllCommentsOp(updateAssign bool) lex.Action {
|
|||||||
|
|
||||||
func literalToken(pType tokenType, 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: pType, CheckForPostTraverse: checkForPost}, nil
|
return &token{TokenType: pType, CheckForPostTraverse: checkForPost, Match: m}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +332,7 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`env\([^\)]+\)`), envOp(false))
|
lexer.Add([]byte(`env\([^\)]+\)`), envOp(false))
|
||||||
|
|
||||||
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(`\{`), literalToken(openCollectObject, false))
|
lexer.Add([]byte(`\{`), literalToken(openCollectObject, false))
|
||||||
lexer.Add([]byte(`\}`), literalToken(closeCollectObject, true))
|
lexer.Add([]byte(`\}`), literalToken(closeCollectObject, true))
|
||||||
lexer.Add([]byte(`\*[\+|\?d]*`), multiplyWithPrefs())
|
lexer.Add([]byte(`\*[\+|\?d]*`), multiplyWithPrefs())
|
||||||
|
@ -90,14 +90,19 @@ func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode
|
|||||||
// rhs is a collect expression that will yield indexes to retreive of the arrays
|
// rhs is a collect expression that will yield indexes to retreive of the arrays
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
prefs := traversePreferences{}
|
||||||
|
|
||||||
|
if expressionNode.Rhs.Rhs != nil && expressionNode.Rhs.Rhs.Operation.Preferences != nil {
|
||||||
|
prefs = expressionNode.Rhs.Rhs.Operation.Preferences.(traversePreferences)
|
||||||
|
}
|
||||||
var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Content
|
var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node.Content
|
||||||
|
|
||||||
//now we traverse the result of the lhs against the indices we found
|
//now we traverse the result of the lhs against the indices we found
|
||||||
result, err := traverseNodesWithArrayIndices(lhs, indicesToTraverse, traversePreferences{})
|
result, err := traverseNodesWithArrayIndices(lhs, indicesToTraverse, prefs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
@ -45,14 +45,13 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[1], (!!map)::{c: banana}\n",
|
"D0, P[1], (!!map)::{c: banana}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// description: "Optional Splat",
|
description: "Optional Splat",
|
||||||
// subdescription: "Just like splat, but won't error if you run it against scalars",
|
subdescription: "Just like splat, but won't error if you run it against scalars",
|
||||||
// document: `"cat"`,
|
document: `"cat"`,
|
||||||
// expression: `.[]?`,
|
expression: `.[]`,
|
||||||
// expected: []string{
|
expected: []string{},
|
||||||
// },
|
},
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
description: "Special characters",
|
description: "Special characters",
|
||||||
subdescription: "Use quotes with brackets around path elements with special characters",
|
subdescription: "Use quotes with brackets around path elements with special characters",
|
||||||
@ -112,13 +111,12 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
expression: `.a?`,
|
expression: `.a?`,
|
||||||
expected: []string{},
|
expected: []string{},
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// skipDoc: true,
|
skipDoc: true,
|
||||||
// document: `[1,2,3]`,
|
document: `[[1,2,3], {a: frog}]`,
|
||||||
// expression: `.["a"]?`,
|
expression: `.[] | .["a"]?`,
|
||||||
// expected: []string{
|
expected: []string{"D0, P[1 a], (!!str)::frog\n"},
|
||||||
// },
|
},
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: ``,
|
document: ``,
|
||||||
|
Loading…
Reference in New Issue
Block a user