]> Cypherpunks repositories - gostls13.git/commitdiff
gofmt (final resting place TBD):
authorRobert Griesemer <gri@golang.org>
Mon, 15 Jun 2009 23:23:16 +0000 (16:23 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 15 Jun 2009 23:23:16 +0000 (16:23 -0700)
- replacement for pretty; app to format a single .go file

printer.go (pkg/go/printer):
- replacement for astprinter.go; implements AST printing
- also replaces pkg/go/ast/format.go for now

cleanups:
- removed/saved away old code

R=r,rsc,iant
DELTA=2833  (1183 added, 1628 deleted, 22 changed)
OCL=30226
CL=30306

13 files changed:
src/pkg/Make.deps
src/pkg/Makefile
src/pkg/go/ast/Makefile
src/pkg/go/ast/format.go [deleted file]
src/pkg/go/printer/Makefile [new file with mode: 0644]
src/pkg/go/printer/printer.go [new file with mode: 0644]
usr/gri/pretty/Makefile
usr/gri/pretty/astprinter.go [deleted file]
usr/gri/pretty/godoc.go
usr/gri/pretty/gofmt.go [new file with mode: 0644]
usr/gri/pretty/pretty.go [deleted file]
usr/gri/pretty/test.sh
usr/gri/pretty/untab.go [deleted file]

index dd83e8b1cb5c38439f16f90fd746a55d78cf6bab..d710c59e35dcf668e61f6cf352f01ffbfa6700e1 100644 (file)
@@ -16,9 +16,10 @@ exec.install: os.install strings.install
 exvar.install: fmt.install http.install io.install log.install strconv.install sync.install
 flag.install: fmt.install os.install strconv.install
 fmt.install: io.install os.install reflect.install strconv.install utf8.install
-go/ast.install: datafmt.install go/token.install io.install os.install unicode.install utf8.install
+go/ast.install: go/token.install unicode.install utf8.install
 go/doc.install: container/vector.install fmt.install go/ast.install go/token.install io.install once.install regexp.install sort.install strings.install template.install
 go/parser.install: container/vector.install fmt.install go/ast.install go/scanner.install go/token.install io.install os.install
+go/printer.install: fmt.install go/ast.install go/token.install io.install os.install reflect.install
 go/scanner.install: go/token.install strconv.install unicode.install utf8.install
 go/token.install: strconv.install
 hash.install: io.install
index 036a82e38b3bb5d128b722f76f3b74406cf5afa3..3339a9d3697b08b959c3eec499218e6b916d554c 100644 (file)
@@ -33,6 +33,7 @@ DIRS=\
        go/ast\
        go/doc\
        go/parser\
+       go/printer\
        go/scanner\
        go/token\
        hash\
index 1fd22ae71f44cb4a9231c2e4ada7bb4d1903e89a..ead35dead149aa17ebe957aa0c391620660c55ac 100644 (file)
@@ -34,21 +34,14 @@ coverage: packages
 O1=\
        ast.$O\
 
-O2=\
-       format.$O\
 
-
-phases: a1 a2
+phases: a1
 _obj$D/ast.a: phases
 
 a1: $(O1)
        $(AR) grc _obj$D/ast.a ast.$O
        rm -f $(O1)
 
-a2: $(O2)
-       $(AR) grc _obj$D/ast.a format.$O
-       rm -f $(O2)
-
 
 newpkg: clean
        mkdir -p _obj$D
@@ -56,7 +49,6 @@ newpkg: clean
 
 $(O1): newpkg
 $(O2): a1
-$(O3): a2
 
 nuke: clean
        rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ast.a
diff --git a/src/pkg/go/ast/format.go b/src/pkg/go/ast/format.go
deleted file mode 100644 (file)
index caeca19..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2009 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.
-
-package ast
-
-import (
-       "datafmt";
-       "go/ast";
-       "go/token";
-       "io";
-       "os";
-)
-
-
-// Format is a customized datafmt.Format for printing of ASTs.
-type Format datafmt.Format;
-
-
-// ----------------------------------------------------------------------------
-// Custom formatters
-
-// The AST-specific formatting state is maintained by a state variable.
-type state struct {
-       // for now we have very little state
-       // TODO maintain list of unassociated comments
-       optSemi bool
-}
-
-
-func (s *state) Copy() datafmt.Environment {
-       copy := *s;
-       return &copy;
-}
-
-
-func isValidPos(s *datafmt.State, value interface{}, ruleName string) bool {
-       pos := value.(token.Position);
-       return pos.IsValid();
-}
-
-
-func isSend(s *datafmt.State, value interface{}, ruleName string) bool {
-       return value.(ast.ChanDir) & ast.SEND != 0;
-}
-
-
-func isRecv(s *datafmt.State, value interface{}, ruleName string) bool {
-       return value.(ast.ChanDir) & ast.RECV != 0;
-}
-
-
-func isMultiLineComment(s *datafmt.State, value interface{}, ruleName string) bool {
-       return value.([]byte)[1] == '*';
-}
-
-
-func clearOptSemi(s *datafmt.State, value interface{}, ruleName string) bool {
-       s.Env().(*state).optSemi = false;
-       return true;
-}
-
-
-func setOptSemi(s *datafmt.State, value interface{}, ruleName string) bool {
-       s.Env().(*state).optSemi = true;
-       return true;
-}
-
-
-func optSemi(s *datafmt.State, value interface{}, ruleName string) bool {
-       if !s.Env().(*state).optSemi {
-               s.Write([]byte{';'});
-       }
-       return true;
-}
-
-
-var fmap = datafmt.FormatterMap {
-       "isValidPos": isValidPos,
-       "isSend": isSend,
-       "isRecv": isRecv,
-       "isMultiLineComment": isMultiLineComment,
-       "/": clearOptSemi,
-       "clearOptSemi": clearOptSemi,
-       "setOptSemi": setOptSemi,
-       "optSemi": optSemi,
-}
-
-
-// ----------------------------------------------------------------------------
-// Printing
-
-// NewFormat parses a datafmt format specification from a file
-// and adds AST-specific custom formatter rules. The result is
-// the customized format or an os.Error, if any.
-//
-func NewFormat(filename string) (Format, os.Error) {
-       src, err := io.ReadFile(filename);
-       if err != nil {
-               return nil, err;
-       }
-       f, err := datafmt.Parse(src, fmap);
-       return Format(f), err;
-}
-
-
-// Fprint formats each AST node provided as argument according to the
-// format f and writes to standard output. The result is the total number
-// of bytes written and an os.Error, if any.
-//
-func (f Format) Fprint(w io.Writer, nodes ...) (int, os.Error) {
-       var s state;
-       return datafmt.Format(f).Fprint(w, &s, nodes);
-}
-
-
-// Fprint formats each AST node provided as argument according to the
-// format f and writes to w. The result is the total number of bytes
-// written and an os.Error, if any.
-//
-func (f Format) Print(nodes ...) (int, os.Error) {
-       return f.Fprint(os.Stdout, nodes);
-}
diff --git a/src/pkg/go/printer/Makefile b/src/pkg/go/printer/Makefile
new file mode 100644 (file)
index 0000000..0b0b5a7
--- /dev/null
@@ -0,0 +1,60 @@
+# Copyright 2009 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.
+
+# DO NOT EDIT.  Automatically generated by gobuild.
+# gobuild -m >Makefile
+
+D=/go/
+
+include $(GOROOT)/src/Make.$(GOARCH)
+AR=gopack
+
+default: packages
+
+clean:
+       rm -rf *.[$(OS)] *.a [$(OS)].out _obj
+
+test: packages
+       gotest
+
+coverage: packages
+       gotest
+       6cov -g `pwd` | grep -v '_test\.go:'
+
+%.$O: %.go
+       $(GC) -I_obj $*.go
+
+%.$O: %.c
+       $(CC) $*.c
+
+%.$O: %.s
+       $(AS) $*.s
+
+O1=\
+       printer.$O\
+
+
+phases: a1
+_obj$D/printer.a: phases
+
+a1: $(O1)
+       $(AR) grc _obj$D/printer.a printer.$O
+       rm -f $(O1)
+
+
+newpkg: clean
+       mkdir -p _obj$D
+       $(AR) grc _obj$D/printer.a
+
+$(O1): newpkg
+$(O2): a1
+
+nuke: clean
+       rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/printer.a
+
+packages: _obj$D/printer.a
+
+install: packages
+       test -d $(GOROOT)/pkg && mkdir -p $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D
+       cp _obj$D/printer.a $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/printer.a
diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go
new file mode 100644 (file)
index 0000000..74318b0
--- /dev/null
@@ -0,0 +1,1019 @@
+// Copyright 2009 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.
+
+// The printer package implements printing of AST nodes.
+package printer
+
+import (
+       "fmt";
+       "go/ast";
+       "go/token";
+       "io";
+       "os";
+       "reflect";
+)
+
+
+// Printing is controlled with these flags supplied
+// to Fprint via the mode parameter.
+//
+const (
+       ExportsOnly uint = 1 << iota;  // print exported code only
+       DocComments;  // print documentation comments
+       OptCommas;  // print optional commas
+       OptSemis;  // print optional semicolons
+)
+
+
+type printer struct {
+       // configuration (does not change after initialization)
+       output io.Writer;
+       mode uint;
+       errors chan os.Error;
+       comments ast.Comments;  // list of unassociated comments; or nil
+
+       // current state (changes during printing)
+       written int;  // number of bytes written
+       level int;  // function nesting level; 0 = package scope, 1 = top-level function scope, etc.
+       indent int;  // indent level
+       pos token.Position;  // output position (possibly estimated) in "AST space"
+
+       // comments
+       cindex int;  // the current comment index
+       cpos token.Position;  // the position of the next comment
+}
+
+
+func (p *printer) hasComment(pos token.Position) bool {
+       return p.cpos.Offset < pos.Offset;
+}
+
+
+func (p *printer) nextComment() {
+       p.cindex++;
+       if p.comments != nil && p.cindex < len(p.comments) && p.comments[p.cindex] != nil {
+               p.cpos = p.comments[p.cindex].Pos();
+       } else {
+               p.cpos = token.Position{1<<30, 1<<30, 1};  // infinite
+       }
+}
+
+
+func (p *printer) setComments(comments ast.Comments) {
+       p.comments = comments;
+       p.cindex = -1;
+       p.nextComment();
+}
+
+
+func (p *printer) init(output io.Writer, mode uint) {
+       p.output = output;
+       p.mode = mode;
+       p.errors = make(chan os.Error);
+       p.setComments(nil);
+}
+
+
+var (
+       blank = []byte{' '};
+       tab = []byte{'\t'};
+       newline = []byte{'\n'};
+       formfeed = []byte{'\f'};
+)
+
+
+// Writing to p.output is done with write0 which also handles errors.
+// It should only be called by write.
+//
+func (p *printer) write0(data []byte) {
+       n, err := p.output.Write(data);
+       p.written += n;
+       if err != nil {
+               p.errors <- err;
+       }
+}
+
+
+func (p *printer) write(data []byte) {
+       i0 := 0;
+       for i, b := range data {
+               if b == '\n' || b == '\f' {
+                       // write segment ending in a newline/formfeed followed by indentation
+                       // TODO should convert '\f' into '\n' if the output is not going through
+                       //      tabwriter
+                       p.write0(data[i0 : i+1]);
+                       for j := p.indent; j > 0; j-- {
+                               p.write0(tab);
+                       }
+                       i0 = i+1;
+
+                       // update p.pos
+                       p.pos.Offset += i+1 - i0 + p.indent;
+                       p.pos.Line++;
+                       p.pos.Column = p.indent + 1;
+               }
+       }
+
+       // write remaining segment
+       p.write0(data[i0 : len(data)]);
+
+       // update p.pos
+       n := len(data) - i0;
+       p.pos.Offset += n;
+       p.pos.Column += n;
+}
+
+
+// Reduce contiguous sequences of '\t' in a []byte to a single '\t'.
+func untabify(src []byte) []byte {
+       dst := make([]byte, len(src));
+       j := 0;
+       for i, c := range src {
+               if c != '\t' || i == 0 || src[i-1] != '\t' {
+                       dst[j] = c;
+                       j++;
+               }
+       }
+       return dst[0 : j];
+}
+
+
+func (p *printer) adjustSpacingAndMergeComments() {
+       for ; p.hasComment(p.pos); p.nextComment() {
+               // we have a comment that comes before the current position
+               comment := p.comments[p.cindex];
+               p.write(untabify(comment.Text));
+               // TODO
+               // - classify comment and provide better formatting
+               // - add extra newlines if so indicated by source positions
+       }
+}
+
+
+func (p *printer) print(args ...) {
+       v := reflect.NewValue(args).(reflect.StructValue);
+       for i := 0; i < v.Len(); i++ {
+               p.adjustSpacingAndMergeComments();
+               f := v.Field(i);
+               switch x := f.Interface().(type) {
+               case int:
+                       // indentation delta
+                       p.indent += x;
+                       if p.indent < 0 {
+                               panic("print: negative indentation");
+                       }
+               case []byte:
+                       p.write(x);
+               case string:
+                       p.write(io.StringBytes(x));
+               case token.Token:
+                       p.write(io.StringBytes(x.String()));
+               case token.Position:
+                       // set current position
+                       p.pos = x;
+               default:
+                       panicln("print: unsupported argument type", f.Type().String());
+               }
+       }
+}
+
+
+// ----------------------------------------------------------------------------
+// Predicates
+
+func (p *printer) optSemis() bool {
+       return p.mode & OptSemis != 0;
+}
+
+
+func (p *printer) exportsOnly() bool {
+       return p.mode & ExportsOnly != 0;
+}
+
+
+// The isVisibleX predicates return true if X should produce any output
+// given the printing mode and depending on whether X contains exported
+// names.
+
+func (p *printer) isVisibleIdent(x *ast.Ident) bool {
+       // identifiers in local scopes (p.level > 0) are always visible
+       // if the surrounding code is printed in the first place
+       return !p.exportsOnly() || x.IsExported() || p.level > 0;
+}
+
+
+func (p *printer) isVisibleIdentList(list []*ast.Ident) bool {
+       for _, x := range list {
+               if p.isVisibleIdent(x) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+
+func (p *printer) isVisibleFieldList(list []*ast.Field) bool {
+       for _, f := range list {
+               if len(f.Names) == 0 {
+                       // anonymous field
+                       // TODO should only return true if the anonymous field
+                       //      type is visible (for now be conservative and
+                       //      print it so that the generated code is valid)
+                       return true;
+               }
+               if p.isVisibleIdentList(f.Names) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+
+func (p *printer) isVisibleSpec(spec ast.Spec) bool {
+       switch s := spec.(type) {
+       case *ast.ImportSpec:
+               return !p.exportsOnly();
+       case *ast.ValueSpec:
+               return p.isVisibleIdentList(s.Names);
+       case *ast.TypeSpec:
+               return p.isVisibleIdent(s.Name);
+       }
+       panic("unreachable");
+       return false;
+}
+
+
+func (p *printer) isVisibleSpecList(list []ast.Spec) bool {
+       for _, s := range list {
+               if p.isVisibleSpec(s) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+
+func (p *printer) isVisibleDecl(decl ast.Decl) bool {
+       switch d := decl.(type) {
+       case *ast.BadDecl:
+               return false;
+       case *ast.GenDecl:
+               return p.isVisibleSpecList(d.Specs);
+       case *ast.FuncDecl:
+               return p.isVisibleIdent(d.Name);
+       }
+       panic("unreachable");
+       return false;
+}
+
+
+// ----------------------------------------------------------------------------
+// Printing of common AST nodes.
+
+func (p *printer) comment(c *ast.Comment) {
+       if c != nil {
+               text := c.Text;
+               if text[1] == '/' {
+                       // //-style comment - dont print the '\n'
+                       // TODO scanner should probably not include the '\n' in this case
+                       text = text[0 : len(text)-1];
+               }
+               p.print(tab, c.Pos(), text);  // tab-separated trailing comment
+       }
+}
+
+
+func (p *printer) doc(d ast.Comments) {
+       if p.mode & DocComments != 0 {
+               for _, c := range d {
+                       p.print(c.Pos(), c.Text);
+               }
+       }
+}
+
+
+func (p *printer) expr(x ast.Expr) bool
+
+func (p *printer) identList(list []*ast.Ident) {
+       needsComma := false;
+       for i, x := range list {
+               if p.isVisibleIdent(x) {
+                       if needsComma {
+                               p.print(token.COMMA, blank);
+                       }
+                       p.expr(x);
+                       needsComma = true;
+               }
+       }
+}
+
+
+func (p *printer) exprList(list []ast.Expr) {
+       for i, x := range list {
+               if i > 0 {
+                       p.print(token.COMMA, blank);
+               }
+               p.expr(x);
+       }
+}
+
+
+func (p *printer) parameters(list []*ast.Field) {
+       p.print(token.LPAREN);
+       if len(list) > 0 {
+               for i, par := range list {
+                       if i > 0 {
+                               p.print(token.COMMA, blank);
+                       }
+                       p.identList(par.Names);  // p.level > 0; all identifiers will be printed
+                       if len(par.Names) > 0 {
+                               // at least one identifier
+                               p.print(blank);
+                       };
+                       p.expr(par.Type);
+               }
+       }
+       p.print(token.RPAREN);
+}
+
+
+func (p *printer) signature(params, result []*ast.Field) {
+       p.parameters(params);
+       if result != nil {
+               p.print(blank);
+
+               if len(result) == 1 && result[0].Names == nil {
+                       // single anonymous result; no ()'s unless it's a function type
+                       f := result[0];
+                       if _, isFtyp := f.Type.(*ast.FuncType); !isFtyp {
+                               p.expr(f.Type);
+                               return;
+                       }
+               }
+
+               p.parameters(result);
+       }
+}
+
+
+// Returns true if the field list ends in a closing brace.
+func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isInterface bool) bool {
+       hasBody := p.isVisibleFieldList(list);
+       if !lbrace.IsValid() || p.exportsOnly() && !hasBody {
+               // forward declaration without {}'s or no visible exported fields
+               // (in all other cases, the {}'s must be printed even if there are
+               // no fields, otherwise the type is incorrect)
+               return false;  // no {}'s
+       }
+
+       p.print(blank, lbrace, token.LBRACE);
+
+       if hasBody {
+               p.print(+1, newline);
+
+               var needsSemi bool;
+               var lastWasAnon bool;  // true if the previous line was an anonymous field
+               var lastComment *ast.Comment;  // the comment from the previous line
+               for _, f := range list {
+                       hasNames := p.isVisibleIdentList(f.Names);
+                       isAnon := len(f.Names) == 0;
+
+                       if hasNames || isAnon {
+                               // at least one visible identifier or anonymous field
+                               // TODO this is conservative - see isVisibleFieldList
+                               if needsSemi {
+                                       p.print(token.SEMICOLON);
+                                       p.comment(lastComment);
+                                       if lastWasAnon == isAnon {
+                                               // previous and current line have same structure;
+                                               // continue with existing columns
+                                               p.print(newline);
+                                       } else {
+                                               // previous and current line have different structure;
+                                               // flush tabwriter and start new columns (the "type
+                                               // column" on a line with named fields may line up
+                                               // with the "trailing comment column" on a line with
+                                               // an anonymous field, leading to bad alignment)
+                                               p.print(formfeed);
+                                       }
+                               }
+
+                               p.doc(f.Doc);
+                               if hasNames {
+                                       p.identList(f.Names);
+                                       p.print(tab);
+                               }
+
+                               if isInterface {
+                                       if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
+                                               // methods
+                                               p.signature(ftyp.Params, ftyp.Results);
+                                       } else {
+                                               // embedded interface
+                                               p.expr(f.Type);
+                                       }
+                               } else {
+                                       p.expr(f.Type);
+                                       if f.Tag != nil && !p.exportsOnly() {
+                                               p.print(tab);
+                                               p.expr(&ast.StringList{f.Tag});
+                                       }
+                               }
+
+                               needsSemi = true;
+                               lastWasAnon = isAnon;
+                               lastComment = f.Comment;
+                       }
+               }
+
+               if p.optSemis() {
+                       p.print(token.SEMICOLON);
+               }
+
+               p.comment(lastComment);
+               p.print(-1, newline);
+       }
+
+       p.print(rbrace, token.RBRACE);
+       return true;  // field list with {}'s
+}
+
+
+// ----------------------------------------------------------------------------
+// Expressions
+
+func (p *printer) stmt(s ast.Stmt) (optSemi bool)
+
+// Returns true if a separating semicolon is optional.
+func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
+       p.print(expr.Pos());
+
+       switch x := expr.(type) {
+       case *ast.BadExpr:
+               p.print("BadExpr");
+
+       case *ast.Ident:
+               p.print(x.Value);
+
+       case *ast.BinaryExpr:
+               prec := x.Op.Precedence();
+               if prec < prec1 {
+                       p.print(token.LPAREN);
+               }
+               p.expr1(x.X, prec);
+               p.print(blank, x.OpPos, x.Op, blank);
+               p.expr1(x.Y, prec);
+               if prec < prec1 {
+                       p.print(token.RPAREN);
+               }
+
+       case *ast.KeyValueExpr:
+               p.expr(x.Key);
+               p.print(blank, x.Colon, token.COLON, blank);
+               p.expr(x.Value);
+
+       case *ast.StarExpr:
+               p.print(token.MUL);
+               p.expr(x.X);
+
+       case *ast.UnaryExpr:
+               prec := token.UnaryPrec;
+               if prec < prec1 {
+                       p.print(token.LPAREN);
+               }
+               p.print(x.Op);
+               if x.Op == token.RANGE {
+                       p.print(blank);
+               }
+               p.expr1(x.X, prec);
+               if prec < prec1 {
+                       p.print(token.RPAREN);
+               }
+
+       case *ast.IntLit:
+               p.print(x.Value);
+
+       case *ast.FloatLit:
+               p.print(x.Value);
+
+       case *ast.CharLit:
+               p.print(x.Value);
+
+       case *ast.StringLit:
+               p.print(x.Value);
+
+       case *ast.StringList:
+               for i, x := range x.Strings {
+                       if i > 0 {
+                               p.print(blank);
+                       }
+                       p.expr(x);
+               }
+
+       case *ast.FuncLit:
+               p.level++;
+               p.expr(x.Type);
+               p.print(blank);
+               p.stmt(x.Body);
+               p.level--;
+
+       case *ast.ParenExpr:
+               p.print(token.LPAREN);
+               p.expr(x.X);
+               p.print(x.Rparen, token.RPAREN);
+
+       case *ast.SelectorExpr:
+               p.expr1(x.X, token.HighestPrec);
+               p.print(token.PERIOD);
+               p.expr1(x.Sel, token.HighestPrec);
+
+       case *ast.TypeAssertExpr:
+               p.expr1(x.X, token.HighestPrec);
+               p.print(token.PERIOD, token.LPAREN);
+               p.expr(x.Type);
+               p.print(token.RPAREN);
+
+       case *ast.IndexExpr:
+               p.expr1(x.X, token.HighestPrec);
+               p.print(token.LBRACK);
+               p.expr(x.Index);
+               if x.End != nil {
+                       p.print(blank, token.COLON, blank);
+                       p.expr(x.End);
+               }
+               p.print(token.RBRACK);
+
+       case *ast.CallExpr:
+               p.expr1(x.Fun, token.HighestPrec);
+               p.print(x.Lparen, token.LPAREN);
+               p.exprList(x.Args);
+               p.print(x.Rparen, token.RPAREN);
+
+       case *ast.CompositeLit:
+               p.expr1(x.Type, token.HighestPrec);
+               p.print(x.Lbrace, token.LBRACE);
+               p.exprList(x.Elts);
+               if p.mode & OptCommas != 0 {
+                       p.print(token.COMMA);
+               }
+               p.print(x.Rbrace, token.RBRACE);
+
+       case *ast.Ellipsis:
+               p.print(token.ELLIPSIS);
+
+       case *ast.ArrayType:
+               p.print(token.LBRACK);
+               if x.Len != nil {
+                       p.expr(x.Len);
+               }
+               p.print(token.RBRACK);
+               p.expr(x.Elt);
+
+       case *ast.StructType:
+               p.print(token.STRUCT);
+               optSemi = p.fieldList(x.Lbrace, x.Fields, x.Rbrace, false);
+
+       case *ast.FuncType:
+               p.print(token.FUNC);
+               p.signature(x.Params, x.Results);
+
+       case *ast.InterfaceType:
+               p.print(token.INTERFACE);
+               optSemi = p.fieldList(x.Lbrace, x.Methods, x.Rbrace, true);
+
+       case *ast.MapType:
+               p.print(token.MAP, blank, token.LBRACK);
+               p.expr(x.Key);
+               p.print(token.RBRACK);
+               p.expr(x.Value);
+
+       case *ast.ChanType:
+               switch x.Dir {
+               case ast.SEND | ast.RECV:
+                       p.print(token.CHAN);
+               case ast.RECV:
+                       p.print(token.ARROW, token.CHAN);
+               case ast.SEND:
+                       p.print(token.CHAN, blank, token.ARROW);
+               }
+               p.print(blank);
+               p.expr(x.Value);
+
+       default:
+               panic("unreachable");
+       }
+
+       return optSemi;
+}
+
+
+// Returns true if a separating semicolon is optional.
+func (p *printer) expr(x ast.Expr) bool {
+       return p.expr1(x, token.LowestPrec);
+}
+
+
+// ----------------------------------------------------------------------------
+// Statements
+
+func (p *printer) decl(decl ast.Decl) (optSemi bool)
+
+// Print the statement list indented, but without a newline after the last statement.
+func (p *printer) stmtList(list []ast.Stmt) {
+       if len(list) > 0 {
+               p.print(+1, newline);
+               optSemi := false;
+               for i, s := range list {
+                       if i > 0 {
+                               if !optSemi || p.optSemis() {
+                                       // semicolon is required
+                                       p.print(token.SEMICOLON);
+                               }
+                               p.print(newline);
+                       }
+                       optSemi = p.stmt(s);
+               }
+               if p.optSemis() {
+                       p.print(token.SEMICOLON);
+               }
+               p.print(-1);
+       }
+}
+
+
+func (p *printer) block(s *ast.BlockStmt) {
+       p.print(s.Pos(), token.LBRACE);
+       if len(s.List) > 0 {
+               p.stmtList(s.List);
+               p.print(newline);
+       }
+       p.print(s.Rbrace, token.RBRACE);
+}
+
+
+func (p *printer) switchBlock(s *ast.BlockStmt) {
+       p.print(s.Pos(), token.LBRACE);
+       if len(s.List) > 0 {
+               for i, s := range s.List {
+                       // s is one of *ast.CaseClause, *ast.TypeCaseClause, *ast.CommClause;
+                       p.print(newline);
+                       p.stmt(s);
+               }
+               p.print(newline);
+       }
+       p.print(s.Rbrace, token.RBRACE);
+}
+
+
+func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
+       if init == nil && post == nil {
+               // no semicolons required
+               if expr != nil {
+                       p.print(blank);
+                       p.expr(expr);
+               }
+       } else {
+               // all semicolons required
+               // (they are not separators, print them explicitly)
+               p.print(blank);
+               if init != nil {
+                       p.stmt(init);
+               }
+               p.print(token.SEMICOLON, blank);
+               if expr != nil {
+                       p.expr(expr);
+               }
+               if isForStmt {
+                       p.print(token.SEMICOLON, blank);
+                       if post != nil {
+                               p.stmt(post);
+                       }
+               }
+       }
+}
+
+
+// Returns true if a separating semicolon is optional.
+func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
+       p.print(stmt.Pos());
+
+       switch s := stmt.(type) {
+       case *ast.BadStmt:
+               p.print("BadStmt");
+
+       case *ast.DeclStmt:
+               optSemi = p.decl(s.Decl);
+
+       case *ast.EmptyStmt:
+               // nothing to do
+
+       case *ast.LabeledStmt:
+               p.print(-1, newline);
+               p.expr(s.Label);
+               p.print(token.COLON, tab, +1);
+               optSemi = p.stmt(s.Stmt);
+
+       case *ast.ExprStmt:
+               p.expr(s.X);
+
+       case *ast.IncDecStmt:
+               p.expr(s.X);
+               p.print(s.Tok);
+
+       case *ast.AssignStmt:
+               p.exprList(s.Lhs);
+               p.print(blank, s.TokPos, s.Tok, blank);
+               p.exprList(s.Rhs);
+
+       case *ast.GoStmt:
+               p.print(token.GO, blank);
+               p.expr(s.Call);
+
+       case *ast.DeferStmt:
+               p.print(token.DEFER, blank);
+               p.expr(s.Call);
+
+       case *ast.ReturnStmt:
+               p.print(token.RETURN);
+               if s.Results != nil {
+                       p.print(blank);
+                       p.exprList(s.Results);
+               }
+
+       case *ast.BranchStmt:
+               p.print(s.Tok);
+               if s.Label != nil {
+                       p.print(blank);
+                       p.expr(s.Label);
+               }
+
+       case *ast.BlockStmt:
+               p.block(s);
+               optSemi = true;
+
+       case *ast.IfStmt:
+               p.print(token.IF);
+               p.controlClause(false, s.Init, s.Cond, nil);
+               p.print(blank);
+               p.block(s.Body);
+               optSemi = true;
+               if s.Else != nil {
+                       p.print(blank, token.ELSE, blank);
+                       optSemi = p.stmt(s.Else);
+               }
+
+       case *ast.CaseClause:
+               if s.Values != nil {
+                       p.print(token.CASE, blank);
+                       p.exprList(s.Values);
+               } else {
+                       p.print(token.DEFAULT);
+               }
+               p.print(s.Colon, token.COLON);
+               p.stmtList(s.Body);
+
+       case *ast.SwitchStmt:
+               p.print(token.SWITCH);
+               p.controlClause(false, s.Init, s.Tag, nil);
+               p.print(blank);
+               p.switchBlock(s.Body);
+               optSemi = true;
+
+       case *ast.TypeCaseClause:
+               if s.Type != nil {
+                       p.print(token.CASE, blank);
+                       p.expr(s.Type);
+               } else {
+                       p.print(token.DEFAULT);
+               }
+               p.print(s.Colon, token.COLON);
+               p.stmtList(s.Body);
+
+       case *ast.TypeSwitchStmt:
+               p.print(token.SWITCH);
+               if s.Init != nil {
+                       p.print(blank);
+                       p.stmt(s.Init);
+                       p.print(token.SEMICOLON);
+               }
+               p.print(blank);
+               p.stmt(s.Assign);
+               p.print(blank);
+               p.switchBlock(s.Body);
+               optSemi = true;
+
+       case *ast.CommClause:
+               if s.Rhs != nil {
+                       p.print(token.CASE, blank);
+                       if s.Lhs != nil {
+                               p.expr(s.Lhs);
+                               p.print(blank, s.Tok, blank);
+                       }
+                       p.expr(s.Rhs);
+               } else {
+                       p.print(token.DEFAULT);
+               }
+               p.print(s.Colon, token.COLON);
+               p.stmtList(s.Body);
+
+       case *ast.SelectStmt:
+               p.print(token.SELECT, blank);
+               p.switchBlock(s.Body);
+               optSemi = true;
+
+       case *ast.ForStmt:
+               p.print(token.FOR);
+               p.controlClause(true, s.Init, s.Cond, s.Post);
+               p.print(blank);
+               p.block(s.Body);
+               optSemi = true;
+
+       case *ast.RangeStmt:
+               p.print(token.FOR, blank);
+               p.expr(s.Key);
+               if s.Value != nil {
+                       p.print(token.COMMA, blank);
+                       p.expr(s.Value);
+               }
+               p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank);
+               p.expr(s.X);
+               p.print(blank);
+               p.block(s.Body);
+               optSemi = true;
+
+       default:
+               panic("unreachable");
+       }
+
+       return optSemi;
+}
+
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+// Returns true if a separating semicolon is optional.
+func (p *printer) spec(spec ast.Spec) (optSemi bool) {
+       switch s := spec.(type) {
+       case *ast.ImportSpec:
+               p.doc(s.Doc);
+               if s.Name != nil {
+                       p.expr(s.Name);
+               }
+               // TODO fix for longer package names
+               p.print(tab, s.Path[0].Pos(), s.Path[0].Value);
+
+       case *ast.ValueSpec:
+               p.doc(s.Doc);
+               p.identList(s.Names);
+               if s.Type != nil {
+                       p.print(blank);  // TODO switch to tab? (indent problem with structs)
+                       p.expr(s.Type);
+               }
+               if s.Values != nil {
+                       p.print(tab, token.ASSIGN, blank);
+                       p.exprList(s.Values);
+               }
+
+       case *ast.TypeSpec:
+               p.doc(s.Doc);
+               p.expr(s.Name);
+               p.print(blank);  // TODO switch to tab? (but indent problem with structs)
+               optSemi = p.expr(s.Type);
+
+       default:
+               panic("unreachable");
+       }
+
+       return optSemi;
+}
+
+
+// Returns true if a separating semicolon is optional.
+func (p *printer) decl(decl ast.Decl) (optSemi bool) {
+       switch d := decl.(type) {
+       case *ast.BadDecl:
+               p.print(d.Pos(), "BadDecl");
+
+       case *ast.GenDecl:
+               p.doc(d.Doc);
+               p.print(d.Pos(), d.Tok, blank);
+
+               if d.Lparen.IsValid() {
+                       // group of parenthesized declarations
+                       p.print(d.Lparen, token.LPAREN);
+                       if p.isVisibleSpecList(d.Specs) {
+                               p.print(+1, newline);
+                               semi := false;
+                               for _, s := range d.Specs {
+                                       if p.isVisibleSpec(s) {
+                                               if semi {
+                                                       p.print(token.SEMICOLON, newline);
+                                               }
+                                               p.spec(s);
+                                               semi = true;
+                                       }
+                               }
+                               if p.optSemis() {
+                                       p.print(token.SEMICOLON);
+                               }
+                               p.print(-1, newline);
+                       }
+                       p.print(d.Rparen, token.RPAREN);
+                       optSemi = true;
+
+               } else {
+                       // single declaration
+                       optSemi = p.spec(d.Specs[0]);
+               }
+
+       case *ast.FuncDecl:
+               p.level++;
+               p.doc(d.Doc);
+               p.print(d.Pos(), token.FUNC, blank);
+               if recv := d.Recv; recv != nil {
+                       // method: print receiver
+                       p.print(token.LPAREN);
+                       if len(recv.Names) > 0 {
+                               p.expr(recv.Names[0]);
+                               p.print(blank);
+                       }
+                       p.expr(recv.Type);
+                       p.print(token.RPAREN, blank);
+               }
+               p.expr(d.Name);
+               p.signature(d.Type.Params, d.Type.Results);
+               if !p.exportsOnly() && d.Body != nil {
+                       p.print(blank);
+                       p.stmt(d.Body);
+               }
+               p.level--;
+
+       default:
+               panic("unreachable");
+       }
+
+       return optSemi;
+}
+
+
+// ----------------------------------------------------------------------------
+// Programs
+
+func (p *printer) program(prog *ast.Program) {
+       // set unassociated comments if all code is printed
+       if !p.exportsOnly() {
+               // TODO enable this once comments are properly interspersed
+               //p.setComments(prog.Comments);
+       }
+
+       p.doc(prog.Doc);
+       p.print(prog.Pos(), token.PACKAGE, blank);
+       p.expr(prog.Name);
+
+       for _, d := range prog.Decls {
+               if p.isVisibleDecl(d) {
+                       p.print(newline, newline);
+                       p.decl(d);
+                       if p.optSemis() {
+                               p.print(token.SEMICOLON);
+                       }
+               }
+       }
+}
+
+
+// ----------------------------------------------------------------------------
+// Public interface
+
+// Fprint "pretty-prints" an AST node to output and returns the number of
+// bytes written, and an error, if any. The node type must be *ast.Program,
+// or assignment-compatible to ast.Expr, ast.Decl, or ast.Stmt. Printing is
+// controlled by the mode parameter. For best results, the output should be
+// a tabwriter.Writer.
+//
+func Fprint(output io.Writer, node interface{}, mode uint) (int, os.Error) {
+       var p printer;
+       p.init(output, mode);
+
+       go func() {
+               switch n := node.(type) {
+               case ast.Expr:
+                       p.expr(n);
+               case ast.Stmt:
+                       p.stmt(n);
+               case ast.Decl:
+                       p.decl(n);
+               case *ast.Program:
+                       p.program(n);
+               default:
+                       p.errors <- os.NewError("unsupported node type");
+               }
+               p.print(newline);
+               p.errors <- nil;  // no errors
+       }();
+       err := <-p.errors;  // wait for completion of goroutine
+
+       return p.written, err;
+}
index 96f1184b951f971e5f2cd1b3218a3418fcd62d2a..3171a087a50242ba89d5c4ba28f6d0099468c75a 100644 (file)
@@ -4,34 +4,26 @@
 
 include $(GOROOT)/src/Make.$(GOARCH)
 
-all: untab godoc pretty
-
-untab: untab.$O
-       $(LD) -o untab untab.$O
+all: godoc gofmt
 
 godoc: godoc.$O
        $(LD) -o godoc godoc.$O
 
-pretty: pretty.$O
-       $(LD) -o pretty pretty.$O
+gofmt: gofmt.$O
+       $(LD) -o gofmt gofmt.$O
 
-test: pretty
+test: gofmt
        ./test.sh
 
-smoketest: pretty
-       ./test.sh astprinter.go
+smoketest: gofmt
+       ./test.sh $(GOROOT)/src/pkg/go/printer/printer.go
 
 install: pretty godoc untab
        cp godoc $(HOME)/bin/godoc
-       cp pretty $(HOME)/bin/pretty
-       cp untab $(HOME)/bin/untab
+       cp gofmt $(HOME)/bin/gofmt
 
 clean:
-       rm -f pretty untab godoc *.$O *.a 6.out *~
-
-godoc.$O:      astprinter.$O
-
-pretty.$O:      astprinter.$O
+       rm -f godoc gofmt *.$O *.a 6.out *~
 
 %.$O:  %.go
        $(GC) $(F) $<
diff --git a/usr/gri/pretty/astprinter.go b/usr/gri/pretty/astprinter.go
deleted file mode 100644 (file)
index 867a840..0000000
+++ /dev/null
@@ -1,1269 +0,0 @@
-// Copyright 2009 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.
-
-package astPrinter
-
-import (
-       "flag";
-       "fmt";
-       "go/ast";
-       "go/token";
-       "io";
-       "os";
-)
-
-
-var (
-       debug = flag.Bool("ast_debug", false, "print debugging information");
-
-       // layout control
-       newlines = flag.Bool("ast_newlines", false, "respect newlines in source");
-       maxnewlines = flag.Int("ast_maxnewlines", 3, "max. number of consecutive newlines");
-
-       // formatting control
-       optsemicolons = flag.Bool("ast_optsemicolons", false, "print optional semicolons");
-)
-
-
-// When we don't have a position use noPos.
-var noPos token.Position;
-
-
-// ----------------------------------------------------------------------------
-// Elementary support
-
-func unimplemented() {
-       panic("unimplemented");
-}
-
-
-func unreachable() {
-       panic("unreachable");
-}
-
-
-func assert(pred bool) {
-       if !pred {
-               panic("assertion failed");
-       }
-}
-
-
-func hasExportedNames(names []*ast.Ident) bool {
-       for i, name := range names {
-               if name.IsExported() {
-                       return true;
-               }
-       }
-       return false;
-}
-
-
-// ----------------------------------------------------------------------------
-// TokenPrinter
-
-// TODO This is not yet used - should fix this.
-
-// An implementation of a TokenPrinter may be provided when
-// initializing an AST Printer. It is used to print tokens.
-//
-type TokenPrinter interface {
-       PrintLit(w io.Writer, tok token.Token, value []byte);
-       PrintIdent(w io.Writer, value string);
-       PrintToken(w io.Writer, token token.Token);
-       PrintComment(w io.Writer, value []byte);
-}
-
-
-type defaultPrinter struct {}
-
-func (p defaultPrinter) PrintLit(w io.Writer, tok token.Token, value []byte) {
-       w.Write(value);
-}
-
-
-func (p defaultPrinter) PrintIdent(w io.Writer, value string) {
-       fmt.Fprint(w, value);
-}
-
-
-func (p defaultPrinter) PrintToken(w io.Writer, token token.Token) {
-       fmt.Fprint(w, token.String());
-}
-
-
-func (p defaultPrinter) PrintComment(w io.Writer, value []byte) {
-       w.Write(value);
-}
-
-
-// ----------------------------------------------------------------------------
-// ASTPrinter
-
-
-// Separators - printed in a delayed fashion, depending on context.
-const (
-       none = iota;
-       blank;
-       tab;
-       comma;
-       semicolon;
-)
-
-
-// Semantic states - control formatting.
-const (
-       normal = iota;
-       opening_scope;  // controls indentation, scope level
-       closing_scope;  // controls indentation, scope level
-       inside_list;  // controls extra line breaks
-)
-
-
-type Printer struct {
-       // output
-       text io.Writer;
-
-       // token printing
-       tprinter TokenPrinter;
-
-       // formatting control
-       html bool;
-       full bool;  // if false, print interface only; print all otherwise
-
-       // comments
-       comments []*ast.Comment;  // the list of unassociated comments
-       cindex int;  // the current comment index
-       cpos token.Position;  // the position of the next comment
-
-       // current state
-       lastpos token.Position;  // position after last string
-       level int;  // scope level
-       indentation int;  // indentation level (may be different from scope level)
-
-       // formatting parameters
-       opt_semi bool;  // // true if semicolon separator is optional in statement list
-       separator int;  // pending separator
-       newlines int;  // pending newlines
-
-       // semantic state
-       state int;  // current semantic state
-       laststate int;  // state for last string
-
-       // expression precedence
-       prec int;
-}
-
-
-func (P *Printer) hasComment(pos token.Position) bool {
-       return P.cpos.Offset < pos.Offset;
-}
-
-
-func (P *Printer) nextComments() {
-       P.cindex++;
-       if P.comments != nil && P.cindex < len(P.comments) && P.comments[P.cindex] != nil {
-               P.cpos = P.comments[P.cindex].Pos();
-       } else {
-               P.cpos = token.Position{1<<30, 1<<30, 1};  // infinite
-       }
-}
-
-
-func (P *Printer) Init(text io.Writer, tprinter TokenPrinter, comments []*ast.Comment, html bool) {
-       // writers
-       P.text = text;
-
-       // token printing
-       if tprinter != nil {
-               P.tprinter = tprinter;
-       } else {
-               P.tprinter = defaultPrinter{};
-       }
-
-       // formatting control
-       P.html = html;
-
-       // comments
-       P.comments = comments;
-       P.cindex = -1;
-       P.nextComments();
-
-       // formatting parameters & semantic state initialized correctly by default
-
-       // expression precedence
-       P.prec = token.LowestPrec;
-}
-
-
-// ----------------------------------------------------------------------------
-// Printing support
-
-func (P *Printer) htmlEscape(s string) string {
-       if P.html {
-               var esc string;
-               for i := 0; i < len(s); i++ {
-                       switch s[i] {
-                       case '<': esc = "&lt;";
-                       case '&': esc = "&amp;";
-                       default: continue;
-                       }
-                       return s[0 : i] + esc + P.htmlEscape(s[i+1 : len(s)]);
-               }
-       }
-       return s;
-}
-
-
-// Reduce contiguous sequences of '\t' in a string to a single '\t'.
-func untabify(s string) string {
-       for i := 0; i < len(s); i++ {
-               if s[i] == '\t' {
-                       j := i;
-                       for j < len(s) && s[j] == '\t' {
-                               j++;
-                       }
-                       if j-i > 1 {  // more then one tab
-                               return s[0 : i+1] + untabify(s[j : len(s)]);
-                       }
-               }
-       }
-       return s;
-}
-
-
-func (P *Printer) Printf(format string, s ...) {
-       n, err := fmt.Fprintf(P.text, format, s);
-       if err != nil {
-               panic("print error - exiting");
-       }
-}
-
-
-func (P *Printer) newline(n int) {
-       if n > 0 {
-               m := int(*maxnewlines);
-               if n > m {
-                       n = m;
-               }
-               for n > 0 {
-                       P.Printf("\n");
-                       n--;
-               }
-               for i := P.indentation; i > 0; i-- {
-                       P.Printf("\t");
-               }
-       }
-}
-
-
-func (P *Printer) TaggedString(pos token.Position, tag, s, endtag string) {
-       // use estimate for pos if we don't have one
-       offs := pos.Offset;
-       if pos.Line == 0 {
-               offs = P.lastpos.Offset;
-       }
-
-       // --------------------------------
-       // print pending separator, if any
-       // - keep track of white space printed for better comment formatting
-       // TODO print white space separators after potential comments and newlines
-       // (currently, we may get trailing white space before a newline)
-       trailing_char := 0;
-       switch P.separator {
-       case none:      // nothing to do
-       case blank:
-               P.Printf(" ");
-               trailing_char = ' ';
-       case tab:
-               P.Printf("\t");
-               trailing_char = '\t';
-       case comma:
-               P.Printf(",");
-               if P.newlines == 0 {
-                       P.Printf(" ");
-                       trailing_char = ' ';
-               }
-       case semicolon:
-               if P.level > 0 {        // no semicolons at level 0
-                       P.Printf(";");
-                       if P.newlines == 0 {
-                               P.Printf(" ");
-                               trailing_char = ' ';
-                       }
-               }
-       default:        panic("UNREACHABLE");
-       }
-       P.separator = none;
-
-       // --------------------------------
-       // interleave comments, if any
-       nlcount := 0;
-       if P.full {
-               for ; P.hasComment(pos); P.nextComments() {
-                       // we have a comment that comes before the string
-                       comment := P.comments[P.cindex];
-                       ctext := string(comment.Text);  // TODO get rid of string conversion here
-
-                       // classify comment (len(ctext) >= 2)
-                       //-style comment
-                       if nlcount > 0 || P.cpos.Offset == 0 {
-                               // only white space before comment on this line
-                               // or file starts with comment
-                               // - indent
-                               if !*newlines && P.cpos.Offset != 0 {
-                                       nlcount = 1;
-                               }
-                               P.newline(nlcount);
-                               nlcount = 0;
-
-                       } else {
-                               // black space before comment on this line
-                               if ctext[1] == '/' {
-                                       //-style comment
-                                       // - put in next cell unless a scope was just opened
-                                       //   in which case we print 2 blanks (otherwise the
-                                       //   entire scope gets indented like the next cell)
-                                       if P.laststate == opening_scope {
-                                               switch trailing_char {
-                                               case ' ': P.Printf(" ");  // one space already printed
-                                               case '\t': // do nothing
-                                               default: P.Printf("  ");
-                                               }
-                                       } else {
-                                               if trailing_char != '\t' {
-                                                       P.Printf("\t");
-                                               }
-                                       }
-                               } else {
-                                       /*-style comment */
-                                       // - print surrounded by blanks
-                                       if trailing_char == 0 {
-                                               P.Printf(" ");
-                                       }
-                                       ctext += " ";
-                               }
-                       }
-
-                       // print comment
-                       if *debug {
-                               P.Printf("[%d]", P.cpos.Offset);
-                       }
-                       // calling untabify increases the change for idempotent output
-                       // since tabs in comments are also interpreted by tabwriter
-                       P.Printf("%s", P.htmlEscape(untabify(ctext)));
-               }
-               // At this point we may have nlcount > 0: In this case we found newlines
-               // that were not followed by a comment. They are recognized (or not) when
-               // printing newlines below.
-       }
-
-       // --------------------------------
-       // interpret state
-       // (any pending separator or comment must be printed in previous state)
-       switch P.state {
-       case normal:
-       case opening_scope:
-       case closing_scope:
-               P.indentation--;
-       case inside_list:
-       default:
-               panic("UNREACHABLE");
-       }
-
-       // --------------------------------
-       // print pending newlines
-       if *newlines && (P.newlines > 0 || P.state == inside_list) && nlcount > P.newlines {
-               // Respect additional newlines in the source, but only if we
-               // enabled this feature (newlines.BVal()) and we are expecting
-               // newlines (P.newlines > 0 || P.state == inside_list).
-               // Otherwise - because we don't have all token positions - we
-               // get funny formatting.
-               P.newlines = nlcount;
-       }
-       nlcount = 0;
-       P.newline(P.newlines);
-       P.newlines = 0;
-
-       // --------------------------------
-       // print string
-       if *debug {
-               P.Printf("[%d]", pos);
-       }
-       P.Printf("%s%s%s", tag, P.htmlEscape(s), endtag);
-
-       // --------------------------------
-       // interpret state
-       switch P.state {
-       case normal:
-       case opening_scope:
-               P.level++;
-               P.indentation++;
-       case closing_scope:
-               P.level--;
-       case inside_list:
-       default:
-               panic("UNREACHABLE");
-       }
-       P.laststate = P.state;
-       P.state = none;
-
-       // --------------------------------
-       // done
-       P.opt_semi = false;
-       pos.Offset += len(s);  // rough estimate
-       pos.Column += len(s);  // rough estimate
-       P.lastpos = pos;
-}
-
-
-func (P *Printer) String(pos token.Position, s string) {
-       P.TaggedString(pos, "", s, "");
-}
-
-
-func (P *Printer) Token(pos token.Position, tok token.Token) {
-       P.String(pos, tok.String());
-       //P.TaggedString(pos, "<b>", tok.String(), "</b>");
-}
-
-
-func (P *Printer) Error(pos token.Position, tok token.Token, msg string) {
-       fmt.Printf("\ninternal printing error: pos = %d, tok = %s, %s\n", pos.Offset, tok.String(), msg);
-       panic();
-}
-
-
-// An astPrinter implements io.Writer.
-// TODO this is not yet used.
-func (P *Printer) Write(p []byte) (n int, err os.Error) {
-       // TODO
-       // - no string conversion every time
-       // - return proper results
-       P.String(noPos, string(p));
-       return len(p), nil;
-}
-
-
-// ----------------------------------------------------------------------------
-// HTML support
-
-func (P *Printer) HtmlIdentifier(x *ast.Ident) {
-       P.String(x.Pos(), x.Value);
-       /*
-       obj := x.Obj;
-       if P.html && obj.Kind != symbolTable.NONE {
-               // depending on whether we have a declaration or use, generate different html
-               // - no need to htmlEscape ident
-               id := utils.IntToString(obj.Id, 10);
-               if x.Loc_ == obj.Pos {
-                       // probably the declaration of x
-                       P.TaggedString(x.Loc_, `<a name="id` + id + `">`, obj.Ident, `</a>`);
-               } else {
-                       // probably not the declaration of x
-                       P.TaggedString(x.Loc_, `<a href="#id` + id + `">`, obj.Ident, `</a>`);
-               }
-       } else {
-               P.String(x.Loc_, obj.Ident);
-       }
-       */
-}
-
-
-func (P *Printer) HtmlPackageName(pos token.Position, name string) {
-       if P.html {
-               sname := name[1 : len(name)-1];  // strip quotes  TODO do this elsewhere eventually
-               // TODO CAPITAL HACK BELOW FIX THIS
-               P.TaggedString(pos, `"<a href="/src/pkg/` + sname + `.go">`, sname, `</a>"`);
-       } else {
-               P.String(pos, name);
-       }
-}
-
-
-// ----------------------------------------------------------------------------
-// Support
-
-func (P *Printer) Expr(x ast.Expr)
-
-func (P *Printer) Idents(list []*ast.Ident, full bool) int {
-       n := 0;
-       for i, x := range list {
-               if n > 0 {
-                       P.Token(noPos, token.COMMA);
-                       P.separator = blank;
-                       P.state = inside_list;
-               }
-               if full || x.IsExported() {
-                       P.Expr(x);
-                       n++;
-               }
-       }
-       return n;
-}
-
-
-func (P *Printer) Exprs(list []ast.Expr) {
-       for i, x := range list {
-               if i > 0 {
-                       P.Token(noPos, token.COMMA);
-                       P.separator = blank;
-                       P.state = inside_list;
-               }
-               P.Expr(x);
-       }
-}
-
-
-func (P *Printer) Parameters(list []*ast.Field) {
-       P.Token(noPos, token.LPAREN);
-       if len(list) > 0 {
-               for i, par := range list {
-                       if i > 0 {
-                               P.separator = comma;
-                       }
-                       n := P.Idents(par.Names, true);
-                       if n > 0 {
-                               P.separator = blank
-                       };
-                       P.Expr(par.Type);
-               }
-       }
-       P.Token(noPos, token.RPAREN);
-}
-
-
-// Returns the separator (semicolon or none) required if
-// the type is terminating a declaration or statement.
-func (P *Printer) Signature(params, result []*ast.Field) {
-       P.Parameters(params);
-       if result != nil {
-               P.separator = blank;
-
-               if len(result) == 1 && result[0].Names == nil {
-                       // single anonymous result
-                       // => no parentheses needed unless it's a function type
-                       fld := result[0];
-                       if dummy, is_ftyp := fld.Type.(*ast.FuncType); !is_ftyp {
-                               P.Expr(fld.Type);
-                               return;
-                       }
-               }
-
-               P.Parameters(result);
-       }
-}
-
-
-func (P *Printer) Fields(lbrace token.Position, list []*ast.Field, rbrace token.Position, is_interface bool) {
-       P.state = opening_scope;
-       P.separator = blank;
-       P.Token(lbrace, token.LBRACE);
-
-       if len(list) > 0 {
-               P.newlines = 1;
-               for i, fld := range list {
-                       if i > 0 {
-                               P.separator = semicolon;
-                               P.newlines = 1;
-                       }
-                       n := P.Idents(fld.Names, P.full);
-                       if n > 0 {
-                               // at least one identifier
-                               P.separator = tab
-                       };
-                       if n > 0 || len(fld.Names) == 0 {
-                               // at least one identifier or anonymous field
-                               if is_interface {
-                                       if ftyp, is_ftyp := fld.Type.(*ast.FuncType); is_ftyp {
-                                               P.Signature(ftyp.Params, ftyp.Results);
-                                       } else {
-                                               P.Expr(fld.Type);
-                                       }
-                               } else {
-                                       P.Expr(fld.Type);
-                                       if fld.Tag != nil {
-                                               P.separator = tab;
-                                               P.Expr(&ast.StringList{fld.Tag});
-                                       }
-                               }
-                       }
-               }
-               P.newlines = 1;
-       }
-
-       P.state = closing_scope;
-       P.Token(rbrace, token.RBRACE);
-       P.opt_semi = true;
-}
-
-
-// ----------------------------------------------------------------------------
-// Expressions
-
-func (P *Printer) Expr1(x ast.Expr, prec1 int)
-func (P *Printer) Stmt(s ast.Stmt)
-
-
-func (P *Printer) DoBadExpr(x *ast.BadExpr) {
-       P.String(noPos, "BadExpr");
-}
-
-
-func (P *Printer) DoIdent(x *ast.Ident) {
-       P.HtmlIdentifier(x);
-}
-
-
-func (P *Printer) DoBinaryExpr(x *ast.BinaryExpr) {
-       prec := x.Op.Precedence();
-       if prec < P.prec {
-               P.Token(noPos, token.LPAREN);
-       }
-       P.Expr1(x.X, prec);
-       P.separator = blank;
-       P.Token(x.OpPos, x.Op);
-       P.separator = blank;
-       P.Expr1(x.Y, prec);
-       if prec < P.prec {
-               P.Token(noPos, token.RPAREN);
-       }
-}
-
-
-func (P *Printer) DoKeyValueExpr(x *ast.KeyValueExpr) {
-       P.Expr(x.Key);
-       P.separator = blank;
-       P.Token(x.Colon, token.COLON);
-       P.separator = blank;
-       P.Expr(x.Value);
-}
-
-
-func (P *Printer) DoStarExpr(x *ast.StarExpr) {
-       P.Token(x.Pos(), token.MUL);
-       P.Expr(x.X);
-}
-
-
-func (P *Printer) DoUnaryExpr(x *ast.UnaryExpr) {
-       prec := token.UnaryPrec;
-       if prec < P.prec {
-               P.Token(noPos, token.LPAREN);
-       }
-       P.Token(x.Pos(), x.Op);
-       if x.Op == token.RANGE {
-               P.separator = blank;
-       }
-       P.Expr1(x.X, prec);
-       if prec < P.prec {
-               P.Token(noPos, token.RPAREN);
-       }
-}
-
-
-func (P *Printer) DoIntLit(x *ast.IntLit) {
-       // TODO get rid of string conversion here
-       P.String(x.Pos(), string(x.Value));
-}
-
-
-func (P *Printer) DoFloatLit(x *ast.FloatLit) {
-       // TODO get rid of string conversion here
-       P.String(x.Pos(), string(x.Value));
-}
-
-
-func (P *Printer) DoCharLit(x *ast.CharLit) {
-       // TODO get rid of string conversion here
-       P.String(x.Pos(), string(x.Value));
-}
-
-
-func (P *Printer) DoStringLit(x *ast.StringLit) {
-       // TODO get rid of string conversion here
-       P.String(x.Pos(), string(x.Value));
-}
-
-
-func (P *Printer) DoStringList(x *ast.StringList) {
-       for i, x := range x.Strings {
-               if i > 0 {
-                       P.separator = blank;
-               }
-               P.DoStringLit(x);
-       }
-}
-
-
-func (P *Printer) DoFuncType(x *ast.FuncType)
-
-func (P *Printer) DoFuncLit(x *ast.FuncLit) {
-       P.DoFuncType(x.Type);
-       P.separator = blank;
-       P.Stmt(x.Body);
-       P.opt_semi = false;  // BUG 6g or spec
-       P.newlines = 0;
-}
-
-
-func (P *Printer) DoParenExpr(x *ast.ParenExpr) {
-       P.Token(x.Pos(), token.LPAREN);
-       P.Expr(x.X);
-       P.Token(x.Rparen, token.RPAREN);
-}
-
-
-func (P *Printer) DoSelectorExpr(x *ast.SelectorExpr) {
-       P.Expr1(x.X, token.HighestPrec);
-       P.Token(noPos, token.PERIOD);
-       P.Expr1(x.Sel, token.HighestPrec);
-}
-
-
-func (P *Printer) DoTypeAssertExpr(x *ast.TypeAssertExpr) {
-       P.Expr1(x.X, token.HighestPrec);
-       P.Token(noPos, token.PERIOD);
-       P.Token(noPos, token.LPAREN);
-       P.Expr(x.Type);
-       P.Token(noPos, token.RPAREN);
-}
-
-
-func (P *Printer) DoIndexExpr(x *ast.IndexExpr) {
-       P.Expr1(x.X, token.HighestPrec);
-       P.Token(noPos, token.LBRACK);
-       P.Expr(x.Index);
-       if x.End != nil {
-               P.Token(noPos, token.COLON);
-               P.Expr(x.End);
-       }
-       P.Token(noPos, token.RBRACK);
-}
-
-
-func (P *Printer) DoCallExpr(x *ast.CallExpr) {
-       P.Expr1(x.Fun, token.HighestPrec);
-       P.Token(x.Lparen, token.LPAREN);
-       P.Exprs(x.Args);
-       P.Token(x.Rparen, token.RPAREN);
-}
-
-
-func (P *Printer) DoCompositeLit(x *ast.CompositeLit) {
-       P.Expr1(x.Type, token.HighestPrec);
-       P.Token(x.Lbrace, token.LBRACE);
-       P.Exprs(x.Elts);
-       P.Token(x.Rbrace, token.RBRACE);
-}
-
-
-func (P *Printer) DoEllipsis(x *ast.Ellipsis) {
-       P.Token(x.Pos(), token.ELLIPSIS);
-}
-
-
-func (P *Printer) DoArrayType(x *ast.ArrayType) {
-       P.Token(x.Pos(), token.LBRACK);
-       if x.Len != nil {
-               P.Expr(x.Len);
-       }
-       P.Token(noPos, token.RBRACK);
-       P.Expr(x.Elt);
-}
-
-
-func (P *Printer) DoStructType(x *ast.StructType) {
-       P.Token(x.Pos(), token.STRUCT);
-       if x.Fields != nil {
-               P.Fields(x.Lbrace, x.Fields, x.Rbrace, false);
-       }
-}
-
-
-func (P *Printer) DoFuncType(x *ast.FuncType) {
-       P.Token(x.Pos(), token.FUNC);
-       P.Signature(x.Params, x.Results);
-}
-
-
-func (P *Printer) DoInterfaceType(x *ast.InterfaceType) {
-       P.Token(x.Pos(), token.INTERFACE);
-       if x.Methods != nil {
-               P.Fields(x.Lbrace, x.Methods, x.Rbrace, true);
-       }
-}
-
-
-func (P *Printer) DoMapType(x *ast.MapType) {
-       P.Token(x.Pos(), token.MAP);
-       P.separator = blank;
-       P.Token(noPos, token.LBRACK);
-       P.Expr(x.Key);
-       P.Token(noPos, token.RBRACK);
-       P.Expr(x.Value);
-}
-
-
-func (P *Printer) DoChanType(x *ast.ChanType) {
-       switch x.Dir {
-       case ast.SEND | ast.RECV:
-               P.Token(x.Pos(), token.CHAN);
-       case ast.RECV:
-               P.Token(x.Pos(), token.ARROW);
-               P.Token(noPos, token.CHAN);
-       case ast.SEND:
-               P.Token(x.Pos(), token.CHAN);
-               P.separator = blank;
-               P.Token(noPos, token.ARROW);
-       }
-       P.separator = blank;
-       P.Expr(x.Value);
-}
-
-
-func (P *Printer) Expr1(x ast.Expr, prec1 int) {
-       if x == nil {
-               return;  // empty expression list
-       }
-
-       saved_prec := P.prec;
-       P.prec = prec1;
-       x.Visit(P);
-       P.prec = saved_prec;
-}
-
-
-func (P *Printer) Expr(x ast.Expr) {
-       P.Expr1(x, token.LowestPrec);
-}
-
-
-// ----------------------------------------------------------------------------
-// Statements
-
-func (P *Printer) Stmt(s ast.Stmt) {
-       s.Visit(P);
-}
-
-
-func (P *Printer) DoBadStmt(s *ast.BadStmt) {
-       panic();
-}
-
-
-func (P *Printer) Decl(d ast.Decl);
-
-func (P *Printer) DoDeclStmt(s *ast.DeclStmt) {
-       P.Decl(s.Decl);
-}
-
-
-func (P *Printer) DoEmptyStmt(s *ast.EmptyStmt) {
-       P.String(s.Pos(), "");
-}
-
-
-func (P *Printer) DoLabeledStmt(s *ast.LabeledStmt) {
-       P.indentation--;
-       P.Expr(s.Label);
-       P.Token(noPos, token.COLON);
-       P.indentation++;
-       // TODO be more clever if s.Stmt is a labeled stat as well
-       P.separator = tab;
-       P.Stmt(s.Stmt);
-}
-
-
-func (P *Printer) DoExprStmt(s *ast.ExprStmt) {
-       P.Expr(s.X);
-}
-
-
-func (P *Printer) DoIncDecStmt(s *ast.IncDecStmt) {
-       P.Expr(s.X);
-       P.Token(noPos, s.Tok);
-}
-
-
-func (P *Printer) DoAssignStmt(s *ast.AssignStmt) {
-       P.Exprs(s.Lhs);
-       P.separator = blank;
-       P.Token(s.TokPos, s.Tok);
-       P.separator = blank;
-       P.Exprs(s.Rhs);
-}
-
-
-func (P *Printer) DoGoStmt(s *ast.GoStmt) {
-       P.Token(s.Pos(), token.GO);
-       P.separator = blank;
-       P.Expr(s.Call);
-}
-
-
-func (P *Printer) DoDeferStmt(s *ast.DeferStmt) {
-       P.Token(s.Pos(), token.DEFER);
-       P.separator = blank;
-       P.Expr(s.Call);
-}
-
-
-func (P *Printer) DoReturnStmt(s *ast.ReturnStmt) {
-       P.Token(s.Pos(), token.RETURN);
-       P.separator = blank;
-       P.Exprs(s.Results);
-}
-
-
-func (P *Printer) DoBranchStmt(s *ast.BranchStmt) {
-       P.Token(s.Pos(), s.Tok);
-       if s.Label != nil {
-               P.separator = blank;
-               P.Expr(s.Label);
-       }
-}
-
-
-func (P *Printer) StatementList(list []ast.Stmt) {
-       if list != nil {
-               for i, s := range list {
-                       if i == 0 {
-                               P.newlines = 1;
-                       } else {  // i > 0
-                               if !P.opt_semi || *optsemicolons {
-                                       // semicolon is required
-                                       P.separator = semicolon;
-                               }
-                       }
-                       P.Stmt(s);
-                       P.newlines = 1;
-                       P.state = inside_list;
-               }
-       }
-}
-
-
-/*
-func (P *Printer) Block(list []ast.Stmt, indent bool) {
-       P.state = opening_scope;
-       P.Token(b.Pos_, b.Tok);
-       if !indent {
-               P.indentation--;
-       }
-       P.StatementList(b.List);
-       if !indent {
-               P.indentation++;
-       }
-       if !*optsemicolons {
-               P.separator = none;
-       }
-       P.state = closing_scope;
-       if b.Tok == token.LBRACE {
-               P.Token(b.Rbrace, token.RBRACE);
-               P.opt_semi = true;
-       } else {
-               P.String(noPos, "");  // process closing_scope state transition!
-       }
-}
-*/
-
-
-func (P *Printer) DoBlockStmt(s *ast.BlockStmt) {
-       P.state = opening_scope;
-       P.Token(s.Pos(), token.LBRACE);
-       P.StatementList(s.List);
-       if !*optsemicolons {
-               P.separator = none;
-       }
-       P.state = closing_scope;
-       P.Token(s.Rbrace, token.RBRACE);
-       P.opt_semi = true;
-}
-
-
-func (P *Printer) ControlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
-       P.separator = blank;
-       if init == nil && post == nil {
-               // no semicolons required
-               if expr != nil {
-                       P.Expr(expr);
-               }
-       } else {
-               // all semicolons required
-               // (they are not separators, print them explicitly)
-               if init != nil {
-                       P.Stmt(init);
-                       P.separator = none;
-               }
-               P.Token(noPos, token.SEMICOLON);
-               P.separator = blank;
-               if expr != nil {
-                       P.Expr(expr);
-                       P.separator = none;
-               }
-               if isForStmt {
-                       P.Token(noPos, token.SEMICOLON);
-                       P.separator = blank;
-                       if post != nil {
-                               P.Stmt(post);
-                       }
-               }
-       }
-       P.separator = blank;
-}
-
-
-func (P *Printer) DoIfStmt(s *ast.IfStmt) {
-       P.Token(s.Pos(), token.IF);
-       P.ControlClause(false, s.Init, s.Cond, nil);
-       P.Stmt(s.Body);
-       if s.Else != nil {
-               P.separator = blank;
-               P.Token(noPos, token.ELSE);
-               P.separator = blank;
-               P.Stmt(s.Else);
-       }
-}
-
-
-func (P *Printer) DoCaseClause(s *ast.CaseClause) {
-       if s.Values != nil {
-               P.Token(s.Pos(), token.CASE);
-               P.separator = blank;
-               P.Exprs(s.Values);
-       } else {
-               P.Token(s.Pos(), token.DEFAULT);
-       }
-       P.Token(s.Colon, token.COLON);
-       P.indentation++;
-       P.StatementList(s.Body);
-       P.indentation--;
-       P.newlines = 1;
-}
-
-
-func (P *Printer) DoSwitchStmt(s *ast.SwitchStmt) {
-       P.Token(s.Pos(), token.SWITCH);
-       P.ControlClause(false, s.Init, s.Tag, nil);
-       P.Stmt(s.Body);
-}
-
-
-func (P *Printer) DoTypeCaseClause(s *ast.TypeCaseClause) {
-       if s.Type != nil {
-               P.Token(s.Pos(), token.CASE);
-               P.separator = blank;
-               P.Expr(s.Type);
-       } else {
-               P.Token(s.Pos(), token.DEFAULT);
-       }
-       P.Token(s.Colon, token.COLON);
-       P.indentation++;
-       P.StatementList(s.Body);
-       P.indentation--;
-       P.newlines = 1;
-}
-
-
-func (P *Printer) DoTypeSwitchStmt(s *ast.TypeSwitchStmt) {
-       P.Token(s.Pos(), token.SWITCH);
-       P.separator = blank;
-       if s.Init != nil {
-               P.Stmt(s.Init);
-               P.separator = none;
-               P.Token(noPos, token.SEMICOLON);
-       }
-       P.separator = blank;
-       P.Stmt(s.Assign);
-       P.separator = blank;
-       P.Stmt(s.Body);
-}
-
-
-func (P *Printer) DoCommClause(s *ast.CommClause) {
-       if s.Rhs != nil {
-               P.Token(s.Pos(), token.CASE);
-               P.separator = blank;
-               if s.Lhs != nil {
-                       P.Expr(s.Lhs);
-                       P.separator = blank;
-                       P.Token(noPos, s.Tok);
-                       P.separator = blank;
-               }
-               P.Expr(s.Rhs);
-       } else {
-               P.Token(s.Pos(), token.DEFAULT);
-       }
-       P.Token(s.Colon, token.COLON);
-       P.indentation++;
-       P.StatementList(s.Body);
-       P.indentation--;
-       P.newlines = 1;
-}
-
-
-func (P *Printer) DoSelectStmt(s *ast.SelectStmt) {
-       P.Token(s.Pos(), token.SELECT);
-       P.separator = blank;
-       P.Stmt(s.Body);
-}
-
-
-func (P *Printer) DoForStmt(s *ast.ForStmt) {
-       P.Token(s.Pos(), token.FOR);
-       P.ControlClause(true, s.Init, s.Cond, s.Post);
-       P.Stmt(s.Body);
-}
-
-
-func (P *Printer) DoRangeStmt(s *ast.RangeStmt) {
-       P.Token(s.Pos(), token.FOR);
-       P.separator = blank;
-       P.Expr(s.Key);
-       if s.Value != nil {
-               P.Token(noPos, token.COMMA);
-               P.separator = blank;
-               P.state = inside_list;
-               P.Expr(s.Value);
-       }
-       P.separator = blank;
-       P.Token(s.TokPos, s.Tok);
-       P.separator = blank;
-       P.Token(noPos, token.RANGE);
-       P.separator = blank;
-       P.Expr(s.X);
-       P.separator = blank;
-       P.Stmt(s.Body);
-}
-
-
-// ----------------------------------------------------------------------------
-// Declarations
-
-func (P *Printer) DoBadDecl(d *ast.BadDecl) {
-       P.String(d.Pos(), "<BAD DECL>");
-}
-
-
-func (P *Printer) importSpec(d *ast.ImportSpec) {
-       if d.Name != nil {
-               P.Expr(d.Name);
-       } else {
-               P.String(d.Path[0].Pos(), "");  // flush pending ';' separator/newlines
-       }
-       P.separator = tab;
-       // TODO fix for longer package names
-       P.HtmlPackageName(d.Path[0].Pos(), string(d.Path[0].Value));
-       P.newlines = 2;
-}
-
-
-func (P *Printer) valueSpec(d *ast.ValueSpec) {
-       P.Idents(d.Names, P.full);
-       if d.Type != nil {
-               P.separator = blank;  // TODO switch to tab? (indentation problem with structs)
-               P.Expr(d.Type);
-       }
-       if d.Values != nil {
-               P.separator = tab;
-               P.Token(noPos, token.ASSIGN);
-               P.separator = blank;
-               P.Exprs(d.Values);
-       }
-       P.newlines = 2;
-}
-
-
-func (P *Printer) typeSpec(d *ast.TypeSpec) {
-       P.Expr(d.Name);
-       P.separator = blank;  // TODO switch to tab? (but indentation problem with structs)
-       P.Expr(d.Type);
-       P.newlines = 2;
-}
-
-
-func (P *Printer) spec(d ast.Spec) {
-       switch s := d.(type) {
-       case *ast.ImportSpec: P.importSpec(s);
-       case *ast.ValueSpec: P.valueSpec(s);
-       case *ast.TypeSpec: P.typeSpec(s);
-       default: panic("unreachable");
-       }
-}
-
-
-func (P *Printer) DoGenDecl(d *ast.GenDecl) {
-       P.Token(d.Pos(), d.Tok);
-       P.separator = blank;
-
-       if d.Lparen.Line > 0 {
-               // group of parenthesized declarations
-               P.state = opening_scope;
-               P.Token(d.Lparen, token.LPAREN);
-               if len(d.Specs) > 0 {
-                       P.newlines = 1;
-                       for i := 0; i < len(d.Specs); i++ {
-                               if i > 0 {
-                                       P.separator = semicolon;
-                               }
-                               P.spec(d.Specs[i]);
-                               P.newlines = 1;
-                       }
-               }
-               P.state = closing_scope;
-               P.Token(d.Rparen, token.RPAREN);
-               P.opt_semi = true;
-               P.newlines = 2;
-
-       } else {
-               // single declaration
-               P.spec(d.Specs[0]);
-       }
-}
-
-
-func (P *Printer) DoFuncDecl(d *ast.FuncDecl) {
-       P.Token(d.Pos(), token.FUNC);
-       P.separator = blank;
-       if recv := d.Recv; recv != nil {
-               // method: print receiver
-               P.Token(noPos, token.LPAREN);
-               if len(recv.Names) > 0 {
-                       P.Expr(recv.Names[0]);
-                       P.separator = blank;
-               }
-               P.Expr(recv.Type);
-               P.Token(noPos, token.RPAREN);
-               P.separator = blank;
-       }
-       P.Expr(d.Name);
-       P.Signature(d.Type.Params, d.Type.Results);
-       if P.full && d.Body != nil {
-               P.separator = blank;
-               P.Stmt(d.Body);
-       }
-       P.newlines = 3;
-}
-
-
-func (P *Printer) Decl(d ast.Decl) {
-       d.Visit(P);
-}
-
-
-// ----------------------------------------------------------------------------
-// Program
-
-func (P *Printer) DoProgram(p *ast.Program) {
-       P.full = true;
-       P.Token(p.Pos(), token.PACKAGE);
-       P.separator = blank;
-       P.Expr(p.Name);
-       P.newlines = 1;
-       for i := 0; i < len(p.Decls); i++ {
-               P.Decl(p.Decls[i]);
-       }
-       P.newlines = 1;
-}
index f2d66079a42088d9e41c5f48d1ec7eeb29d17ffb..c4bc6d7e21b9fb284a2a78426b7178db182f6095 100644 (file)
@@ -33,6 +33,7 @@ import (
        "go/ast";
        "go/doc";
        "go/parser";
+       "go/printer";
        "go/token";
        "http";
        "io";
@@ -47,8 +48,6 @@ import (
        "tabwriter";
        "template";
        "time";
-
-       "astprinter";  // TODO remove eventually in favor of ast.Fprint
 )
 
 
@@ -90,7 +89,6 @@ var (
 
        // layout control
        tabwidth = flag.Int("tabwidth", 4, "tab width");
-       usetabs = flag.Bool("tabs", false, "align with tabs instead of spaces");
        html = flag.Bool("html", false, "print HTML in command-line mode");
 
        // server control
@@ -129,11 +127,7 @@ func isPkgDir(dir *os.Dir) bool {
 
 
 func makeTabwriter(writer io.Writer) *tabwriter.Writer {
-       padchar := byte(' ');
-       if *usetabs {
-               padchar = '\t';
-       }
-       return tabwriter.NewWriter(writer, *tabwidth, 1, padchar, tabwriter.FilterHTML);
+       return tabwriter.NewWriter(writer, *tabwidth, 1, byte(' '), 0);
 }
 
 
@@ -203,22 +197,12 @@ func parse(path string, mode uint) (*ast.Program, *parseErrors) {
 // ----------------------------------------------------------------------------
 // Templates
 
-// Return text for decl.
-func DeclText(d ast.Decl) []byte {
-       var buf io.ByteBuffer;
-       var p astPrinter.Printer;
-       p.Init(&buf, nil, nil, false);
-       d.Visit(&p);
-       return buf.Data();
-}
-
-
-// Return text for expr.
-func ExprText(d ast.Expr) []byte {
+// Return text for an AST node.
+func nodeText(node interface{}, mode uint) []byte {
        var buf io.ByteBuffer;
-       var p astPrinter.Printer;
-       p.Init(&buf, nil, nil, false);
-       d.Visit(&p);
+       tw := makeTabwriter(&buf);
+       printer.Fprint(tw, node, mode);
+       tw.Flush();
        return buf.Data();
 }
 
@@ -235,9 +219,9 @@ func toText(x interface{}) []byte {
        case String:
                return io.StringBytes(v.String());
        case ast.Decl:
-               return DeclText(v);
+               return nodeText(v, printer.ExportsOnly);
        case ast.Expr:
-               return ExprText(v);
+               return nodeText(v, printer.ExportsOnly);
        }
        var buf io.ByteBuffer;
        fmt.Fprint(&buf, x);
@@ -247,23 +231,7 @@ func toText(x interface{}) []byte {
 
 // Template formatter for "html" format.
 func htmlFmt(w io.Writer, x interface{}, format string) {
-       // Can do better than text in some cases.
-       switch v := x.(type) {
-       case ast.Decl:
-               var p astPrinter.Printer;
-               tw := makeTabwriter(w);
-               p.Init(tw, nil, nil, true);
-               v.Visit(&p);
-               tw.Flush();
-       case ast.Expr:
-               var p astPrinter.Printer;
-               tw := makeTabwriter(w);
-               p.Init(tw, nil, nil, true);
-               v.Visit(&p);
-               tw.Flush();
-       default:
-               template.HtmlEscape(w, toText(x));
-       }
+       template.HtmlEscape(w, toText(x));
 }
 
 
@@ -363,11 +331,7 @@ func serveGoSource(c *http.Conn, name string) {
 
        var buf io.ByteBuffer;
        fmt.Fprintln(&buf, "<pre>");
-       var p astPrinter.Printer;
-       writer := makeTabwriter(&buf);  // for nicely formatted output
-       p.Init(writer, nil, nil, true);
-       p.DoProgram(prog);
-       writer.Flush();  // ignore errors
+       template.HtmlEscape(&buf, nodeText(prog, printer.DocComments));
        fmt.Fprintln(&buf, "</pre>");
 
        servePage(c, name + " - Go source", buf.Data());
diff --git a/usr/gri/pretty/gofmt.go b/usr/gri/pretty/gofmt.go
new file mode 100644 (file)
index 0000000..73f9d8e
--- /dev/null
@@ -0,0 +1,107 @@
+// Copyright 2009 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.
+
+package main
+
+import (
+       "flag";
+       "fmt";
+       "go/parser";
+       "go/printer";
+       "io";
+       "os";
+       "sort";
+       "tabwriter";
+)
+
+
+var (
+       // operation modes
+       silent = flag.Bool("s", false, "silent mode: parsing only");
+       verbose = flag.Bool("v", false, "verbose mode: trace parsing");
+       exports = flag.Bool("x", false, "show exports only");
+
+       // layout control
+       tabwidth = flag.Int("tabwidth", 4, "tab width");
+       usetabs = flag.Bool("tabs", false, "align with tabs instead of blanks");
+       optcommas = flag.Bool("optcommas", false, "print optional commas");
+       optsemis = flag.Bool("optsemis", false, "print optional semicolons");
+)
+
+
+func usage() {
+       fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [file.go]\n");
+       flag.PrintDefaults();
+       os.Exit(1);
+}
+
+
+func parserMode() uint {
+       mode := parser.ParseComments;
+       if *verbose {
+               mode |= parser.Trace;
+       }
+       return mode;
+}
+
+
+func printerMode() uint {
+       mode := uint(0);
+       if *exports {
+               mode |= printer.ExportsOnly;
+       }
+       if *optcommas {
+               mode |= printer.OptCommas;
+       }
+       if *optsemis {
+               mode |= printer.OptSemis;
+       }
+       return mode;
+}
+
+
+func makeTabwriter(writer io.Writer) *tabwriter.Writer {
+       padchar := byte(' ');
+       if *usetabs {
+               padchar = '\t';
+       }
+       return tabwriter.NewWriter(writer, *tabwidth, 1, padchar, 0);
+}
+
+
+func main() {
+       flag.Parse();
+
+       var filename string;
+       switch flag.NArg() {
+       case 0: filename = "/dev/stdin";
+       case 1: filename = flag.Arg(0);
+       default: usage();
+       }
+
+       src, err := io.ReadFile(filename);
+       if err != nil {
+               fmt.Fprintf(os.Stderr, "%s: %v\n", filename, err);
+               os.Exit(1);
+       }
+
+       prog, err := parser.Parse(src, parserMode());
+       if err != nil {
+               if errors, ok := err.(parser.ErrorList); ok {
+                       sort.Sort(errors);
+                       for _, e := range errors {
+                               fmt.Fprintf(os.Stderr, "%s:%v\n", filename, e);
+                       }
+               } else {
+                       fmt.Fprintf(os.Stderr, "%s: %v\n", filename, err);
+               }
+               os.Exit(1);
+       }
+
+       if !*silent {
+               w := makeTabwriter(os.Stdout);
+               printer.Fprint(w, prog, printerMode());
+               w.Flush();
+       }
+}
diff --git a/usr/gri/pretty/pretty.go b/usr/gri/pretty/pretty.go
deleted file mode 100644 (file)
index 82fc16b..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2009 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.
-
-package main
-
-import (
-       "astprinter";  // TODO remove once go/printer is fully functional
-       "flag";
-       "fmt";
-       "go/ast";
-       "go/parser";
-       "go/token";
-       "io";
-       "os";
-       "sort";
-       "tabwriter";
-)
-
-
-var (
-       // operation modes
-       columns bool;
-       // TODO remove silent flag eventually, can achieve same by proving no format file
-       silent = flag.Bool("s", false, "silent mode: no pretty print output");
-       verbose = flag.Bool("v", false, "verbose mode: trace parsing");
-
-       // layout control
-       format = flag.String("format", "", "format file");
-       tabwidth = flag.Int("tabwidth", 4, "tab width");
-       usetabs = flag.Bool("tabs", false, "align with tabs instead of blanks");
-)
-
-
-func init() {
-       user, err := os.Getenv("USER");
-       flag.BoolVar(&columns, "columns", user == "gri", "print column no. in error messages");
-}
-
-
-func usage() {
-       fmt.Fprintf(os.Stderr, "usage: pretty { flags } { files }\n");
-       flag.PrintDefaults();
-       os.Exit(1);
-}
-
-
-// TODO(gri) move this function into tabwriter.go? (also used in godoc)
-func makeTabwriter(writer io.Writer) *tabwriter.Writer {
-       padchar := byte(' ');
-       if *usetabs {
-               padchar = '\t';
-       }
-       return tabwriter.NewWriter(writer, *tabwidth, 1, padchar, 0);
-}
-
-
-func main() {
-       // handle flags
-       flag.Parse();
-       if flag.NFlag() == 0 && flag.NArg() == 0 {
-               usage();
-       }
-
-       // initialize astFormat
-       astFormat, err := ast.NewFormat(*format);
-       if *format != "" && err != nil {  // ignore error if no format file given
-               fmt.Fprintf(os.Stderr, "ast.NewFormat(%s): %v\n", *format, err);
-               os.Exit(1);
-       }
-
-       // determine parsing mode
-       mode := parser.ParseComments;
-       if *verbose {
-               mode |= parser.Trace;
-       }
-
-       // process files
-       exitcode := 0;
-       for i := 0; i < flag.NArg(); i++ {
-               filename := flag.Arg(i);
-
-               src, err := io.ReadFile(filename);
-               if err != nil {
-                       fmt.Fprintf(os.Stderr, "%s: %v\n", filename, err);
-                       exitcode = 1;
-                       continue;  // proceed with next file
-               }
-
-               prog, err := parser.Parse(src, mode);
-               if err != nil {
-                       if errors, ok := err.(parser.ErrorList); ok {
-                               sort.Sort(errors);
-                               for _, e := range errors {
-                                       fmt.Fprintf(os.Stderr, "%s:%v\n", filename, e);
-                               }
-                       } else {
-                               fmt.Fprintf(os.Stderr, "%s: %v\n", filename, err);
-                       }
-                       exitcode = 1;
-                       continue;  // proceed with next file
-               }
-
-               if !*silent {
-                       tw := makeTabwriter(os.Stdout);
-                       if *format != "" {
-                               _, err := astFormat.Fprint(tw, prog);
-                               if err != nil {
-                                       fmt.Fprintf(os.Stderr, "format error: %v\n", err);
-                                       exitcode = 1;
-                                       continue;  // proceed with next file
-                               }
-                       } else {
-                               var p astPrinter.Printer;
-                               p.Init(tw, nil, nil /*prog.Comments*/, false);
-                               p.DoProgram(prog);
-                       }
-                       tw.Flush();
-               }
-       }
-
-       os.Exit(exitcode);
-}
index c3308211552cf14ee021365dfdc684d67f6b436f..4015b0c8ed30716c1a31441680b969618a20a0b0 100755 (executable)
@@ -10,7 +10,7 @@ if [ -z "$O" ]; then
        exit 1
 fi
 
-CMD="./pretty -format=ast.txt"
+CMD="./gofmt"
 TMP1=test_tmp1.go
 TMP2=test_tmp2.go
 TMP3=test_tmp3.go
@@ -34,7 +34,7 @@ apply1() {
        # the following have semantic errors: bug039.go | bug040.go
        test_errors.go | calc.go | method1.go | selftest1.go | func3.go | const2.go | \
        bug014.go | bug025.go | bug029.go | bug032.go | bug039.go | bug040.go | bug050.go |  bug068.go | \
-       bug088.go | bug083.go | bug106.go | bug121.go | bug125.go | bug126.go | bug132.go | bug133.go | bug134.go ) ;;
+       bug088.go | bug083.go | bug106.go | bug121.go | bug125.go | bug126.go | bug132.go | bug133.go | bug134.go | bug160.go ) ;;
        * ) $1 $2; count $F;;
        esac
 }
diff --git a/usr/gri/pretty/untab.go b/usr/gri/pretty/untab.go
deleted file mode 100644 (file)
index b18e08f..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2009 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.
-
-package main
-
-import (
-       "flag";
-       "fmt";
-       "io";
-       "os";
-       "tabwriter";
-)
-
-
-var (
-       tabwidth = flag.Int("tabwidth", 4, "tab width");
-       usetabs = flag.Bool("usetabs", false, "align with tabs instead of blanks");
-)
-
-
-func error(format string, params ...) {
-       fmt.Printf(format, params);
-       os.Exit(1);
-}
-
-
-func untab(name string, src *os.File, dst *tabwriter.Writer) {
-       n, err := io.Copy(src, dst);
-       if err != nil {
-               error("error while processing %s (%v)", name, err);
-       }
-       //dst.Flush();
-}
-
-
-func main() {
-       flag.Parse();
-       padchar := byte(' ');
-       if *usetabs {
-               padchar = '\t';
-       }
-       dst := tabwriter.NewWriter(os.Stdout, *tabwidth, 1, padchar, 0);
-       if flag.NArg() > 0 {
-               for i := 0; i < flag.NArg(); i++ {
-                       name := flag.Arg(i);
-                       src, err := os.Open(name, os.O_RDONLY, 0);
-                       if err != nil {
-                               error("could not open %s (%v)\n", name, err);
-                       }
-                       untab(name, src, dst);
-                       src.Close();  // ignore errors
-               }
-       } else {
-               // no files => use stdin
-               untab("/dev/stdin", os.Stdin, dst);
-       }
-}