]> Cypherpunks repositories - gostls13.git/commitdiff
- steps towards "flexible tab stops" simulation in pretty
authorRobert Griesemer <gri@golang.org>
Sat, 8 Nov 2008 02:30:58 +0000 (18:30 -0800)
committerRobert Griesemer <gri@golang.org>
Sat, 8 Nov 2008 02:30:58 +0000 (18:30 -0800)
  printing output
- not yet enabled

R=r
OCL=18842
CL=18842

usr/gri/pretty/ast.go
usr/gri/pretty/parser.go
usr/gri/pretty/printer.go
usr/gri/pretty/scanner.go

index 91c61ec76562c196797784b1e82727ee52956fd1..d566361bf2e0af35a3dcb88500ad052a9174bf76 100644 (file)
@@ -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;
 }
 
index d9e7c921ee82bd8ce53dfed07d3e8906a7597cb9..d091529c07d7d5af6a327de8568fe00ed479c5f6 100644 (file)
@@ -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));
        }
 }
 
index cd20adb894a3d8021c20ed416cec06fbbc36ecd6..4c886ad56d43082c1980bf6d9d8b7512a8c06138 100644 (file)
@@ -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
 }
index 4b331b5620d08fead96c5965d4b0711871179b0e..06428e9b64c54e14dfd87feabcd659046e547fc0 100644 (file)
@@ -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();