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