github.com/flyinox/gosm@v0.0.0-20171117061539-16768cb62077/src/go/ast/print.go (about) 1 // Copyright 2010 The Go 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 // This file contains printing support for ASTs. 6 7 package ast 8 9 import ( 10 "fmt" 11 "go/token" 12 "io" 13 "os" 14 "reflect" 15 ) 16 17 // A FieldFilter may be provided to Fprint to control the output. 18 type FieldFilter func(name string, value reflect.Value) bool 19 20 // NotNilFilter returns true for field values that are not nil; 21 // it returns false otherwise. 22 func NotNilFilter(_ string, v reflect.Value) bool { 23 switch v.Kind() { 24 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: 25 return !v.IsNil() 26 } 27 return true 28 } 29 30 // Fprint prints the (sub-)tree starting at AST node x to w. 31 // If fset != nil, position information is interpreted relative 32 // to that file set. Otherwise positions are printed as integer 33 // values (file set specific offsets). 34 // 35 // A non-nil FieldFilter f may be provided to control the output: 36 // struct fields for which f(fieldname, fieldvalue) is true are 37 // printed; all others are filtered from the output. Unexported 38 // struct fields are never printed. 39 func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) error { 40 return fprint(w, fset, x, f) 41 } 42 43 func fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (err error) { 44 // setup printer 45 p := printer{ 46 output: w, 47 fset: fset, 48 filter: f, 49 ptrmap: make(map[interface{}]int), 50 last: '\n', // force printing of line number on first line 51 } 52 53 // install error handler 54 defer func() { 55 if e := recover(); e != nil { 56 err = e.(localError).err // re-panics if it's not a localError 57 } 58 }() 59 60 // print x 61 if x == nil { 62 p.printf("nil\n") 63 return 64 } 65 p.print(reflect.ValueOf(x)) 66 p.printf("\n") 67 68 return 69 } 70 71 // Print prints x to standard output, skipping nil fields. 72 // Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter). 73 func Print(fset *token.FileSet, x interface{}) error { 74 return Fprint(os.Stdout, fset, x, NotNilFilter) 75 } 76 77 type printer struct { 78 output io.Writer 79 fset *token.FileSet 80 filter FieldFilter 81 ptrmap map[interface{}]int // *T -> line number 82 indent int // current indentation level 83 last byte // the last byte processed by Write 84 line int // current line number 85 } 86 87 var indent = []byte(". ") 88 89 func (p *printer) Write(data []byte) (n int, err error) { 90 var m int 91 for i, b := range data { 92 // invariant: data[0:n] has been written 93 if b == '\n' { 94 m, err = p.output.Write(data[n : i+1]) 95 n += m 96 if err != nil { 97 return 98 } 99 p.line++ 100 } else if p.last == '\n' { 101 _, err = fmt.Fprintf(p.output, "%6d ", p.line) 102 if err != nil { 103 return 104 } 105 for j := p.indent; j > 0; j-- { 106 _, err = p.output.Write(indent) 107 if err != nil { 108 return 109 } 110 } 111 } 112 p.last = b 113 } 114 if len(data) > n { 115 m, err = p.output.Write(data[n:]) 116 n += m 117 } 118 return 119 } 120 121 // localError wraps locally caught errors so we can distinguish 122 // them from genuine panics which we don't want to return as errors. 123 type localError struct { 124 err error 125 } 126 127 // printf is a convenience wrapper that takes care of print errors. 128 func (p *printer) printf(format string, args ...interface{}) { 129 if _, err := fmt.Fprintf(p, format, args...); err != nil { 130 panic(localError{err}) 131 } 132 } 133 134 // Implementation note: Print is written for AST nodes but could be 135 // used to print arbitrary data structures; such a version should 136 // probably be in a different package. 137 // 138 // Note: This code detects (some) cycles created via pointers but 139 // not cycles that are created via slices or maps containing the 140 // same slice or map. Code for general data structures probably 141 // should catch those as well. 142 143 func (p *printer) print(x reflect.Value) { 144 if !NotNilFilter("", x) { 145 p.printf("nil") 146 return 147 } 148 149 switch x.Kind() { 150 case reflect.Interface: 151 p.print(x.Elem()) 152 153 case reflect.Map: 154 p.printf("%s (len = %d) {", x.Type(), x.Len()) 155 if x.Len() > 0 { 156 p.indent++ 157 p.printf("\n") 158 for _, key := range x.MapKeys() { 159 p.print(key) 160 p.printf(": ") 161 p.print(x.MapIndex(key)) 162 p.printf("\n") 163 } 164 p.indent-- 165 } 166 p.printf("}") 167 168 case reflect.Ptr: 169 p.printf("*") 170 // type-checked ASTs may contain cycles - use ptrmap 171 // to keep track of objects that have been printed 172 // already and print the respective line number instead 173 ptr := x.Interface() 174 if line, exists := p.ptrmap[ptr]; exists { 175 p.printf("(obj @ %d)", line) 176 } else { 177 p.ptrmap[ptr] = p.line 178 p.print(x.Elem()) 179 } 180 181 case reflect.Array: 182 p.printf("%s {", x.Type()) 183 if x.Len() > 0 { 184 p.indent++ 185 p.printf("\n") 186 for i, n := 0, x.Len(); i < n; i++ { 187 p.printf("%d: ", i) 188 p.print(x.Index(i)) 189 p.printf("\n") 190 } 191 p.indent-- 192 } 193 p.printf("}") 194 195 case reflect.Slice: 196 if s, ok := x.Interface().([]byte); ok { 197 p.printf("%#q", s) 198 return 199 } 200 p.printf("%s (len = %d) {", x.Type(), x.Len()) 201 if x.Len() > 0 { 202 p.indent++ 203 p.printf("\n") 204 for i, n := 0, x.Len(); i < n; i++ { 205 p.printf("%d: ", i) 206 p.print(x.Index(i)) 207 p.printf("\n") 208 } 209 p.indent-- 210 } 211 p.printf("}") 212 213 case reflect.Struct: 214 t := x.Type() 215 p.printf("%s {", t) 216 p.indent++ 217 first := true 218 for i, n := 0, t.NumField(); i < n; i++ { 219 // exclude non-exported fields because their 220 // values cannot be accessed via reflection 221 if name := t.Field(i).Name; IsExported(name) { 222 value := x.Field(i) 223 if p.filter == nil || p.filter(name, value) { 224 if first { 225 p.printf("\n") 226 first = false 227 } 228 p.printf("%s: ", name) 229 p.print(value) 230 p.printf("\n") 231 } 232 } 233 } 234 p.indent-- 235 p.printf("}") 236 237 default: 238 v := x.Interface() 239 switch v := v.(type) { 240 case string: 241 // print strings in quotes 242 p.printf("%q", v) 243 return 244 case token.Pos: 245 // position values can be printed nicely if we have a file set 246 if p.fset != nil { 247 p.printf("%s", p.fset.Position(v)) 248 return 249 } 250 } 251 // default 252 p.printf("%v", v) 253 } 254 }