Skip to content

Commit 8698433

Browse files
committed
Added line and column operators
1 parent ff047d0 commit 8698433

File tree

10 files changed

+285
-0
lines changed

10 files changed

+285
-0
lines changed

pkg/yqlib/doc/operators/column.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Column
2+
3+
Returns the column of the matching node. Starts from 1, 0 indicates there was no column data.
4+
5+
{% hint style="warning" %}
6+
Note that versions prior to 4.18 require the 'eval/e' command to be specified. 
7+
8+
`yq e <exp> <file>`
9+
{% endhint %}
10+
11+
## Returns column of _value_ node
12+
Given a sample.yml file of:
13+
```yaml
14+
a: cat
15+
b: bob
16+
```
17+
then
18+
```bash
19+
yq '.b | column' sample.yml
20+
```
21+
will output
22+
```yaml
23+
4
24+
```
25+
26+
## Returns column of _key_ node
27+
Pipe through the key operator to get the column of the key
28+
29+
Given a sample.yml file of:
30+
```yaml
31+
a: cat
32+
b: bob
33+
```
34+
then
35+
```bash
36+
yq '.b | key | column' sample.yml
37+
```
38+
will output
39+
```yaml
40+
1
41+
```
42+
43+
## First column is 1
44+
Given a sample.yml file of:
45+
```yaml
46+
a: cat
47+
```
48+
then
49+
```bash
50+
yq '.a | key | column' sample.yml
51+
```
52+
will output
53+
```yaml
54+
1
55+
```
56+
57+
## No column data is 0
58+
Running
59+
```bash
60+
yq --null-input '{"a": "new entry"} | column'
61+
```
62+
will output
63+
```yaml
64+
0
65+
```
66+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Column
2+
3+
Returns the column of the matching node. Starts from 1, 0 indicates there was no column data.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Line
2+
3+
Returns the line of the matching node. Starts from 1, 0 indicates there was no line data.

pkg/yqlib/doc/operators/line.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Line
2+
3+
Returns the line of the matching node. Starts from 1, 0 indicates there was no line data.
4+
5+
{% hint style="warning" %}
6+
Note that versions prior to 4.18 require the 'eval/e' command to be specified.&#x20;
7+
8+
`yq e <exp> <file>`
9+
{% endhint %}
10+
11+
## Returns line of _value_ node
12+
Given a sample.yml file of:
13+
```yaml
14+
a: cat
15+
b:
16+
c: cat
17+
```
18+
then
19+
```bash
20+
yq '.b | line' sample.yml
21+
```
22+
will output
23+
```yaml
24+
3
25+
```
26+
27+
## Returns line of _key_ node
28+
Pipe through the key operator to get the line of the key
29+
30+
Given a sample.yml file of:
31+
```yaml
32+
a: cat
33+
b:
34+
c: cat
35+
```
36+
then
37+
```bash
38+
yq '.b | key| line' sample.yml
39+
```
40+
will output
41+
```yaml
42+
2
43+
```
44+
45+
## First line is 1
46+
Given a sample.yml file of:
47+
```yaml
48+
a: cat
49+
```
50+
then
51+
```bash
52+
yq '.a | line' sample.yml
53+
```
54+
will output
55+
```yaml
56+
1
57+
```
58+
59+
## No line data is 0
60+
Running
61+
```bash
62+
yq --null-input '{"a": "new entry"} | line'
63+
```
64+
will output
65+
```yaml
66+
0
67+
```
68+

pkg/yqlib/expression_tokeniser.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ func initLexer() (*lex.Lexer, error) {
315315
lexer.Add([]byte(`,`), opToken(unionOpType))
316316
lexer.Add([]byte(`:\s*`), opToken(createMapOpType))
317317
lexer.Add([]byte(`length`), opToken(lengthOpType))
318+
lexer.Add([]byte(`line`), opToken(lineOpType))
319+
lexer.Add([]byte(`column`), opToken(columnOpType))
318320

319321
lexer.Add([]byte(`eval`), opToken(evalOpType))
320322

pkg/yqlib/lib.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ var createMapOpType = &operationType{Type: "CREATE_MAP", NumArgs: 2, Precedence:
8080
var shortPipeOpType = &operationType{Type: "SHORT_PIPE", NumArgs: 2, Precedence: 45, Handler: pipeOperator}
8181

8282
var lengthOpType = &operationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: lengthOperator}
83+
var lineOpType = &operationType{Type: "LINE", NumArgs: 0, Precedence: 50, Handler: lineOperator}
84+
var columnOpType = &operationType{Type: "LINE", NumArgs: 0, Precedence: 50, Handler: columnOperator}
85+
8386
var collectOpType = &operationType{Type: "COLLECT", NumArgs: 1, Precedence: 50, Handler: collectOperator}
8487
var mapOpType = &operationType{Type: "MAP", NumArgs: 1, Precedence: 50, Handler: mapOperator}
8588
var evalOpType = &operationType{Type: "EVAL", NumArgs: 1, Precedence: 50, Handler: evalOperator}

pkg/yqlib/operator_column.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package yqlib
2+
3+
import (
4+
"container/list"
5+
"fmt"
6+
7+
yaml "gopkg.in/yaml.v3"
8+
)
9+
10+
func columnOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
11+
log.Debugf("columnOperator")
12+
13+
var results = list.New()
14+
15+
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
16+
candidate := el.Value.(*CandidateNode)
17+
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Node.Column), Tag: "!!int"}
18+
result := candidate.CreateReplacement(node)
19+
results.PushBack(result)
20+
}
21+
22+
return context.ChildContext(results), nil
23+
}

pkg/yqlib/operator_column_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package yqlib
2+
3+
import (
4+
"testing"
5+
)
6+
7+
var columnOperatorScenarios = []expressionScenario{
8+
{
9+
description: "Returns column of _value_ node",
10+
document: "a: cat\nb: bob",
11+
expression: `.b | column`,
12+
expected: []string{
13+
"D0, P[b], (!!int)::4\n",
14+
},
15+
},
16+
{
17+
description: "Returns column of _key_ node",
18+
subdescription: "Pipe through the key operator to get the column of the key",
19+
document: "a: cat\nb: bob",
20+
expression: `.b | key | column`,
21+
expected: []string{
22+
"D0, P[b], (!!int)::1\n",
23+
},
24+
},
25+
{
26+
description: "First column is 1",
27+
document: "a: cat",
28+
expression: `.a | key | column`,
29+
expected: []string{
30+
"D0, P[a], (!!int)::1\n",
31+
},
32+
},
33+
{
34+
description: "No column data is 0",
35+
expression: `{"a": "new entry"} | column`,
36+
expected: []string{
37+
"D0, P[], (!!int)::0\n",
38+
},
39+
},
40+
}
41+
42+
func TestColumnOperatorScenarios(t *testing.T) {
43+
for _, tt := range columnOperatorScenarios {
44+
testScenario(t, &tt)
45+
}
46+
documentOperatorScenarios(t, "column", columnOperatorScenarios)
47+
}

pkg/yqlib/operator_line.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package yqlib
2+
3+
import (
4+
"container/list"
5+
"fmt"
6+
7+
yaml "gopkg.in/yaml.v3"
8+
)
9+
10+
func lineOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
11+
log.Debugf("lineOperator")
12+
13+
var results = list.New()
14+
15+
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
16+
candidate := el.Value.(*CandidateNode)
17+
node := &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", candidate.Node.Line), Tag: "!!int"}
18+
result := candidate.CreateReplacement(node)
19+
results.PushBack(result)
20+
}
21+
22+
return context.ChildContext(results), nil
23+
}

pkg/yqlib/operator_line_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package yqlib
2+
3+
import (
4+
"testing"
5+
)
6+
7+
var lineOperatorScenarios = []expressionScenario{
8+
{
9+
description: "Returns line of _value_ node",
10+
document: "a: cat\nb:\n c: cat",
11+
expression: `.b | line`,
12+
expected: []string{
13+
"D0, P[b], (!!int)::3\n",
14+
},
15+
},
16+
{
17+
description: "Returns line of _key_ node",
18+
subdescription: "Pipe through the key operator to get the line of the key",
19+
document: "a: cat\nb:\n c: cat",
20+
expression: `.b | key| line`,
21+
expected: []string{
22+
"D0, P[b], (!!int)::2\n",
23+
},
24+
},
25+
{
26+
description: "First line is 1",
27+
document: "a: cat",
28+
expression: `.a | line`,
29+
expected: []string{
30+
"D0, P[a], (!!int)::1\n",
31+
},
32+
},
33+
{
34+
description: "No line data is 0",
35+
expression: `{"a": "new entry"} | line`,
36+
expected: []string{
37+
"D0, P[], (!!int)::0\n",
38+
},
39+
},
40+
}
41+
42+
func TestLineOperatorScenarios(t *testing.T) {
43+
for _, tt := range lineOperatorScenarios {
44+
testScenario(t, &tt)
45+
}
46+
documentOperatorScenarios(t, "line", lineOperatorScenarios)
47+
}

0 commit comments

Comments
 (0)