github.com/shijuvar/go@v0.0.0-20141209052335-e8f13700b70c/src/go/ast/print.go (about)

     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  }