mirror of
https://github.com/taigrr/yq
synced 2025-01-18 04:53:17 -08:00
Compare commits
No commits in common. "master" and "v4.8.0" have entirely different histories.
@ -11,7 +11,7 @@ var (
|
|||||||
GitDescribe string
|
GitDescribe string
|
||||||
|
|
||||||
// Version is main version number that is being run at the moment.
|
// Version is main version number that is being run at the moment.
|
||||||
Version = "4.9.3"
|
Version = "4.8.0"
|
||||||
|
|
||||||
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
|
// VersionPrerelease is a pre-release marker for the version. If this is "" (empty string)
|
||||||
// then it means that it is a final release. Otherwise, this is a pre-release
|
// then it means that it is a final release. Otherwise, this is a pre-release
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM mikefarah/yq:4.9.3
|
FROM mikefarah/yq:4.8.0
|
||||||
|
|
||||||
COPY entrypoint.sh /entrypoint.sh
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
@ -12,14 +12,6 @@ type Context struct {
|
|||||||
DontAutoCreate bool
|
DontAutoCreate bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Context) SingleReadonlyChildContext(candidate *CandidateNode) Context {
|
|
||||||
list := list.New()
|
|
||||||
list.PushBack(candidate)
|
|
||||||
newContext := n.ChildContext(list)
|
|
||||||
newContext.DontAutoCreate = true
|
|
||||||
return newContext
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Context) SingleChildContext(candidate *CandidateNode) Context {
|
func (n *Context) SingleChildContext(candidate *CandidateNode) Context {
|
||||||
list := list.New()
|
list := list.New()
|
||||||
list.PushBack(candidate)
|
list.PushBack(candidate)
|
||||||
@ -60,9 +52,3 @@ func (n *Context) Clone() Context {
|
|||||||
}
|
}
|
||||||
return clone
|
return clone
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Context) ReadOnlyClone() Context {
|
|
||||||
clone := n.Clone()
|
|
||||||
clone.DontAutoCreate = true
|
|
||||||
return clone
|
|
||||||
}
|
|
||||||
|
@ -169,22 +169,6 @@ b: &meow purr
|
|||||||
a: *meow
|
a: *meow
|
||||||
```
|
```
|
||||||
|
|
||||||
## Set alias to blank does nothing
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
b: &meow purr
|
|
||||||
a: cat
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.a alias = ""' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
b: &meow purr
|
|
||||||
a: cat
|
|
||||||
```
|
|
||||||
|
|
||||||
## Set alias relatively using assign-update
|
## Set alias relatively using assign-update
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -1,14 +1,5 @@
|
|||||||
The `or` and `and` operators take two parameters and return a boolean result.
|
The `or` and `and` operators take two parameters and return a boolean result. `not` flips a boolean from true to false, or vice versa. These are most commonly used with the `select` operator to filter particular nodes.
|
||||||
|
## OR example
|
||||||
`not` flips a boolean from true to false, or vice versa.
|
|
||||||
|
|
||||||
`any` will return `true` if there are any `true` values in a array sequence, and `all` will return true if _all_ elements in an array are true.
|
|
||||||
|
|
||||||
`any_c(condition)` and `all_c(condition)` are like `any` and `all` but they take a condition expression that is used against each element to determine if it's `true`. Note: in `jq` you can simply pass a condition to `any` or `all` and it simply works - `yq` isn't that clever..yet
|
|
||||||
|
|
||||||
These are most commonly used with the `select` operator to filter particular nodes.
|
|
||||||
|
|
||||||
## `or` example
|
|
||||||
Running
|
Running
|
||||||
```bash
|
```bash
|
||||||
yq eval --null-input 'true or false'
|
yq eval --null-input 'true or false'
|
||||||
@ -18,7 +9,7 @@ will output
|
|||||||
true
|
true
|
||||||
```
|
```
|
||||||
|
|
||||||
## `and` example
|
## AND example
|
||||||
Running
|
Running
|
||||||
```bash
|
```bash
|
||||||
yq eval --null-input 'true and false'
|
yq eval --null-input 'true and false'
|
||||||
@ -50,104 +41,6 @@ will output
|
|||||||
b: fly
|
b: fly
|
||||||
```
|
```
|
||||||
|
|
||||||
## `any` returns true if any boolean in a given array is true
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- false
|
|
||||||
- true
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval 'any' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
true
|
|
||||||
```
|
|
||||||
|
|
||||||
## `any` returns false for an empty array
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
[]
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval 'any' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
false
|
|
||||||
```
|
|
||||||
|
|
||||||
## `any_c` returns true if any element in the array is true for the given condition.
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a:
|
|
||||||
- rad
|
|
||||||
- awesome
|
|
||||||
b:
|
|
||||||
- meh
|
|
||||||
- whatever
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] |= any_c(. == "awesome")' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a: true
|
|
||||||
b: false
|
|
||||||
```
|
|
||||||
|
|
||||||
## `all` returns true if all booleans in a given array are true
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- true
|
|
||||||
- true
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval 'all' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
true
|
|
||||||
```
|
|
||||||
|
|
||||||
## `all` returns true for an empty array
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
[]
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval 'all' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
true
|
|
||||||
```
|
|
||||||
|
|
||||||
## `all_c` returns true if all elements in the array are true for the given condition.
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
a:
|
|
||||||
- rad
|
|
||||||
- awesome
|
|
||||||
b:
|
|
||||||
- meh
|
|
||||||
- 12
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval '.[] |= all_c(tag == "!!str")' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
a: true
|
|
||||||
b: false
|
|
||||||
```
|
|
||||||
|
|
||||||
## Not true is false
|
## Not true is false
|
||||||
Running
|
Running
|
||||||
```bash
|
```bash
|
||||||
|
@ -35,19 +35,6 @@ will output
|
|||||||
value: b
|
value: b
|
||||||
```
|
```
|
||||||
|
|
||||||
## to_entries null
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
null
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval 'to_entries' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
## from_entries map
|
## from_entries map
|
||||||
Given a sample.yml file of:
|
Given a sample.yml file of:
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
This is used to filter out duplicated items in an array.
|
|
||||||
|
|
||||||
## Unique array of scalars (string/numbers)
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- 1
|
|
||||||
- 2
|
|
||||||
- 3
|
|
||||||
- 2
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval 'unique' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
- 1
|
|
||||||
- 2
|
|
||||||
- 3
|
|
||||||
```
|
|
||||||
|
|
||||||
## Unique nulls
|
|
||||||
Unique works on the node value, so it considers different representations of nulls to be different
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- ~
|
|
||||||
- null
|
|
||||||
- ~
|
|
||||||
- null
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval 'unique' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
- ~
|
|
||||||
- null
|
|
||||||
```
|
|
||||||
|
|
||||||
## Unique all nulls
|
|
||||||
Run against the node tag to unique all the nulls
|
|
||||||
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- ~
|
|
||||||
- null
|
|
||||||
- ~
|
|
||||||
- null
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval 'unique_by(tag)' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
- ~
|
|
||||||
```
|
|
||||||
|
|
||||||
## Unique array object fields
|
|
||||||
Given a sample.yml file of:
|
|
||||||
```yaml
|
|
||||||
- name: harry
|
|
||||||
pet: cat
|
|
||||||
- name: billy
|
|
||||||
pet: dog
|
|
||||||
- name: harry
|
|
||||||
pet: dog
|
|
||||||
```
|
|
||||||
then
|
|
||||||
```bash
|
|
||||||
yq eval 'unique_by(.name)' sample.yml
|
|
||||||
```
|
|
||||||
will output
|
|
||||||
```yaml
|
|
||||||
- name: harry
|
|
||||||
pet: cat
|
|
||||||
- name: billy
|
|
||||||
pet: dog
|
|
||||||
```
|
|
||||||
|
|
@ -62,55 +62,20 @@ cat
|
|||||||
|
|
||||||
Note that this node holds not only its value 'cat', but comments and metadata too, including path and parent information.
|
Note that this node holds not only its value 'cat', but comments and metadata too, including path and parent information.
|
||||||
|
|
||||||
The `=` operator then pipes the 'root' context through the `rhs` expression of `.b` to return the node
|
The `=` operator then pipes the 'root' context through the `rhs` expression of `.b` to retun the node
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
dog
|
dog
|
||||||
```
|
```
|
||||||
|
|
||||||
Both sides have now been evaluated, so now the operator copies across the value from the RHS to the value on the LHS, and it returns the now updated context:
|
Both sides have now been evaluated, so the operator performs its actual operation of assignment, and copies across the value from the RHS to the value on the LHS, and it returns the now updated 'root' context:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
a: dog
|
a: cat
|
||||||
b: dog
|
b: dog
|
||||||
```
|
```
|
||||||
|
|
||||||
# Relative update (e.g. `|=`)
|
# Relative update (e.g. `|=`)
|
||||||
There is another form of the `=` operator which we call the relative form. It's very similar to `=` but with one key difference when evaluating the RHS expression.
|
There is another form of the `=` operator which we call the relative form. It's very similar to `=` but with one key differnce when evaluating the RHS expression.
|
||||||
|
|
||||||
In the plain form, we pass in the 'root' level context to the RHS expression. In relative form, we pass in _each result of the LHS_ to the RHS expression. Let's go through an example.
|
In the plain form, we pass in the 'root' level context to the RHS expresssion. In relative form, we pass in each result of the LHS to the RHS expression. Let's go through an example.
|
||||||
|
|
||||||
Given a document like:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
a: 1
|
|
||||||
b: thing
|
|
||||||
```
|
|
||||||
|
|
||||||
with an expression:
|
|
||||||
|
|
||||||
```
|
|
||||||
.a |= . + 1
|
|
||||||
```
|
|
||||||
|
|
||||||
Similar to the `=` operator, `|=` takes two operands, the LHS and RHS.
|
|
||||||
|
|
||||||
It pipes the current context (the whole document) through the LHS expression of `.a` to get the node value:
|
|
||||||
|
|
||||||
```
|
|
||||||
1
|
|
||||||
```
|
|
||||||
|
|
||||||
Now it pipes _that LHS context_ into the RHS expression `. + 1` (whereas in the `=` plain form it piped the original document context into the RHS) to yield:
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
2
|
|
||||||
```
|
|
||||||
|
|
||||||
The assignment operator then copies across the value from the RHS to the value on the LHS, and it returns the now updated 'root' context:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
a: 2
|
|
||||||
b: thing
|
|
||||||
```
|
|
||||||
|
@ -1,9 +1 @@
|
|||||||
The `or` and `and` operators take two parameters and return a boolean result.
|
The `or` and `and` operators take two parameters and return a boolean result. `not` flips a boolean from true to false, or vice versa. These are most commonly used with the `select` operator to filter particular nodes.
|
||||||
|
|
||||||
`not` flips a boolean from true to false, or vice versa.
|
|
||||||
|
|
||||||
`any` will return `true` if there are any `true` values in a array sequence, and `all` will return true if _all_ elements in an array are true.
|
|
||||||
|
|
||||||
`any_c(condition)` and `all_c(condition)` are like `any` and `all` but they take a condition expression that is used against each element to determine if it's `true`. Note: in `jq` you can simply pass a condition to `any` or `all` and it simply works - `yq` isn't that clever..yet
|
|
||||||
|
|
||||||
These are most commonly used with the `select` operator to filter particular nodes.
|
|
@ -1 +0,0 @@
|
|||||||
This is used to filter out duplicated items in an array.
|
|
@ -54,7 +54,7 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
|
|||||||
opStack = opStack[0 : len(opStack)-1]
|
opStack = opStack[0 : len(opStack)-1]
|
||||||
log.Debugf("deleteing open bracket from opstack")
|
log.Debugf("deleteing open bracket from opstack")
|
||||||
|
|
||||||
//and append a collect to the result
|
//and append a collect to the opStack
|
||||||
// hack - see if there's the optional traverse flag
|
// hack - see if there's the optional traverse flag
|
||||||
// on the close op - move it to the collect op.
|
// on the close op - move it to the collect op.
|
||||||
// allows for .["cat"]?
|
// allows for .["cat"]?
|
||||||
@ -68,12 +68,6 @@ func (p *expressionPostFixerImpl) ConvertToPostfix(infixTokens []*token) ([]*Ope
|
|||||||
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")
|
||||||
|
|
||||||
//traverseArrayCollect is a sneaky op that needs to be included too
|
|
||||||
//when closing a []
|
|
||||||
if len(opStack) > 0 && opStack[len(opStack)-1].Operation != nil && opStack[len(opStack)-1].Operation.OperationType == traverseArrayOpType {
|
|
||||||
opStack, result = popOpToResult(opStack, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -12,21 +12,6 @@ var pathTests = []struct {
|
|||||||
expectedTokens []interface{}
|
expectedTokens []interface{}
|
||||||
expectedPostFix []interface{}
|
expectedPostFix []interface{}
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
`.[0]`,
|
|
||||||
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "0 (int64)", "]"),
|
|
||||||
append(make([]interface{}, 0), "SELF", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`.[0][1]`,
|
|
||||||
append(make([]interface{}, 0), "SELF", "TRAVERSE_ARRAY", "[", "0 (int64)", "]", "TRAVERSE_ARRAY", "[", "1 (int64)", "]"),
|
|
||||||
append(make([]interface{}, 0), "SELF", "0 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY", "1 (int64)", "COLLECT", "SHORT_PIPE", "TRAVERSE_ARRAY"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
`"\""`,
|
|
||||||
append(make([]interface{}, 0), "\" (string)"),
|
|
||||||
append(make([]interface{}, 0), "\" (string)"),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
`[]|join(".")`,
|
`[]|join(".")`,
|
||||||
append(make([]interface{}, 0), "[", "EMPTY", "]", "PIPE", "JOIN", "(", ". (string)", ")"),
|
append(make([]interface{}, 0), "[", "EMPTY", "]", "PIPE", "JOIN", "(", ". (string)", ")"),
|
||||||
|
@ -189,7 +189,6 @@ func stringValue(wrapped bool) lex.Action {
|
|||||||
if wrapped {
|
if wrapped {
|
||||||
value = unwrap(value)
|
value = unwrap(value)
|
||||||
}
|
}
|
||||||
value = strings.ReplaceAll(value, "\\\"", "\"")
|
|
||||||
return &token{TokenType: operationToken, Operation: createValueOperation(value, value)}, nil
|
return &token{TokenType: operationToken, Operation: createValueOperation(value, value)}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,8 +259,6 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`sortKeys`), opToken(sortKeysOpType))
|
lexer.Add([]byte(`sortKeys`), opToken(sortKeysOpType))
|
||||||
lexer.Add([]byte(`select`), opToken(selectOpType))
|
lexer.Add([]byte(`select`), opToken(selectOpType))
|
||||||
lexer.Add([]byte(`has`), opToken(hasOpType))
|
lexer.Add([]byte(`has`), opToken(hasOpType))
|
||||||
lexer.Add([]byte(`unique`), opToken(uniqueOpType))
|
|
||||||
lexer.Add([]byte(`unique_by`), opToken(uniqueByOpType))
|
|
||||||
lexer.Add([]byte(`explode`), opToken(explodeOpType))
|
lexer.Add([]byte(`explode`), opToken(explodeOpType))
|
||||||
lexer.Add([]byte(`or`), opToken(orOpType))
|
lexer.Add([]byte(`or`), opToken(orOpType))
|
||||||
lexer.Add([]byte(`and`), opToken(andOpType))
|
lexer.Add([]byte(`and`), opToken(andOpType))
|
||||||
@ -277,11 +274,6 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`join`), opToken(joinStringOpType))
|
lexer.Add([]byte(`join`), opToken(joinStringOpType))
|
||||||
lexer.Add([]byte(`sub`), opToken(subStringOpType))
|
lexer.Add([]byte(`sub`), opToken(subStringOpType))
|
||||||
|
|
||||||
lexer.Add([]byte(`any`), opToken(anyOpType))
|
|
||||||
lexer.Add([]byte(`any_c`), opToken(anyConditionOpType))
|
|
||||||
lexer.Add([]byte(`all`), opToken(allOpType))
|
|
||||||
lexer.Add([]byte(`all_c`), opToken(allConditionOpType))
|
|
||||||
|
|
||||||
lexer.Add([]byte(`split`), opToken(splitStringOpType))
|
lexer.Add([]byte(`split`), opToken(splitStringOpType))
|
||||||
lexer.Add([]byte(`keys`), opToken(keysOpType))
|
lexer.Add([]byte(`keys`), opToken(keysOpType))
|
||||||
|
|
||||||
@ -335,7 +327,7 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`[Nn][Uu][Ll][Ll]`), nullValue())
|
lexer.Add([]byte(`[Nn][Uu][Ll][Ll]`), nullValue())
|
||||||
lexer.Add([]byte(`~`), nullValue())
|
lexer.Add([]byte(`~`), nullValue())
|
||||||
|
|
||||||
lexer.Add([]byte(`"([^"\\]*(\\.[^"\\]*)*)"`), stringValue(true))
|
lexer.Add([]byte(`"[^"]*"`), stringValue(true))
|
||||||
lexer.Add([]byte(`strenv\([^\)]+\)`), envOp(true))
|
lexer.Add([]byte(`strenv\([^\)]+\)`), envOp(true))
|
||||||
lexer.Add([]byte(`env\([^\)]+\)`), envOp(false))
|
lexer.Add([]byte(`env\([^\)]+\)`), envOp(false))
|
||||||
|
|
||||||
@ -411,19 +403,16 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
|
|||||||
skipNextToken = false
|
skipNextToken = false
|
||||||
currentToken := tokens[index]
|
currentToken := tokens[index]
|
||||||
|
|
||||||
log.Debug("processing %v", currentToken.toString(true))
|
|
||||||
|
|
||||||
if currentToken.TokenType == traverseArrayCollect {
|
if currentToken.TokenType == traverseArrayCollect {
|
||||||
//need to put a traverse array then a collect currentToken
|
//need to put a traverse array then a collect currentToken
|
||||||
// do this by adding traverse then converting currentToken to collect
|
// do this by adding traverse then converting currentToken to collect
|
||||||
|
|
||||||
if index == 0 || tokens[index-1].TokenType != operationToken ||
|
if index == 0 || tokens[index-1].TokenType != operationToken ||
|
||||||
tokens[index-1].Operation.OperationType != traversePathOpType {
|
tokens[index-1].Operation.OperationType != traversePathOpType {
|
||||||
log.Debug(" adding self")
|
|
||||||
op := &Operation{OperationType: selfReferenceOpType, StringValue: "SELF"}
|
op := &Operation{OperationType: selfReferenceOpType, StringValue: "SELF"}
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
}
|
}
|
||||||
log.Debug(" adding traverse array")
|
|
||||||
op := &Operation{OperationType: traverseArrayOpType, StringValue: "TRAVERSE_ARRAY"}
|
op := &Operation{OperationType: traverseArrayOpType, StringValue: "TRAVERSE_ARRAY"}
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
|
|
||||||
@ -434,19 +423,17 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
|
|||||||
if index != len(tokens)-1 && currentToken.AssignOperation != nil &&
|
if index != len(tokens)-1 && currentToken.AssignOperation != nil &&
|
||||||
tokens[index+1].TokenType == operationToken &&
|
tokens[index+1].TokenType == operationToken &&
|
||||||
tokens[index+1].Operation.OperationType == assignOpType {
|
tokens[index+1].Operation.OperationType == assignOpType {
|
||||||
log.Debug(" its an update assign")
|
|
||||||
currentToken.Operation = currentToken.AssignOperation
|
currentToken.Operation = currentToken.AssignOperation
|
||||||
currentToken.Operation.UpdateAssign = tokens[index+1].Operation.UpdateAssign
|
currentToken.Operation.UpdateAssign = tokens[index+1].Operation.UpdateAssign
|
||||||
skipNextToken = true
|
skipNextToken = true
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug(" adding token to the fixed list")
|
|
||||||
postProcessedTokens = append(postProcessedTokens, currentToken)
|
postProcessedTokens = append(postProcessedTokens, currentToken)
|
||||||
|
|
||||||
if index != len(tokens)-1 &&
|
if index != len(tokens)-1 &&
|
||||||
((currentToken.TokenType == openCollect && tokens[index+1].TokenType == closeCollect) ||
|
((currentToken.TokenType == openCollect && tokens[index+1].TokenType == closeCollect) ||
|
||||||
(currentToken.TokenType == openCollectObject && tokens[index+1].TokenType == closeCollectObject)) {
|
(currentToken.TokenType == openCollectObject && tokens[index+1].TokenType == closeCollectObject)) {
|
||||||
log.Debug(" adding empty")
|
|
||||||
op := &Operation{OperationType: emptyOpType, StringValue: "EMPTY"}
|
op := &Operation{OperationType: emptyOpType, StringValue: "EMPTY"}
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
}
|
}
|
||||||
@ -454,19 +441,12 @@ func (p *expressionTokeniserImpl) handleToken(tokens []*token, index int, postPr
|
|||||||
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
||||||
tokens[index+1].TokenType == operationToken &&
|
tokens[index+1].TokenType == operationToken &&
|
||||||
tokens[index+1].Operation.OperationType == traversePathOpType {
|
tokens[index+1].Operation.OperationType == traversePathOpType {
|
||||||
log.Debug(" adding pipe because the next thing is traverse")
|
|
||||||
op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"}
|
op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"}
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
}
|
}
|
||||||
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
if index != len(tokens)-1 && currentToken.CheckForPostTraverse &&
|
||||||
tokens[index+1].TokenType == openCollect {
|
tokens[index+1].TokenType == openCollect {
|
||||||
|
|
||||||
// if tokens[index].TokenType == closeCollect {
|
|
||||||
// log.Debug(" adding pipe because next is opencollect")
|
|
||||||
// op := &Operation{OperationType: shortPipeOpType, Value: "PIPE"}
|
|
||||||
// postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
|
||||||
// }
|
|
||||||
log.Debug(" adding traverArray because next is opencollect")
|
|
||||||
op := &Operation{OperationType: traverseArrayOpType}
|
op := &Operation{OperationType: traverseArrayOpType}
|
||||||
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
postProcessedTokens = append(postProcessedTokens, &token{TokenType: operationToken, Operation: op})
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@ type operationType struct {
|
|||||||
Handler operatorHandler
|
Handler operatorHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// operators TODO:
|
||||||
|
// - mergeEmpty (sets only if the document is empty, do I do that now?)
|
||||||
|
|
||||||
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
|
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
|
||||||
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
|
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
|
||||||
var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator}
|
var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator}
|
||||||
@ -58,11 +61,6 @@ var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence:
|
|||||||
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
|
||||||
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: collectOperator}
|
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: collectOperator}
|
||||||
|
|
||||||
var anyOpType = &operationType{Type: "ANY", NumArgs: 0, Precedence: 50, Handler: anyOperator}
|
|
||||||
var allOpType = &operationType{Type: "ALL", NumArgs: 0, Precedence: 50, Handler: allOperator}
|
|
||||||
var anyConditionOpType = &operationType{Type: "ANY_CONDITION", NumArgs: 1, Precedence: 50, Handler: anyOperator}
|
|
||||||
var allConditionOpType = &operationType{Type: "ALL_CONDITION", NumArgs: 1, Precedence: 50, Handler: allOperator}
|
|
||||||
|
|
||||||
var toEntriesOpType = &operationType{Type: "TO_ENTRIES", NumArgs: 0, Precedence: 50, Handler: toEntriesOperator}
|
var toEntriesOpType = &operationType{Type: "TO_ENTRIES", NumArgs: 0, Precedence: 50, Handler: toEntriesOperator}
|
||||||
var fromEntriesOpType = &operationType{Type: "FROM_ENTRIES", NumArgs: 0, Precedence: 50, Handler: fromEntriesOperator}
|
var fromEntriesOpType = &operationType{Type: "FROM_ENTRIES", NumArgs: 0, Precedence: 50, Handler: fromEntriesOperator}
|
||||||
var withEntriesOpType = &operationType{Type: "WITH_ENTRIES", NumArgs: 1, Precedence: 50, Handler: withEntriesOperator}
|
var withEntriesOpType = &operationType{Type: "WITH_ENTRIES", NumArgs: 1, Precedence: 50, Handler: withEntriesOperator}
|
||||||
@ -101,8 +99,6 @@ var recursiveDescentOpType = &operationType{Type: "RECURSIVE_DESCENT", NumArgs:
|
|||||||
|
|
||||||
var selectOpType = &operationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: selectOperator}
|
var selectOpType = &operationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: selectOperator}
|
||||||
var hasOpType = &operationType{Type: "HAS", NumArgs: 1, Precedence: 50, Handler: hasOperator}
|
var hasOpType = &operationType{Type: "HAS", NumArgs: 1, Precedence: 50, Handler: hasOperator}
|
||||||
var uniqueOpType = &operationType{Type: "UNIQUE", NumArgs: 0, Precedence: 50, Handler: unique}
|
|
||||||
var uniqueByOpType = &operationType{Type: "UNIQUE_BY", NumArgs: 1, Precedence: 50, Handler: uniqueBy}
|
|
||||||
var deleteChildOpType = &operationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: deleteChildOperator}
|
var deleteChildOpType = &operationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: deleteChildOperator}
|
||||||
|
|
||||||
type Operation struct {
|
type Operation struct {
|
||||||
|
@ -39,7 +39,7 @@ func toNodes(candidate *CandidateNode) []*yaml.Node {
|
|||||||
func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("Add operator")
|
log.Debugf("Add operator")
|
||||||
|
|
||||||
return crossFunction(d, context.ReadOnlyClone(), expressionNode, add, false)
|
return crossFunction(d, context, expressionNode, add, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func add(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
|
@ -14,22 +14,6 @@ var addOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[1 a], (!!int)::3\n",
|
"D0, P[1 a], (!!int)::3\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{}`,
|
|
||||||
expression: "(.a + .b) as $x",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: 0`,
|
|
||||||
expression: ".a += .b.c",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: 0\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Concatenate and assign arrays",
|
description: "Concatenate and assign arrays",
|
||||||
document: `{a: {val: thing, b: [cat,dog]}}`,
|
document: `{a: {val: thing, b: [cat,dog]}}`,
|
||||||
|
@ -2,7 +2,7 @@ package yqlib
|
|||||||
|
|
||||||
func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func alternativeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- alternative")
|
log.Debugf("-- alternative")
|
||||||
return crossFunction(d, context.ReadOnlyClone(), expressionNode, alternativeFunc, true)
|
return crossFunction(d, context, expressionNode, alternativeFunc, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func alternativeFunc(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
|
@ -5,20 +5,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var alternativeOperatorScenarios = []expressionScenario{
|
var alternativeOperatorScenarios = []expressionScenario{
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
expression: `.b // .c`,
|
|
||||||
document: `a: bridge`,
|
|
||||||
expected: []string{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
expression: `(.b // "hello") as $x`,
|
|
||||||
document: `a: bridge`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: bridge\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "LHS is defined",
|
description: "LHS is defined",
|
||||||
expression: `.a // "hello"`,
|
expression: `.a // "hello"`,
|
||||||
|
@ -12,7 +12,7 @@ func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
|
|
||||||
aliasName := ""
|
aliasName := ""
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
log.Debugf("Setting aliasName : %v", candidate.GetKey())
|
log.Debugf("Setting aliasName : %v", candidate.GetKey())
|
||||||
|
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
@ -41,10 +41,8 @@ func assignAliasOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if aliasName != "" {
|
candidate.Node.Kind = yaml.AliasNode
|
||||||
candidate.Node.Kind = yaml.AliasNode
|
candidate.Node.Value = aliasName
|
||||||
candidate.Node.Value = aliasName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return context, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
@ -68,7 +66,7 @@ func assignAnchorOperator(d *dataTreeNavigator, context Context, expressionNode
|
|||||||
|
|
||||||
anchorName := ""
|
anchorName := ""
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
@ -89,7 +87,7 @@ func assignAnchorOperator(d *dataTreeNavigator, context Context, expressionNode
|
|||||||
log.Debugf("Setting anchorName of : %v", candidate.GetKey())
|
log.Debugf("Setting anchorName of : %v", candidate.GetKey())
|
||||||
|
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
@ -58,22 +58,6 @@ var anchorOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::a: &cat {b: cat}\n",
|
"D0, P[], (doc)::a: &cat {b: cat}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: {c: cat}`,
|
|
||||||
expression: `.a anchor |= .b`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: {c: cat}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: {c: cat}`,
|
|
||||||
expression: `.a anchor = .b`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: {c: cat}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Get alias",
|
description: "Get alias",
|
||||||
document: `{b: &billyBob meow, a: *billyBob}`,
|
document: `{b: &billyBob meow, a: *billyBob}`,
|
||||||
@ -90,30 +74,6 @@ var anchorOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::{b: &meow purr, a: *meow}\n",
|
"D0, P[], (doc)::{b: &meow purr, a: *meow}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "Set alias to blank does nothing",
|
|
||||||
document: `{b: &meow purr, a: cat}`,
|
|
||||||
expression: `.a alias = ""`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{b: &meow purr, a: cat}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{b: &meow purr, a: cat}`,
|
|
||||||
expression: `.a alias = .c`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{b: &meow purr, a: cat}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{b: &meow purr, a: cat}`,
|
|
||||||
expression: `.a alias |= .c`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{b: &meow purr, a: cat}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Set alias relatively using assign-update",
|
description: "Set alias relatively using assign-update",
|
||||||
document: `{b: &meow purr, a: {f: meow}}`,
|
document: `{b: &meow purr, a: {f: meow}}`,
|
||||||
@ -199,7 +159,7 @@ foobar:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAnchorAliasOperatorScenarios(t *testing.T) {
|
func TestAnchorAliaseOperatorScenarios(t *testing.T) {
|
||||||
for _, tt := range anchorOperatorScenarios {
|
for _, tt := range anchorOperatorScenarios {
|
||||||
testScenario(t, &tt)
|
testScenario(t, &tt)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ func assignUpdateOperator(d *dataTreeNavigator, context Context, expressionNode
|
|||||||
}
|
}
|
||||||
var rhs Context
|
var rhs Context
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err = d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err = d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
@ -44,7 +44,7 @@ func assignAttributesOperator(d *dataTreeNavigator, context Context, expressionN
|
|||||||
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
|
@ -12,22 +12,6 @@ var assignOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], ()::a:\n b: cat\nx: frog\n",
|
"D0, P[], ()::a:\n b: cat\nx: frog\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{}",
|
|
||||||
expression: `.a |= .b`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{a: null}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{}",
|
|
||||||
expression: `.a = .b`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{a: null}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Update node to be the child value",
|
description: "Update node to be the child value",
|
||||||
document: `{a: {b: {g: foof}}}`,
|
document: `{a: {b: {g: foof}}}`,
|
||||||
|
@ -2,13 +2,14 @@ package yqlib
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v3"
|
yaml "gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func isTruthyNode(node *yaml.Node) (bool, error) {
|
func isTruthy(c *CandidateNode) (bool, error) {
|
||||||
|
node := unwrapDoc(c.Node)
|
||||||
value := true
|
value := true
|
||||||
|
|
||||||
if node.Tag == "!!null" {
|
if node.Tag == "!!null" {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@ -22,11 +23,6 @@ func isTruthyNode(node *yaml.Node) (bool, error) {
|
|||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTruthy(c *CandidateNode) (bool, error) {
|
|
||||||
node := unwrapDoc(c.Node)
|
|
||||||
return isTruthyNode(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
type boolOp func(bool, bool) bool
|
type boolOp func(bool, bool) bool
|
||||||
|
|
||||||
func performBoolOp(op boolOp) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func performBoolOp(op boolOp) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
@ -65,76 +61,9 @@ func performBoolOp(op boolOp) func(d *dataTreeNavigator, context Context, lhs *C
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func findBoolean(wantBool bool, d *dataTreeNavigator, context Context, expressionNode *ExpressionNode, sequenceNode *yaml.Node) (bool, error) {
|
|
||||||
for _, node := range sequenceNode.Content {
|
|
||||||
|
|
||||||
if expressionNode != nil {
|
|
||||||
//need to evaluate the expression against the node
|
|
||||||
candidate := &CandidateNode{Node: node}
|
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if rhs.MatchingNodes.Len() > 0 {
|
|
||||||
node = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node
|
|
||||||
} else {
|
|
||||||
// no results found, ignore this entry
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
truthy, err := isTruthyNode(node)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if truthy == wantBool {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func allOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
var results = list.New()
|
|
||||||
|
|
||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
|
||||||
candidate := el.Value.(*CandidateNode)
|
|
||||||
candidateNode := unwrapDoc(candidate.Node)
|
|
||||||
if candidateNode.Kind != yaml.SequenceNode {
|
|
||||||
return Context{}, fmt.Errorf("any only supports arrays, was %v", candidateNode.Tag)
|
|
||||||
}
|
|
||||||
booleanResult, err := findBoolean(false, d, context, expressionNode.Rhs, candidateNode)
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
result := createBooleanCandidate(candidate, !booleanResult)
|
|
||||||
results.PushBack(result)
|
|
||||||
}
|
|
||||||
return context.ChildContext(results), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func anyOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
var results = list.New()
|
|
||||||
|
|
||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
|
||||||
candidate := el.Value.(*CandidateNode)
|
|
||||||
candidateNode := unwrapDoc(candidate.Node)
|
|
||||||
if candidateNode.Kind != yaml.SequenceNode {
|
|
||||||
return Context{}, fmt.Errorf("any only supports arrays, was %v", candidateNode.Tag)
|
|
||||||
}
|
|
||||||
booleanResult, err := findBoolean(true, d, context, expressionNode.Rhs, candidateNode)
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
result := createBooleanCandidate(candidate, booleanResult)
|
|
||||||
results.PushBack(result)
|
|
||||||
}
|
|
||||||
return context.ChildContext(results), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func orOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func orOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- orOp")
|
log.Debugf("-- orOp")
|
||||||
return crossFunction(d, context.ReadOnlyClone(), expressionNode, performBoolOp(
|
return crossFunction(d, context, expressionNode, performBoolOp(
|
||||||
func(b1 bool, b2 bool) bool {
|
func(b1 bool, b2 bool) bool {
|
||||||
log.Debugf("-- peformingOrOp with %v and %v", b1, b2)
|
log.Debugf("-- peformingOrOp with %v and %v", b1, b2)
|
||||||
return b1 || b2
|
return b1 || b2
|
||||||
@ -143,7 +72,7 @@ func orOperator(d *dataTreeNavigator, context Context, expressionNode *Expressio
|
|||||||
|
|
||||||
func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func andOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- AndOp")
|
log.Debugf("-- AndOp")
|
||||||
return crossFunction(d, context.ReadOnlyClone(), expressionNode, performBoolOp(
|
return crossFunction(d, context, expressionNode, performBoolOp(
|
||||||
func(b1 bool, b2 bool) bool {
|
func(b1 bool, b2 bool) bool {
|
||||||
return b1 && b2
|
return b1 && b2
|
||||||
}), true)
|
}), true)
|
||||||
|
@ -6,20 +6,12 @@ import (
|
|||||||
|
|
||||||
var booleanOperatorScenarios = []expressionScenario{
|
var booleanOperatorScenarios = []expressionScenario{
|
||||||
{
|
{
|
||||||
description: "`or` example",
|
description: "OR example",
|
||||||
expression: `true or false`,
|
expression: `true or false`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[], (!!bool)::true\n",
|
"D0, P[], (!!bool)::true\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "b: hi",
|
|
||||||
expression: `.a or .c`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::false\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: "b: hi",
|
document: "b: hi",
|
||||||
@ -37,7 +29,7 @@ var booleanOperatorScenarios = []expressionScenario{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "`and` example",
|
description: "AND example",
|
||||||
expression: `true and false`,
|
expression: `true and false`,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"D0, P[], (!!bool)::false\n",
|
"D0, P[], (!!bool)::false\n",
|
||||||
@ -51,86 +43,6 @@ var booleanOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!seq)::- {a: bird, b: dog}\n- {a: cat, b: fly}\n",
|
"D0, P[], (!!seq)::- {a: bird, b: dog}\n- {a: cat, b: fly}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "`any` returns true if any boolean in a given array is true",
|
|
||||||
document: `[false, true]`,
|
|
||||||
expression: "any",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::true\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "`any` returns false for an empty array",
|
|
||||||
document: `[]`,
|
|
||||||
expression: "any",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::false\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "`any_c` returns true if any element in the array is true for the given condition.",
|
|
||||||
document: "a: [rad, awesome]\nb: [meh, whatever]",
|
|
||||||
expression: `.[] |= any_c(. == "awesome")`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: true\nb: false\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `[{pet: cat}]`,
|
|
||||||
expression: `any_c(.name == "harry") as $c`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::[{pet: cat}]\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `[{pet: cat}]`,
|
|
||||||
expression: `all_c(.name == "harry") as $c`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::[{pet: cat}]\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `[false, false]`,
|
|
||||||
expression: "any",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::false\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "`all` returns true if all booleans in a given array are true",
|
|
||||||
document: `[true, true]`,
|
|
||||||
expression: "all",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::true\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `[false, true]`,
|
|
||||||
expression: "all",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::false\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "`all` returns true for an empty array",
|
|
||||||
document: `[]`,
|
|
||||||
expression: "all",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::true\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "`all_c` returns true if all elements in the array are true for the given condition.",
|
|
||||||
document: "a: [rad, awesome]\nb: [meh, 12]",
|
|
||||||
expression: `.[] |= all_c(tag == "!!str")`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: true\nb: false\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
expression: `false or false`,
|
expression: `false or false`,
|
||||||
@ -149,22 +61,6 @@ var booleanOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[b], (!!bool)::true\n",
|
"D0, P[b], (!!bool)::true\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{}`,
|
|
||||||
expression: `(.a.b or .c) as $x`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{}`,
|
|
||||||
expression: `(.a.b and .c) as $x`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Not true is false",
|
description: "Not true is false",
|
||||||
expression: `true | not`,
|
expression: `true | not`,
|
||||||
|
@ -17,12 +17,9 @@ import (
|
|||||||
...
|
...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func collectObjectOperator(d *dataTreeNavigator, originalContext Context, expressionNode *ExpressionNode) (Context, error) {
|
func collectObjectOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- collectObjectOperation")
|
log.Debugf("-- collectObjectOperation")
|
||||||
|
|
||||||
context := originalContext.Clone()
|
|
||||||
context.DontAutoCreate = false
|
|
||||||
|
|
||||||
if context.MatchingNodes.Len() == 0 {
|
if context.MatchingNodes.Len() == 0 {
|
||||||
node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Value: "{}"}
|
node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map", Value: "{}"}
|
||||||
candidate := &CandidateNode{Node: node}
|
candidate := &CandidateNode{Node: node}
|
||||||
|
@ -5,30 +5,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var collectObjectOperatorScenarios = []expressionScenario{
|
var collectObjectOperatorScenarios = []expressionScenario{
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "a: []",
|
|
||||||
expression: `.a += [{"key": "att2", "value": "val2"}]`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: [{key: att2, value: val2}]\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "",
|
|
||||||
expression: `.a += {"key": "att2", "value": "val2"}`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], ()::a:\n key: att2\n value: val2\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "",
|
|
||||||
expression: `.a += [0]`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], ()::a:\n - 0\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: `Collect empty object`,
|
description: `Collect empty object`,
|
||||||
document: ``,
|
document: ``,
|
||||||
|
@ -5,14 +5,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var collectOperatorScenarios = []expressionScenario{
|
var collectOperatorScenarios = []expressionScenario{
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: ``,
|
|
||||||
expression: `.a += [0]`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], ()::a:\n - 0\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Collect empty",
|
description: "Collect empty",
|
||||||
document: ``,
|
document: ``,
|
||||||
|
@ -27,7 +27,7 @@ func assignCommentsOperator(d *dataTreeNavigator, context Context, expressionNod
|
|||||||
|
|
||||||
comment := ""
|
comment := ""
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ func assignCommentsOperator(d *dataTreeNavigator, context Context, expressionNod
|
|||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
@ -62,22 +62,6 @@ var commentOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::a: cat\n\n# cat\n",
|
"D0, P[], (doc)::a: cat\n\n# cat\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: cat`,
|
|
||||||
expression: `. footComment=.b.d`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: cat\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: cat`,
|
|
||||||
expression: `. footComment|=.b.d`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: cat\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Remove comment",
|
description: "Remove comment",
|
||||||
document: "a: cat # comment\nb: dog # leave this",
|
document: "a: cat # comment\nb: dog # leave this",
|
||||||
|
@ -7,7 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
nodesToDelete, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
contextToUse := context.Clone()
|
||||||
|
contextToUse.DontAutoCreate = true
|
||||||
|
nodesToDelete, err := d.GetMatchingNodes(contextToUse, expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
|
@ -59,9 +59,7 @@ func toEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
|
|||||||
case yaml.SequenceNode:
|
case yaml.SequenceNode:
|
||||||
results.PushBack(toEntriesfromSeq(candidate))
|
results.PushBack(toEntriesfromSeq(candidate))
|
||||||
default:
|
default:
|
||||||
if candidateNode.Tag != "!!null" {
|
return Context{}, fmt.Errorf("%v has no keys", candidate.Node.Tag)
|
||||||
return Context{}, fmt.Errorf("%v has no keys", candidate.Node.Tag)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,26 +133,26 @@ func withEntriesOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
//to_entries on the context
|
//to_entries on the context
|
||||||
toEntries, err := toEntriesOperator(d, context, expressionNode)
|
toEntries, err := toEntriesOperator(d, context, expressionNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//run expression against entries
|
//run expression against entries
|
||||||
// splat toEntries and pipe it into Rhs
|
// splat toEntries and pipe it into Rhs
|
||||||
splatted, err := splat(d, toEntries, traversePreferences{})
|
splatted, err := splat(d, toEntries, traversePreferences{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := d.GetMatchingNodes(splatted, expressionNode.Rhs)
|
result, err := d.GetMatchingNodes(splatted, expressionNode.Rhs)
|
||||||
log.Debug("expressionNode.Rhs %v", expressionNode.Rhs.Operation.OperationType)
|
log.Debug("expressionNode.Rhs %v", expressionNode.Rhs.Operation.OperationType)
|
||||||
log.Debug("result %v", result)
|
log.Debug("result %v", result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
collected, err := collectOperator(d, result, expressionNode)
|
collected, err := collectOperator(d, result, expressionNode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//from_entries on the result
|
//from_entries on the result
|
||||||
|
@ -21,12 +21,6 @@ var entriesOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!seq)::- key: 0\n value: a\n- key: 1\n value: b\n",
|
"D0, P[], (!!seq)::- key: 0\n value: a\n- key: 1\n value: b\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: "to_entries null",
|
|
||||||
document: `null`,
|
|
||||||
expression: `to_entries`,
|
|
||||||
expected: []string{},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "from_entries map",
|
description: "from_entries map",
|
||||||
document: `{a: 1, b: 2}`,
|
document: `{a: 1, b: 2}`,
|
||||||
|
@ -4,32 +4,20 @@ import "gopkg.in/yaml.v3"
|
|||||||
|
|
||||||
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- equalsOperation")
|
log.Debugf("-- equalsOperation")
|
||||||
return crossFunction(d, context.ReadOnlyClone(), expressionNode, isEquals(false), true)
|
return crossFunction(d, context, expressionNode, isEquals(false), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
value := false
|
value := false
|
||||||
log.Debugf("-- isEquals cross function")
|
|
||||||
if lhs == nil && rhs == nil {
|
if lhs == nil && rhs == nil {
|
||||||
owner := &CandidateNode{}
|
owner := &CandidateNode{}
|
||||||
return createBooleanCandidate(owner, !flip), nil
|
return createBooleanCandidate(owner, !flip), nil
|
||||||
} else if lhs == nil {
|
} else if lhs == nil {
|
||||||
log.Debugf("lhs nil, but rhs is not")
|
return createBooleanCandidate(rhs, flip), nil
|
||||||
rhsNode := unwrapDoc(rhs.Node)
|
|
||||||
value := rhsNode.Tag == "!!null"
|
|
||||||
if flip {
|
|
||||||
value = !value
|
|
||||||
}
|
|
||||||
return createBooleanCandidate(rhs, value), nil
|
|
||||||
} else if rhs == nil {
|
} else if rhs == nil {
|
||||||
log.Debugf("lhs not nil, but rhs is")
|
return createBooleanCandidate(lhs, flip), nil
|
||||||
lhsNode := unwrapDoc(lhs.Node)
|
|
||||||
value := lhsNode.Tag == "!!null"
|
|
||||||
if flip {
|
|
||||||
value = !value
|
|
||||||
}
|
|
||||||
return createBooleanCandidate(lhs, value), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lhsNode := unwrapDoc(lhs.Node)
|
lhsNode := unwrapDoc(lhs.Node)
|
||||||
@ -49,6 +37,6 @@ func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *Candid
|
|||||||
}
|
}
|
||||||
|
|
||||||
func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("-- notEqualsOperator")
|
log.Debugf("-- equalsOperation")
|
||||||
return crossFunction(d, context.ReadOnlyClone(), expressionNode, isEquals(true), true)
|
return crossFunction(d, context, expressionNode, isEquals(true), true)
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var equalsOperatorScenarios = []expressionScenario{
|
var equalsOperatorScenarios = []expressionScenario{
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
expression: ".a == .b",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::true\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: "cat",
|
document: "cat",
|
||||||
@ -21,60 +14,6 @@ var equalsOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!bool)::false\n",
|
"D0, P[], (!!bool)::false\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{}",
|
|
||||||
expression: "(.a == .b) as $x",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{}",
|
|
||||||
expression: ".a == .b",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::true\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{}",
|
|
||||||
expression: "(.a != .b) as $x",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{}",
|
|
||||||
expression: ".a != .b",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::false\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{a: {b: 10}}",
|
|
||||||
expression: "select(.c != null)",
|
|
||||||
expected: []string{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{a: {b: 10}}",
|
|
||||||
expression: "select(.d == .c)",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{a: {b: 10}}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: "{a: {b: 10}}",
|
|
||||||
expression: "select(null == .c)",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{a: {b: 10}}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
skipDoc: true,
|
skipDoc: true,
|
||||||
document: "{a: { b: {things: \"\"}, f: [1], g: [] }}",
|
document: "{a: { b: {things: \"\"}, f: [1], g: [] }}",
|
||||||
|
@ -12,19 +12,14 @@ func hasOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
|
|||||||
log.Debugf("-- hasOperation")
|
log.Debugf("-- hasOperation")
|
||||||
var results = list.New()
|
var results = list.New()
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
|
wanted := rhs.MatchingNodes.Front().Value.(*CandidateNode).Node
|
||||||
|
wantedKey := wanted.Value
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
wantedKey := "null"
|
|
||||||
wanted := &yaml.Node{Tag: "!!null"}
|
|
||||||
if rhs.MatchingNodes.Len() != 0 {
|
|
||||||
wanted = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node
|
|
||||||
wantedKey = wanted.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
|
@ -13,22 +13,6 @@ var hasOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (!!bool)::true\n",
|
"D0, P[], (!!bool)::true\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: hello`,
|
|
||||||
expression: `has(.b) as $c`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::a: hello\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `a: hello`,
|
|
||||||
expression: `has(.b)`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!bool)::false\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Has map key",
|
description: "Has map key",
|
||||||
document: `- a: "yes"
|
document: `- a: "yes"
|
||||||
|
@ -11,7 +11,9 @@ func selectOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
|
|||||||
|
|
||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
|
childContext := context.SingleChildContext(candidate)
|
||||||
|
childContext.DontAutoCreate = true
|
||||||
|
rhs, err := d.GetMatchingNodes(childContext, expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
@ -22,9 +24,7 @@ func selectOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
|
|||||||
|
|
||||||
if first != nil {
|
if first != nil {
|
||||||
result := first.Value.(*CandidateNode)
|
result := first.Value.(*CandidateNode)
|
||||||
log.Debugf("result %v", NodeToString(result))
|
|
||||||
includeResult, errDecoding := isTruthy(result)
|
includeResult, errDecoding := isTruthy(result)
|
||||||
log.Debugf("isTruthy %v", includeResult)
|
|
||||||
if errDecoding != nil {
|
if errDecoding != nil {
|
||||||
return Context{}, errDecoding
|
return Context{}, errDecoding
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ func sortKeysOperator(d *dataTreeNavigator, context Context, expressionNode *Exp
|
|||||||
|
|
||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,6 @@ var sortKeysOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[], (doc)::{a: blah, b: bing, c: frog}\n",
|
"D0, P[], (doc)::{a: blah, b: bing, c: frog}\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{c: frog}`,
|
|
||||||
expression: `sortKeys(.d)`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{c: frog}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Sort keys recursively",
|
description: "Sort keys recursively",
|
||||||
subdescription: "Note the array elements are left unsorted, but maps inside arrays are sorted",
|
subdescription: "Note the array elements are left unsorted, but maps inside arrays are sorted",
|
||||||
|
@ -13,7 +13,7 @@ func getSubstituteParameters(d *dataTreeNavigator, block *ExpressionNode, contex
|
|||||||
regEx := ""
|
regEx := ""
|
||||||
replacementText := ""
|
replacementText := ""
|
||||||
|
|
||||||
regExNodes, err := d.GetMatchingNodes(context.ReadOnlyClone(), block.Lhs)
|
regExNodes, err := d.GetMatchingNodes(context, block.Lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
@ -78,7 +78,7 @@ func joinStringOperator(d *dataTreeNavigator, context Context, expressionNode *E
|
|||||||
log.Debugf("-- joinStringOperator")
|
log.Debugf("-- joinStringOperator")
|
||||||
joinStr := ""
|
joinStr := ""
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ func splitStringOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
log.Debugf("-- splitStringOperator")
|
log.Debugf("-- splitStringOperator")
|
||||||
splitStr := ""
|
splitStr := ""
|
||||||
|
|
||||||
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
log.Debugf("AssignStyleOperator: %v")
|
log.Debugf("AssignStyleOperator: %v")
|
||||||
var style yaml.Style
|
var style yaml.Style
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
@ -54,7 +54,7 @@ func assignStyleOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
log.Debugf("Setting style of : %v", candidate.GetKey())
|
log.Debugf("Setting style of : %v", candidate.GetKey())
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ func subtractAssignOperator(d *dataTreeNavigator, context Context, expressionNod
|
|||||||
func subtractOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func subtractOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
log.Debugf("Subtract operator")
|
log.Debugf("Subtract operator")
|
||||||
|
|
||||||
return crossFunction(d, context.ReadOnlyClone(), expressionNode, subtract, false)
|
return crossFunction(d, context, expressionNode, subtract, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func subtract(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
func subtract(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) {
|
||||||
|
@ -5,14 +5,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var subtractOperatorScenarios = []expressionScenario{
|
var subtractOperatorScenarios = []expressionScenario{
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{}`,
|
|
||||||
expression: "(.a - .b) as $x",
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Number subtraction - float",
|
description: "Number subtraction - float",
|
||||||
subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",
|
subdescription: "If the lhs or rhs are floats then the expression will be calculated with floats.",
|
||||||
|
@ -12,7 +12,7 @@ func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
|
|||||||
tag := ""
|
tag := ""
|
||||||
|
|
||||||
if !expressionNode.Operation.UpdateAssign {
|
if !expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ func assignTagOperator(d *dataTreeNavigator, context Context, expressionNode *Ex
|
|||||||
candidate := el.Value.(*CandidateNode)
|
candidate := el.Value.(*CandidateNode)
|
||||||
log.Debugf("Setting tag of : %v", candidate.GetKey())
|
log.Debugf("Setting tag of : %v", candidate.GetKey())
|
||||||
if expressionNode.Operation.UpdateAssign {
|
if expressionNode.Operation.UpdateAssign {
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ 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.ReadOnlyClone(), expressionNode.Rhs)
|
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
@ -308,6 +308,6 @@ func traverseMergeAnchor(newMatches *orderedmap.OrderedMap, originalCandidate *C
|
|||||||
|
|
||||||
func traverseArray(candidate *CandidateNode, operation *Operation, prefs traversePreferences) (*list.List, error) {
|
func traverseArray(candidate *CandidateNode, operation *Operation, prefs traversePreferences) (*list.List, error) {
|
||||||
log.Debug("operation Value %v", operation.Value)
|
log.Debug("operation Value %v", operation.Value)
|
||||||
indices := []*yaml.Node{{Value: operation.StringValue}}
|
indices := []*yaml.Node{&yaml.Node{Value: operation.StringValue}}
|
||||||
return traverseArrayWithIndices(candidate, indices, prefs)
|
return traverseArrayWithIndices(candidate, indices, prefs)
|
||||||
}
|
}
|
||||||
|
@ -27,22 +27,6 @@ foobar:
|
|||||||
`
|
`
|
||||||
|
|
||||||
var traversePathOperatorScenarios = []expressionScenario{
|
var traversePathOperatorScenarios = []expressionScenario{
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `[[1]]`,
|
|
||||||
expression: `.[0][0]`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[0 0], (!!int)::1\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `[[[1]]]`,
|
|
||||||
expression: `.[0][0][0]`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[0 0 0], (!!int)::1\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Simple map navigation",
|
description: "Simple map navigation",
|
||||||
document: `{a: {b: apple}}`,
|
document: `{a: {b: apple}}`,
|
||||||
@ -94,14 +78,6 @@ var traversePathOperatorScenarios = []expressionScenario{
|
|||||||
"D0, P[flying fox], (!!str)::frog\n",
|
"D0, P[flying fox], (!!str)::frog\n",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `c: dog`,
|
|
||||||
expression: `.[.a.b] as $x`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::c: dog\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Dynamic keys",
|
description: "Dynamic keys",
|
||||||
subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`,
|
subdescription: `Expressions within [] can be used to dynamically lookup / calculate keys`,
|
||||||
|
@ -5,20 +5,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var unionOperatorScenarios = []expressionScenario{
|
var unionOperatorScenarios = []expressionScenario{
|
||||||
// {
|
|
||||||
// skipDoc: true,
|
|
||||||
// document: "{}",
|
|
||||||
// expression: `(.a, .b.c) as $x`,
|
|
||||||
// expected: []string{
|
|
||||||
// "D0, P[], (doc)::{}\n",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// skipDoc: true,
|
|
||||||
// document: "{}",
|
|
||||||
// expression: `(.a, .b.c)`,
|
|
||||||
// expected: []string{},
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
description: "Combine scalars",
|
description: "Combine scalars",
|
||||||
expression: `1, true, "cat"`,
|
expression: `1, true, "cat"`,
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
package yqlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"container/list"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/elliotchance/orderedmap"
|
|
||||||
yaml "gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
func unique(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
selfExpression := &ExpressionNode{Operation: &Operation{OperationType: selfReferenceOpType}}
|
|
||||||
uniqueByExpression := &ExpressionNode{Operation: &Operation{OperationType: uniqueByOpType}, Rhs: selfExpression}
|
|
||||||
return uniqueBy(d, context, uniqueByExpression)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func uniqueBy(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
|
||||||
|
|
||||||
log.Debugf("-- uniqueBy Operator")
|
|
||||||
var results = list.New()
|
|
||||||
|
|
||||||
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
|
|
||||||
candidate := el.Value.(*CandidateNode)
|
|
||||||
candidateNode := unwrapDoc(candidate.Node)
|
|
||||||
|
|
||||||
if candidateNode.Kind != yaml.SequenceNode {
|
|
||||||
return Context{}, fmt.Errorf("Only arrays are supported for unique")
|
|
||||||
}
|
|
||||||
|
|
||||||
var newMatches = orderedmap.NewOrderedMap()
|
|
||||||
for _, node := range candidateNode.Content {
|
|
||||||
child := &CandidateNode{Node: node}
|
|
||||||
rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(child), expressionNode.Rhs)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return Context{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
keyValue := "null"
|
|
||||||
|
|
||||||
if rhs.MatchingNodes.Len() > 0 {
|
|
||||||
first := rhs.MatchingNodes.Front()
|
|
||||||
keyCandidate := first.Value.(*CandidateNode)
|
|
||||||
keyValue = keyCandidate.Node.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
_, exists := newMatches.Get(keyValue)
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
newMatches.Set(keyValue, child.Node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resultNode := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
|
|
||||||
for el := newMatches.Front(); el != nil; el = el.Next() {
|
|
||||||
resultNode.Content = append(resultNode.Content, el.Value.(*yaml.Node))
|
|
||||||
}
|
|
||||||
|
|
||||||
results.PushBack(candidate.CreateChild(nil, resultNode))
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.ChildContext(results), nil
|
|
||||||
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package yqlib
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var uniqueOperatorScenarios = []expressionScenario{
|
|
||||||
{
|
|
||||||
description: "Unique array of scalars (string/numbers)",
|
|
||||||
document: `[1,2,3,2]`,
|
|
||||||
expression: `unique`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!seq)::- 1\n- 2\n- 3\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Unique nulls",
|
|
||||||
subdescription: "Unique works on the node value, so it considers different representations of nulls to be different",
|
|
||||||
document: `[~,null, ~, null]`,
|
|
||||||
expression: `unique`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!seq)::- ~\n- null\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Unique all nulls",
|
|
||||||
subdescription: "Run against the node tag to unique all the nulls",
|
|
||||||
document: `[~,null, ~, null]`,
|
|
||||||
expression: `unique_by(tag)`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!seq)::- ~\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Unique array object fields",
|
|
||||||
document: `[{name: harry, pet: cat}, {name: billy, pet: dog}, {name: harry, pet: dog}]`,
|
|
||||||
expression: `unique_by(.name)`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!seq)::- {name: harry, pet: cat}\n- {name: billy, pet: dog}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `[{name: harry, pet: cat}, {pet: fish}, {name: harry, pet: dog}]`,
|
|
||||||
expression: `unique_by(.name)`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!seq)::- {name: harry, pet: cat}\n- {pet: fish}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `[{name: harry, pet: cat}, {pet: fish}, {name: harry, pet: dog}]`,
|
|
||||||
expression: `unique_by(.cat.dog)`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (!!seq)::- {name: harry, pet: cat}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUniqueOperatorScenarios(t *testing.T) {
|
|
||||||
for _, tt := range uniqueOperatorScenarios {
|
|
||||||
testScenario(t, &tt)
|
|
||||||
}
|
|
||||||
documentScenarios(t, "Unique", uniqueOperatorScenarios)
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ func getVariableOperator(d *dataTreeNavigator, context Context, expressionNode *
|
|||||||
}
|
}
|
||||||
|
|
||||||
func assignVariableOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
func assignVariableOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
|
||||||
lhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.Lhs)
|
lhs, err := d.GetMatchingNodes(context, expressionNode.Lhs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, nil
|
return Context{}, nil
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var variableOperatorScenarios = []expressionScenario{
|
var variableOperatorScenarios = []expressionScenario{
|
||||||
{
|
|
||||||
skipDoc: true,
|
|
||||||
document: `{}`,
|
|
||||||
expression: `.a.b as $foo`,
|
|
||||||
expected: []string{
|
|
||||||
"D0, P[], (doc)::{}\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: "Single value variable",
|
description: "Single value variable",
|
||||||
document: `a: cat`,
|
document: `a: cat`,
|
||||||
|
@ -24,19 +24,7 @@ func emptyOperator(d *dataTreeNavigator, context Context, expressionNode *Expres
|
|||||||
|
|
||||||
type crossFunctionCalculation func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
|
type crossFunctionCalculation func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error)
|
||||||
|
|
||||||
func resultsForRhs(d *dataTreeNavigator, context Context, lhsCandidate *CandidateNode, rhs Context, calculation crossFunctionCalculation, results *list.List, calcWhenEmpty bool) error {
|
func resultsForRhs(d *dataTreeNavigator, context Context, lhsCandidate *CandidateNode, rhs Context, calculation crossFunctionCalculation, results *list.List) error {
|
||||||
|
|
||||||
if calcWhenEmpty && rhs.MatchingNodes.Len() == 0 {
|
|
||||||
resultCandidate, err := calculation(d, context, lhsCandidate, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if resultCandidate != nil {
|
|
||||||
results.PushBack(resultCandidate)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() {
|
||||||
log.Debugf("Applying calc")
|
log.Debugf("Applying calc")
|
||||||
rhsCandidate := rightEl.Value.(*CandidateNode)
|
rhsCandidate := rightEl.Value.(*CandidateNode)
|
||||||
@ -44,9 +32,7 @@ func resultsForRhs(d *dataTreeNavigator, context Context, lhsCandidate *Candidat
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if resultCandidate != nil {
|
results.PushBack(resultCandidate)
|
||||||
results.PushBack(resultCandidate)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -66,7 +52,14 @@ func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *Expressi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if calcWhenEmpty && lhs.MatchingNodes.Len() == 0 {
|
if calcWhenEmpty && lhs.MatchingNodes.Len() == 0 {
|
||||||
err := resultsForRhs(d, context, nil, rhs, calculation, results, calcWhenEmpty)
|
if rhs.MatchingNodes.Len() == 0 {
|
||||||
|
resultCandidate, err := calculation(d, context, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, err
|
||||||
|
}
|
||||||
|
results.PushBack(resultCandidate)
|
||||||
|
}
|
||||||
|
err := resultsForRhs(d, context, nil, rhs, calculation, results)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
@ -75,7 +68,7 @@ func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *Expressi
|
|||||||
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
lhsCandidate := el.Value.(*CandidateNode)
|
lhsCandidate := el.Value.(*CandidateNode)
|
||||||
|
|
||||||
err := resultsForRhs(d, context, lhsCandidate, rhs, calculation, results, calcWhenEmpty)
|
err := resultsForRhs(d, context, lhsCandidate, rhs, calculation, results)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Context{}, err
|
return Context{}, err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
find . \( -path ./vendor \) -prune -o -name "*.go" -exec goimports -w {} \;
|
find . \( -path ./vendor \) -prune -o -name "*.go" -exec goimports -w {} \;
|
||||||
gofmt -w -s .
|
|
||||||
go mod tidy
|
go mod tidy
|
||||||
go mod vendor
|
go mod vendor
|
@ -1,5 +1,5 @@
|
|||||||
name: yq
|
name: yq
|
||||||
version: '4.9.3'
|
version: '4.8.0'
|
||||||
summary: A lightweight and portable command-line YAML processor
|
summary: A lightweight and portable command-line YAML processor
|
||||||
description: |
|
description: |
|
||||||
The aim of the project is to be the jq or sed of yaml files.
|
The aim of the project is to be the jq or sed of yaml files.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user