Home | History | Annotate | Download | only in ast
      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 }
    252