Added line and column operators

This commit is contained in:
Mike Farah 2022-02-27 11:56:46 +11:00
parent ff047d0748
commit 8698433d44
10 changed files with 285 additions and 0 deletions

View File

@ -0,0 +1,66 @@
# Column
Returns the column of the matching node. Starts from 1, 0 indicates there was no column data.
{% hint style="warning" %}
Note that versions prior to 4.18 require the 'eval/e' command to be specified. 
`yq e <exp> <file>`
{% endhint %}
## Returns column of _value_ node
Given a sample.yml file of:
```yaml
a: cat
b: bob
```
then
```bash
yq '.b | column' sample.yml
```
will output
```yaml
4
```
## Returns column of _key_ node
Pipe through the key operator to get the column of the key
Given a sample.yml file of:
```yaml
a: cat
b: bob
```
then
```bash
yq '.b | key | column' sample.yml
```
will output
```yaml
1
```
## First column is 1
Given a sample.yml file of:
```yaml
a: cat
```
then
```bash
yq '.a | key | column' sample.yml
```
will output
```yaml
1
```
## No column data is 0
Running
```bash
yq --null-input '{"a": "new entry"} | column'
```
will output
```yaml
0
```

View File

@ -0,0 +1,3 @@
# Column
Returns the column of the matching node. Starts from 1, 0 indicates there was no column data.

View File

@ -0,0 +1,3 @@
# Line
Returns the line of the matching node. Starts from 1, 0 indicates there was no line data.

View File

@ -0,0 +1,68 @@
# Line
Returns the line of the matching node. Starts from 1, 0 indicates there was no line data.
{% hint style="warning" %}
Note that versions prior to 4.18 require the 'eval/e' command to be specified.&#x20;
`yq e <exp> <file>`
{% endhint %}
## Returns line of _value_ node
Given a sample.yml file of:
```yaml
a: cat
b:
c: cat
```
then
```bash
yq '.b | line' sample.yml
```
will output
```yaml
3
```
## Returns line of _key_ node
Pipe through the key operator to get the line of the key
Given a sample.yml file of:
```yaml
a: cat
b:
c: cat
```
then
```bash
yq '.b | key| line' sample.yml
```
will output
```yaml
2
```
## First line is 1
Given a sample.yml file of:
```yaml
a: cat
```
then
```bash
yq '.a | line' sample.yml
```
will output
```yaml
1
```
## No line data is 0
Running
```bash
yq --null-input '{"a": "new entry"} | line'
```
will output
```yaml
0
```

View File

@ -315,6 +315,8 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`,`), opToken(unionOpType))
lexer.Add([]byte(`:\s*`), opToken(createMapOpType))
lexer.Add([]byte(`length`), opToken(lengthOpType))
lexer.Add([]byte(`line`), opToken(lineOpType))
lexer.Add([]byte(`column`), opToken(columnOpType))
lexer.Add([]byte(`eval`), opToken(evalOpType))

View File

@ -80,6 +80,9 @@ var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence:
var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence: 45, Handler: pipeOperator}
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
var lineOpType = &operationType{Type: "LINE", NumArgs: 0, Precedence: 50, Handler: lineOperator}
var columnOpType = &operationType{Type: "LINE", NumArgs: 0, Precedence: 50, Handler: columnOperator}
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 1, Precedence: 50, Handler: collectOperator}
var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 50, Handler: mapOperator}
var evalOpType = &operationType{Type: "EVAL", NumArgs: 1, Precedence: 50, Handler: evalOperator}

View File

@ -0,0 +1,23 @@
package yqlib
import (
"container/list"
"fmt"
yaml "gopkg.in/yaml.v3"
)
func columnOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("columnOperator")
var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Node.Column), Tag: "!!int"}
result := candidate.CreateReplacement(node)
results.PushBack(result)
}
return context.ChildContext(results), nil
}

View File

@ -0,0 +1,47 @@
package yqlib
import (
"testing"
)
var columnOperatorScenarios = []expressionScenario{
{
description: "Returns column of _value_ node",
document: "a: cat\nb: bob",
expression: `.b | column`,
expected: []string{
"D0, P[b], (!!int)::4\n",
},
},
{
description: "Returns column of _key_ node",
subdescription: "Pipe through the key operator to get the column of the key",
document: "a: cat\nb: bob",
expression: `.b | key | column`,
expected: []string{
"D0, P[b], (!!int)::1\n",
},
},
{
description: "First column is 1",
document: "a: cat",
expression: `.a | key | column`,
expected: []string{
"D0, P[a], (!!int)::1\n",
},
},
{
description: "No column data is 0",
expression: `{"a": "new entry"} | column`,
expected: []string{
"D0, P[], (!!int)::0\n",
},
},
}
func TestColumnOperatorScenarios(t *testing.T) {
for _, tt := range columnOperatorScenarios {
testScenario(t, &tt)
}
documentOperatorScenarios(t, "column", columnOperatorScenarios)
}

View File

@ -0,0 +1,23 @@
package yqlib
import (
"container/list"
"fmt"
yaml "gopkg.in/yaml.v3"
)
func lineOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("lineOperator")
var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Node.Line), Tag: "!!int"}
result := candidate.CreateReplacement(node)
results.PushBack(result)
}
return context.ChildContext(results), nil
}

View File

@ -0,0 +1,47 @@
package yqlib
import (
"testing"
)
var lineOperatorScenarios = []expressionScenario{
{
description: "Returns line of _value_ node",
document: "a: cat\nb:\n c: cat",
expression: `.b | line`,
expected: []string{
"D0, P[b], (!!int)::3\n",
},
},
{
description: "Returns line of _key_ node",
subdescription: "Pipe through the key operator to get the line of the key",
document: "a: cat\nb:\n c: cat",
expression: `.b | key| line`,
expected: []string{
"D0, P[b], (!!int)::2\n",
},
},
{
description: "First line is 1",
document: "a: cat",
expression: `.a | line`,
expected: []string{
"D0, P[a], (!!int)::1\n",
},
},
{
description: "No line data is 0",
expression: `{"a": "new entry"} | line`,
expected: []string{
"D0, P[], (!!int)::0\n",
},
},
}
func TestLineOperatorScenarios(t *testing.T) {
for _, tt := range lineOperatorScenarios {
testScenario(t, &tt)
}
documentOperatorScenarios(t, "line", lineOperatorScenarios)
}