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
}
-// 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
*/
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;
}
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 {
P.cpos = 1000000000; // infinite
}
+ // Print package
P.String(p.pos, "package ");
P.Expr(p.ident);
P.newl = 2;
P.Declaration(p.decls.at(i), false);
}
P.newl = 1;
+
P.String(0, ""); // flush
}
export func TokenString(tok int) string {
- switch (tok) {
+ switch tok {
case ILLEGAL: return "ILLEGAL";
case IDENT: return "IDENT";
}
-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 ;
}
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;
ch := S.ch;
pos := S.chpos;
S.Next();
- switch (ch) {
+ switch ch {
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\':
return string(ch);
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();