github.com/hashicorp/hcl/v2@v2.20.0/hclwrite/node.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package hclwrite
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  )
    11  
    12  // node represents a node in the AST.
    13  type node struct {
    14  	content nodeContent
    15  
    16  	list          *nodes
    17  	before, after *node
    18  }
    19  
    20  func newNode(c nodeContent) *node {
    21  	return &node{
    22  		content: c,
    23  	}
    24  }
    25  
    26  func (n *node) Equal(other *node) bool {
    27  	return cmp.Equal(n.content, other.content)
    28  }
    29  
    30  func (n *node) BuildTokens(to Tokens) Tokens {
    31  	return n.content.BuildTokens(to)
    32  }
    33  
    34  // Detach removes the receiver from the list it currently belongs to. If the
    35  // node is not currently in a list, this is a no-op.
    36  func (n *node) Detach() {
    37  	if n.list == nil {
    38  		return
    39  	}
    40  	if n.before != nil {
    41  		n.before.after = n.after
    42  	}
    43  	if n.after != nil {
    44  		n.after.before = n.before
    45  	}
    46  	if n.list.first == n {
    47  		n.list.first = n.after
    48  	}
    49  	if n.list.last == n {
    50  		n.list.last = n.before
    51  	}
    52  	n.list = nil
    53  	n.before = nil
    54  	n.after = nil
    55  }
    56  
    57  // ReplaceWith removes the receiver from the list it currently belongs to and
    58  // inserts a new node with the given content in its place. If the node is not
    59  // currently in a list, this function will panic.
    60  //
    61  // The return value is the newly-constructed node, containing the given content.
    62  // After this function returns, the reciever is no longer attached to a list.
    63  func (n *node) ReplaceWith(c nodeContent) *node {
    64  	if n.list == nil {
    65  		panic("can't replace node that is not in a list")
    66  	}
    67  
    68  	before := n.before
    69  	after := n.after
    70  	list := n.list
    71  	n.before, n.after, n.list = nil, nil, nil
    72  
    73  	nn := newNode(c)
    74  	nn.before = before
    75  	nn.after = after
    76  	nn.list = list
    77  	if before != nil {
    78  		before.after = nn
    79  	}
    80  	if after != nil {
    81  		after.before = nn
    82  	}
    83  	return nn
    84  }
    85  
    86  func (n *node) assertUnattached() {
    87  	if n.list != nil {
    88  		panic(fmt.Sprintf("attempt to attach already-attached node %#v", n))
    89  	}
    90  }
    91  
    92  // nodeContent is the interface type implemented by all AST content types.
    93  type nodeContent interface {
    94  	walkChildNodes(w internalWalkFunc)
    95  	BuildTokens(to Tokens) Tokens
    96  }
    97  
    98  // nodes is a list of nodes.
    99  type nodes struct {
   100  	first, last *node
   101  }
   102  
   103  func (ns *nodes) BuildTokens(to Tokens) Tokens {
   104  	for n := ns.first; n != nil; n = n.after {
   105  		to = n.BuildTokens(to)
   106  	}
   107  	return to
   108  }
   109  
   110  func (ns *nodes) Clear() {
   111  	ns.first = nil
   112  	ns.last = nil
   113  }
   114  
   115  func (ns *nodes) Append(c nodeContent) *node {
   116  	n := &node{
   117  		content: c,
   118  	}
   119  	ns.AppendNode(n)
   120  	n.list = ns
   121  	return n
   122  }
   123  
   124  func (ns *nodes) AppendNode(n *node) {
   125  	if ns.last != nil {
   126  		n.before = ns.last
   127  		ns.last.after = n
   128  	}
   129  	n.list = ns
   130  	ns.last = n
   131  	if ns.first == nil {
   132  		ns.first = n
   133  	}
   134  }
   135  
   136  // Insert inserts a nodeContent at a given position.
   137  // This is just a wrapper for InsertNode. See InsertNode for details.
   138  func (ns *nodes) Insert(pos *node, c nodeContent) *node {
   139  	n := &node{
   140  		content: c,
   141  	}
   142  	ns.InsertNode(pos, n)
   143  	n.list = ns
   144  	return n
   145  }
   146  
   147  // InsertNode inserts a node at a given position.
   148  // The first argument is a node reference before which to insert.
   149  // To insert it to an empty list, set position to nil.
   150  func (ns *nodes) InsertNode(pos *node, n *node) {
   151  	if pos == nil {
   152  		// inserts n to empty list.
   153  		ns.first = n
   154  		ns.last = n
   155  	} else {
   156  		// inserts n before pos.
   157  		pos.before.after = n
   158  		n.before = pos.before
   159  		pos.before = n
   160  		n.after = pos
   161  	}
   162  
   163  	n.list = ns
   164  }
   165  
   166  func (ns *nodes) AppendUnstructuredTokens(tokens Tokens) *node {
   167  	if len(tokens) == 0 {
   168  		return nil
   169  	}
   170  	n := newNode(tokens)
   171  	ns.AppendNode(n)
   172  	n.list = ns
   173  	return n
   174  }
   175  
   176  // FindNodeWithContent searches the nodes for a node whose content equals
   177  // the given content. If it finds one then it returns it. Otherwise it returns
   178  // nil.
   179  func (ns *nodes) FindNodeWithContent(content nodeContent) *node {
   180  	for n := ns.first; n != nil; n = n.after {
   181  		if n.content == content {
   182  			return n
   183  		}
   184  	}
   185  	return nil
   186  }
   187  
   188  // nodeSet is an unordered set of nodes. It is used to describe a set of nodes
   189  // that all belong to the same list that have some role or characteristic
   190  // in common.
   191  type nodeSet map[*node]struct{}
   192  
   193  func newNodeSet() nodeSet {
   194  	return make(nodeSet)
   195  }
   196  
   197  func (ns nodeSet) Has(n *node) bool {
   198  	if ns == nil {
   199  		return false
   200  	}
   201  	_, exists := ns[n]
   202  	return exists
   203  }
   204  
   205  func (ns nodeSet) Add(n *node) {
   206  	ns[n] = struct{}{}
   207  }
   208  
   209  func (ns nodeSet) Remove(n *node) {
   210  	delete(ns, n)
   211  }
   212  
   213  func (ns nodeSet) Clear() {
   214  	for n := range ns {
   215  		delete(ns, n)
   216  	}
   217  }
   218  
   219  func (ns nodeSet) List() []*node {
   220  	if len(ns) == 0 {
   221  		return nil
   222  	}
   223  
   224  	ret := make([]*node, 0, len(ns))
   225  
   226  	// Determine which list we are working with. We assume here that all of
   227  	// the nodes belong to the same list, since that is part of the contract
   228  	// for nodeSet.
   229  	var list *nodes
   230  	for n := range ns {
   231  		list = n.list
   232  		break
   233  	}
   234  
   235  	// We recover the order by iterating over the whole list. This is not
   236  	// the most efficient way to do it, but our node lists should always be
   237  	// small so not worth making things more complex.
   238  	for n := list.first; n != nil; n = n.after {
   239  		if ns.Has(n) {
   240  			ret = append(ret, n)
   241  		}
   242  	}
   243  	return ret
   244  }
   245  
   246  // FindNodeWithContent searches the nodes for a node whose content equals
   247  // the given content. If it finds one then it returns it. Otherwise it returns
   248  // nil.
   249  func (ns nodeSet) FindNodeWithContent(content nodeContent) *node {
   250  	for n := range ns {
   251  		if n.content == content {
   252  			return n
   253  		}
   254  	}
   255  	return nil
   256  }
   257  
   258  type internalWalkFunc func(*node)
   259  
   260  // inTree can be embedded into a content struct that has child nodes to get
   261  // a standard implementation of the NodeContent interface and a record of
   262  // a potential parent node.
   263  type inTree struct {
   264  	parent   *node
   265  	children *nodes
   266  }
   267  
   268  func newInTree() inTree {
   269  	return inTree{
   270  		children: &nodes{},
   271  	}
   272  }
   273  
   274  func (it *inTree) assertUnattached() {
   275  	if it.parent != nil {
   276  		panic(fmt.Sprintf("node is already attached to %T", it.parent.content))
   277  	}
   278  }
   279  
   280  func (it *inTree) walkChildNodes(w internalWalkFunc) {
   281  	for n := it.children.first; n != nil; n = n.after {
   282  		w(n)
   283  	}
   284  }
   285  
   286  func (it *inTree) BuildTokens(to Tokens) Tokens {
   287  	for n := it.children.first; n != nil; n = n.after {
   288  		to = n.BuildTokens(to)
   289  	}
   290  	return to
   291  }
   292  
   293  // leafNode can be embedded into a content struct to give it a do-nothing
   294  // implementation of walkChildNodes
   295  type leafNode struct {
   296  }
   297  
   298  func (n *leafNode) walkChildNodes(w internalWalkFunc) {
   299  }