cuelang.org/go@v0.13.0/cue/ast/astutil/apply.go (about) 1 // Copyright 2018 The CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package astutil 16 17 import ( 18 "encoding/hex" 19 "fmt" 20 "hash/fnv" 21 "reflect" 22 23 "cuelang.org/go/cue/ast" 24 ) 25 26 // A Cursor describes a node encountered during Apply. 27 // Information about the node and its parent is available 28 // from the Node, Parent, and Index methods. 29 // 30 // The methods Replace, Delete, InsertBefore, and InsertAfter 31 // can be used to change the AST without disrupting Apply. 32 // Delete, InsertBefore, and InsertAfter are only defined for modifying 33 // a StructLit and will panic in any other context. 34 type Cursor interface { 35 // Node returns the current Node. 36 Node() ast.Node 37 38 // Parent returns the parent of the current Node. 39 Parent() Cursor 40 41 // Index reports the index >= 0 of the current Node in the slice of Nodes 42 // that contains it, or a value < 0 if the current Node is not part of a 43 // list. 44 Index() int 45 46 // Import reports an opaque identifier that refers to the given package. It 47 // may only be called if the input to apply was an ast.File. If the import 48 // does not exist, it will be added. 49 // 50 // Deprecated: use [ast.NewImport] as an [ast.Ident.Node], and then 51 // [Sanitize]. 52 Import(path string) *ast.Ident 53 54 // Replace replaces the current Node with n. 55 // The replacement node is not walked by Apply. Comments of the old node 56 // are copied to the new node if it has not yet an comments associated 57 // with it. 58 Replace(n ast.Node) 59 60 // Delete deletes the current Node from its containing struct. 61 // If the current Node is not part of a struct, Delete panics. 62 Delete() 63 64 // InsertAfter inserts n after the current Node in its containing struct. 65 // If the current Node is not part of a struct, InsertAfter panics. 66 // Unless n is wrapped by ApplyRecursively, Apply does not walk n. 67 InsertAfter(n ast.Node) 68 69 // InsertBefore inserts n before the current Node in its containing struct. 70 // If the current Node is not part of a struct, InsertBefore panics. 71 // Unless n is wrapped by ApplyRecursively, Apply does not walk n. 72 InsertBefore(n ast.Node) 73 74 self() *cursor 75 } 76 77 // ApplyRecursively indicates that a node inserted with InsertBefore, 78 // or InsertAfter should be processed recursively. 79 func ApplyRecursively(n ast.Node) ast.Node { 80 return recursive{n} 81 } 82 83 type recursive struct { 84 ast.Node 85 } 86 87 type info struct { 88 f *ast.File 89 current *declsCursor 90 91 importPatch []*ast.Ident 92 } 93 94 type cursor struct { 95 file *info 96 parent Cursor 97 node ast.Node 98 typ interface{} // the type of the node 99 index int // position of any of the sub types. 100 replaced bool 101 } 102 103 func newCursor(parent Cursor, n ast.Node, typ interface{}) *cursor { 104 return &cursor{ 105 parent: parent, 106 typ: typ, 107 node: n, 108 index: -1, 109 } 110 } 111 112 func fileInfo(c Cursor) (info *info) { 113 for ; c != nil; c = c.Parent() { 114 if i := c.self().file; i != nil { 115 return i 116 } 117 } 118 return nil 119 } 120 121 func (c *cursor) self() *cursor { return c } 122 func (c *cursor) Parent() Cursor { return c.parent } 123 func (c *cursor) Index() int { return c.index } 124 func (c *cursor) Node() ast.Node { return c.node } 125 126 // Deprecated: use [ast.NewImport] as an [ast.Ident.Node], and then 127 // [Sanitize]. 128 func (c *cursor) Import(importPath string) *ast.Ident { 129 info := fileInfo(c) 130 if info == nil { 131 return nil 132 } 133 134 name := ImportPathName(importPath) 135 136 // TODO: come up with something much better. 137 // For instance, hoist the uniquer form cue/export.go to 138 // here and make export.go use this. 139 hash := fnv.New32() 140 name += hex.EncodeToString(hash.Sum([]byte(importPath)))[:6] 141 142 spec := insertImport(&info.current.decls, &ast.ImportSpec{ 143 Name: ast.NewIdent(name), 144 Path: ast.NewString(importPath), 145 }) 146 147 ident := &ast.Ident{Node: spec} // Name is set later. 148 info.importPatch = append(info.importPatch, ident) 149 150 ident.Name = name 151 152 return ident 153 } 154 155 func (c *cursor) Replace(n ast.Node) { 156 // panic if the value cannot convert to the original type. 157 reflect.ValueOf(n).Convert(reflect.TypeOf(c.typ).Elem()) 158 if ast.Comments(n) != nil { 159 CopyComments(n, c.node) 160 } 161 if r, ok := n.(recursive); ok { 162 n = r.Node 163 } else { 164 c.replaced = true 165 } 166 c.node = n 167 } 168 169 func (c *cursor) InsertAfter(n ast.Node) { panic("unsupported") } 170 func (c *cursor) InsertBefore(n ast.Node) { panic("unsupported") } 171 func (c *cursor) Delete() { panic("unsupported") } 172 173 // Apply traverses a syntax tree recursively, starting with root, 174 // and calling pre and post for each node as described below. 175 // Apply returns the syntax tree, possibly modified. 176 // 177 // If pre is not nil, it is called for each node before the node's 178 // children are traversed (pre-order). If pre returns false, no 179 // children are traversed, and post is not called for that node. 180 // 181 // If post is not nil, and a prior call of pre didn't return false, 182 // post is called for each node after its children are traversed 183 // (post-order). If post returns false, traversal is terminated and 184 // Apply returns immediately. 185 // 186 // Only fields that refer to AST nodes are considered children; 187 // i.e., token.Pos, Scopes, Objects, and fields of basic types 188 // (strings, etc.) are ignored. 189 // 190 // Children are traversed in the order in which they appear in the 191 // respective node's struct definition. 192 func Apply(node ast.Node, before, after func(Cursor) bool) ast.Node { 193 apply(&applier{before: before, after: after}, nil, &node) 194 return node 195 } 196 197 // A applyVisitor's before method is invoked for each node encountered by Walk. 198 // If the result applyVisitor w is true, Walk visits each of the children 199 // of node with the applyVisitor w, followed by a call of w.After. 200 type applyVisitor interface { 201 Before(Cursor) applyVisitor 202 After(Cursor) bool 203 } 204 205 // Helper functions for common node lists. They may be empty. 206 207 type declsCursor struct { 208 *cursor 209 decls, after, process []ast.Decl 210 delete bool 211 } 212 213 func (c *declsCursor) InsertAfter(n ast.Node) { 214 if r, ok := n.(recursive); ok { 215 n = r.Node 216 c.process = append(c.process, n.(ast.Decl)) 217 } 218 c.after = append(c.after, n.(ast.Decl)) 219 } 220 221 func (c *declsCursor) InsertBefore(n ast.Node) { 222 if r, ok := n.(recursive); ok { 223 n = r.Node 224 c.process = append(c.process, n.(ast.Decl)) 225 } 226 c.decls = append(c.decls, n.(ast.Decl)) 227 } 228 229 func (c *declsCursor) Delete() { c.delete = true } 230 231 func applyDeclList(v applyVisitor, parent Cursor, list []ast.Decl) []ast.Decl { 232 c := &declsCursor{ 233 cursor: newCursor(parent, nil, nil), 234 decls: make([]ast.Decl, 0, len(list)), 235 } 236 if file, ok := parent.Node().(*ast.File); ok { 237 c.cursor.file = &info{f: file, current: c} 238 } 239 for i, x := range list { 240 c.node = x 241 c.typ = &list[i] 242 applyCursor(v, c) 243 if !c.delete { 244 c.decls = append(c.decls, c.node.(ast.Decl)) 245 } 246 c.delete = false 247 for i := 0; i < len(c.process); i++ { 248 x := c.process[i] 249 c.node = x 250 c.typ = &c.process[i] 251 applyCursor(v, c) 252 if c.delete { 253 panic("cannot delete a node that was added with InsertBefore or InsertAfter") 254 } 255 } 256 c.decls = append(c.decls, c.after...) 257 c.after = c.after[:0] 258 c.process = c.process[:0] 259 } 260 261 // TODO: ultimately, programmatically linked nodes have to be resolved 262 // at the end. 263 // if info := c.cursor.file; info != nil { 264 // done := map[*ast.ImportSpec]bool{} 265 // for _, ident := range info.importPatch { 266 // spec := ident.Node.(*ast.ImportSpec) 267 // if done[spec] { 268 // continue 269 // } 270 // done[spec] = true 271 272 // path, _ := strconv.Unquote(spec.Path) 273 274 // ident.Name = 275 // } 276 // } 277 278 return c.decls 279 } 280 281 func apply[N ast.Node](v applyVisitor, parent Cursor, nodePtr *N) { 282 node := *nodePtr 283 c := newCursor(parent, node, nodePtr) 284 applyCursor(v, c) 285 if ast.Node(node) != c.node { 286 *nodePtr = c.node.(N) 287 } 288 } 289 290 func applyList[N ast.Node](v applyVisitor, parent Cursor, list []N) { 291 c := newCursor(parent, nil, nil) 292 for i, node := range list { 293 c.index = i 294 c.node = node 295 c.typ = &list[i] 296 applyCursor(v, c) 297 if ast.Node(node) != c.node { 298 list[i] = c.node.(N) 299 } 300 } 301 } 302 303 // applyCursor traverses an AST in depth-first order: It starts by calling 304 // v.Visit(node); node must not be nil. If the visitor w returned by 305 // v.Visit(node) is not nil, apply is invoked recursively with visitor 306 // w for each of the non-nil children of node, followed by a call of 307 // w.Visit(nil). 308 func applyCursor(v applyVisitor, c Cursor) { 309 if v = v.Before(c); v == nil { 310 return 311 } 312 313 node := c.Node() 314 315 // TODO: record the comment groups and interleave with the values like for 316 // parsing and printing? 317 applyList(v, c, ast.Comments(node)) 318 319 // apply children 320 // (the order of the cases matches the order 321 // of the corresponding node types in go) 322 switch n := node.(type) { 323 // Comments and fields 324 case *ast.Comment: 325 // nothing to do 326 327 case *ast.CommentGroup: 328 applyList(v, c, n.List) 329 330 case *ast.Attribute: 331 // nothing to do 332 333 case *ast.Field: 334 apply(v, c, &n.Label) 335 if n.Value != nil { 336 apply(v, c, &n.Value) 337 } 338 applyList(v, c, n.Attrs) 339 340 case *ast.StructLit: 341 n.Elts = applyDeclList(v, c, n.Elts) 342 343 // Expressions 344 case *ast.BottomLit, *ast.BadExpr, *ast.Ident, *ast.BasicLit: 345 // nothing to do 346 347 case *ast.Interpolation: 348 applyList(v, c, n.Elts) 349 350 case *ast.ListLit: 351 applyList(v, c, n.Elts) 352 353 case *ast.Ellipsis: 354 if n.Type != nil { 355 apply(v, c, &n.Type) 356 } 357 358 case *ast.ParenExpr: 359 apply(v, c, &n.X) 360 361 case *ast.SelectorExpr: 362 apply(v, c, &n.X) 363 apply(v, c, &n.Sel) 364 365 case *ast.IndexExpr: 366 apply(v, c, &n.X) 367 apply(v, c, &n.Index) 368 369 case *ast.SliceExpr: 370 apply(v, c, &n.X) 371 if n.Low != nil { 372 apply(v, c, &n.Low) 373 } 374 if n.High != nil { 375 apply(v, c, &n.High) 376 } 377 378 case *ast.CallExpr: 379 apply(v, c, &n.Fun) 380 applyList(v, c, n.Args) 381 382 case *ast.UnaryExpr: 383 apply(v, c, &n.X) 384 385 case *ast.BinaryExpr: 386 apply(v, c, &n.X) 387 apply(v, c, &n.Y) 388 389 // Declarations 390 case *ast.ImportSpec: 391 if n.Name != nil { 392 apply(v, c, &n.Name) 393 } 394 apply(v, c, &n.Path) 395 396 case *ast.BadDecl: 397 // nothing to do 398 399 case *ast.ImportDecl: 400 applyList(v, c, n.Specs) 401 402 case *ast.EmbedDecl: 403 apply(v, c, &n.Expr) 404 405 case *ast.LetClause: 406 apply(v, c, &n.Ident) 407 apply(v, c, &n.Expr) 408 409 case *ast.Alias: 410 apply(v, c, &n.Ident) 411 apply(v, c, &n.Expr) 412 413 case *ast.Comprehension: 414 applyList(v, c, n.Clauses) 415 apply(v, c, &n.Value) 416 417 // Files and packages 418 case *ast.File: 419 n.Decls = applyDeclList(v, c, n.Decls) 420 421 case *ast.Package: 422 apply(v, c, &n.Name) 423 424 case *ast.ForClause: 425 if n.Key != nil { 426 apply(v, c, &n.Key) 427 } 428 apply(v, c, &n.Value) 429 apply(v, c, &n.Source) 430 431 case *ast.IfClause: 432 apply(v, c, &n.Condition) 433 434 default: 435 panic(fmt.Sprintf("Walk: unexpected node type %T", n)) 436 } 437 438 v.After(c) 439 } 440 441 type applier struct { 442 before func(Cursor) bool 443 after func(Cursor) bool 444 445 commentStack []commentFrame 446 current commentFrame 447 } 448 449 type commentFrame struct { 450 cg []*ast.CommentGroup 451 pos int8 452 } 453 454 func (f *applier) Before(c Cursor) applyVisitor { 455 node := c.Node() 456 if f.before == nil || (f.before(c) && node == c.Node()) { 457 f.commentStack = append(f.commentStack, f.current) 458 f.current = commentFrame{cg: ast.Comments(node)} 459 f.visitComments(c, f.current.pos) 460 return f 461 } 462 return nil 463 } 464 465 func (f *applier) After(c Cursor) bool { 466 f.visitComments(c, 127) 467 p := len(f.commentStack) - 1 468 f.current = f.commentStack[p] 469 f.commentStack = f.commentStack[:p] 470 f.current.pos++ 471 if f.after != nil { 472 f.after(c) 473 } 474 return true 475 } 476 477 func (f *applier) visitComments(p Cursor, pos int8) { 478 c := &f.current 479 for i, cg := range c.cg { 480 if cg.Position == pos { 481 continue 482 } 483 cursor := newCursor(p, cg, cg) 484 if f.before == nil || (f.before(cursor) && !cursor.replaced) { 485 for j, c := range cg.List { 486 cursor := newCursor(p, c, &c) 487 if f.before == nil || (f.before(cursor) && !cursor.replaced) { 488 if f.after != nil { 489 f.after(cursor) 490 } 491 } 492 cg.List[j] = cursor.node.(*ast.Comment) 493 } 494 if f.after != nil { 495 f.after(cursor) 496 } 497 } 498 c.cg[i] = cursor.node.(*ast.CommentGroup) 499 } 500 }