1
0
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:
Robert Griesemer 2010-08-19 09:39:35 -07:00
parent bdeebf4907
commit 3112bb0727
5 changed files with 206 additions and 1 deletions

View File

@ -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.

View File

@ -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)
} }

View 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
View 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())
}
}

View File

@ -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
} }