6 "github.com/google/go-cmp/cmp"
9 // node represents a node in the AST.
17 func newNode(c nodeContent) *node {
23 func (n *node) Equal(other *node) bool {
24 return cmp.Equal(n.content, other.content)
27 func (n *node) BuildTokens(to Tokens) Tokens {
28 return n.content.BuildTokens(to)
31 // Detach removes the receiver from the list it currently belongs to. If the
32 // node is not currently in a list, this is a no-op.
33 func (n *node) Detach() {
38 n.before.after = n.after
41 n.after.before = n.before
43 if n.list.first == n {
44 n.list.first = n.after
47 n.list.last = n.before
54 // ReplaceWith removes the receiver from the list it currently belongs to and
55 // inserts a new node with the given content in its place. If the node is not
56 // currently in a list, this function will panic.
58 // The return value is the newly-constructed node, containing the given content.
59 // After this function returns, the reciever is no longer attached to a list.
60 func (n *node) ReplaceWith(c nodeContent) *node {
62 panic("can't replace node that is not in a list")
68 n.before, n.after, n.list = nil, nil, nil
83 func (n *node) assertUnattached() {
85 panic(fmt.Sprintf("attempt to attach already-attached node %#v", n))
89 // nodeContent is the interface type implemented by all AST content types.
90 type nodeContent interface {
91 walkChildNodes(w internalWalkFunc)
92 BuildTokens(to Tokens) Tokens
95 // nodes is a list of nodes.
100 func (ns *nodes) BuildTokens(to Tokens) Tokens {
101 for n := ns.first; n != nil; n = n.after {
102 to = n.BuildTokens(to)
107 func (ns *nodes) Clear() {
112 func (ns *nodes) Append(c nodeContent) *node {
121 func (ns *nodes) AppendNode(n *node) {
133 func (ns *nodes) AppendUnstructuredTokens(tokens Tokens) *node {
134 if len(tokens) == 0 {
143 // nodeSet is an unordered set of nodes. It is used to describe a set of nodes
144 // that all belong to the same list that have some role or characteristic
146 type nodeSet map[*node]struct{}
148 func newNodeSet() nodeSet {
152 func (ns nodeSet) Has(n *node) bool {
160 func (ns nodeSet) Add(n *node) {
164 func (ns nodeSet) Remove(n *node) {
168 func (ns nodeSet) List() []*node {
173 ret := make([]*node, 0, len(ns))
175 // Determine which list we are working with. We assume here that all of
176 // the nodes belong to the same list, since that is part of the contract
184 // We recover the order by iterating over the whole list. This is not
185 // the most efficient way to do it, but our node lists should always be
186 // small so not worth making things more complex.
187 for n := list.first; n != nil; n = n.after {
195 type internalWalkFunc func(*node)
197 // inTree can be embedded into a content struct that has child nodes to get
198 // a standard implementation of the NodeContent interface and a record of
199 // a potential parent node.
205 func newInTree() inTree {
211 func (it *inTree) assertUnattached() {
212 if it.parent != nil {
213 panic(fmt.Sprintf("node is already attached to %T", it.parent.content))
217 func (it *inTree) walkChildNodes(w internalWalkFunc) {
218 for n := it.children.first; n != nil; n = n.after {
223 func (it *inTree) BuildTokens(to Tokens) Tokens {
224 for n := it.children.first; n != nil; n = n.after {
225 to = n.BuildTokens(to)
230 // leafNode can be embedded into a content struct to give it a do-nothing
231 // implementation of walkChildNodes
232 type leafNode struct {
235 func (n *leafNode) walkChildNodes(w internalWalkFunc) {