if P.tok == Scanner.ELSE {
P.Next();
s1 := AST.BadStat;
- if P.sixg {
+ if P.tok == Scanner.IF {
+ s1 = P.ParseIfStat();
+ } else if P.sixg {
s1 = P.ParseStatement();
if s1 != nil {
// not the empty statement
}
s.post = s1;
}
- } else if P.tok == Scanner.IF {
- s1 = P.ParseIfStat();
} else {
s1 = AST.NewStat(P.pos, Scanner.LBRACE);
s1.block, s1.end = P.ParseBlock();
// ----------------------------------------------------------------------------
// Declarations
-func (P *Parser) ParseImportSpec() *AST.Decl {
+func (P *Parser) ParseImportSpec(pos int) *AST.Decl {
P.Trace("ImportSpec");
- d := AST.NewDecl(P.pos, Scanner.IMPORT, false);
+ d := AST.NewDecl(pos, Scanner.IMPORT, false);
if P.tok == Scanner.PERIOD {
P.Error(P.pos, `"import ." not yet handled properly`);
P.Next();
}
-func (P *Parser) ParseConstSpec(exported bool) *AST.Decl {
+func (P *Parser) ParseConstSpec(exported bool, pos int) *AST.Decl {
P.Trace("ConstSpec");
- d := AST.NewDecl(P.pos, Scanner.CONST, exported);
+ d := AST.NewDecl(pos, Scanner.CONST, exported);
d.ident = P.ParseIdent();
d.typ = P.TryType();
if P.tok == Scanner.ASSIGN {
}
-func (P *Parser) ParseTypeSpec(exported bool) *AST.Decl {
+func (P *Parser) ParseTypeSpec(exported bool, pos int) *AST.Decl {
P.Trace("TypeSpec");
- d := AST.NewDecl(P.pos, Scanner.TYPE, exported);
+ d := AST.NewDecl(pos, Scanner.TYPE, exported);
d.ident = P.ParseIdent();
d.typ = P.ParseType();
P.opt_semi = true;
}
-func (P *Parser) ParseVarSpec(exported bool) *AST.Decl {
+func (P *Parser) ParseVarSpec(exported bool, pos int) *AST.Decl {
P.Trace("VarSpec");
- d := AST.NewDecl(P.pos, Scanner.VAR, exported);
+ d := AST.NewDecl(pos, Scanner.VAR, exported);
d.ident = P.ParseIdentList();
if P.tok == Scanner.ASSIGN {
P.Next();
// TODO replace this by using function pointers derived from methods
-func (P *Parser) ParseSpec(exported bool, keyword int) *AST.Decl {
+func (P *Parser) ParseSpec(exported bool, pos int, keyword int) *AST.Decl {
switch keyword {
- case Scanner.IMPORT: return P.ParseImportSpec();
- case Scanner.CONST: return P.ParseConstSpec(exported);
- case Scanner.TYPE: return P.ParseTypeSpec(exported);
- case Scanner.VAR: return P.ParseVarSpec(exported);
+ case Scanner.IMPORT: return P.ParseImportSpec(pos);
+ case Scanner.CONST: return P.ParseConstSpec(exported, pos);
+ case Scanner.TYPE: return P.ParseTypeSpec(exported, pos);
+ case Scanner.VAR: return P.ParseVarSpec(exported, pos);
}
panic("UNREACHABLE");
return nil;
P.Trace("Decl");
d := AST.BadDecl;
+ pos := P.pos;
P.Expect(keyword);
if P.tok == Scanner.LPAREN {
P.Next();
- d = AST.NewDecl(P.pos, keyword, exported);
+ d = AST.NewDecl(pos, keyword, exported);
d.list = array.New(0);
for P.tok != Scanner.RPAREN && P.tok != Scanner.EOF {
- d.list.Push(P.ParseSpec(exported, keyword));
+ d.list.Push(P.ParseSpec(exported, pos, keyword));
if P.tok == Scanner.SEMICOLON {
P.Next();
} else {
P.opt_semi = true;
} else {
- d = P.ParseSpec(exported, keyword);
+ d = P.ParseSpec(exported, pos, keyword);
}
P.Ecart();
var (
debug = flag.Bool("debug", false, nil, "print debugging information");
- tabwidth = flag.Int("tabwidth", 4, nil, "tab width");
+ tabwidth = flag.Int("tabwidth", 8, nil, "tab width");
usetabs = flag.Bool("usetabs", true, nil, "align with tabs instead of blanks");
- comments = flag.Bool("comments", false, nil, "enable printing of comments");
+ comments = flag.Bool("comments", true, nil, "enable printing of comments");
)
// ----------------------------------------------------------------------------
// Printer
-// A variety of separators which are printed in a delayed fashion;
-// depending on the next token.
+// Separators are printed in a delayed fashion, depending on the next token.
const (
none = iota;
blank;
)
+// Formatting actions control formatting parameters during printing.
+const (
+ no_action = iota;
+ open_scope;
+ close_scope;
+)
+
+
type Printer struct {
// output
writer *tabwriter.Writer;
// comments
- comments *array.Array;
- cindex int;
- cpos int;
+ comments *array.Array; // the list of all comments
+ cindex int; // the current comments index
+ cpos int; // the position of the next comment
// current state
lastpos int; // pos after last string
- level int; // true scope level
- indentation int; // indentation level
+ level int; // scope level
+ indentation int; // indentation level (may be different from scope level)
- // formatting control
+ // formatting parameters
separator int; // pending separator
newlines int; // pending newlines
-}
-
-
-func (P *Printer) PendingComment(pos int) bool {
- return comments.BVal() && P.cpos < pos;
+
+ // formatting action
+ action int; // action executed on formatting parameters
+ lastaction int; // action for last string
}
P.cindex = -1;
P.NextComment();
- // formatting control initialized correctly by default
+ // formatting parameters & action initialized correctly by default
}
// --------------------------------
// interleave comments, if any
nlcount := 0;
- for P.PendingComment(pos) {
+ for comments.BVal() && P.cpos < pos {
// we have a comment/newline that comes before the string
comment := P.comments.At(P.cindex).(*AST.Comment);
ctext := comment.text;
if ctext == "\n" {
- // found a newline in src - count them
+ // found a newline in src - count it
nlcount++;
} else {
// black space before comment on this line
if ctext[1] == '/' {
//-style comment
- // - put in next cell
- if trailing_char != '\t' {
- P.Printf("\t");
+ // - 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.lastaction == open_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 */
P.NextComment();
}
+
+ // --------------------------------
+ // handle extra newlines
+ if nlcount > 0 {
+ P.newlines += nlcount - 1;
+ }
+
+ // --------------------------------
+ // interpret control
+ // (any pending separator or comment must be printed in previous state)
+ switch P.action {
+ case none:
+ case open_scope:
+ case close_scope:
+ P.indentation--;
+ default:
+ panic("UNREACHABLE");
+ }
// --------------------------------
// adjust formatting depending on state
}
P.Printf("%s", s);
+ // --------------------------------
+ // interpret control
+ switch P.action {
+ case none:
+ case open_scope:
+ P.level++;
+ P.indentation++;
+ //P.newlines = 1;
+ case close_scope:
+ P.level--;
+ default:
+ panic("UNREACHABLE");
+ }
+ P.lastaction = P.action;
+ P.action = none;
+
// --------------------------------
// done
P.lastpos = pos + len(s); // rough estimate
}
-func (P *Printer) OpenScope(pos int, paren string) {
- P.String(pos, paren);
- P.level++;
- P.indentation++;
- P.newlines = 1;
-}
-
-
-func (P *Printer) CloseScope(pos int, paren string) {
- P.indentation--;
- P.String(pos, paren);
- P.level--;
-}
-
-
func (P *Printer) Error(pos int, tok int, msg string) {
P.String(0, "<");
P.Token(pos, tok);
func (P *Printer) Fields(list *array.Array, end int) {
- P.OpenScope(0, "{");
+ P.action = open_scope;
+ P.String(0, "{");
+
if list != nil {
+ P.newlines = 1;
var prev int;
for i, n := 0, list.Len(); i < n; i++ {
x := list.At(i).(*AST.Expr);
}
P.newlines = 1;
}
- P.CloseScope(end, "}");
+
+ P.action = close_scope;
+ P.String(end, "}");
}
// list
// (don't use binary expression printing because of different spacing)
P.Expr(x.x);
- P.String(x.pos, ", ");
+ P.String(x.pos, ",");
+ P.separator = blank;
P.Expr(x.y);
case Scanner.PERIOD:
func (P *Printer) StatementList(list *array.Array) {
if list != nil {
+ P.newlines = 1;
for i, n := 0, list.Len(); i < n; i++ {
P.Stat(list.At(i).(*AST.Stat));
P.newlines = 1;
func (P *Printer) Block(pos int, list *array.Array, end int, indent bool) {
- P.OpenScope(pos, "{");
+ P.action = open_scope;
+ P.String(pos, "{");
if !indent {
P.indentation--;
}
P.indentation++;
}
P.separator = none;
- P.CloseScope(end, "}");
+ P.action = close_scope;
+ P.String(end, "}");
}
}
P.String(0, ":");
P.indentation++;
- P.newlines = 1;
P.StatementList(s.block);
P.indentation--;
P.newlines = 1;
func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) {
if !parenthesized {
if d.exported {
- P.String(d.pos, "export ");
+ P.String(d.pos, "export");
+ P.separator = blank;
}
P.Token(d.pos, d.tok);
P.separator = blank;
}
if d.tok != Scanner.FUNC && d.list != nil {
- P.OpenScope(0, "(");
- for i := 0; i < d.list.Len(); i++ {
- P.Declaration(d.list.At(i).(*AST.Decl), true);
- P.separator = semicolon;
+ P.action = open_scope;
+ P.String(0, "(");
+ if d.list.Len() > 0 {
P.newlines = 1;
+ for i := 0; i < d.list.Len(); i++ {
+ P.Declaration(d.list.At(i).(*AST.Decl), true);
+ P.separator = semicolon;
+ P.newlines = 1;
+ }
}
- P.CloseScope(d.end, ")");
+ P.action = close_scope;
+ P.String(d.end, ")");
} else {
if d.tok == Scanner.FUNC && d.typ.key != nil {
if d.typ != nil {
if d.tok != Scanner.FUNC {
+ // TODO would like to change this to a tab separator
+ // but currently this causes trouble when the type is
+ // a struct/interface (fields are indented wrongly)
P.separator = blank;
}
P.Type(d.typ);
if d.val != nil {
P.String(0, "\t");
if d.tok != Scanner.IMPORT {
- P.String(0, "= ");
+ P.String(0, "=");
+ P.separator = blank;
}
P.Expr(d.val);
}
// Program
func (P *Printer) Program(p *AST.Program) {
- P.String(p.pos, "package ");
+ P.String(p.pos, "package");
+ P.separator = blank;
P.Expr(p.ident);
P.newlines = 1;
for i := 0; i < p.decls.Len(); i++ {