github.com/cilki/sh@v2.6.4+incompatible/syntax/walk.go (about) 1 // Copyright (c) 2016, Daniel Martà <mvdan@mvdan.cc> 2 // See LICENSE for licensing information 3 4 package syntax 5 6 import ( 7 "fmt" 8 "io" 9 "reflect" 10 ) 11 12 func walkStmts(sl StmtList, f func(Node) bool) { 13 for _, s := range sl.Stmts { 14 Walk(s, f) 15 } 16 for _, c := range sl.Last { 17 Walk(&c, f) 18 } 19 } 20 21 func walkWords(words []*Word, f func(Node) bool) { 22 for _, w := range words { 23 Walk(w, f) 24 } 25 } 26 27 // Walk traverses a syntax tree in depth-first order: It starts by calling 28 // f(node); node must not be nil. If f returns true, Walk invokes f 29 // recursively for each of the non-nil children of node, followed by 30 // f(nil). 31 func Walk(node Node, f func(Node) bool) { 32 if !f(node) { 33 return 34 } 35 36 switch x := node.(type) { 37 case *File: 38 walkStmts(x.StmtList, f) 39 case *Comment: 40 case *Stmt: 41 for _, c := range x.Comments { 42 if !x.End().After(c.Pos()) { 43 defer Walk(&c, f) 44 break 45 } 46 Walk(&c, f) 47 } 48 if x.Cmd != nil { 49 Walk(x.Cmd, f) 50 } 51 for _, r := range x.Redirs { 52 Walk(r, f) 53 } 54 case *Assign: 55 if x.Name != nil { 56 Walk(x.Name, f) 57 } 58 if x.Value != nil { 59 Walk(x.Value, f) 60 } 61 if x.Index != nil { 62 Walk(x.Index, f) 63 } 64 if x.Array != nil { 65 Walk(x.Array, f) 66 } 67 case *Redirect: 68 if x.N != nil { 69 Walk(x.N, f) 70 } 71 Walk(x.Word, f) 72 if x.Hdoc != nil { 73 Walk(x.Hdoc, f) 74 } 75 case *CallExpr: 76 for _, a := range x.Assigns { 77 Walk(a, f) 78 } 79 walkWords(x.Args, f) 80 case *Subshell: 81 walkStmts(x.StmtList, f) 82 case *Block: 83 walkStmts(x.StmtList, f) 84 case *IfClause: 85 walkStmts(x.Cond, f) 86 walkStmts(x.Then, f) 87 walkStmts(x.Else, f) 88 case *WhileClause: 89 walkStmts(x.Cond, f) 90 walkStmts(x.Do, f) 91 case *ForClause: 92 Walk(x.Loop, f) 93 walkStmts(x.Do, f) 94 case *WordIter: 95 Walk(x.Name, f) 96 walkWords(x.Items, f) 97 case *CStyleLoop: 98 if x.Init != nil { 99 Walk(x.Init, f) 100 } 101 if x.Cond != nil { 102 Walk(x.Cond, f) 103 } 104 if x.Post != nil { 105 Walk(x.Post, f) 106 } 107 case *BinaryCmd: 108 Walk(x.X, f) 109 Walk(x.Y, f) 110 case *FuncDecl: 111 Walk(x.Name, f) 112 Walk(x.Body, f) 113 case *Word: 114 for _, wp := range x.Parts { 115 Walk(wp, f) 116 } 117 case *Lit: 118 case *SglQuoted: 119 case *DblQuoted: 120 for _, wp := range x.Parts { 121 Walk(wp, f) 122 } 123 case *CmdSubst: 124 walkStmts(x.StmtList, f) 125 case *ParamExp: 126 Walk(x.Param, f) 127 if x.Index != nil { 128 Walk(x.Index, f) 129 } 130 if x.Repl != nil { 131 if x.Repl.Orig != nil { 132 Walk(x.Repl.Orig, f) 133 } 134 if x.Repl.With != nil { 135 Walk(x.Repl.With, f) 136 } 137 } 138 if x.Exp != nil && x.Exp.Word != nil { 139 Walk(x.Exp.Word, f) 140 } 141 case *ArithmExp: 142 Walk(x.X, f) 143 case *ArithmCmd: 144 Walk(x.X, f) 145 case *BinaryArithm: 146 Walk(x.X, f) 147 Walk(x.Y, f) 148 case *BinaryTest: 149 Walk(x.X, f) 150 Walk(x.Y, f) 151 case *UnaryArithm: 152 Walk(x.X, f) 153 case *UnaryTest: 154 Walk(x.X, f) 155 case *ParenArithm: 156 Walk(x.X, f) 157 case *ParenTest: 158 Walk(x.X, f) 159 case *CaseClause: 160 Walk(x.Word, f) 161 for _, ci := range x.Items { 162 Walk(ci, f) 163 } 164 for _, c := range x.Last { 165 Walk(&c, f) 166 } 167 case *CaseItem: 168 for _, c := range x.Comments { 169 if c.Pos().After(x.Pos()) { 170 defer Walk(&c, f) 171 break 172 } 173 Walk(&c, f) 174 } 175 walkWords(x.Patterns, f) 176 walkStmts(x.StmtList, f) 177 case *TestClause: 178 Walk(x.X, f) 179 case *DeclClause: 180 walkWords(x.Opts, f) 181 for _, a := range x.Assigns { 182 Walk(a, f) 183 } 184 case *ArrayExpr: 185 for _, el := range x.Elems { 186 Walk(el, f) 187 } 188 for _, c := range x.Last { 189 Walk(&c, f) 190 } 191 case *ArrayElem: 192 for _, c := range x.Comments { 193 if c.Pos().After(x.Pos()) { 194 defer Walk(&c, f) 195 break 196 } 197 Walk(&c, f) 198 } 199 if x.Index != nil { 200 Walk(x.Index, f) 201 } 202 if x.Value != nil { 203 Walk(x.Value, f) 204 } 205 case *ExtGlob: 206 Walk(x.Pattern, f) 207 case *ProcSubst: 208 walkStmts(x.StmtList, f) 209 case *TimeClause: 210 if x.Stmt != nil { 211 Walk(x.Stmt, f) 212 } 213 case *CoprocClause: 214 if x.Name != nil { 215 Walk(x.Name, f) 216 } 217 Walk(x.Stmt, f) 218 case *LetClause: 219 for _, expr := range x.Exprs { 220 Walk(expr, f) 221 } 222 default: 223 panic(fmt.Sprintf("syntax.Walk: unexpected node type %T", x)) 224 } 225 226 f(nil) 227 } 228 229 // DebugPrint prints the provided syntax tree, spanning multiple lines and with 230 // indentation. Can be useful to investigate the content of a syntax tree. 231 func DebugPrint(w io.Writer, node Node) error { 232 p := debugPrinter{out: w} 233 p.print(reflect.ValueOf(node)) 234 return p.err 235 } 236 237 type debugPrinter struct { 238 out io.Writer 239 level int 240 err error 241 } 242 243 func (p *debugPrinter) printf(format string, args ...interface{}) { 244 _, err := fmt.Fprintf(p.out, format, args...) 245 if err != nil && p.err == nil { 246 p.err = err 247 } 248 } 249 250 func (p *debugPrinter) newline() { 251 p.printf("\n") 252 for i := 0; i < p.level; i++ { 253 p.printf(". ") 254 } 255 } 256 257 func (p *debugPrinter) print(x reflect.Value) { 258 switch x.Kind() { 259 case reflect.Interface: 260 if x.IsNil() { 261 p.printf("nil") 262 return 263 } 264 p.print(x.Elem()) 265 case reflect.Ptr: 266 if x.IsNil() { 267 p.printf("nil") 268 return 269 } 270 p.printf("*") 271 p.print(x.Elem()) 272 case reflect.Slice: 273 p.printf("%s (len = %d) {", x.Type(), x.Len()) 274 if x.Len() > 0 { 275 p.level++ 276 p.newline() 277 for i := 0; i < x.Len(); i++ { 278 p.printf("%d: ", i) 279 p.print(x.Index(i)) 280 if i == x.Len()-1 { 281 p.level-- 282 } 283 p.newline() 284 } 285 } 286 p.printf("}") 287 288 case reflect.Struct: 289 if v, ok := x.Interface().(Pos); ok { 290 p.printf("%v:%v", v.Line(), v.Col()) 291 return 292 } 293 t := x.Type() 294 p.printf("%s {", t) 295 p.level++ 296 p.newline() 297 for i := 0; i < t.NumField(); i++ { 298 p.printf("%s: ", t.Field(i).Name) 299 p.print(x.Field(i)) 300 if i == x.NumField()-1 { 301 p.level-- 302 } 303 p.newline() 304 } 305 p.printf("}") 306 default: 307 p.printf("%#v", x.Interface()) 308 } 309 }