Skip to content

Commit

Permalink
Added line and column operators
Browse files Browse the repository at this point in the history
  • Loading branch information
mikefarah committed Feb 27, 2022
1 parent ff047d0 commit 8698433
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 0 deletions.
66 changes: 66 additions & 0 deletions pkg/yqlib/doc/operators/column.md
Original file line number Diff line number Diff line change
@@ -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
```

3 changes: 3 additions & 0 deletions pkg/yqlib/doc/operators/headers/column.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Column

Returns the column of the matching node. Starts from 1, 0 indicates there was no column data.
3 changes: 3 additions & 0 deletions pkg/yqlib/doc/operators/headers/line.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Line

Returns the line of the matching node. Starts from 1, 0 indicates there was no line data.
68 changes: 68 additions & 0 deletions pkg/yqlib/doc/operators/line.md
Original file line number Diff line number Diff line change
@@ -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
```

2 changes: 2 additions & 0 deletions pkg/yqlib/expression_tokeniser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
3 changes: 3 additions & 0 deletions pkg/yqlib/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
23 changes: 23 additions & 0 deletions pkg/yqlib/operator_column.go
Original file line number Diff line number Diff line change
@@ -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
}
47 changes: 47 additions & 0 deletions pkg/yqlib/operator_column_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
23 changes: 23 additions & 0 deletions pkg/yqlib/operator_line.go
Original file line number Diff line number Diff line change
@@ -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
}
47 changes: 47 additions & 0 deletions pkg/yqlib/operator_line_test.go
Original file line number Diff line number Diff line change
@@ -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)
}

0 comments on commit 8698433

Please sign in to comment.