diff options
author | Robert Griesemer <gri@golang.org> | 2008-11-07 18:30:58 -0800 |
---|---|---|
committer | Robert Griesemer <gri@golang.org> | 2008-11-07 18:30:58 -0800 |
commit | 31af41abde3f455cd8a79f9bed6944a0fd52799e (patch) | |
tree | 1f44e7b493eec775ca1de63063ed04f21adfa4b7 /usr | |
parent | dc89385174339cf91a5d5b1a8b24de2af3711cf1 (diff) | |
download | golang-31af41abde3f455cd8a79f9bed6944a0fd52799e.tar.gz |
- steps towards "flexible tab stops" simulation in pretty
printing output
- not yet enabled
R=r
OCL=18842
CL=18842
Diffstat (limited to 'usr')
-rw-r--r-- | usr/gri/pretty/ast.go | 12 | ||||
-rw-r--r-- | usr/gri/pretty/parser.go | 9 | ||||
-rw-r--r-- | usr/gri/pretty/printer.go | 164 | ||||
-rw-r--r-- | usr/gri/pretty/scanner.go | 20 |
4 files changed, 179 insertions, 26 deletions
diff --git a/usr/gri/pretty/ast.go b/usr/gri/pretty/ast.go index 91c61ec76..d566361bf 100644 --- a/usr/gri/pretty/ast.go +++ b/usr/gri/pretty/ast.go @@ -27,6 +27,11 @@ export type List struct { } +func (p *List) Init() { + p.a = new([] Any, 10) [0 : 0]; +} + + func (p *List) len() int { if p == nil { return 0; } return len(p.a); @@ -78,9 +83,14 @@ func (p *List) Pop() Any { } +func (p *List) Clear() { + p.a = p.a[0 : 0]; +} + + export func NewList() *List { p := new(List); - p.a = new([] Any, 10) [0 : 0]; + p.Init(); return p; } diff --git a/usr/gri/pretty/parser.go b/usr/gri/pretty/parser.go index d9e7c921e..d091529c0 100644 --- a/usr/gri/pretty/parser.go +++ b/usr/gri/pretty/parser.go @@ -77,8 +77,13 @@ func (P *Parser) Next0() { func (P *Parser) Next() { - for P.Next0(); P.tok == Scanner.COMMENT; P.Next0() { - P.comments.Add(AST.NewComment(P.pos, P.val)); + P.Next0(); + if P.tok == Scanner.COMMENT { + pos, s := P.pos, P.val; + for P.Next0(); P.tok == Scanner.COMMENT; P.Next0() { + s += P.val; + } + P.comments.Add(AST.NewComment(pos, s)); } } diff --git a/usr/gri/pretty/printer.go b/usr/gri/pretty/printer.go index cd20adb89..4c886ad56 100644 --- a/usr/gri/pretty/printer.go +++ b/usr/gri/pretty/printer.go @@ -7,11 +7,128 @@ package Printer import Strings "strings" import Scanner "scanner" import AST "ast" +import Flag "flag" +import Fmt "fmt" +var tabwith = Flag.Int("tabwidth", 4, nil, "tab width"); + + +// ---------------------------------------------------------------------------- +// Support + +func assert(p bool) { + if !p { + panic("assert failed"); + } +} + + +func PrintBlanks(n int) { + // TODO make this faster + for ; n > 0; n-- { + print(" "); + } +} + + +// ---------------------------------------------------------------------------- +// Implemententation of flexible tab stops. +// (http://nickgravgaard.com/elastictabstops/index.html) + +type Buffer struct { + lines AST.List; // a list of lines; and each line is a list of strings + widths AST.List; +} + + +func (b *Buffer) Newline() { + b.lines.Add(AST.NewList()); +} + + +func (b *Buffer) Init() { + b.lines.Init(); + b.widths.Init(); + b.Newline(); +} + + +func (b *Buffer) ComputeWidths() { + // iterate through all columns j + for j := 0; ; j++ { + width := -1; // initial column width + + // iterate through all lines i + for i := 0; i < b.lines.len(); i++ { + line := b.lines.at(i).(*AST.List); + if j < line.len() { + // the j.th column exists in this line + w := len(line.at(j).(string)); + if w > width { + width = w; + } + } + } + + if width >= 0 { + assert(b.widths.len() == j); + b.widths.Add(width); + } else { + // no column j - we are done + return; + } + } +} -export type Printer struct { - pos int; // actual output position +func (b *Buffer) Flush() { + b.ComputeWidths(); + + // print the lines + for i := 0; i < b.lines.len(); i++ { + line := b.lines.at(i).(*AST.List); + for j := 0; j < line.len(); j++ { + s := line.at(j).(string); + d := b.widths.at(j).(int) - len(s); + assert(d >= 0); + if d < int(tabwith.IVal()) { + d = int(tabwith.IVal()); + } + PrintBlanks(d); // +1 padding + print(s); + } + println(); + } + + b.lines.Clear(); + b.widths.Clear(); + b.Newline(); +} + + +func (b *Buffer) Indent(n int) { + line := b.lines.at(b.lines.len() - 1).(*AST.List); + for ; n > 0; n-- { + line.Add(""); + } +} + + +func (b *Buffer) Print(s string) { + i := b.lines.len() - 1; + line := b.lines.at(i).(*AST.List); + j := line.len() - 1; + if j < 0 { + line.Add(s); + } else { + line.set(j, line.at(j).(string) + s); + } +} + + +export type Printer struct { + buf Buffer; + // formatting control level int; // true scope level indent int; // indentation level @@ -25,24 +142,22 @@ export type Printer struct { } -// Bottleneck interface - all output goes through here. -func (P *Printer) print(s string) { - print(s); - // TODO do we need the code below? - // P.pos += Strings.utflen(s); -} - +const NEW_CODE = false; func (P *Printer) String(pos int, s string) { if P.semi && P.level > 0 { // no semicolons at level 0 - print(";"); + if NEW_CODE { + P.buf.Print(";"); + } else { + print(";"); + } } /* for pos > P.cpos { // we have a comment c := P.clist.at(P.cindex).(*AST.Comment); - if c.text[1] == '/' { + if len(c.text) > 1 && c.text[1] == '/' { print(" " + c.text); if P.newl <= 0 { P.newl = 1; // line comments must have a newline @@ -60,15 +175,30 @@ func (P *Printer) String(pos int, s string) { */ if P.newl > 0 { + if NEW_CODE { + P.buf.Flush(); + } for i := P.newl; i > 0; i-- { - print("\n"); + if NEW_CODE { + P.buf.Newline(); + } else { + print("\n"); + } } - for i := P.indent; i > 0; i-- { - print("\t"); + if NEW_CODE { + P.buf.Indent(P.indent); + } else { + for i := P.indent; i > 0; i-- { + print("\t"); + } } } - print(s); + if NEW_CODE { + P.buf.Print(s); + } else { + print(s); + } P.semi, P.newl = false, 0; } @@ -519,6 +649,8 @@ 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.clist = p.comments; P.cindex = 0; if p.comments.len() > 0 { @@ -527,6 +659,7 @@ func (P *Printer) Program(p *AST.Program) { P.cpos = 1000000000; // infinite } + // Print package P.String(p.pos, "package "); P.Expr(p.ident); P.newl = 2; @@ -534,5 +667,6 @@ func (P *Printer) Program(p *AST.Program) { P.Declaration(p.decls.at(i), false); } P.newl = 1; + P.String(0, ""); // flush } diff --git a/usr/gri/pretty/scanner.go b/usr/gri/pretty/scanner.go index 4b331b562..06428e9b6 100644 --- a/usr/gri/pretty/scanner.go +++ b/usr/gri/pretty/scanner.go @@ -109,7 +109,7 @@ export const ( export func TokenString(tok int) string { - switch (tok) { + switch tok { case ILLEGAL: return "ILLEGAL"; case IDENT: return "IDENT"; @@ -249,11 +249,6 @@ func init() { } -func is_whitespace(ch int) bool { - return ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t'; -} - - func is_letter(ch int) bool { return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 128 ; } @@ -524,12 +519,20 @@ func (S *Scanner) Expect(ch int) { func (S *Scanner) SkipWhitespace() { - for is_whitespace(S.ch) { + for S.ch == ' ' || S.ch == '\r' { S.Next(); } } +func (S *Scanner) ScanWhitespace() string { + // first char ('\n' or '\t', 1 byte) already consumed + pos := S.chpos - 1; + S.SkipWhitespace(); + return S.src[pos : S.chpos]; +} + + func (S *Scanner) ScanComment() string { // first '/' already consumed pos := S.chpos - 1; @@ -686,7 +689,7 @@ func (S *Scanner) ScanEscape(quote int) string { ch := S.ch; pos := S.chpos; S.Next(); - switch (ch) { + switch ch { case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\': return string(ch); @@ -825,6 +828,7 @@ func (S *Scanner) Scan() (pos, tok int, val string) { S.Next(); // always make progress switch ch { case -1: tok = EOF; + case '\n', '\t': tok, val = COMMENT, S.ScanWhitespace(); case '"': tok, val = STRING, S.ScanString(); case '\'': tok, val = INT, S.ScanChar(); case '`': tok, val = STRING, S.ScanRawString(); |