mirror of
https://github.com/golang/go
synced 2024-11-25 22:07:58 -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
|
-trace
|
||||||
print parse trace.
|
print parse trace.
|
||||||
|
-ast
|
||||||
|
print AST (before rewrites).
|
||||||
-comments=true
|
-comments=true
|
||||||
print comments; if false, all comments are elided from the output.
|
print comments; if false, all comments are elided from the output.
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ var (
|
|||||||
// debugging support
|
// debugging support
|
||||||
comments = flag.Bool("comments", true, "print comments")
|
comments = flag.Bool("comments", true, "print comments")
|
||||||
trace = flag.Bool("trace", false, "print parse trace")
|
trace = flag.Bool("trace", false, "print parse trace")
|
||||||
|
printAST = flag.Bool("ast", false, "print AST (before rewrites)")
|
||||||
|
|
||||||
// layout control
|
// layout control
|
||||||
tabWidth = flag.Int("tabwidth", 8, "tab width")
|
tabWidth = flag.Int("tabwidth", 8, "tab width")
|
||||||
@ -97,6 +98,10 @@ func processFile(f *os.File) os.Error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *printAST {
|
||||||
|
ast.Print(file)
|
||||||
|
}
|
||||||
|
|
||||||
if rewrite != nil {
|
if rewrite != nil {
|
||||||
file = rewrite(file)
|
file = rewrite(file)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ TARG=go/ast
|
|||||||
GOFILES=\
|
GOFILES=\
|
||||||
ast.go\
|
ast.go\
|
||||||
filter.go\
|
filter.go\
|
||||||
|
print.go\
|
||||||
scope.go\
|
scope.go\
|
||||||
walk.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)
|
s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
|
||||||
}
|
}
|
||||||
if s == "" {
|
if s == "" {
|
||||||
s = "???"
|
s = "-"
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user