1 // Copyright 2016 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 implements printing of syntax tree structures. 6 7 package syntax 8 9 import ( 10 "fmt" 11 "io" 12 "reflect" 13 "unicode" 14 "unicode/utf8" 15 ) 16 17 // Fdump dumps the structure of the syntax tree rooted at n to w. 18 // It is intended for debugging purposes; no specific output format 19 // is guaranteed. 20 func Fdump(w io.Writer, n Node) (err error) { 21 p := dumper{ 22 output: w, 23 ptrmap: make(map[Node]int), 24 last: '\n', // force printing of line number on first line 25 } 26 27 defer func() { 28 if e := recover(); e != nil { 29 err = e.(localError).err // re-panics if it's not a localError 30 } 31 }() 32 33 if n == nil { 34 p.printf("nil\n") 35 return 36 } 37 p.dump(reflect.ValueOf(n), n) 38 p.printf("\n") 39 40 return 41 } 42 43 type dumper struct { 44 output io.Writer 45 ptrmap map[Node]int // node -> dump line number 46 indent int // current indentation level 47 last byte // last byte processed by Write 48 line int // current line number 49 } 50 51 var indentBytes = []byte(". ") 52 53 func (p *dumper) Write(data []byte) (n int, err error) { 54 var m int 55 for i, b := range data { 56 // invariant: data[0:n] has been written 57 if b == '\n' { 58 m, err = p.output.Write(data[n : i+1]) 59 n += m 60 if err != nil { 61 return 62 } 63 } else if p.last == '\n' { 64 p.line++ 65 _, err = fmt.Fprintf(p.output, "%6d ", p.line) 66 if err != nil { 67 return 68 } 69 for j := p.indent; j > 0; j-- { 70 _, err = p.output.Write(indentBytes) 71 if err != nil { 72 return 73 } 74 } 75 } 76 p.last = b 77 } 78 if len(data) > n { 79 m, err = p.output.Write(data[n:]) 80 n += m 81 } 82 return 83 } 84 85 // localError wraps locally caught errors so we can distinguish 86 // them from genuine panics which we don't want to return as errors. 87 type localError struct { 88 err error 89 } 90 91 // printf is a convenience wrapper that takes care of print errors. 92 func (p *dumper) printf(format string, args ...interface{}) { 93 if _, err := fmt.Fprintf(p, format, args...); err != nil { 94 panic(localError{err}) 95 } 96 } 97 98 // dump prints the contents of x. 99 // If x is the reflect.Value of a struct s, where &s 100 // implements Node, then &s should be passed for n - 101 // this permits printing of the unexported span and 102 // comments fields of the embedded isNode field by 103 // calling the Span() and Comment() instead of using 104 // reflection. 105 func (p *dumper) dump(x reflect.Value, n Node) { 106 switch x.Kind() { 107 case reflect.Interface: 108 if x.IsNil() { 109 p.printf("nil") 110 return 111 } 112 p.dump(x.Elem(), nil) 113 114 case reflect.Ptr: 115 if x.IsNil() { 116 p.printf("nil") 117 return 118 } 119 120 // special cases for identifiers w/o attached comments (common case) 121 if x, ok := x.Interface().(*Name); ok { 122 p.printf("%s @ %v", x.Value, x.Pos()) 123 return 124 } 125 126 p.printf("*") 127 // Fields may share type expressions, and declarations 128 // may share the same group - use ptrmap to keep track 129 // of nodes that have been printed already. 130 if ptr, ok := x.Interface().(Node); ok { 131 if line, exists := p.ptrmap[ptr]; exists { 132 p.printf("(Node @ %d)", line) 133 return 134 } 135 p.ptrmap[ptr] = p.line 136 n = ptr 137 } 138 p.dump(x.Elem(), n) 139 140 case reflect.Slice: 141 if x.IsNil() { 142 p.printf("nil") 143 return 144 } 145 p.printf("%s (%d entries) {", x.Type(), x.Len()) 146 if x.Len() > 0 { 147 p.indent++ 148 p.printf("\n") 149 for i, n := 0, x.Len(); i < n; i++ { 150 p.printf("%d: ", i) 151 p.dump(x.Index(i), nil) 152 p.printf("\n") 153 } 154 p.indent-- 155 } 156 p.printf("}") 157 158 case reflect.Struct: 159 typ := x.Type() 160 161 // if span, ok := x.Interface().(lexical.Span); ok { 162 // p.printf("%s", &span) 163 // return 164 // } 165 166 p.printf("%s {", typ) 167 p.indent++ 168 169 first := true 170 if n != nil { 171 p.printf("\n") 172 first = false 173 // p.printf("Span: %s\n", n.Span()) 174 // if c := *n.Comments(); c != nil { 175 // p.printf("Comments: ") 176 // p.dump(reflect.ValueOf(c), nil) // a Comment is not a Node 177 // p.printf("\n") 178 // } 179 } 180 181 for i, n := 0, typ.NumField(); i < n; i++ { 182 // Exclude non-exported fields because their 183 // values cannot be accessed via reflection. 184 if name := typ.Field(i).Name; isExported(name) { 185 if first { 186 p.printf("\n") 187 first = false 188 } 189 p.printf("%s: ", name) 190 p.dump(x.Field(i), nil) 191 p.printf("\n") 192 } 193 } 194 195 p.indent-- 196 p.printf("}") 197 198 default: 199 switch x := x.Interface().(type) { 200 case string: 201 // print strings in quotes 202 p.printf("%q", x) 203 default: 204 p.printf("%v", x) 205 } 206 } 207 } 208 209 func isExported(name string) bool { 210 ch, _ := utf8.DecodeRuneInString(name) 211 return unicode.IsUpper(ch) 212 } 213