Copyright 2010 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
This file contains printing support for ASTs.

package ast

import (
	
	
	
	
	
)
A FieldFilter may be provided to Fprint to control the output.
type FieldFilter func(name string, value reflect.Value) bool
NotNilFilter returns true for field values that are not nil; it returns false otherwise.
func ( string,  reflect.Value) bool {
	switch .Kind() {
	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
		return !.IsNil()
	}
	return true
}
Fprint prints the (sub-)tree starting at AST node x to w. If fset != nil, position information is interpreted relative to that file set. Otherwise positions are printed as integer values (file set specific offsets). A non-nil FieldFilter f may be provided to control the output: struct fields for which f(fieldname, fieldvalue) is true are printed; all others are filtered from the output. Unexported struct fields are never printed.
func ( io.Writer,  *token.FileSet,  interface{},  FieldFilter) error {
	return fprint(, , , )
}

setup printer
	 := printer{
		output: ,
		fset:   ,
		filter: ,
		ptrmap: make(map[interface{}]int),
		last:   '\n', // force printing of line number on first line
	}
install error handler
	defer func() {
		if  := recover();  != nil {
			 = .(localError).err // re-panics if it's not a localError
		}
	}()
print x
	if  == nil {
		.printf("nil\n")
		return
	}
	.print(reflect.ValueOf())
	.printf("\n")

	return
}
Print prints x to standard output, skipping nil fields. Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter).
func ( *token.FileSet,  interface{}) error {
	return Fprint(os.Stdout, , , NotNilFilter)
}

type printer struct {
	output io.Writer
	fset   *token.FileSet
	filter FieldFilter
	ptrmap map[interface{}]int // *T -> line number
	indent int                 // current indentation level
	last   byte                // the last byte processed by Write
	line   int                 // current line number
}

var indent = []byte(".  ")

func ( *printer) ( []byte) ( int,  error) {
	var  int
invariant: data[0:n] has been written
		if  == '\n' {
			,  = .output.Write([ : +1])
			 += 
			if  != nil {
				return
			}
			.line++
		} else if .last == '\n' {
			_,  = fmt.Fprintf(.output, "%6d  ", .line)
			if  != nil {
				return
			}
			for  := .indent;  > 0; -- {
				_,  = .output.Write(indent)
				if  != nil {
					return
				}
			}
		}
		.last = 
	}
	if len() >  {
		,  = .output.Write([:])
		 += 
	}
	return
}
localError wraps locally caught errors so we can distinguish them from genuine panics which we don't want to return as errors.
type localError struct {
	err error
}
printf is a convenience wrapper that takes care of print errors.
func ( *printer) ( string,  ...interface{}) {
	if ,  := fmt.Fprintf(, , ...);  != nil {
		panic(localError{})
	}
}
Implementation note: Print is written for AST nodes but could be used to print arbitrary data structures; such a version should probably be in a different package. Note: This code detects (some) cycles created via pointers but not cycles that are created via slices or maps containing the same slice or map. Code for general data structures probably should catch those as well.

func ( *printer) ( reflect.Value) {
	if !NotNilFilter("", ) {
		.printf("nil")
		return
	}

	switch .Kind() {
	case reflect.Interface:
		.(.Elem())

	case reflect.Map:
		.printf("%s (len = %d) {", .Type(), .Len())
		if .Len() > 0 {
			.indent++
			.printf("\n")
			for ,  := range .MapKeys() {
				.()
				.printf(": ")
				.(.MapIndex())
				.printf("\n")
			}
			.indent--
		}
		.printf("}")

	case reflect.Ptr:
type-checked ASTs may contain cycles - use ptrmap to keep track of objects that have been printed already and print the respective line number instead
		 := .Interface()
		if ,  := .ptrmap[];  {
			.printf("(obj @ %d)", )
		} else {
			.ptrmap[] = .line
			.(.Elem())
		}

	case reflect.Array:
		.printf("%s {", .Type())
		if .Len() > 0 {
			.indent++
			.printf("\n")
			for ,  := 0, .Len();  < ; ++ {
				.printf("%d: ", )
				.(.Index())
				.printf("\n")
			}
			.indent--
		}
		.printf("}")

	case reflect.Slice:
		if ,  := .Interface().([]byte);  {
			.printf("%#q", )
			return
		}
		.printf("%s (len = %d) {", .Type(), .Len())
		if .Len() > 0 {
			.indent++
			.printf("\n")
			for ,  := 0, .Len();  < ; ++ {
				.printf("%d: ", )
				.(.Index())
				.printf("\n")
			}
			.indent--
		}
		.printf("}")

	case reflect.Struct:
		 := .Type()
		.printf("%s {", )
		.indent++
		 := true
exclude non-exported fields because their values cannot be accessed via reflection
			if  := .Field().Name; IsExported() {
				 := .Field()
				if .filter == nil || .filter(, ) {
					if  {
						.printf("\n")
						 = false
					}
					.printf("%s: ", )
					.()
					.printf("\n")
				}
			}
		}
		.indent--
		.printf("}")

	default:
		 := .Interface()
		switch v := .(type) {
print strings in quotes
			.printf("%q", )
			return
position values can be printed nicely if we have a file set
			if .fset != nil {
				.printf("%s", .fset.Position())
				return
			}
default
		.printf("%v", )
	}