diff --git a/pkg/yqlib/lib.go b/pkg/yqlib/lib.go index 6ae95f84..fadaf502 100644 --- a/pkg/yqlib/lib.go +++ b/pkg/yqlib/lib.go @@ -47,6 +47,7 @@ var Pipe = &OperationType{Type: "PIPE", NumArgs: 2, Precedence: 45, Handler: Pip var Length = &OperationType{Type: "LENGTH", NumArgs: 0, Precedence: 50, Handler: LengthOperator} var Collect = &OperationType{Type: "COLLECT", NumArgs: 0, Precedence: 50, Handler: CollectOperator} var GetStyle = &OperationType{Type: "GET_STYLE", NumArgs: 0, Precedence: 50, Handler: GetStyleOperator} +var GetComment = &OperationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50, Handler: GetCommentsOperator} var Explode = &OperationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: ExplodeOperator} diff --git a/pkg/yqlib/operator_comments.go b/pkg/yqlib/operator_comments.go index e3aeedb1..58cc17ac 100644 --- a/pkg/yqlib/operator_comments.go +++ b/pkg/yqlib/operator_comments.go @@ -1,8 +1,13 @@ package yqlib -import "container/list" +import ( + "container/list" + "strings" -type AssignCommentPreferences struct { + "gopkg.in/yaml.v3" +) + +type CommentOpPreferences struct { LineComment bool HeadComment bool FootComment bool @@ -27,7 +32,7 @@ func AssignCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, path return nil, err } - preferences := pathNode.Operation.Preferences.(*AssignCommentPreferences) + preferences := pathNode.Operation.Preferences.(*CommentOpPreferences) for el := lhs.Front(); el != nil; el = el.Next() { candidate := el.Value.(*CandidateNode) @@ -45,3 +50,27 @@ func AssignCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, path } return matchingNodes, nil } + +func GetCommentsOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { + preferences := pathNode.Operation.Preferences.(*CommentOpPreferences) + log.Debugf("GetComments operator!") + var results = list.New() + + for el := matchingNodes.Front(); el != nil; el = el.Next() { + candidate := el.Value.(*CandidateNode) + comment := "" + if preferences.LineComment { + comment = candidate.Node.LineComment + } else if preferences.HeadComment { + comment = candidate.Node.HeadComment + } else if preferences.FootComment { + comment = candidate.Node.FootComment + } + comment = strings.Replace(comment, "# ", "", 1) + + node := &yaml.Node{Kind: yaml.ScalarNode, Value: comment, Tag: "!!str"} + lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path} + results.PushBack(lengthCand) + } + return results, nil +} diff --git a/pkg/yqlib/operator_comments_test.go b/pkg/yqlib/operator_comments_test.go index 221a227b..46a16952 100644 --- a/pkg/yqlib/operator_comments_test.go +++ b/pkg/yqlib/operator_comments_test.go @@ -6,7 +6,7 @@ import ( var commentOperatorScenarios = []expressionScenario{ { - description: "Add line comment", + description: "Set line comment", document: `a: cat`, expression: `.a lineComment="single"`, expected: []string{ @@ -14,7 +14,7 @@ var commentOperatorScenarios = []expressionScenario{ }, }, { - description: "Add head comment", + description: "Set head comment", document: `a: cat`, expression: `. headComment="single"`, expected: []string{ @@ -22,7 +22,7 @@ var commentOperatorScenarios = []expressionScenario{ }, }, { - description: "Add foot comment, using an expression", + description: "Set foot comment, using an expression", document: `a: cat`, expression: `. footComment=.a`, expected: []string{ @@ -45,6 +45,30 @@ var commentOperatorScenarios = []expressionScenario{ "D0, P[], (!!map)::a: cat\n", }, }, + { + description: "Get line comment", + document: "# welcome!\n\na: cat # meow\n\n# have a great day", + expression: `.a | lineComment`, + expected: []string{ + "D0, P[a], (!!str)::meow\n", + }, + }, + { + description: "Get head comment", + document: "# welcome!\n\na: cat # meow\n\n# have a great day", + expression: `. | headComment`, + expected: []string{ + "D0, P[], (!!str)::welcome!\n", + }, + }, + { + description: "Get foot comment", + document: "# welcome!\n\na: cat # meow\n\n# have a great day", + expression: `. | footComment`, + expected: []string{ + "D0, P[], (!!str)::have a great day\n", + }, + }, } func TestCommentOperatorScenarios(t *testing.T) { diff --git a/pkg/yqlib/operator_multiply_test.go b/pkg/yqlib/operator_multiply_test.go index 4b8dc283..3da2858f 100644 --- a/pkg/yqlib/operator_multiply_test.go +++ b/pkg/yqlib/operator_multiply_test.go @@ -6,6 +6,7 @@ import ( var multiplyOperatorScenarios = []expressionScenario{ { + skipDoc: true, document: `{a: {also: [1]}, b: {also: me}}`, expression: `. * {"a" : .b}`, expected: []string{ @@ -13,6 +14,7 @@ var multiplyOperatorScenarios = []expressionScenario{ }, }, { + skipDoc: true, document: `{a: {also: me}, b: {also: [1]}}`, expression: `. * {"a":.b}`, expected: []string{ @@ -20,13 +22,15 @@ var multiplyOperatorScenarios = []expressionScenario{ }, }, { - document: `{a: {also: me}, b: {also: {g: wizz}}}`, - expression: `. * {"a":.b}`, + description: "Merge objects together", + document: `{a: {also: me}, b: {also: {g: wizz}}}`, + expression: `. * {"a":.b}`, expected: []string{ "D0, P[], (!!map)::{a: {also: {g: wizz}}, b: {also: {g: wizz}}}\n", }, }, { + skipDoc: true, document: `{a: {also: {g: wizz}}, b: {also: me}}`, expression: `. * {"a":.b}`, expected: []string{ @@ -34,6 +38,7 @@ var multiplyOperatorScenarios = []expressionScenario{ }, }, { + skipDoc: true, document: `{a: {also: {g: wizz}}, b: {also: [1]}}`, expression: `. * {"a":.b}`, expected: []string{ @@ -41,6 +46,7 @@ var multiplyOperatorScenarios = []expressionScenario{ }, }, { + skipDoc: true, document: `{a: {also: [1]}, b: {also: {g: wizz}}}`, expression: `. * {"a":.b}`, expected: []string{ @@ -48,6 +54,7 @@ var multiplyOperatorScenarios = []expressionScenario{ }, }, { + skipDoc: true, document: `{a: {things: great}, b: {also: me}}`, expression: `. * {"a":.b}`, expected: []string{ @@ -55,6 +62,7 @@ var multiplyOperatorScenarios = []expressionScenario{ }, }, { + description: "Merge keeps style of LHS", document: `a: {things: great} b: also: "me" @@ -68,36 +76,41 @@ b: }, }, { - document: `{a: [1,2,3], b: [3,4,5]}`, - expression: `. * {"a":.b}`, + description: "Merge arrays", + document: `{a: [1,2,3], b: [3,4,5]}`, + expression: `. * {"a":.b}`, expected: []string{ "D0, P[], (!!map)::{a: [3, 4, 5], b: [3, 4, 5]}\n", }, }, { - document: `{a: cat}`, - expression: `. * {"a": {"c": .a}}`, + description: "Merge to prefix an element", + document: `{a: cat, b: dog}`, + expression: `. * {"a": {"c": .a}}`, expected: []string{ - "D0, P[], (!!map)::{a: {c: cat}}\n", + "D0, P[], (!!map)::{a: {c: cat}, b: dog}\n", }, }, { - document: `{a: &cat {c: frog}, b: {f: *cat}, c: {g: thongs}}`, - expression: `.c * .b`, + description: "Merge with simple aliases", + document: `{a: &cat {c: frog}, b: {f: *cat}, c: {g: thongs}}`, + expression: `.c * .b`, expected: []string{ "D0, P[c], (!!map)::{g: thongs, f: *cat}\n", }, }, { - document: `{a: {c: &cat frog}, b: {f: *cat}, c: {g: thongs}}`, - expression: `.c * .a`, + description: "Merge does not copy anchor names", + document: `{a: {c: &cat frog}, b: {f: *cat}, c: {g: thongs}}`, + expression: `.c * .a`, expected: []string{ "D0, P[c], (!!map)::{g: thongs, c: frog}\n", }, }, { - document: mergeDocSample, - expression: `.foobar * .foobarList`, + description: "Merge with merge anchors", + document: mergeDocSample, + expression: `.foobar * .foobarList`, expected: []string{ "D0, P[foobar], (!!map)::c: foobarList_c\n<<: [*foo, *bar]\nthing: foobar_thing\nb: foobarList_b\n", }, @@ -108,4 +121,5 @@ func TestMultiplyOperatorScenarios(t *testing.T) { for _, tt := range multiplyOperatorScenarios { testScenario(t, &tt) } + documentScenarios(t, "Mulitply Operator", multiplyOperatorScenarios) } diff --git a/pkg/yqlib/operators_test.go b/pkg/yqlib/operators_test.go index c9745b16..03a32597 100644 --- a/pkg/yqlib/operators_test.go +++ b/pkg/yqlib/operators_test.go @@ -15,6 +15,7 @@ type expressionScenario struct { document string expression string expected []string + skipDoc bool } func testScenario(t *testing.T, s *expressionScenario) { @@ -48,36 +49,39 @@ func documentScenarios(t *testing.T, title string, scenarios []expressionScenari printer := NewPrinter(false, true, false, 2, true) for index, s := range scenarios { - if s.description != "" { - w.WriteString(fmt.Sprintf("### %v\n", s.description)) - } else { - w.WriteString(fmt.Sprintf("### Example %v\n", index)) - } - if s.document != "" { - w.WriteString(fmt.Sprintf("sample.yml:\n")) - w.WriteString(fmt.Sprintf("```yaml\n%v\n```\n", s.document)) - } - if s.expression != "" { - w.WriteString(fmt.Sprintf("Expression\n")) - w.WriteString(fmt.Sprintf("```bash\nyq '%v' < sample.yml\n```\n", s.expression)) - } + if !s.skipDoc { - w.WriteString(fmt.Sprintf("Result\n")) + if s.description != "" { + w.WriteString(fmt.Sprintf("### %v\n", s.description)) + } else { + w.WriteString(fmt.Sprintf("### Example %v\n", index)) + } + if s.document != "" { + w.WriteString(fmt.Sprintf("sample.yml:\n")) + w.WriteString(fmt.Sprintf("```yaml\n%v\n```\n", s.document)) + } + if s.expression != "" { + w.WriteString(fmt.Sprintf("Expression\n")) + w.WriteString(fmt.Sprintf("```bash\nyq '%v' < sample.yml\n```\n", s.expression)) + } - nodes := readDoc(t, s.document) - path, errPath := treeCreator.ParsePath(s.expression) - if errPath != nil { - t.Error(errPath) - return - } - var output bytes.Buffer - results, err := treeNavigator.GetMatchingNodes(nodes, path) - printer.PrintResults(results, bufio.NewWriter(&output)) + w.WriteString(fmt.Sprintf("Result\n")) - w.WriteString(fmt.Sprintf("```yaml\n%v```\n", output.String())) + nodes := readDoc(t, s.document) + path, errPath := treeCreator.ParsePath(s.expression) + if errPath != nil { + t.Error(errPath) + return + } + var output bytes.Buffer + results, err := treeNavigator.GetMatchingNodes(nodes, path) + printer.PrintResults(results, bufio.NewWriter(&output)) - if err != nil { - panic(err) + w.WriteString(fmt.Sprintf("```yaml\n%v```\n", output.String())) + + if err != nil { + panic(err) + } } } diff --git a/pkg/yqlib/path_tokeniser.go b/pkg/yqlib/path_tokeniser.go index 53575cdd..4fa41c0e 100644 --- a/pkg/yqlib/path_tokeniser.go +++ b/pkg/yqlib/path_tokeniser.go @@ -194,10 +194,16 @@ func initLexer() (*lex.Lexer, error) { lexer.Add([]byte(`style\s*=`), opToken(AssignStyle)) 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(`lineComment\s*=`), opTokenWithPrefs(AssignComment, &CommentOpPreferences{LineComment: true})) + lexer.Add([]byte(`lineComment`), opTokenWithPrefs(GetComment, &CommentOpPreferences{LineComment: true})) + + lexer.Add([]byte(`headComment\s*=`), opTokenWithPrefs(AssignComment, &CommentOpPreferences{HeadComment: true})) + lexer.Add([]byte(`headComment`), opTokenWithPrefs(GetComment, &CommentOpPreferences{HeadComment: true})) + + lexer.Add([]byte(`footComment\s*=`), opTokenWithPrefs(AssignComment, &CommentOpPreferences{FootComment: true})) + lexer.Add([]byte(`footComment`), opTokenWithPrefs(GetComment, &CommentOpPreferences{FootComment: true})) + + lexer.Add([]byte(`comments\s*=`), opTokenWithPrefs(AssignComment, &CommentOpPreferences{LineComment: true, HeadComment: true, FootComment: true})) // lexer.Add([]byte(`style`), opToken(GetStyle)) // lexer.Add([]byte(`and`), opToken())