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