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