Skip to content

Commit

Permalink
Added string case operators
Browse files Browse the repository at this point in the history
  • Loading branch information
mikefarah committed Feb 22, 2022
1 parent 71706af commit d7b158f
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 0 deletions.
32 changes: 32 additions & 0 deletions pkg/yqlib/doc/operators/string-operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,38 @@ Note that versions prior to 4.18 require the 'eval/e' command to be specified.&#
`yq e <exp> <file>`
{% endhint %}

## To up (upper) case
Works with unicode characters

Given a sample.yml file of:
```yaml
água
```
then
```bash
yq 'upcase' sample.yml
```
will output
```yaml
ÁGUA
```

## To down (lower) case
Works with unicode characters

Given a sample.yml file of:
```yaml
ÁgUA
```
then
```bash
yq 'downcase' sample.yml
```
will output
```yaml
água
```

## Join strings
Given a sample.yml file of:
```yaml
Expand Down
6 changes: 6 additions & 0 deletions pkg/yqlib/expression_tokeniser.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,12 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`capture`), opToken(captureOpType))
lexer.Add([]byte(`test`), opToken(testOpType))

lexer.Add([]byte(`upcase`), opTokenWithPrefs(changeCaseOpType, nil, changeCasePrefs{ToUpperCase: true}))
lexer.Add([]byte(`ascii_upcase`), opTokenWithPrefs(changeCaseOpType, nil, changeCasePrefs{ToUpperCase: true}))

lexer.Add([]byte(`downcase`), opTokenWithPrefs(changeCaseOpType, nil, changeCasePrefs{ToUpperCase: false}))
lexer.Add([]byte(`ascii_downcase`), opTokenWithPrefs(changeCaseOpType, nil, changeCasePrefs{ToUpperCase: false}))

lexer.Add([]byte(`sort`), opToken(sortOpType))
lexer.Add([]byte(`sort_by`), opToken(sortByOpType))
lexer.Add([]byte(`reverse`), opToken(reverseOpType))
Expand Down
3 changes: 3 additions & 0 deletions pkg/yqlib/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,16 @@ var explodeOpType = &operationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50,
var sortByOpType = &operationType{Type: "SORT_BY", NumArgs: 1, Precedence: 50, Handler: sortByOperator}
var reverseOpType = &operationType{Type: "REVERSE", NumArgs: 0, Precedence: 50, Handler: reverseOperator}
var sortOpType = &operationType{Type: "SORT", NumArgs: 0, Precedence: 50, Handler: sortOperator}

var sortKeysOpType = &operationType{Type: "SORT_KEYS", NumArgs: 1, Precedence: 50, Handler: sortKeysOperator}

var joinStringOpType = &operationType{Type: "JOIN", NumArgs: 1, Precedence: 50, Handler: joinStringOperator}
var subStringOpType = &operationType{Type: "SUBSTR", NumArgs: 1, Precedence: 50, Handler: substituteStringOperator}
var matchOpType = &operationType{Type: "MATCH", NumArgs: 1, Precedence: 50, Handler: matchOperator}
var captureOpType = &operationType{Type: "CAPTURE", NumArgs: 1, Precedence: 50, Handler: captureOperator}
var testOpType = &operationType{Type: "TEST", NumArgs: 1, Precedence: 50, Handler: testOperator}
var splitStringOpType = &operationType{Type: "SPLIT", NumArgs: 1, Precedence: 50, Handler: splitStringOperator}
var changeCaseOpType = &operationType{Type: "CHANGE_CASE", NumArgs: 0, Precedence: 50, Handler: changeCaseOperator}

var loadOpType = &operationType{Type: "LOAD", NumArgs: 1, Precedence: 50, Handler: loadYamlOperator}

Expand Down
31 changes: 31 additions & 0 deletions pkg/yqlib/operator_strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,37 @@ import (
"gopkg.in/yaml.v3"
)

type changeCasePrefs struct {
ToUpperCase bool
}

func changeCaseOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
results := list.New()
prefs := expressionNode.Operation.Preferences.(changeCasePrefs)

for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)

node := unwrapDoc(candidate.Node)

if guessTagFromCustomType(node) != "!!str" {
return Context{}, fmt.Errorf("cannot change case with %v, can only operate on strings. ", node.Tag)
}

newStringNode := &yaml.Node{Kind: yaml.ScalarNode, Tag: node.Tag, Style: node.Style}
if prefs.ToUpperCase {
newStringNode.Value = strings.ToUpper(node.Value)
} else {
newStringNode.Value = strings.ToLower(node.Value)
}
results.PushBack(candidate.CreateReplacement(newStringNode))

}

return context.ChildContext(results), nil

}

func getSubstituteParameters(d *dataTreeNavigator, block *ExpressionNode, context Context) (string, string, error) {
regEx := ""
replacementText := ""
Expand Down
34 changes: 34 additions & 0 deletions pkg/yqlib/operator_strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,40 @@ import (
)

var stringsOperatorScenarios = []expressionScenario{
{
description: "To up (upper) case",
subdescription: "Works with unicode characters",
document: `água`,
expression: "upcase",
expected: []string{
"D0, P[], (!!str)::ÁGUA\n",
},
},
{
skipDoc: true,
document: `!camel água`,
expression: "upcase",
expected: []string{
"D0, P[], (!camel)::ÁGUA\n",
},
},
{
description: "To down (lower) case",
subdescription: "Works with unicode characters",
document: `ÁgUA`,
expression: "downcase",
expected: []string{
"D0, P[], (!!str)::água\n",
},
},
{
skipDoc: true,
document: `!camel ÁgUA`,
expression: "downcase",
expected: []string{
"D0, P[], (!camel)::água\n",
},
},
{
description: "Join strings",
document: `[cat, meow, 1, null, true]`,
Expand Down

0 comments on commit d7b158f

Please sign in to comment.