The Go Programming Language

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 suppport 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) (n int, err os.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			n = p.written
    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{}) (int, os.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		written int                 // number of bytes written to output
    80		indent  int                 // current indentation level
    81		last    byte                // the last byte processed by Write
    82		line    int                 // current line number
    83	}
    84	
    85	var indent = []byte(".  ")
    86	
    87	func (p *printer) Write(data []byte) (n int, err os.Error) {
    88		var m int
    89		for i, b := range data {
    90			// invariant: data[0:n] has been written
    91			if b == '\n' {
    92				m, err = p.output.Write(data[n : i+1])
    93				n += m
    94				if err != nil {
    95					return
    96				}
    97				p.line++
    98			} else if p.last == '\n' {
    99				_, err = fmt.Fprintf(p.output, "%6d  ", p.line)
   100				if err != nil {
   101					return
   102				}
   103				for j := p.indent; j > 0; j-- {
   104					_, err = p.output.Write(indent)
   105					if err != nil {
   106						return
   107					}
   108				}
   109			}
   110			p.last = b
   111		}
   112		m, err = p.output.Write(data[n:])
   113		n += m
   114		return
   115	}
   116	
   117	// localError wraps locally caught os.Errors so we can distinguish
   118	// them from genuine panics which we don't want to return as errors.
   119	type localError struct {
   120		err os.Error
   121	}
   122	
   123	// printf is a convenience wrapper that takes care of print errors.
   124	func (p *printer) printf(format string, args ...interface{}) {
   125		n, err := fmt.Fprintf(p, format, args...)
   126		p.written += n
   127		if err != nil {
   128			panic(localError{err})
   129		}
   130	}
   131	
   132	// Implementation note: Print is written for AST nodes but could be
   133	// used to print arbitrary data structures; such a version should
   134	// probably be in a different package.
   135	//
   136	// Note: This code detects (some) cycles created via pointers but
   137	// not cycles that are created via slices or maps containing the
   138	// same slice or map. Code for general data structures probably
   139	// should catch those as well.
   140	
   141	func (p *printer) print(x reflect.Value) {
   142		if !NotNilFilter("", x) {
   143			p.printf("nil")
   144			return
   145		}
   146	
   147		switch x.Kind() {
   148		case reflect.Interface:
   149			p.print(x.Elem())
   150	
   151		case reflect.Map:
   152			p.printf("%s (len = %d) {\n", x.Type().String(), x.Len())
   153			p.indent++
   154			for _, key := range x.MapKeys() {
   155				p.print(key)
   156				p.printf(": ")
   157				p.print(x.MapIndex(key))
   158				p.printf("\n")
   159			}
   160			p.indent--
   161			p.printf("}")
   162	
   163		case reflect.Ptr:
   164			p.printf("*")
   165			// type-checked ASTs may contain cycles - use ptrmap
   166			// to keep track of objects that have been printed
   167			// already and print the respective line number instead
   168			ptr := x.Interface()
   169			if line, exists := p.ptrmap[ptr]; exists {
   170				p.printf("(obj @ %d)", line)
   171			} else {
   172				p.ptrmap[ptr] = p.line
   173				p.print(x.Elem())
   174			}
   175	
   176		case reflect.Slice:
   177			if s, ok := x.Interface().([]byte); ok {
   178				p.printf("%#q", s)
   179				return
   180			}
   181			p.printf("%s (len = %d) {\n", x.Type().String(), x.Len())
   182			p.indent++
   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			p.printf("}")
   190	
   191		case reflect.Struct:
   192			p.printf("%s {\n", x.Type().String())
   193			p.indent++
   194			t := x.Type()
   195			for i, n := 0, t.NumField(); i < n; i++ {
   196				name := t.Field(i).Name
   197				value := x.Field(i)
   198				if p.filter == nil || p.filter(name, value) {
   199					p.printf("%s: ", name)
   200					p.print(value)
   201					p.printf("\n")
   202				}
   203			}
   204			p.indent--
   205			p.printf("}")
   206	
   207		default:
   208			v := x.Interface()
   209			switch v := v.(type) {
   210			case string:
   211				// print strings in quotes
   212				p.printf("%q", v)
   213				return
   214			case token.Pos:
   215				// position values can be printed nicely if we have a file set
   216				if p.fset != nil {
   217					p.printf("%s", p.fset.Position(v))
   218					return
   219				}
   220			}
   221			// default
   222			p.printf("%v", v)
   223		}
   224	}

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.