github.com/neugram/ng@v0.0.0-20180309130942-d472ff93d872/syntax/walk.go (about) 1 // Copyright 2017 The Neugram Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package syntax 6 7 import ( 8 "fmt" 9 "reflect" 10 11 "neugram.io/ng/syntax/expr" 12 "neugram.io/ng/syntax/stmt" 13 ) 14 15 // Walk traverses a syntax tree, calling preFn and postFn for each node. 16 // 17 // If a preFn is provided it is called for each node before its children 18 // are traversed. If preFn returns false no children are traversed. 19 // 20 // If a postFn is provided it is called for each node after its children 21 // are traversed. TODO: If a postFn returns false traversal ends. 22 func Walk(root Node, preFn, postFn WalkFunc) (result Node) { 23 type rootNode struct { 24 Node 25 } 26 parent := &rootNode{Node: root} 27 28 w := walker{preFn: preFn, postFn: postFn} 29 w.walk(parent, root, "Node", nil) 30 31 return root 32 } 33 34 // A WalkFunc is invoked by Walk when traversing nodes in a syntax tree. 35 // 36 // The return value determines how traversal will proceed, it is 37 // described in detail in the Walk function documentation. 38 type WalkFunc func(*Cursor) bool 39 40 // A Cursor describes a Node during a syntax tree traversal. 41 type Cursor struct { 42 Node Node // the current Node 43 Parent Node // the parent of the current Node 44 Name string // name of the parent field containing the current Node 45 46 iter *iterator 47 } 48 49 // TODO: Replace(Node), InsertAfter(Node), InsertBefore(Node), Delete() 50 51 type iterator struct { 52 index int 53 } 54 55 type walker struct { 56 preFn WalkFunc 57 postFn WalkFunc 58 c Cursor // reusable Cursor 59 iter iterator // reusable iterator 60 } 61 62 func (w *walker) walk(parent, node Node, fieldName string, iter *iterator) { 63 // typed nil -> untyped nil 64 if v := reflect.ValueOf(node); v.Kind() == reflect.Ptr && v.IsNil() { 65 node = nil 66 } 67 68 oldCursor := w.c 69 w.c = Cursor{ 70 Node: node, 71 Parent: parent, 72 Name: fieldName, 73 iter: iter, 74 } 75 defer func() { w.c = oldCursor }() 76 77 if w.preFn != nil && !w.preFn(&w.c) { 78 return 79 } 80 81 switch node := node.(type) { 82 case nil: 83 // done 84 85 case *File: 86 w.walkSlice(node, "Stmts") 87 88 case *stmt.Import: 89 // done 90 91 case *stmt.ImportSet: 92 w.walkSlice(node, "Imports") 93 94 case *stmt.TypeDecl: 95 // done 96 97 case *stmt.TypeDeclSet: 98 w.walkSlice(node, "TypeDecls") 99 100 case *stmt.MethodikDecl: 101 w.walkSlice(node, "Methods") 102 103 case *stmt.Const: 104 w.walkSlice(node, "Values") 105 106 case *stmt.ConstSet: 107 w.walkSlice(node, "Consts") 108 109 case *stmt.Var: 110 w.walkSlice(node, "Values") 111 112 case *stmt.VarSet: 113 w.walkSlice(node, "Vars") 114 115 case *stmt.Assign: 116 w.walkSlice(node, "Left") 117 w.walkSlice(node, "Right") 118 119 case *stmt.Block: 120 w.walkSlice(node, "Stmts") 121 122 case *stmt.If: 123 w.walk(node, node.Init, "Init", nil) 124 w.walk(node, node.Cond, "Cond", nil) 125 w.walk(node, node.Body, "Body", nil) 126 w.walk(node, node.Else, "Else", nil) 127 128 case *stmt.For: 129 w.walk(node, node.Init, "Init", nil) 130 w.walk(node, node.Cond, "Cond", nil) 131 w.walk(node, node.Post, "Post", nil) 132 w.walk(node, node.Body, "Body", nil) 133 134 case *stmt.Switch: 135 w.walk(node, node.Init, "Init", nil) 136 w.walk(node, node.Cond, "Cond", nil) 137 w.walkSlice(node, "Cases") 138 139 case stmt.SwitchCase: 140 w.walkSlice(node, "Conds") 141 w.walk(node, node.Body, "Body", nil) 142 143 case *stmt.TypeSwitch: 144 w.walk(node, node.Init, "Init", nil) 145 w.walk(node, node.Assign, "Assign", nil) 146 w.walkSlice(node, "Cases") 147 148 case stmt.TypeSwitchCase: 149 w.walk(node, node.Body, "Body", nil) 150 151 case *stmt.Go: 152 w.walk(node, node.Call, "Call", nil) 153 154 case *stmt.Range: 155 w.walk(node, node.Key, "Key", nil) 156 w.walk(node, node.Val, "Val", nil) 157 w.walk(node, node.Expr, "Expr", nil) 158 w.walk(node, node.Body, "Body", nil) 159 160 case *stmt.Return: 161 w.walkSlice(node, "Exprs") 162 163 case *stmt.Defer: 164 w.walk(node, node.Expr, "Expr", nil) 165 166 case *stmt.Simple: 167 w.walk(node, node.Expr, "Expr", nil) 168 169 case *stmt.Send: 170 w.walk(node, node.Chan, "Chan", nil) 171 w.walk(node, node.Value, "Value", nil) 172 173 case *stmt.Branch: 174 175 case *stmt.Labeled: 176 w.walk(node, node.Stmt, "Stmt", nil) 177 178 case *stmt.Select: 179 w.walkSlice(node, "Cases") 180 181 case stmt.SelectCase: 182 w.walk(node, node.Stmt, "Stmt", nil) 183 w.walk(node, node.Body, "Body", nil) 184 185 case *stmt.Bad: 186 187 case *expr.Binary: 188 w.walk(node, node.Left, "Left", nil) 189 w.walk(node, node.Right, "Right", nil) 190 191 case *expr.Unary: 192 w.walk(node, node.Expr, "Expr", nil) 193 194 case *expr.Bad: 195 196 case *expr.Selector: 197 w.walk(node, node.Left, "Left", nil) 198 w.walk(node, node.Right, "Right", nil) 199 200 case *expr.Slice: 201 w.walk(node, node.Low, "Low", nil) 202 w.walk(node, node.High, "High", nil) 203 w.walk(node, node.Max, "Max", nil) 204 205 case *expr.Index: 206 w.walk(node, node.Left, "Left", nil) 207 w.walkSlice(node, "Indicies") 208 209 case *expr.TypeAssert: 210 w.walk(node, node.Left, "Left", nil) 211 212 case *expr.BasicLiteral: 213 214 case *expr.FuncLiteral: 215 if body, isStmt := node.Body.(*stmt.Block); isStmt { 216 w.walk(node, body, "Body", nil) 217 } 218 219 case *expr.CompLiteral: 220 w.walkSlice(node, "Keys") 221 w.walkSlice(node, "Values") 222 223 case *expr.MapLiteral: 224 w.walkSlice(node, "Keys") 225 w.walkSlice(node, "Values") 226 227 case *expr.ArrayLiteral: 228 w.walkSlice(node, "Keys") 229 w.walkSlice(node, "Values") 230 231 case *expr.SliceLiteral: 232 w.walkSlice(node, "Keys") 233 w.walkSlice(node, "Values") 234 235 case *expr.TableLiteral: 236 w.walkSlice(node, "ColNames") 237 // TODO: handle rows 238 239 case *expr.Type: 240 241 case *expr.Ident: 242 243 case *expr.Call: 244 w.walk(node, node.Func, "Func", nil) 245 w.walkSlice(node, "Args") 246 247 case *expr.Range: 248 w.walk(node, node.Start, "Start", nil) 249 w.walk(node, node.End, "End", nil) 250 w.walk(node, node.Exact, "Exact", nil) 251 252 case *expr.ShellList: 253 w.walkSlice(node, "AndOr") 254 255 case *expr.ShellAndOr: 256 w.walkSlice(node, "Pipeline") 257 258 case *expr.ShellPipeline: 259 w.walkSlice(node, "Cmd") 260 261 case *expr.ShellCmd: 262 w.walk(node, node.SimpleCmd, "SimpleCmd", nil) 263 w.walk(node, node.Subshell, "Subshell", nil) 264 265 case *expr.ShellSimpleCmd: 266 w.walkSlice(node, "Redirect") 267 w.walkSlice(node, "Assign") 268 269 case *expr.ShellRedirect: 270 271 case expr.ShellAssign: 272 273 case *expr.Shell: 274 w.walkSlice(node, "Cmds") 275 276 default: 277 panic(fmt.Sprintf("syntax.Walk: unknown node (type %T)", node)) 278 } 279 280 if w.postFn != nil && !w.postFn(&w.c) { 281 // TODO: implement abort 282 } 283 } 284 285 func (w *walker) walkSlice(parent Node, fieldName string) { 286 oldIter := w.iter 287 defer func() { w.iter = oldIter }() 288 289 w.iter.index = 0 290 291 for { 292 v := reflect.Indirect(reflect.ValueOf(parent)).FieldByName(fieldName) 293 if w.iter.index >= v.Len() { 294 break 295 } 296 297 var node Node 298 if e := v.Index(w.iter.index); e.IsValid() { 299 node = e.Interface().(Node) 300 } 301 302 w.walk(parent, node, fieldName, &w.iter) 303 w.iter.index++ 304 } 305 }