mirror of
https://github.com/golang/go
synced 2024-11-22 00:04:41 -07:00
go/ast, gofmt: facility for printing AST nodes
go/ast: implement Fprint and print functions to print AST nodes gofmt: print AST nodes by setting -ast flag R=rsc, r CC=golang-dev https://golang.org/cl/1981044
This commit is contained in:
parent
bdeebf4907
commit
3112bb0727
@ -33,6 +33,8 @@ Debugging flags:
|
||||
|
||||
-trace
|
||||
print parse trace.
|
||||
-ast
|
||||
print AST (before rewrites).
|
||||
-comments=true
|
||||
print comments; if false, all comments are elided from the output.
|
||||
|
||||
|
@ -28,6 +28,7 @@ var (
|
||||
// debugging support
|
||||
comments = flag.Bool("comments", true, "print comments")
|
||||
trace = flag.Bool("trace", false, "print parse trace")
|
||||
printAST = flag.Bool("ast", false, "print AST (before rewrites)")
|
||||
|
||||
// layout control
|
||||
tabWidth = flag.Int("tabwidth", 8, "tab width")
|
||||
@ -97,6 +98,10 @@ func processFile(f *os.File) os.Error {
|
||||
return err
|
||||
}
|
||||
|
||||
if *printAST {
|
||||
ast.Print(file)
|
||||
}
|
||||
|
||||
if rewrite != nil {
|
||||
file = rewrite(file)
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ TARG=go/ast
|
||||
GOFILES=\
|
||||
ast.go\
|
||||
filter.go\
|
||||
print.go\
|
||||
scope.go\
|
||||
walk.go\
|
||||
|
||||
|
197
src/pkg/go/ast/print.go
Normal file
197
src/pkg/go/ast/print.go
Normal file
@ -0,0 +1,197 @@
|
||||
// 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 suppport for ASTs.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
||||
// 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, value reflect.Value) bool {
|
||||
v, ok := value.(interface {
|
||||
IsNil() bool
|
||||
})
|
||||
return !ok || !v.IsNil()
|
||||
}
|
||||
|
||||
|
||||
// Fprint prints the (sub-)tree starting at AST node x to w.
|
||||
//
|
||||
// A non-nil FieldFilter f may be provided to control the output:
|
||||
// struct fields for which f(fieldname, fieldvalue) is true are
|
||||
// are printed; all others are filtered from the output.
|
||||
//
|
||||
func Fprint(w io.Writer, x interface{}, f FieldFilter) (n int, err os.Error) {
|
||||
// setup printer
|
||||
p := printer{output: w, filter: f}
|
||||
|
||||
// install error handler
|
||||
defer func() {
|
||||
n = p.written
|
||||
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.NewValue(x))
|
||||
p.printf("\n")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Print prints x to standard output, skipping nil fields.
|
||||
// Print(x) is the same as Fprint(os.Stdout, x, NotNilFilter).
|
||||
func Print(x interface{}) (int, os.Error) {
|
||||
return Fprint(os.Stdout, x, NotNilFilter)
|
||||
}
|
||||
|
||||
|
||||
type printer struct {
|
||||
output io.Writer
|
||||
filter FieldFilter
|
||||
written int // number of bytes written to output
|
||||
indent int // current indentation level
|
||||
last byte // the last byte processed by Write
|
||||
}
|
||||
|
||||
|
||||
var indent = []byte(". ")
|
||||
|
||||
func (p *printer) Write(data []byte) (n int, err os.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
|
||||
}
|
||||
} else if p.last == '\n' {
|
||||
for j := p.indent; j > 0; j-- {
|
||||
_, err = p.output.Write(indent)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
p.last = b
|
||||
}
|
||||
m, err = p.output.Write(data[n:])
|
||||
n += m
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// localError wraps locally caught os.Errors so we can distinguish
|
||||
// them from genuine panics which we don't want to return as errors.
|
||||
type localError struct {
|
||||
err os.Error
|
||||
}
|
||||
|
||||
|
||||
// printf is a convenience wrapper that takes care of print errors.
|
||||
func (p *printer) printf(format string, args ...interface{}) {
|
||||
n, err := fmt.Fprintf(p, format, args)
|
||||
p.written += n
|
||||
if err != nil {
|
||||
panic(localError{err})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Implementation note: Print is written for AST nodes but could be
|
||||
// used to print any acyclic data structure. It would also be easy
|
||||
// to generalize it to arbitrary data structures; such a version
|
||||
// should probably be in a different package.
|
||||
|
||||
func (p *printer) print(x reflect.Value) {
|
||||
// Note: This test is only needed because AST nodes
|
||||
// embed a token.Position, and thus all of them
|
||||
// understand the String() method (but it only
|
||||
// applies to the Position field).
|
||||
// TODO: Should reconsider this AST design decision.
|
||||
if pos, ok := x.Interface().(token.Position); ok {
|
||||
p.printf("%s", pos)
|
||||
return
|
||||
}
|
||||
|
||||
if !NotNilFilter("", x) {
|
||||
p.printf("nil")
|
||||
return
|
||||
}
|
||||
|
||||
switch v := x.(type) {
|
||||
case *reflect.InterfaceValue:
|
||||
p.print(v.Elem())
|
||||
|
||||
case *reflect.MapValue:
|
||||
p.printf("%s (len = %d) {\n", x.Type().String(), v.Len())
|
||||
p.indent++
|
||||
for _, key := range v.Keys() {
|
||||
p.print(key)
|
||||
p.printf(": ")
|
||||
p.print(v.Elem(key))
|
||||
}
|
||||
p.indent--
|
||||
p.printf("}")
|
||||
|
||||
case *reflect.PtrValue:
|
||||
p.printf("*")
|
||||
p.print(v.Elem())
|
||||
|
||||
case *reflect.SliceValue:
|
||||
if s, ok := v.Interface().([]byte); ok {
|
||||
p.printf("%#q", s)
|
||||
return
|
||||
}
|
||||
p.printf("%s (len = %d) {\n", x.Type().String(), v.Len())
|
||||
p.indent++
|
||||
for i, n := 0, v.Len(); i < n; i++ {
|
||||
p.printf("%d: ", i)
|
||||
p.print(v.Elem(i))
|
||||
p.printf("\n")
|
||||
}
|
||||
p.indent--
|
||||
p.printf("}")
|
||||
|
||||
case *reflect.StructValue:
|
||||
p.printf("%s {\n", x.Type().String())
|
||||
p.indent++
|
||||
t := v.Type().(*reflect.StructType)
|
||||
for i, n := 0, t.NumField(); i < n; i++ {
|
||||
name := t.Field(i).Name
|
||||
value := v.Field(i)
|
||||
if p.filter == nil || p.filter(name, value) {
|
||||
p.printf("%s: ", name)
|
||||
p.print(value)
|
||||
p.printf("\n")
|
||||
}
|
||||
}
|
||||
p.indent--
|
||||
p.printf("}")
|
||||
|
||||
default:
|
||||
p.printf("%v", x.Interface())
|
||||
}
|
||||
}
|
@ -353,7 +353,7 @@ func (pos Position) String() string {
|
||||
s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
|
||||
}
|
||||
if s == "" {
|
||||
s = "???"
|
||||
s = "-"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user