comment ops!

This commit is contained in:
Mike Farah 2020-11-06 11:23:26 +11:00
parent b1f139c965
commit 5ab584afac
9 changed files with 138 additions and 71 deletions

1
pkg/yqlib/doc/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.md

View File

@ -1,64 +0,0 @@
# Equal Operator
## Examples
### Example 0
sample.yml:
```yaml
[cat,goat,dog]
```
Expression
```bash
yq '.[] | (. == "*at")' < sample.yml
```
Result
```yaml
true
true
false
```
### Example 1
sample.yml:
```yaml
[3, 4, 5]
```
Expression
```bash
yq '.[] | (. == 4)' < sample.yml
```
Result
```yaml
false
true
false
```
### Example 2
sample.yml:
```yaml
a: { cat: {b: apple, c: whatever}, pat: {b: banana} }
```
Expression
```bash
yq '.a | (.[].b == "apple")' < sample.yml
```
Result
```yaml
true
false
```
### Example 3
Expression
```bash
yq 'null == null' < sample.yml
```
Result
```yaml
true
```
### Example 4
Expression
```bash
yq 'null == ~' < sample.yml
```
Result
```yaml
true
```

View File

@ -18,14 +18,12 @@ type OperationType struct {
// operators TODO: // operators TODO:
// - generator doc from operator tests // - generator doc from operator tests
// - stripComments not recursive // - set comments not recursive
// - documentIndex - retrieves document index, can be used with select // - documentIndex - retrieves document index, can be used with select
// - mergeAppend (merges and appends arrays) // - mergeAppend (merges and appends arrays)
// - mergeEmpty (sets only if the document is empty, do I do that now?) // - mergeEmpty (sets only if the document is empty, do I do that now?)
// - updateTag - not recursive // - updateTag - not recursive
// - select by tag (tag==)
// - get tag (tag) // - get tag (tag)
// - select by style (style==)
// - compare ?? // - compare ??
// - validate ?? // - validate ??
// - exists // - exists
@ -38,6 +36,7 @@ var Union = &OperationType{Type: "UNION", NumArgs: 2, Precedence: 10, Handler: U
var Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignUpdateOperator} var Assign = &OperationType{Type: "ASSIGN", NumArgs: 2, Precedence: 40, Handler: AssignUpdateOperator}
var AssignAttributes = &OperationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: AssignAttributesOperator} var AssignAttributes = &OperationType{Type: "ASSIGN_ATTRIBUTES", NumArgs: 2, Precedence: 40, Handler: AssignAttributesOperator}
var AssignStyle = &OperationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: AssignStyleOperator} var AssignStyle = &OperationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 40, Handler: AssignStyleOperator}
var AssignComment = &OperationType{Type: "ASSIGN_COMMENT", NumArgs: 2, Precedence: 40, Handler: AssignCommentsOperator}
var Multiply = &OperationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 40, Handler: MultiplyOperator} var Multiply = &OperationType{Type: "MULTIPLY", NumArgs: 2, Precedence: 40, Handler: MultiplyOperator}

View File

@ -0,0 +1,47 @@
package yqlib
import "container/list"
type AssignCommentPreferences struct {
LineComment bool
HeadComment bool
FootComment bool
}
func AssignCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
log.Debugf("AssignComments operator!")
rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
if err != nil {
return nil, err
}
comment := ""
if rhs.Front() != nil {
comment = rhs.Front().Value.(*CandidateNode).Node.Value
}
lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs)
if err != nil {
return nil, err
}
preferences := pathNode.Operation.Preferences.(*AssignCommentPreferences)
for el := lhs.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
log.Debugf("Setting comment of : %v", candidate.GetKey())
if preferences.LineComment {
candidate.Node.LineComment = comment
}
if preferences.HeadComment {
candidate.Node.HeadComment = comment
}
if preferences.FootComment {
candidate.Node.FootComment = comment
}
}
return matchingNodes, nil
}

View File

@ -0,0 +1,55 @@
package yqlib
import (
"testing"
)
var commentOperatorScenarios = []expressionScenario{
{
description: "Add line comment",
document: `a: cat`,
expression: `.a lineComment="single"`,
expected: []string{
"D0, P[], (doc)::a: cat # single\n",
},
},
{
description: "Add head comment",
document: `a: cat`,
expression: `. headComment="single"`,
expected: []string{
"D0, P[], (doc)::# single\n\na: cat\n",
},
},
{
description: "Add foot comment, using an expression",
document: `a: cat`,
expression: `. footComment=.a`,
expected: []string{
"D0, P[], (doc)::a: cat\n\n# cat\n",
},
},
{
description: "Remove comment",
document: "a: cat # comment\nb: dog # leave this",
expression: `.a lineComment=""`,
expected: []string{
"D0, P[], (doc)::a: cat\nb: dog # leave this\n",
},
},
{
description: "Remove all comments",
document: "# hi\n\na: cat # comment\n\n# great\n",
expression: `.. comments=""`,
expected: []string{
"D0, P[], (!!map)::a: cat\n",
},
},
}
func TestCommentOperatorScenarios(t *testing.T) {
for _, tt := range commentOperatorScenarios {
testScenario(t, &tt)
}
documentScenarios(t, "Comments Operator", commentOperatorScenarios)
}

View File

@ -49,5 +49,5 @@ func TestEqualOperatorScenarios(t *testing.T) {
for _, tt := range equalsOperatorScenarios { for _, tt := range equalsOperatorScenarios {
testScenario(t, &tt) testScenario(t, &tt)
} }
documentScenarios(t, "Equal Operator", equalsOperatorScenarios) documentScenarios(t, "Equals Operator", equalsOperatorScenarios)
} }

View File

@ -8,8 +8,18 @@ import (
) )
func AssignStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { func AssignStyleOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
customStyle := pathNode.Rhs.Operation.StringValue
log.Debugf("AssignStyleOperator: %v", customStyle) log.Debugf("AssignStyleOperator: %v")
rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs)
if err != nil {
return nil, err
}
customStyle := ""
if rhs.Front() != nil {
customStyle = rhs.Front().Value.(*CandidateNode).Node.Value
}
var style yaml.Style var style yaml.Style
if customStyle == "tagged" { if customStyle == "tagged" {

View File

@ -12,6 +12,14 @@ var styleOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::{a: 'cat'}\n", "D0, P[], (doc)::{a: 'cat'}\n",
}, },
}, },
{
description: "Set style using a path",
document: `{a: cat, b: double}`,
expression: `.a style=.b`,
expected: []string{
"D0, P[], (doc)::{a: \"cat\", b: double}\n",
},
},
{ {
document: `{a: "cat", b: 'dog'}`, document: `{a: "cat", b: 'dog'}`,
expression: `.. style=""`, expression: `.. style=""`,
@ -42,4 +50,5 @@ func TestStyleOperatorScenarios(t *testing.T) {
for _, tt := range styleOperatorScenarios { for _, tt := range styleOperatorScenarios {
testScenario(t, &tt) testScenario(t, &tt)
} }
documentScenarios(t, "Style Operator", styleOperatorScenarios)
} }

View File

@ -84,9 +84,13 @@ func documentToken() lex.Action {
} }
func opToken(op *OperationType) lex.Action { func opToken(op *OperationType) lex.Action {
return opTokenWithPrefs(op, nil)
}
func opTokenWithPrefs(op *OperationType, preferences interface{}) lex.Action {
return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { return func(s *lex.Scanner, m *machines.Match) (interface{}, error) {
value := string(m.Bytes) value := string(m.Bytes)
op := &Operation{OperationType: op, Value: op.Type, StringValue: value} op := &Operation{OperationType: op, Value: op.Type, StringValue: value, Preferences: preferences}
return &Token{TokenType: OperationToken, Operation: op}, nil return &Token{TokenType: OperationToken, Operation: op}, nil
} }
} }
@ -190,6 +194,12 @@ func initLexer() (*lex.Lexer, error) {
lexer.Add([]byte(`style\s*=`), opToken(AssignStyle)) lexer.Add([]byte(`style\s*=`), opToken(AssignStyle))
lexer.Add([]byte(`style`), opToken(GetStyle)) lexer.Add([]byte(`style`), opToken(GetStyle))
lexer.Add([]byte(`lineComment\s*=`), opTokenWithPrefs(AssignComment, &AssignCommentPreferences{LineComment: true}))
lexer.Add([]byte(`headComment\s*=`), opTokenWithPrefs(AssignComment, &AssignCommentPreferences{HeadComment: true}))
lexer.Add([]byte(`footComment\s*=`), opTokenWithPrefs(AssignComment, &AssignCommentPreferences{FootComment: true}))
lexer.Add([]byte(`comments\s*=`), opTokenWithPrefs(AssignComment, &AssignCommentPreferences{LineComment: true, HeadComment: true, FootComment: true}))
// lexer.Add([]byte(`style`), opToken(GetStyle))
// lexer.Add([]byte(`and`), opToken()) // lexer.Add([]byte(`and`), opToken())
lexer.Add([]byte(`collect`), opToken(Collect)) lexer.Add([]byte(`collect`), opToken(Collect))