...

Source file src/go/ast/print.go

Documentation: go/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  

View as plain text