diff --git a/pkg/yqlib/doc/Assign.md b/pkg/yqlib/doc/Assign.md index a783587..c5f1ce6 100644 --- a/pkg/yqlib/doc/Assign.md +++ b/pkg/yqlib/doc/Assign.md @@ -100,7 +100,7 @@ a: ``` then ```bash -yq eval '.a[] | select(. == "apple") |= "frog"' sample.yml +yq eval '.a.[] | select(. == "apple") |= "frog"' sample.yml ``` will output ```yaml diff --git a/pkg/yqlib/doc/Collect into Array.md b/pkg/yqlib/doc/Collect into Array.md index d06927e..8195ee9 100644 --- a/pkg/yqlib/doc/Collect into Array.md +++ b/pkg/yqlib/doc/Collect into Array.md @@ -10,6 +10,7 @@ yq eval --null-input '[]' ``` will output ```yaml +[] ``` ## Collect single diff --git a/pkg/yqlib/doc/Collect into Object.md b/pkg/yqlib/doc/Collect into Object.md index ded3288..ffbc11b 100644 --- a/pkg/yqlib/doc/Collect into Object.md +++ b/pkg/yqlib/doc/Collect into Object.md @@ -34,7 +34,7 @@ pets: ``` then ```bash -yq eval '{.name: .pets[]}' sample.yml +yq eval '{.name: .pets.[]}' sample.yml ``` will output ```yaml @@ -57,7 +57,7 @@ pets: ``` then ```bash -yq eval '{.name: .pets[]}' sample.yml +yq eval '{.name: .pets.[]}' sample.yml ``` will output ```yaml diff --git a/pkg/yqlib/doc/Document Index Operator.md b/pkg/yqlib/doc/Document Index Operator.md deleted file mode 100644 index 30ba1ec..0000000 --- a/pkg/yqlib/doc/Document Index Operator.md +++ /dev/null @@ -1,53 +0,0 @@ -## Retrieve a document index -Given a sample.yml file of: -```yaml -a: cat ---- -a: frog -``` -then -```bash -yq eval '.a | documentIndex' sample.yml -``` -will output -```yaml -0 ---- -1 -``` - -## Filter by document index -Given a sample.yml file of: -```yaml -a: cat ---- -a: frog -``` -then -```bash -yq eval 'select(. | documentIndex == 1)' sample.yml -``` -will output -```yaml -a: frog -``` - -## Print Document Index with matches -Given a sample.yml file of: -```yaml -a: cat ---- -a: frog -``` -then -```bash -yq eval '.a | ({"match": ., "doc": (. | documentIndex)})' sample.yml -``` -will output -```yaml -match: cat -doc: 0 -match: frog -doc: 1 -``` - diff --git a/pkg/yqlib/doc/Select.md b/pkg/yqlib/doc/Select.md index 1726853..0dfe276 100644 --- a/pkg/yqlib/doc/Select.md +++ b/pkg/yqlib/doc/Select.md @@ -26,7 +26,7 @@ a: ``` then ```bash -yq eval '(.a[] | select(. == "*at")) |= "rabbit"' sample.yml +yq eval '(.a.[] | select(. == "*at")) |= "rabbit"' sample.yml ``` will output ```yaml diff --git a/pkg/yqlib/operator_assign_test.go b/pkg/yqlib/operator_assign_test.go index 82e928d..0625134 100644 --- a/pkg/yqlib/operator_assign_test.go +++ b/pkg/yqlib/operator_assign_test.go @@ -73,7 +73,7 @@ var assignOperatorScenarios = []expressionScenario{ { description: "Update selected results", document: `{a: {b: apple, c: cactus}}`, - expression: `.a[] | select(. == "apple") |= "frog"`, + expression: `.a.[] | select(. == "apple") |= "frog"`, expected: []string{ "D0, P[], (doc)::{a: {b: frog, c: cactus}}\n", }, diff --git a/pkg/yqlib/operator_collect.go b/pkg/yqlib/operator_collect.go index 9eda72e..ffdfda6 100644 --- a/pkg/yqlib/operator_collect.go +++ b/pkg/yqlib/operator_collect.go @@ -3,12 +3,18 @@ package yqlib import ( "container/list" - "gopkg.in/yaml.v3" + yaml "gopkg.in/yaml.v3" ) func CollectOperator(d *dataTreeNavigator, matchMap *list.List, pathNode *PathTreeNode) (*list.List, error) { log.Debugf("-- collectOperation") + if matchMap.Len() == 0 { + node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq", Value: "[]"} + candidate := &CandidateNode{Node: node} + return nodeToMap(candidate), nil + } + var results = list.New() node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"} diff --git a/pkg/yqlib/operator_collect_object_test.go b/pkg/yqlib/operator_collect_object_test.go index 4682789..165b0c9 100644 --- a/pkg/yqlib/operator_collect_object_test.go +++ b/pkg/yqlib/operator_collect_object_test.go @@ -41,7 +41,7 @@ var collectObjectOperatorScenarios = []expressionScenario{ { description: `Using splat to create multiple objects`, document: `{name: Mike, pets: [cat, dog]}`, - expression: `{.name: .pets[]}`, + expression: `{.name: .pets.[]}`, expected: []string{ "D0, P[], (!!map)::Mike: cat\n", "D0, P[], (!!map)::Mike: dog\n", @@ -51,7 +51,7 @@ var collectObjectOperatorScenarios = []expressionScenario{ description: `Working with multiple documents`, dontFormatInputForDoc: false, document: "{name: Mike, pets: [cat, dog]}\n---\n{name: Rosey, pets: [monkey, sheep]}", - expression: `{.name: .pets[]}`, + expression: `{.name: .pets.[]}`, expected: []string{ "D0, P[], (!!map)::Mike: cat\n", "D0, P[], (!!map)::Mike: dog\n", @@ -62,7 +62,7 @@ var collectObjectOperatorScenarios = []expressionScenario{ { skipDoc: true, document: `{name: Mike, pets: [cat, dog], food: [hotdog, burger]}`, - expression: `{.name: .pets[], "f":.food[]}`, + expression: `{.name: .pets.[], "f":.food.[]}`, expected: []string{ "D0, P[], (!!map)::Mike: cat\nf: hotdog\n", "D0, P[], (!!map)::Mike: cat\nf: burger\n", diff --git a/pkg/yqlib/operator_collect_test.go b/pkg/yqlib/operator_collect_test.go index 26f075d..cadfef8 100644 --- a/pkg/yqlib/operator_collect_test.go +++ b/pkg/yqlib/operator_collect_test.go @@ -9,7 +9,9 @@ var collectOperatorScenarios = []expressionScenario{ description: "Collect empty", document: ``, expression: `[]`, - expected: []string{}, + expected: []string{ + "D0, P[], (!!seq)::[]\n", + }, }, { description: "Collect single", @@ -52,7 +54,7 @@ var collectOperatorScenarios = []expressionScenario{ }, { document: `a: {b: [1,2,3]}`, - expression: `[.a.b[]]`, + expression: `[.a.b.[]]`, skipDoc: true, expected: []string{ "D0, P[a b], (!!seq)::- 1\n- 2\n- 3\n", diff --git a/pkg/yqlib/operator_create_map_test.go b/pkg/yqlib/operator_create_map_test.go index 291abd0..6a97246 100644 --- a/pkg/yqlib/operator_create_map_test.go +++ b/pkg/yqlib/operator_create_map_test.go @@ -21,14 +21,14 @@ var createMapOperatorScenarios = []expressionScenario{ }, { document: `{name: Mike, pets: [cat, dog]}`, - expression: `.name: .pets[]`, + expression: `.name: .pets.[]`, expected: []string{ "D0, P[], (!!seq)::- [{Mike: cat}, {Mike: dog}]\n", }, }, { document: `{name: Mike, pets: [cat, dog], food: [hotdog, burger]}`, - expression: `.name: .pets[], "f":.food[]`, + expression: `.name: .pets.[], "f":.food.[]`, expected: []string{ "D0, P[], (!!seq)::- [{Mike: cat}, {Mike: dog}]\n", "D0, P[], (!!seq)::- [{f: hotdog}, {f: burger}]\n", @@ -36,7 +36,7 @@ var createMapOperatorScenarios = []expressionScenario{ }, { document: "{name: Mike, pets: [cat, dog], food: [hotdog, burger]}\n---\n{name: Fred, pets: [mouse], food: [pizza, onion, apple]}", - expression: `.name: .pets[], "f":.food[]`, + expression: `.name: .pets.[], "f":.food.[]`, expected: []string{ "D0, P[], (!!seq)::- [{Mike: cat}, {Mike: dog}]\n- [{Fred: mouse}]\n", "D0, P[], (!!seq)::- [{f: hotdog}, {f: burger}]\n- [{f: pizza}, {f: onion}, {f: apple}]\n", diff --git a/pkg/yqlib/operator_select_test.go b/pkg/yqlib/operator_select_test.go index 3b495ee..d98805a 100644 --- a/pkg/yqlib/operator_select_test.go +++ b/pkg/yqlib/operator_select_test.go @@ -23,7 +23,7 @@ var selectOperatorScenarios = []expressionScenario{ { skipDoc: true, document: `a: [cat,goat,dog]`, - expression: `.a[] | select(. == "*at")`, + expression: `.a.[] | select(. == "*at")`, expected: []string{ "D0, P[a 0], (!!str)::cat\n", "D0, P[a 1], (!!str)::goat\n"}, @@ -31,7 +31,7 @@ var selectOperatorScenarios = []expressionScenario{ { description: "Select and update matching values in map", document: `a: { things: cat, bob: goat, horse: dog }`, - expression: `(.a[] | select(. == "*at")) |= "rabbit"`, + expression: `(.a.[] | select(. == "*at")) |= "rabbit"`, expected: []string{ "D0, P[], (doc)::a: {things: rabbit, bob: rabbit, horse: dog}\n", }, @@ -39,7 +39,7 @@ var selectOperatorScenarios = []expressionScenario{ { skipDoc: true, document: `a: { things: {include: true}, notMe: {include: false}, andMe: {include: fold} }`, - expression: `.a[] | select(.include)`, + expression: `.a.[] | select(.include)`, expected: []string{ "D0, P[a things], (!!map)::{include: true}\n", "D0, P[a andMe], (!!map)::{include: fold}\n", diff --git a/pkg/yqlib/operators_test.go b/pkg/yqlib/operators_test.go index 4f38aa9..15e52d7 100644 --- a/pkg/yqlib/operators_test.go +++ b/pkg/yqlib/operators_test.go @@ -29,7 +29,7 @@ func testScenario(t *testing.T, s *expressionScenario) { node, err := treeCreator.ParsePath(s.expression) if err != nil { - t.Error(err) + t.Error(fmt.Errorf("Error parsing expression %v of %v: %v", s.expression, s.description, err)) return } inputs := list.New() diff --git a/pkg/yqlib/path_parse_test.go b/pkg/yqlib/path_parse_test.go index b78a4bd..f8c4dec 100644 --- a/pkg/yqlib/path_parse_test.go +++ b/pkg/yqlib/path_parse_test.go @@ -37,8 +37,18 @@ var pathTests = []struct { // {`.a, .b`, append(make([]interface{}, 0), "a", "OR", "b")}, // {`[.a, .b]`, append(make([]interface{}, 0), "[", "a", "OR", "b", "]")}, // {`."[a", ."b]"`, append(make([]interface{}, 0), "[a", "OR", "b]")}, - // {`.a[]`, append(make([]interface{}, 0), "a", "PIPE", "[]")}, + // {`.a.[]`, append(make([]interface{}, 0), "a", "PIPE", "[]")}, // {`.[].a`, append(make([]interface{}, 0), "[]", "PIPE", "a")}, + // { + // `["cat"]`, + // append(make([]interface{}, 0), "[", "cat (string)", "]"), + // append(make([]interface{}, 0), "cat (string)", "COLLECT", "PIPE"), + // }, + { + `[]`, + append(make([]interface{}, 0), "[", "]"), + append(make([]interface{}, 0), "EMPTY", "COLLECT", "PIPE"), + }, { `d0.a`, append(make([]interface{}, 0), "d0", "PIPE", "a"), @@ -85,7 +95,7 @@ var pathTests = []struct { append(make([]interface{}, 0), "a", "mike (string)", "CREATE_MAP", "COLLECT_OBJECT", "PIPE"), }, { - `{.a: .c, .b[]: .f.g[]}`, + `{.a: .c, .b.[]: .f.g.[]}`, append(make([]interface{}, 0), "{", "a", "CREATE_MAP", "c", "UNION", "b", "PIPE", "[]", "CREATE_MAP", "f", "PIPE", "g", "PIPE", "[]", "}"), append(make([]interface{}, 0), "a", "c", "CREATE_MAP", "b", "[]", "PIPE", "f", "g", "PIPE", "[]", "PIPE", "CREATE_MAP", "UNION", "COLLECT_OBJECT", "PIPE"), }, diff --git a/pkg/yqlib/path_postfix.go b/pkg/yqlib/path_postfix.go index 1f360e4..bac3096 100644 --- a/pkg/yqlib/path_postfix.go +++ b/pkg/yqlib/path_postfix.go @@ -3,7 +3,7 @@ package yqlib import ( "errors" - "gopkg.in/op/go-logging.v1" + logging "gopkg.in/op/go-logging.v1" ) type PathPostFixer interface { diff --git a/pkg/yqlib/path_tokeniser.go b/pkg/yqlib/path_tokeniser.go index 17af2a2..7f510b4 100644 --- a/pkg/yqlib/path_tokeniser.go +++ b/pkg/yqlib/path_tokeniser.go @@ -33,6 +33,7 @@ type Token struct { func (t *Token) toString() string { if t.TokenType == OperationToken { + log.Debug("toString, its an op") return t.Operation.toString() } else if t.TokenType == OpenBracket { return "(" @@ -58,13 +59,7 @@ func pathToken(wrapped bool) lex.Action { if wrapped { value = unwrap(value) } - op := &Operation{OperationType: TraversePath, Value: value, StringValue: value} - return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil - } -} - -func literalPathToken(value string) lex.Action { - return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + log.Debug("PathToken %v", value) op := &Operation{OperationType: TraversePath, Value: value, StringValue: value} return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil } @@ -78,6 +73,7 @@ func documentToken() lex.Action { if errParsingInt != nil { return nil, errParsingInt } + log.Debug("documentToken %v", string(m.Bytes)) op := &Operation{OperationType: DocumentFilter, Value: number, StringValue: numberString} return &Token{TokenType: OperationToken, Operation: op, CheckForPostTraverse: true}, nil } @@ -93,6 +89,7 @@ func opAssignableToken(opType *OperationType, assignOpType *OperationType) lex.A func opTokenWithPrefs(op *OperationType, assignOpType *OperationType, preferences interface{}) lex.Action { return func(s *lex.Scanner, m *machines.Match) (interface{}, error) { + log.Debug("opTokenWithPrefs %v", string(m.Bytes)) value := string(m.Bytes) op := &Operation{OperationType: op, Value: op.Type, StringValue: value, Preferences: preferences} var assign *Operation @@ -187,7 +184,7 @@ func initLexer() (*lex.Lexer, error) { lexer.Add([]byte(`\(`), literalToken(OpenBracket, false)) lexer.Add([]byte(`\)`), literalToken(CloseBracket, true)) - lexer.Add([]byte(`\.?\[\]`), literalPathToken("[]")) + lexer.Add([]byte(`\.\[\]`), pathToken(false)) lexer.Add([]byte(`\.\.`), opToken(RecursiveDescent)) lexer.Add([]byte(`,`), opToken(Union))