mirror of
				https://github.com/taigrr/yq
				synced 2025-01-18 04:53:17 -08:00 
			
		
		
		
	Add operator!
This commit is contained in:
		
							parent
							
								
									3d6a231722
								
							
						
					
					
						commit
						1ce30b25dc
					
				
							
								
								
									
										107
									
								
								pkg/yqlib/doc/Add.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								pkg/yqlib/doc/Add.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | ||||
| Add behaves differently according to the type of the LHS: | ||||
| - arrays: concatenate | ||||
| - number scalars: arithmetic addition (soon) | ||||
| - string scalars: concatenate (soon) | ||||
| ## Concatenate arrays | ||||
| Given a sample.yml file of: | ||||
| ```yaml | ||||
| a: | ||||
|   - 1 | ||||
|   - 2 | ||||
| b: | ||||
|   - 3 | ||||
|   - 4 | ||||
| ``` | ||||
| then | ||||
| ```bash | ||||
| yq eval '.a + .b' sample.yml | ||||
| ``` | ||||
| will output | ||||
| ```yaml | ||||
| - 1 | ||||
| - 2 | ||||
| - 3 | ||||
| - 4 | ||||
| ``` | ||||
| 
 | ||||
| ## Concatenate null to array | ||||
| Given a sample.yml file of: | ||||
| ```yaml | ||||
| a: | ||||
|   - 1 | ||||
|   - 2 | ||||
| ``` | ||||
| then | ||||
| ```bash | ||||
| yq eval '.a + null' sample.yml | ||||
| ``` | ||||
| will output | ||||
| ```yaml | ||||
| - 1 | ||||
| - 2 | ||||
| ``` | ||||
| 
 | ||||
| ## Add object to array | ||||
| Given a sample.yml file of: | ||||
| ```yaml | ||||
| a: | ||||
|   - 1 | ||||
|   - 2 | ||||
| c: | ||||
|   cat: meow | ||||
| ``` | ||||
| then | ||||
| ```bash | ||||
| yq eval '.a + .c' sample.yml | ||||
| ``` | ||||
| will output | ||||
| ```yaml | ||||
| - 1 | ||||
| - 2 | ||||
| - cat: meow | ||||
| ``` | ||||
| 
 | ||||
| ## Add string to array | ||||
| Given a sample.yml file of: | ||||
| ```yaml | ||||
| a: | ||||
|   - 1 | ||||
|   - 2 | ||||
| ``` | ||||
| then | ||||
| ```bash | ||||
| yq eval '.a + "hello"' sample.yml | ||||
| ``` | ||||
| will output | ||||
| ```yaml | ||||
| - 1 | ||||
| - 2 | ||||
| - hello | ||||
| ``` | ||||
| 
 | ||||
| ## Update array (append) | ||||
| Given a sample.yml file of: | ||||
| ```yaml | ||||
| a: | ||||
|   - 1 | ||||
|   - 2 | ||||
| b: | ||||
|   - 3 | ||||
|   - 4 | ||||
| ``` | ||||
| then | ||||
| ```bash | ||||
| yq eval '.a = .a + .b' sample.yml | ||||
| ``` | ||||
| will output | ||||
| ```yaml | ||||
| a: | ||||
|   - 1 | ||||
|   - 2 | ||||
|   - 3 | ||||
|   - 4 | ||||
| b: | ||||
|   - 3 | ||||
|   - 4 | ||||
| ``` | ||||
| 
 | ||||
							
								
								
									
										4
									
								
								pkg/yqlib/doc/headers/Add.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								pkg/yqlib/doc/headers/Add.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| Add behaves differently according to the type of the LHS: | ||||
| - arrays: concatenate | ||||
| - number scalars: arithmetic addition (soon) | ||||
| - string scalars: concatenate (soon) | ||||
| @ -36,7 +36,8 @@ var AssignStyle = &OperationType{Type: "ASSIGN_STYLE", NumArgs: 2, Precedence: 4 | ||||
| var AssignTag = &OperationType{Type: "ASSIGN_TAG", NumArgs: 2, Precedence: 40, Handler: AssignTagOperator} | ||||
| 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: 45, Handler: MultiplyOperator} | ||||
| var Add = &OperationType{Type: "ADD", NumArgs: 2, Precedence: 45, Handler: AddOperator} | ||||
| 
 | ||||
| var Equals = &OperationType{Type: "EQUALS", NumArgs: 2, Precedence: 40, Handler: EqualsOperator} | ||||
| var CreateMap = &OperationType{Type: "CREATE_MAP", NumArgs: 2, Precedence: 40, Handler: CreateMapOperator} | ||||
|  | ||||
							
								
								
									
										69
									
								
								pkg/yqlib/operator_add.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								pkg/yqlib/operator_add.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | ||||
| package yqlib | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	"container/list" | ||||
| 
 | ||||
| 	yaml "gopkg.in/yaml.v3" | ||||
| ) | ||||
| 
 | ||||
| func toNodes(candidates *list.List) []*yaml.Node { | ||||
| 
 | ||||
| 	if candidates.Len() == 0 { | ||||
| 		return []*yaml.Node{} | ||||
| 	} | ||||
| 	candidate := candidates.Front().Value.(*CandidateNode) | ||||
| 
 | ||||
| 	if candidate.Node.Tag == "!!null" { | ||||
| 		return []*yaml.Node{} | ||||
| 	} | ||||
| 
 | ||||
| 	switch candidate.Node.Kind { | ||||
| 	case yaml.SequenceNode: | ||||
| 		return candidate.Node.Content | ||||
| 	default: | ||||
| 		return []*yaml.Node{candidate.Node} | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func AddOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) { | ||||
| 	log.Debugf("Add operator") | ||||
| 	var results = list.New() | ||||
| 	lhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Lhs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	rhs, err := d.GetMatchingNodes(matchingNodes, pathNode.Rhs) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	for el := lhs.Front(); el != nil; el = el.Next() { | ||||
| 		lhsCandidate := el.Value.(*CandidateNode) | ||||
| 		lhsNode := UnwrapDoc(lhsCandidate.Node) | ||||
| 
 | ||||
| 		var newBlank = &CandidateNode{ | ||||
| 			Path:     lhsCandidate.Path, | ||||
| 			Document: lhsCandidate.Document, | ||||
| 			Filename: lhsCandidate.Filename, | ||||
| 			Node:     &yaml.Node{}, | ||||
| 		} | ||||
| 
 | ||||
| 		switch lhsNode.Kind { | ||||
| 		case yaml.MappingNode: | ||||
| 			return nil, fmt.Errorf("Maps not yet supported for addition") | ||||
| 		case yaml.SequenceNode: | ||||
| 			newBlank.Node.Kind = yaml.SequenceNode | ||||
| 			newBlank.Node.Style = lhsNode.Style | ||||
| 			newBlank.Node.Tag = "!!seq" | ||||
| 			newBlank.Node.Content = append(lhsNode.Content, toNodes(rhs)...) | ||||
| 			results.PushBack(newBlank) | ||||
| 		case yaml.ScalarNode: | ||||
| 			return nil, fmt.Errorf("Scalars not yet supported for addition") | ||||
| 		} | ||||
| 	} | ||||
| 	return results, nil | ||||
| } | ||||
							
								
								
									
										55
									
								
								pkg/yqlib/operator_add_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								pkg/yqlib/operator_add_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| package yqlib | ||||
| 
 | ||||
| import ( | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| var addOperatorScenarios = []expressionScenario{ | ||||
| 	{ | ||||
| 		description: "Concatenate arrays", | ||||
| 		document:    `{a: [1,2], b: [3,4]}`, | ||||
| 		expression:  `.a + .b`, | ||||
| 		expected: []string{ | ||||
| 			"D0, P[a], (!!seq)::[1, 2, 3, 4]\n", | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		description: "Concatenate null to array", | ||||
| 		document:    `{a: [1,2]}`, | ||||
| 		expression:  `.a + null`, | ||||
| 		expected: []string{ | ||||
| 			"D0, P[a], (!!seq)::[1, 2]\n", | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		description: "Add object to array", | ||||
| 		document:    `{a: [1,2], c: {cat: meow}}`, | ||||
| 		expression:  `.a + .c`, | ||||
| 		expected: []string{ | ||||
| 			"D0, P[a], (!!seq)::[1, 2, {cat: meow}]\n", | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		description: "Add string to array", | ||||
| 		document:    `{a: [1,2]}`, | ||||
| 		expression:  `.a + "hello"`, | ||||
| 		expected: []string{ | ||||
| 			"D0, P[a], (!!seq)::[1, 2, hello]\n", | ||||
| 		}, | ||||
| 	}, | ||||
| 	{ | ||||
| 		description: "Update array (append)", | ||||
| 		document:    `{a: [1,2], b: [3,4]}`, | ||||
| 		expression:  `.a = .a + .b`, | ||||
| 		expected: []string{ | ||||
| 			"D0, P[], (doc)::{a: [1, 2, 3, 4], b: [3, 4]}\n", | ||||
| 		}, | ||||
| 	}, | ||||
| } | ||||
| 
 | ||||
| func TestAddOperatorScenarios(t *testing.T) { | ||||
| 	for _, tt := range addOperatorScenarios { | ||||
| 		testScenario(t, &tt) | ||||
| 	} | ||||
| 	documentScenarios(t, "Add", addOperatorScenarios) | ||||
| } | ||||
| @ -252,6 +252,7 @@ func initLexer() (*lex.Lexer, error) { | ||||
| 	lexer.Add([]byte(`\{`), literalToken(OpenCollectObject, false)) | ||||
| 	lexer.Add([]byte(`\}`), literalToken(CloseCollectObject, true)) | ||||
| 	lexer.Add([]byte(`\*`), opToken(Multiply)) | ||||
| 	lexer.Add([]byte(`\+`), opToken(Add)) | ||||
| 
 | ||||
| 	err := lexer.Compile() | ||||
| 	if err != nil { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user