// 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 astimport ()// A FieldFilter may be provided to [Fprint] to control the output.typeFieldFilterfunc(name string, value reflect.Value) bool// NotNilFilter is a [FieldFilter] that returns true for field values// that are not nil; it returns false otherwise.func ( string, reflect.Value) bool {switch .Kind() {casereflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:return !.IsNil() }returntrue}// 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, any, FieldFilter) error {returnfprint(, , , )}func fprint( io.Writer, *token.FileSet, any, FieldFilter) ( error) {// setup printer := printer{output: ,fset: ,filter: ,ptrmap: make(map[any]int),last: '\n', // force printing of line number on first line }// install error handlerdeferfunc() {if := recover(); != nil { = .(localError).err// re-panics if it's not a localError } }()// print xif == 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, any) error {returnFprint(os.Stdout, , , NotNilFilter)}type printer struct { output io.Writer fset *token.FileSet filter FieldFilter ptrmap map[any]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) {varintfor , := range {// invariant: data[0:n] has been writtenif == '\n' { , = .output.Write([ : +1]) += if != nil {return } .line++ } elseif .last == '\n' { _, = fmt.Fprintf(.output, "%6d ", .line)if != nil {return }for := .indent; > 0; -- { _, = .output.Write(indent)if != nil {return } } } .last = }iflen() > { , = .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, ...any) {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() {casereflect.Interface: .(.Elem())casereflect.Map: .printf("%s (len = %d) {", .Type(), .Len())if .Len() > 0 { .indent++ .printf("\n")for , := range .MapKeys() { .() .printf(": ") .(.MapIndex()) .printf("\n") } .indent-- } .printf("}")casereflect.Pointer: .printf("*")// 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()) }casereflect.Array: .printf("%s {", .Type())if .Len() > 0 { .indent++ .printf("\n")for , := 0, .Len(); < ; ++ { .printf("%d: ", ) .(.Index()) .printf("\n") } .indent-- } .printf("}")casereflect.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("}")casereflect.Struct: := .Type() .printf("%s {", ) .indent++ := truefor , := 0, .NumField(); < ; ++ {// exclude non-exported fields because their // values cannot be accessed via reflectionif := .Field().Name; IsExported() { := .Field()if .filter == nil || .filter(, ) {if { .printf("\n") = false } .printf("%s: ", ) .() .printf("\n") } } } .indent-- .printf("}")default: := .Interface()switch v := .(type) {casestring:// print strings in quotes .printf("%q", )returncasetoken.Pos:// position values can be printed nicely if we have a file setif .fset != nil { .printf("%s", .fset.Position())return } }// default .printf("%v", ) }}
The pages are generated with Goldsv0.6.9-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds.