...

Source file src/pkg/go/ast/print.go

     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