diff --git a/pkg/yqlib/candidate_node.go b/pkg/yqlib/candidate_node.go index 493fb118..6f698c41 100644 --- a/pkg/yqlib/candidate_node.go +++ b/pkg/yqlib/candidate_node.go @@ -81,8 +81,7 @@ type CandidateNode struct { LeadingContent string TrailingContent string - Path []interface{} /// the path we took to get to this node - Document uint // the document index of this node + Document uint // the document index of this node Filename string Line int @@ -95,13 +94,13 @@ type CandidateNode struct { IsMapKey bool } -func (n *CandidateNode) GetKey() string { - keyPrefix := "" - if n.IsMapKey { - keyPrefix = "key-" - } - return fmt.Sprintf("%v%v - %v", keyPrefix, n.Document, n.Path) -} +// func (n *CandidateNode) GetKey() string { +// keyPrefix := "" +// if n.IsMapKey { +// keyPrefix = "key-" +// } +// return fmt.Sprintf("%v%v - %v", keyPrefix, n.Document, n.Path) +// } func (n *CandidateNode) unwrapDocument() *CandidateNode { if n.Kind == DocumentNode { @@ -114,15 +113,47 @@ func (n *CandidateNode) GetNiceTag() string { return n.unwrapDocument().Tag } -func (n *CandidateNode) GetNicePath() string { - if n.Path != nil && len(n.Path) >= 0 { - pathStr := make([]string, len(n.Path)) - for i, v := range n.Path { - pathStr[i] = fmt.Sprintf("%v", v) - } - return strings.Join(pathStr, ".") +func (n *CandidateNode) getParsedKey() interface{} { + if n.Key == nil { + return nil } - return "" + if n.Key.Tag == "!!str" { + return n.Key.Value + } + index, err := parseInt(n.Key.Value) + if err != nil { + return n.Key.Value + } + return index + +} + +func (n *CandidateNode) GetPath() []interface{} { + if n.Parent != nil { + return append(n.Parent.GetPath(), n.getParsedKey()) + } + return []interface{}{n.getParsedKey()} +} + +func (n *CandidateNode) GetNicePath() string { + var sb strings.Builder + path := n.GetPath() + for i, element := range path { + elementStr := fmt.Sprintf("%v", element) + switch element.(type) { + case int: + sb.WriteString("[" + elementStr + "]") + default: + if i == 0 { + sb.WriteString(elementStr) + } else if strings.ContainsRune(elementStr, '.') { + sb.WriteString("[" + elementStr + "]") + } else { + sb.WriteString("." + elementStr) + } + } + } + return sb.String() } func (n *CandidateNode) AsList() *list.List { @@ -178,7 +209,6 @@ func (n *CandidateNode) guessTagFromCustomType() string { func (n *CandidateNode) CreateReplacement(kind Kind, tag string, value string) *CandidateNode { return &CandidateNode{ - Path: n.createChildPath(nil), Parent: n.Parent, Key: n.Key, IsMapKey: n.IsMapKey, @@ -199,20 +229,6 @@ func (n *CandidateNode) CreateReplacementWithDocWrappers(kind Kind, tag string, return replacement } -func (n *CandidateNode) createChildPath(path interface{}) []interface{} { - if path == nil { - newPath := make([]interface{}, len(n.Path)) - copy(newPath, n.Path) - return newPath - } - - //don't use append as they may actually modify the path of the orignal node! - newPath := make([]interface{}, len(n.Path)+1) - copy(newPath, n.Path) - newPath[len(n.Path)] = path - return newPath -} - func (n *CandidateNode) CopyChildren() []*CandidateNode { clonedKids := make([]*CandidateNode, len(n.Content)) for i, child := range n.Content { @@ -258,7 +274,6 @@ func (n *CandidateNode) doCopy(cloneContent bool) *CandidateNode { LeadingContent: n.LeadingContent, TrailingContent: n.TrailingContent, - Path: n.Path, Document: n.Document, Filename: n.Filename, @@ -290,7 +305,7 @@ func (n *CandidateNode) UpdateFrom(other *CandidateNode, prefs assignPreferences } func (n *CandidateNode) UpdateAttributesFrom(other *CandidateNode, prefs assignPreferences) { - log.Debug("UpdateAttributesFrom: n: %v other: %v", n.GetKey(), other.GetKey()) + log.Debug("UpdateAttributesFrom: n: %v other: %v", n.Key.Value, other.Key.Value) if n.Kind != other.Kind { // clear out the contents when switching to a different type // e.g. map to array diff --git a/pkg/yqlib/data_tree_navigator.go b/pkg/yqlib/data_tree_navigator.go index 4b834de3..c18ba2fb 100644 --- a/pkg/yqlib/data_tree_navigator.go +++ b/pkg/yqlib/data_tree_navigator.go @@ -12,7 +12,7 @@ type DataTreeNavigator interface { // a new context of matching candidates GetMatchingNodes(context Context, expressionNode *ExpressionNode) (Context, error) - DeeplyAssign(context Context, rhsNode *CandidateNode) error + DeeplyAssign(context Context, path []interface{}, rhsNode *CandidateNode) error } type dataTreeNavigator struct { @@ -22,7 +22,7 @@ func NewDataTreeNavigator() DataTreeNavigator { return &dataTreeNavigator{} } -func (d *dataTreeNavigator) DeeplyAssign(context Context, rhsCandidateNode *CandidateNode) error { +func (d *dataTreeNavigator) DeeplyAssign(context Context, path []interface{}, rhsCandidateNode *CandidateNode) error { assignmentOp := &Operation{OperationType: assignOpType, Preferences: assignPreferences{}} @@ -30,7 +30,7 @@ func (d *dataTreeNavigator) DeeplyAssign(context Context, rhsCandidateNode *Cand assignmentOpNode := &ExpressionNode{ Operation: assignmentOp, - LHS: createTraversalTree(rhsCandidateNode.Path, traversePreferences{}, false), + LHS: createTraversalTree(path, traversePreferences{}, false), RHS: &ExpressionNode{Operation: rhsOp}, } diff --git a/pkg/yqlib/decoder_properties.go b/pkg/yqlib/decoder_properties.go index 0c34fb52..6c428be8 100644 --- a/pkg/yqlib/decoder_properties.go +++ b/pkg/yqlib/decoder_properties.go @@ -51,7 +51,6 @@ func (dec *propertiesDecoder) applyPropertyComments(context Context, path []inte assignmentOp := &Operation{OperationType: assignOpType, Preferences: assignPreferences{}} rhsCandidateNode := &CandidateNode{ - Path: path, Tag: "!!str", Value: fmt.Sprintf("%v", path[len(path)-1]), HeadComment: dec.processComment(strings.Join(comments, "\n")), @@ -86,9 +85,8 @@ func (dec *propertiesDecoder) applyProperty(context Context, properties *propert rhsNode := createStringScalarNode(value) rhsNode.Tag = rhsNode.guessTagFromCustomType() - rhsNode.Path = path - return dec.d.DeeplyAssign(context, rhsNode) + return dec.d.DeeplyAssign(context, path, rhsNode) } func (dec *propertiesDecoder) Decode() (*CandidateNode, error) { diff --git a/pkg/yqlib/decoder_toml.go b/pkg/yqlib/decoder_toml.go index f0f27f08..92614a1c 100644 --- a/pkg/yqlib/decoder_toml.go +++ b/pkg/yqlib/decoder_toml.go @@ -62,12 +62,11 @@ func (dec *tomlDecoder) processKeyValueIntoMap(rootMap *CandidateNode, tomlNode if err != nil { return err } - valueNode.Path = path context := Context{} context = context.SingleChildContext(rootMap) - return dec.d.DeeplyAssign(context, valueNode) + return dec.d.DeeplyAssign(context, path, valueNode) } func (dec *tomlDecoder) decodeKeyValuesIntoMap(rootMap *CandidateNode, tomlNode *toml.Node) (bool, error) { @@ -279,7 +278,6 @@ func (dec *tomlDecoder) processTable(currentNode *toml.Node) (bool, error) { tableNodeValue := &CandidateNode{ Kind: MappingNode, Tag: "!!map", - Path: fullPath, } tableValue := dec.parser.Expression() @@ -296,7 +294,7 @@ func (dec *tomlDecoder) processTable(currentNode *toml.Node) (bool, error) { c := Context{} c = c.SingleChildContext(dec.rootMap) - err = dec.d.DeeplyAssign(c, tableNodeValue) + err = dec.d.DeeplyAssign(c, fullPath, tableNodeValue) if err != nil { return false, err } @@ -305,7 +303,6 @@ func (dec *tomlDecoder) processTable(currentNode *toml.Node) (bool, error) { func (dec *tomlDecoder) arrayAppend(context Context, path []interface{}, rhsNode *CandidateNode) error { rhsCandidateNode := &CandidateNode{ - Path: path, Kind: SequenceNode, Tag: "!!seq", Content: []*CandidateNode{rhsNode}, @@ -341,7 +338,6 @@ func (dec *tomlDecoder) processArrayTable(currentNode *toml.Node) (bool, error) tableNodeValue := &CandidateNode{ Kind: MappingNode, Tag: "!!map", - Path: fullPath, } tableValue := dec.parser.Expression() diff --git a/pkg/yqlib/operator_create_map.go b/pkg/yqlib/operator_create_map.go index 775451b1..4390ce60 100644 --- a/pkg/yqlib/operator_create_map.go +++ b/pkg/yqlib/operator_create_map.go @@ -10,8 +10,6 @@ func createMapOperator(d *dataTreeNavigator, context Context, expressionNode *Ex //each matchingNodes entry should turn into a sequence of keys to create. //then collect object should do a cross function of the same index sequence for all matches. - var path []interface{} - var document uint sequences := list.New() @@ -36,19 +34,16 @@ func createMapOperator(d *dataTreeNavigator, context Context, expressionNode *Ex node := listToNodeSeq(sequences) node.Document = document - node.Path = path return context.SingleChildContext(node), nil } func sequenceFor(d *dataTreeNavigator, context Context, matchingNode *CandidateNode, expressionNode *ExpressionNode) (*CandidateNode, error) { - var path []interface{} var document uint var matches = list.New() if matchingNode != nil { - path = matchingNode.Path document = matchingNode.Document matches.PushBack(matchingNode) } @@ -63,7 +58,6 @@ func sequenceFor(d *dataTreeNavigator, context Context, matchingNode *CandidateN rhs.unwrapDocument(), } node.Document = document - node.Path = path return &node, nil }, false) @@ -74,7 +68,6 @@ func sequenceFor(d *dataTreeNavigator, context Context, matchingNode *CandidateN innerList := listToNodeSeq(mapPairs.MatchingNodes) innerList.Style = FlowStyle innerList.Document = document - innerList.Path = path return innerList, nil } diff --git a/pkg/yqlib/operator_datetime.go b/pkg/yqlib/operator_datetime.go index da56f186..46d359fb 100644 --- a/pkg/yqlib/operator_datetime.go +++ b/pkg/yqlib/operator_datetime.go @@ -88,8 +88,9 @@ func formatDateTime(d *dataTreeNavigator, context Context, expressionNode *Expre } } node.Document = candidate.Document + node.Parent = candidate.Parent + node.Key = candidate.Key node.FileIndex = candidate.FileIndex - node.Path = candidate.Path results.PushBack(node) } diff --git a/pkg/yqlib/operator_delete.go b/pkg/yqlib/operator_delete.go index ea89aa29..1d0f4c56 100644 --- a/pkg/yqlib/operator_delete.go +++ b/pkg/yqlib/operator_delete.go @@ -35,7 +35,8 @@ func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode * } parentNode := candidate.Parent - childPath := candidate.Path[len(candidate.Path)-1] + parentPath := parentNode.GetPath() + childPath := parentPath[len(parentPath)-1] if parentNode.Kind == MappingNode { deleteFromMap(candidate.Parent, childPath) @@ -60,7 +61,7 @@ func deleteFromMap(candidate *CandidateNode, childPath interface{}) { shouldDelete := key.Value == childPath - log.Debugf("shouldDelete %v ? %v", value.GetKey(), shouldDelete) + log.Debugf("shouldDelete %v ? %v", value.ToDebugString(), shouldDelete) if !shouldDelete { newContents = append(newContents, key, value) diff --git a/pkg/yqlib/operator_path.go b/pkg/yqlib/operator_path.go index c956f353..07a97e30 100644 --- a/pkg/yqlib/operator_path.go +++ b/pkg/yqlib/operator_path.go @@ -156,9 +156,11 @@ func getPathOperator(d *dataTreeNavigator, context Context, expressionNode *Expr candidate := el.Value.(*CandidateNode) node := candidate.CreateReplacement(SequenceNode, "!!seq", "") - content := make([]*CandidateNode, len(candidate.Path)) - for pathIndex := 0; pathIndex < len(candidate.Path); pathIndex++ { - path := candidate.Path[pathIndex] + path := candidate.GetPath() + + content := make([]*CandidateNode, len(path)) + for pathIndex := 0; pathIndex < len(path); pathIndex++ { + path := path[pathIndex] content[pathIndex] = createPathNodeFor(path) } node.Content = content