src/pkg/go/ast/print.go - The Go Programming Language

Golang

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