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