From 654bc2badc69ef4db50ed245db1f458af63b8d17 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 17 Nov 2008 19:58:52 -0800 Subject: [PATCH] - factored out tabwriter a separate writer filter (to be moved into std lib eventually) - rewrote tabwriter to use byte buffers instead of strings (byte buffers to be moved into stdlib eventually) - support for recent syntax changes - no space printed after function name and before function parameters - comments still disabled due to a known bug R=r OCL=19430 CL=19430 --- usr/gri/pretty/Makefile | 3 +- usr/gri/pretty/parser.go | 13 +- usr/gri/pretty/printer.go | 216 ++++----------------------- usr/gri/pretty/selftest2.go | 4 +- usr/gri/pretty/tabwriter.go | 285 ++++++++++++++++++++++++++++++++++++ 5 files changed, 326 insertions(+), 195 deletions(-) create mode 100644 usr/gri/pretty/tabwriter.go diff --git a/usr/gri/pretty/Makefile b/usr/gri/pretty/Makefile index 000d88f1da..462ab5f556 100644 --- a/usr/gri/pretty/Makefile +++ b/usr/gri/pretty/Makefile @@ -32,7 +32,8 @@ parser.6: scanner.6 ast.6 platform.6: utils.6 -printer.6: scanner.6 ast.6 +printer.6: scanner.6 ast.6 tabwriter.6 + %.6: %.go $(G) $(F) $< diff --git a/usr/gri/pretty/parser.go b/usr/gri/pretty/parser.go index 5cd454918b..9e6bc10c06 100644 --- a/usr/gri/pretty/parser.go +++ b/usr/gri/pretty/parser.go @@ -469,8 +469,14 @@ func (P *Parser) ParseFunctionType() *AST.Type { func (P *Parser) ParseMethodSpec(list *AST.List) { P.Trace("MethodDecl"); - list.Add(P.ParseIdent()); - list.Add(AST.NewTypeExpr(P.ParseFunctionType())); + list.Add(P.ParseIdentList()); + t := AST.BadType; + if P.sixg { + t = P.ParseType(); + } else { + t = P.ParseFunctionType(); + } + list.Add(AST.NewTypeExpr(t)); P.Ecart(); } @@ -1485,7 +1491,8 @@ func (P *Parser) ParseDeclaration() *AST.Decl { d := AST.BadDecl; exported := false; - if P.tok == Scanner.EXPORT { + // TODO don't use bool flag for export + if P.tok == Scanner.EXPORT || P.tok == Scanner.PACKAGE { if P.scope_lev == 0 { exported = true; } else { diff --git a/usr/gri/pretty/printer.go b/usr/gri/pretty/printer.go index 57b3124ba7..039199db46 100644 --- a/usr/gri/pretty/printer.go +++ b/usr/gri/pretty/printer.go @@ -9,6 +9,9 @@ import Scanner "scanner" import AST "ast" import Flag "flag" import Fmt "fmt" +import IO "io" +import OS "os" +import TabWriter "tabwriter" var tabwith = Flag.Int("tabwidth", 4, nil, "tab width"); var comments = Flag.Bool("comments", false, nil, "enable printing of comments"); @@ -24,180 +27,11 @@ func assert(p bool) { } -func PrintBlanks(n int) { - // TODO make this faster - for ; n > 0; n-- { - print(" "); - } -} - - -// ---------------------------------------------------------------------------- -// Implemententation of flexible tab stops. - -// Buffer is a representation for a list of lines consisting of -// cells. A new cell is added for each Tab() call, and a new line -// is added for each Newline() call. -// -// The lines are formatted and printed such that all cells in a column -// of adjacent cells have the same width (by adding padding). For more -// details see: http://nickgravgaard.com/elastictabstops/index.html . - -type Buffer struct { - cell string; // current cell (last cell in last line, not in lines yet) - lines AST.List; // list of lines; each line is a list of cells (strings) - widths AST.List; // list of column widths - (re-)used during formatting -} - - -// Implementation -// (Do not use these functions outside the Buffer implementation). - -func (b *Buffer) AddLine() { - b.lines.Add(AST.NewList()); -} - - -func (b *Buffer) Line(i int) *AST.List { - return b.lines.at(i).(*AST.List); -} - - -func (b *Buffer) LastLine() *AST.List { - return b.lines.last().(*AST.List); -} - - -// debugging support -func (b *Buffer) Dump() { - for i := 0; i < b.lines.len(); i++ { - line := b.Line(i); - print("(", i, ") "); - for j := 0; j < line.len(); j++ { - print("[", line.at(j).(string), "]"); - } - print("\n"); - } - print("\n"); -} - - -func (b *Buffer) PrintLines(line0, line1 int) { - for i := line0; i < line1; i++ { - line := b.Line(i); - for j := 0; j < line.len(); j++ { - s := line.at(j).(string); - print(s); - if j < b.widths.len() { - nsep := b.widths.at(j).(int) - len(s); - assert(nsep >= 0); - PrintBlanks(nsep); - } else { - assert(j == b.widths.len()); - } - } - println(); - } -} - - -func (b *Buffer) Format(line0, line1 int) { - column := b.widths.len(); - - last := line0; - for this := line0; this < line1; this++ { - line := b.Line(this); - - if column < line.len() - 1 { - // cell exists in this column - // (note that the last cell per line is ignored) - - // print unprinted lines until beginning of block - b.PrintLines(last, this); - last = this; - - // column block begin - width := int(tabwith.IVal()); // minimal width - for ; this < line1; this++ { - line := b.Line(this); - if column < line.len() - 1 { - // cell exists in this column - // update width - w := len(line.at(column).(string)) + 1; // 1 = minimum space between cells - if w > width { - width = w; - } - } else { - break - } - } - // column block end - - // format and print all columns to the right of this column - // (we know the widths of this column and all columns to the left) - b.widths.Add(width); - b.Format(last, this); - b.widths.Pop(); - last = this; - } - } - - // print unprinted lines until end - b.PrintLines(last, line1); -} - - -// Buffer interface -// (Use these functions to interact with Buffers). - -func (b *Buffer) Init() { - b.lines.Init(); - b.widths.Init(); - b.AddLine(); // the very first line -} - - -func (b *Buffer) EmptyLine() bool { - return b.LastLine().len() == 0 && len(b.cell) == 0; -} - - -func (b *Buffer) Tab() { - b.LastLine().Add(b.cell); - b.cell = ""; -} - - -func (b *Buffer) Newline() { - b.Tab(); // add last cell to current line - - if b.LastLine().len() == 1 { - // The current line has only one cell which does not have an impact - // on the formatting of the following lines (the last cell per line - // is ignored by Format), thus we can print the buffer contents. - assert(b.widths.len() == 0); - b.Format(0, b.lines.len()); - assert(b.widths.len() == 0); - - // reset the buffer - b.lines.Clear(); - } - - b.AddLine(); - assert(len(b.cell) == 0); -} - - -func (b *Buffer) Print(s string) { - b.cell += s; -} - - // ---------------------------------------------------------------------------- // Printer export type Printer struct { - buf Buffer; + writer IO.Write; // formatting control lastpos int; // pos after last string @@ -213,13 +47,18 @@ export type Printer struct { } +func (P *Printer) Printf(fmt string, s ...) { + Fmt.fprintf(P.writer, fmt, s); +} + + func (P *Printer) String(pos int, s string) { if pos == 0 { pos = P.lastpos; // estimate } if P.semi && P.level > 0 { // no semicolons at level 0 - P.buf.Print(";"); + P.Printf(";"); } //print("--", pos, "[", s, "]\n"); @@ -238,26 +77,25 @@ func (P *Printer) String(pos int, s string) { case Scanner.COMMENT_BB: // black space before and after comment on the same line // - print surrounded by blanks - P.buf.Print(" "); - P.buf.Print(text); - P.buf.Print(" "); + P.Printf(" %s ", text); case Scanner.COMMENT_BW: // only white space after comment on the same line // - put into next cell - P.buf.Tab(); - P.buf.Print(text); + P.Printf("\t%s", text); case Scanner.COMMENT_WW, Scanner.COMMENT_WB: // only white space before comment on the same line // - indent + /* if !P.buf.EmptyLine() { P.buf.Newline(); } + */ for i := P.indent; i > 0; i-- { - P.buf.Tab(); + P.Printf("\t"); } - P.buf.Print(text); + P.Printf("%s", text); default: panic("UNREACHABLE"); @@ -266,9 +104,9 @@ func (P *Printer) String(pos int, s string) { if text[1] == '/' { // line comments must end in newline // TODO should we set P.newl instead? - P.buf.Newline(); + P.Printf("\n"); for i := P.indent; i > 0; i-- { - P.buf.Tab(); + P.Printf("\t"); } at_line_begin = true; } @@ -286,19 +124,18 @@ func (P *Printer) String(pos int, s string) { } if P.newl > 0 { - P.buf.Newline(); + P.Printf("\n"); if P.newl > 1 { for i := P.newl; i > 1; i-- { - //P.buf.Flush(); - P.buf.Newline(); + P.Printf("\n"); } } for i := P.indent; i > 0; i-- { - P.buf.Tab(); + P.Printf("\t"); } } - P.buf.Print(s); + P.Printf("%s", s); P.lastpos = pos + len(s); P.semi, P.newl = false, 0; @@ -311,8 +148,7 @@ func (P *Printer) Blank() { func (P *Printer) Tab() { - P.String(0, ""); - P.buf.Tab(); + P.String(0, "\t"); } @@ -712,7 +548,9 @@ func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) { P.Expr(d.ident); if d.typ != nil { - P.Blank(); + if d.tok != Scanner.FUNC { + P.Blank(); + } P.Type(d.typ); } @@ -756,7 +594,7 @@ func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) { func (P *Printer) Program(p *AST.Program) { // TODO should initialize all fields? - P.buf.Init(); + P.writer = TabWriter.MakeTabWriter(OS.Stdout, 4); P.clist = p.comments; P.cindex = 0; diff --git a/usr/gri/pretty/selftest2.go b/usr/gri/pretty/selftest2.go index a6c49b675c..2b7b04be1b 100644 --- a/usr/gri/pretty/selftest2.go +++ b/usr/gri/pretty/selftest2.go @@ -22,14 +22,14 @@ var ( func main() { -// the prolog +// the prologue for i := 0; i <= 10 /* limit */; i++ { println(i); // the index println(i + 1); // the index + 1 println(i + 1000); // the index + 1000 println(); } -// the epilog +// the epilogue println("foo"); // foo println("foobar"); // foobar var x int; diff --git a/usr/gri/pretty/tabwriter.go b/usr/gri/pretty/tabwriter.go new file mode 100644 index 0000000000..fa3331da45 --- /dev/null +++ b/usr/gri/pretty/tabwriter.go @@ -0,0 +1,285 @@ +// 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 tabwriter + +import ( + OS "os"; + IO "io"; + Vector "vector"; +) + + +// ---------------------------------------------------------------------------- +// ByteArray + +type ByteArray struct { + a *[]byte; +} + + +func (b *ByteArray) Init(initial_size int) { + b.a = new([]byte, initial_size)[0 : 0]; +} + + +func (b *ByteArray) Clear() { + b.a = b.a[0 : 0]; +} + + +func (b *ByteArray) Len() int { + return len(b.a); +} + + +func (b *ByteArray) At(i int) byte { + return b.a[i]; +} + + +func (b *ByteArray) Set(i int, x byte) { + b.a[i] = x; +} + + +func (b *ByteArray) Slice(i, j int) *[]byte { + return b.a[i : j]; // BUG should really be &b.a[i : j] +} + + +func (b *ByteArray) Append(s *[]byte) { + a := b.a; + n := len(a); + m := n + len(s); + + if m > cap(a) { + n2 := 2*n; + if m > n2 { + n2 = m; + } + b := new([]byte, n2); + for i := 0; i < n; i++ { + b[i] = a[i]; + } + a = b; + } + + a = a[0 : m]; + for i := len(s) - 1; i >= 0; i-- { + a[n + i] = s[i]; + } + b.a = a; +} + + +// ---------------------------------------------------------------------------- +// Implemententation of flexible tab stops. + +// TabWriter is a representation for a list of lines consisting of +// cells. A new cell is added for each Tab() call, and a new line +// is added for each Newline() call. +// +// The lines are formatted and printed such that all cells in a column +// of adjacent cells have the same width (by adding padding). For more +// details see: http://nickgravgaard.com/elastictabstops/index.html . + +type TabWriter struct { + // configuration + writer IO.Write; + tabwidth int; + + // current state + buf ByteArray; // the collected text w/o tabs and newlines + width int; // width of last incomplete cell + lines Vector.Vector; // list of lines; each line is a list of cell widths + widths Vector.Vector; // list of column widths - (re-)used during formatting +} + + +func (b *TabWriter) AddLine() { + b.lines.Append(Vector.New()); +} + + +func (b *TabWriter) Init(writer IO.Write, tabwidth int) { + b.writer = writer; + b.tabwidth = tabwidth; + + b.buf.Init(1024); + b.lines.Init(); + b.widths.Init(); + b.AddLine(); // the very first line +} + + +func (b *TabWriter) Line(i int) *Vector.Vector { + return b.lines.At(i).(*Vector.Vector); +} + + +func (b *TabWriter) LastLine() *Vector.Vector { + return b.lines.At(b.lines.Len() - 1).(*Vector.Vector); +} + + +// debugging support +func (b *TabWriter) Dump() { + pos := 0; + for i := 0; i < b.lines.Len(); i++ { + line := b.Line(i); + print("(", i, ") "); + for j := 0; j < line.Len(); j++ { + w := line.At(j).(int); + print("[", string(b.buf.a[pos : pos + w]), "]"); + pos += w; + } + print("\n"); + } + print("\n"); +} + + +var Blanks = &[]byte{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '} +var Newline = &[]byte{'\n'} + +func (b *TabWriter) WriteBlanks(n int) { + for n >= len(Blanks) { + m, err := b.writer.Write(Blanks); + n -= len(Blanks); + } + m, err := b.writer.Write(Blanks[0 : n]); +} + + +func (b *TabWriter) PrintLines(pos int, line0, line1 int) int { + for i := line0; i < line1; i++ { + line := b.Line(i); + for j := 0; j < line.Len(); j++ { + w := line.At(j).(int); + m, err := b.writer.Write(b.buf.a[pos : pos + w]); + if m != w { + panic(); + } + pos += w; + if j < b.widths.Len() { + b.WriteBlanks(b.widths.At(j).(int) - w); + } + } + m, err := b.writer.Write(Newline); + } + return pos; +} + + +func (b *TabWriter) Format(pos int, line0, line1 int) int { + column := b.widths.Len(); + + last := line0; + for this := line0; this < line1; this++ { + line := b.Line(this); + + if column < line.Len() - 1 { + // cell exists in this column + // (note that the last cell per line is ignored) + + // print unprinted lines until beginning of block + pos = b.PrintLines(pos, last, this); + last = this; + + // column block begin + width := b.tabwidth; // minimal width + for ; this < line1; this++ { + line = b.Line(this); + if column < line.Len() - 1 { + // cell exists in this column + // update width + w := line.At(column).(int) + 1; // 1 = minimum space between cells + if w > width { + width = w; + } + } else { + break + } + } + // column block end + + // format and print all columns to the right of this column + // (we know the widths of this column and all columns to the left) + b.widths.Append(width); + pos = b.Format(pos, last, this); + b.widths.Remove(b.widths.Len() - 1); + last = this; + } + } + + // print unprinted lines until end + return b.PrintLines(pos, last, line1); +} + + +func (b *TabWriter) EmptyLine() bool { + return b.LastLine().Len() == 0 && b.width == 0; +} + + +func (b *TabWriter) Tab() { + b.LastLine().Append(b.width); + b.width = 0; +} + + +func (b *TabWriter) Newline() { + b.Tab(); // add last cell to current line + + if b.LastLine().Len() == 1 { + // The current line has only one cell which does not have an impact + // on the formatting of the following lines (the last cell per line + // is ignored by Format), thus we can print the TabWriter contents. + if b.widths.Len() != 0 { + panic(); + } + //b.Dump(); + b.Format(0, 0, b.lines.Len()); + if b.widths.Len() != 0 { + panic(); + } + + // reset the TabWriter + b.width = 0; + b.buf.Clear(); + b.lines.Reset(); + } + + b.AddLine(); +} + + +func (b *TabWriter) Write(buf *[]byte) (i int, err *OS.Error) { + i0, n := 0, len(buf); + for i = 0; i < n; i++ { + switch buf[i] { + case '\t': + b.width += i - i0; + b.buf.Append(buf[i0 : i]); + i0 = i + 1; // don't append '\t' + b.Tab(); + case '\n': + b.width += i - i0; + b.buf.Append(buf[i0 : i]); + i0 = i + 1; // don't append '\n' + b.Newline(); + } + } + b.width += n - i0; + b.buf.Append(buf[i0 : n]); + return i, nil; +} + + +export func MakeTabWriter(writer IO.Write, tabwidth int) IO.Write { + b := new(TabWriter); + b.Init(writer, tabwidth); + return b; +}