123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- package ast
- import (
- "fmt"
- "go/token"
- "io"
- "os"
- "reflect"
- apitoken "github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/token"
- )
- // 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 NotNilFilter(_ string, v reflect.Value) bool {
- switch v.Kind() {
- case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
- return !v.IsNil()
- }
- return true
- }
- // Fprint prints the value of x to the writer w.
- func Fprint(w io.Writer, x interface{}, f FieldFilter) error {
- return fprint(w, x, f)
- }
- func fprint(w io.Writer, x interface{}, f FieldFilter) (err error) {
- // setup printer
- p := printer{
- output: w,
- filter: f,
- ptrmap: make(map[interface{}]int),
- last: '\n', // force printing of line number on first line
- }
- // install error handler
- defer func() {
- if e := recover(); e != nil {
- err = e.(localError).err // re-panics if it's not a localError
- }
- }()
- // print x
- if x == nil {
- p.printf("nil\n")
- return
- }
- p.print(reflect.ValueOf(x))
- p.printf("\n")
- return
- }
- func Print(x interface{}) error {
- return Fprint(os.Stdout, x, NotNilFilter)
- }
- type printer struct {
- output io.Writer
- filter FieldFilter
- ptrmap map[interface{}]int // *T -> line number
- prefixIndent int // current indentation level
- last byte // the last byte processed by Write
- line int // current line number
- }
- var prefixIndent = []byte(". ")
- // Write implements io.Writer.
- func (p *printer) Write(data []byte) (n int, err error) {
- var m int
- for i, b := range data {
- // invariant: data[0:n] has been written
- if b == '\n' {
- m, err = p.output.Write(data[n : i+1])
- n += m
- if err != nil {
- return
- }
- p.line++
- } else if p.last == '\n' {
- _, err = fmt.Fprintf(p.output, "%6d ", p.line)
- if err != nil {
- return
- }
- for j := p.prefixIndent; j > 0; j-- {
- _, err = p.output.Write(prefixIndent)
- if err != nil {
- return
- }
- }
- }
- p.last = b
- }
- if len(data) > n {
- m, err = p.output.Write(data[n:])
- n += m
- }
- 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 (p *printer) printf(format string, args ...interface{}) {
- if _, err := fmt.Fprintf(p, format, args...); err != nil {
- panic(localError{err})
- }
- }
- // 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 (p *printer) print(x reflect.Value) {
- if !NotNilFilter("", x) {
- p.printf("nil")
- return
- }
- switch x.Kind() {
- case reflect.Interface:
- p.print(x.Elem())
- case reflect.Map:
- p.printf("%s (len = %d) {", x.Type(), x.Len())
- if x.Len() > 0 {
- p.prefixIndent++
- p.printf("\n")
- for _, key := range x.MapKeys() {
- p.print(key)
- p.printf(": ")
- p.print(x.MapIndex(key))
- p.printf("\n")
- }
- p.prefixIndent--
- }
- p.printf("}")
- case reflect.Pointer:
- p.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
- ptr := x.Interface()
- if line, exists := p.ptrmap[ptr]; exists {
- p.printf("(obj @ %d)", line)
- } else {
- p.ptrmap[ptr] = p.line
- p.print(x.Elem())
- }
- case reflect.Array:
- p.printf("%s {", x.Type())
- if x.Len() > 0 {
- p.prefixIndent++
- p.printf("\n")
- for i, n := 0, x.Len(); i < n; i++ {
- p.printf("%d: ", i)
- p.print(x.Index(i))
- p.printf("\n")
- }
- p.prefixIndent--
- }
- p.printf("}")
- case reflect.Slice:
- if s, ok := x.Interface().([]byte); ok {
- p.printf("%#q", s)
- return
- }
- p.printf("%s (len = %d) {", x.Type(), x.Len())
- if x.Len() > 0 {
- p.prefixIndent++
- p.printf("\n")
- for i, n := 0, x.Len(); i < n; i++ {
- p.printf("%d: ", i)
- p.print(x.Index(i))
- p.printf("\n")
- }
- p.prefixIndent--
- }
- p.printf("}")
- case reflect.Struct:
- if val, ok := x.Interface().(apitoken.Position); ok {
- p.printf(val.String())
- return
- }
- t := x.Type()
- p.printf("%s {", t)
- p.prefixIndent++
- first := true
- for i, n := 0, t.NumField(); i < n; i++ {
- // exclude non-exported fields because their
- // values cannot be accessed via reflection
- if name := t.Field(i).Name; token.IsExported(name) {
- value := x.Field(i)
- if p.filter == nil || p.filter(name, value) {
- if first {
- p.printf("\n")
- first = false
- }
- p.printf("%s: ", name)
- p.print(value)
- p.printf("\n")
- }
- }
- }
- p.prefixIndent--
- p.printf("}")
- default:
- v := x.Interface()
- switch v := v.(type) {
- case string:
- // print strings in quotes
- p.printf("%q", v)
- return
- }
- // default
- p.printf("%v", v)
- }
- }
|