Imported Upstream version 4.7.3
[platform/upstream/gcc48.git] / libgo / go / 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. 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         m, err = p.output.Write(data[n:])
112         n += m
113         return
114 }
115
116 // localError wraps locally caught errors so we can distinguish
117 // them from genuine panics which we don't want to return as errors.
118 type localError struct {
119         err error
120 }
121
122 // printf is a convenience wrapper that takes care of print errors.
123 func (p *printer) printf(format string, args ...interface{}) {
124         if _, err := fmt.Fprintf(p, format, args...); err != nil {
125                 panic(localError{err})
126         }
127 }
128
129 // Implementation note: Print is written for AST nodes but could be
130 // used to print arbitrary data structures; such a version should
131 // probably be in a different package.
132 //
133 // Note: This code detects (some) cycles created via pointers but
134 // not cycles that are created via slices or maps containing the
135 // same slice or map. Code for general data structures probably
136 // should catch those as well.
137
138 func (p *printer) print(x reflect.Value) {
139         if !NotNilFilter("", x) {
140                 p.printf("nil")
141                 return
142         }
143
144         switch x.Kind() {
145         case reflect.Interface:
146                 p.print(x.Elem())
147
148         case reflect.Map:
149                 p.printf("%s (len = %d) {", x.Type(), x.Len())
150                 if x.Len() > 0 {
151                         p.indent++
152                         p.printf("\n")
153                         for _, key := range x.MapKeys() {
154                                 p.print(key)
155                                 p.printf(": ")
156                                 p.print(x.MapIndex(key))
157                                 p.printf("\n")
158                         }
159                         p.indent--
160                 }
161                 p.printf("}")
162
163         case reflect.Ptr:
164                 p.printf("*")
165                 // type-checked ASTs may contain cycles - use ptrmap
166                 // to keep track of objects that have been printed
167                 // already and print the respective line number instead
168                 ptr := x.Interface()
169                 if line, exists := p.ptrmap[ptr]; exists {
170                         p.printf("(obj @ %d)", line)
171                 } else {
172                         p.ptrmap[ptr] = p.line
173                         p.print(x.Elem())
174                 }
175
176         case reflect.Array:
177                 p.printf("%s {", x.Type())
178                 if x.Len() > 0 {
179                         p.indent++
180                         p.printf("\n")
181                         for i, n := 0, x.Len(); i < n; i++ {
182                                 p.printf("%d: ", i)
183                                 p.print(x.Index(i))
184                                 p.printf("\n")
185                         }
186                         p.indent--
187                 }
188                 p.printf("}")
189
190         case reflect.Slice:
191                 if s, ok := x.Interface().([]byte); ok {
192                         p.printf("%#q", s)
193                         return
194                 }
195                 p.printf("%s (len = %d) {", x.Type(), x.Len())
196                 if x.Len() > 0 {
197                         p.indent++
198                         p.printf("\n")
199                         for i, n := 0, x.Len(); i < n; i++ {
200                                 p.printf("%d: ", i)
201                                 p.print(x.Index(i))
202                                 p.printf("\n")
203                         }
204                         p.indent--
205                 }
206                 p.printf("}")
207
208         case reflect.Struct:
209                 t := x.Type()
210                 p.printf("%s {", t)
211                 p.indent++
212                 first := true
213                 for i, n := 0, t.NumField(); i < n; i++ {
214                         // exclude non-exported fields because their
215                         // values cannot be accessed via reflection
216                         if name := t.Field(i).Name; IsExported(name) {
217                                 value := x.Field(i)
218                                 if p.filter == nil || p.filter(name, value) {
219                                         if first {
220                                                 p.printf("\n")
221                                                 first = false
222                                         }
223                                         p.printf("%s: ", name)
224                                         p.print(value)
225                                         p.printf("\n")
226                                 }
227                         }
228                 }
229                 p.indent--
230                 p.printf("}")
231
232         default:
233                 v := x.Interface()
234                 switch v := v.(type) {
235                 case string:
236                         // print strings in quotes
237                         p.printf("%q", v)
238                         return
239                 case token.Pos:
240                         // position values can be printed nicely if we have a file set
241                         if p.fset != nil {
242                                 p.printf("%s", p.fset.Position(v))
243                                 return
244                         }
245                 }
246                 // default
247                 p.printf("%v", v)
248         }
249 }