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 }