- snapshot of state before trying yet another, hopefully better working

way to integrate comments into the generated output
- various simplificatins and cleanups throughout

R=r
OCL=20062
CL=20062
This commit is contained in:
Robert Griesemer 2008-11-26 13:23:26 -08:00
parent d040d26863
commit 732b53a1fe
6 changed files with 179 additions and 155 deletions

View File

@ -180,14 +180,14 @@ export var BadDecl = NewDecl(0, Scanner.ILLEGAL, false);
// Program // Program
export type Comment struct { export type Comment struct {
pos, tok int; pos int;
text string; text string;
} }
export func NewComment(pos, tok int, text string) *Comment { export func NewComment(pos int, text string) *Comment {
c := new(Comment); c := new(Comment);
c.pos, c.tok, c.text = pos, tok, text; c.pos, c.text = pos, text;
return c; return c;
} }

View File

@ -122,7 +122,7 @@ export func Compile(src_file string, flags *Flags) (*AST.Program, int) {
err.Init(src_file, src, flags.columns); err.Init(src_file, src, flags.columns);
var scanner Scanner.Scanner; var scanner Scanner.Scanner;
scanner.Init(&err, src, flags.testmode); scanner.Init(&err, src, true, flags.testmode);
var tstream *<-chan *Scanner.Token; var tstream *<-chan *Scanner.Token;
if flags.tokenchan { if flags.tokenchan {

View File

@ -78,15 +78,8 @@ func (P *Parser) Next0() {
func (P *Parser) Next() { func (P *Parser) Next() {
// TODO This is too expensive for every token - fix for P.Next0(); P.tok == Scanner.COMMENT; P.Next0() {
for P.Next0(); P.comments.Push(AST.NewComment(P.pos, P.val));
P.tok == Scanner.COMMENT_WW ||
P.tok == Scanner.COMMENT_WB ||
P.tok == Scanner.COMMENT_BW ||
P.tok == Scanner.COMMENT_BB ;
P.Next0()
{
P.comments.Push(AST.NewComment(P.pos, P.tok, P.val));
} }
} }

View File

@ -49,8 +49,7 @@ func main() {
return; return;
} }
if !silent.BVal() && !flags.testmode { if !silent.BVal() && !flags.testmode {
var P Printer.Printer; Printer.Print(prog);
(&P).Program(prog);
} }
} }
} }

View File

@ -4,20 +4,20 @@
package Printer package Printer
import "array" import (
import Strings "strings" "os";
import Scanner "scanner" "array";
import AST "ast" "tabwriter";
import Flag "flag" "flag";
import Fmt "fmt" "fmt";
import IO "io" Scanner "scanner";
import OS "os" AST "ast";
import TabWriter "tabwriter" )
var ( var (
tabwidth = Flag.Int("tabwidth", 4, nil, "tab width"); tabwidth = flag.Int("tabwidth", 4, nil, "tab width");
usetabs = Flag.Bool("usetabs", false, nil, "align with tabs instead of blanks"); 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", false, nil, "enable printing of comments");
) )
@ -34,25 +34,60 @@ func assert(p bool) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Printer // Printer
export type Printer struct { type Printer struct {
writer IO.Write; // output
writer *tabwriter.Writer;
// comments
comments *array.Array;
cindex int;
cpos int;
// formatting control // formatting control
lastpos int; // pos after last string lastpos int; // pos after last string
level int; // true scope level level int; // true scope level
indent int; // indentation level indent int; // indentation level
semi bool; // pending ";" semi bool; // pending ";"
newl int; // pending "\n"'s newl int; // pending "\n"'s
// comments
clist *array.Array;
cindex int;
cpos int;
} }
func (P *Printer) Printf(fmt string, s ...) { func (P *Printer) NextComment() {
Fmt.fprintf(P.writer, fmt, s); P.cindex++;
if P.comments != nil && P.cindex < P.comments.Len() {
P.cpos = P.comments.At(P.cindex).(*AST.Comment).pos;
} else {
P.cpos = 1<<30; // infinite
}
}
func (P *Printer) Init(writer *tabwriter.Writer, comments *array.Array) {
// writer
padchar := byte(' ');
if usetabs.BVal() {
padchar = '\t';
}
P.writer = tabwriter.New(os.Stdout, int(tabwidth.IVal()), 1, padchar, true);
// comments
P.comments = comments;
P.cindex = -1;
P.NextComment();
// formatting control initialized correctly by default
}
// ----------------------------------------------------------------------------
// Printing support
func (P *Printer) Printf(format string, s ...) {
n, err := fmt.fprintf(P.writer, format, s);
if err != nil {
panic("print error - exiting");
}
P.lastpos += n;
} }
@ -60,6 +95,7 @@ func (P *Printer) String(pos int, s string) {
if pos == 0 { if pos == 0 {
pos = P.lastpos; // estimate pos = P.lastpos; // estimate
} }
P.lastpos = pos;
if P.semi && P.level > 0 { // no semicolons at level 0 if P.semi && P.level > 0 { // no semicolons at level 0
P.Printf(";"); P.Printf(";");
@ -67,66 +103,78 @@ func (P *Printer) String(pos int, s string) {
//print("--", pos, "[", s, "]\n"); //print("--", pos, "[", s, "]\n");
src_nl := 0;
at_line_begin := false; at_line_begin := false;
for comments.BVal() && P.cpos < pos { for comments.BVal() && P.cpos < pos {
//print("cc", P.cpos, "\n"); //print("cc", P.cpos, "\n");
// we have a comment that comes before s // we have a comment/newline that comes before s
comment := P.clist.At(P.cindex).(*AST.Comment); comment := P.comments.At(P.cindex).(*AST.Comment);
text := comment.text; ctext := comment.text;
assert(len(text) >= 3); // classification char + "//" or "/*"
// classify comment if ctext == "\n" {
switch comment.tok { // found a newline in src
case Scanner.COMMENT_BB: src_nl++;
// black space before and after comment on the same line
// - print surrounded by blanks
P.Printf(" %s ", text);
case Scanner.COMMENT_BW:
// only white space after comment on the same line
// - put into next cell
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.Printf("\t");
}
P.Printf("%s", text);
default:
panic("UNREACHABLE");
}
if text[1] == '/' {
// line comments must end in newline
// TODO should we set P.newl instead?
P.Printf("\n");
for i := P.indent; i > 0; i-- {
P.Printf("\t");
}
at_line_begin = true;
}
P.cindex++;
if P.cindex < P.clist.Len() {
P.cpos = P.clist.At(P.cindex).(*AST.Comment).pos;
} else { } else {
P.cpos = 1000000000; // infinite // classify comment
assert(len(ctext) >= 3); // classification char + "//" or "/*"
//-style comment
if src_nl > 0 || P.cpos == 0 {
// only white space before comment on this line
// or file starts with comment
// - indent
P.Printf("\n");
for i := P.indent; i > 0; i-- {
P.Printf("\t");
}
P.Printf("%s", ctext);
} else {
// black space before comment on this line
if ctext[1] == '/' {
//-style comment
// - put in next cell
P.Printf("\t%s", ctext);
} else {
/*-style comment */
// - print surrounded by blanks
P.Printf(" %s ", ctext);
}
}
if ctext[1] == '/' {
//-style comments must end in newline
if P.newl == 0 {
P.newl = 1;
}
/*
// TODO should we set P.newl instead?
P.Printf("\n");
for i := P.indent; i > 0; i-- {
P.Printf("\t");
}
at_line_begin = true;
*/
}
src_nl = 0;
} }
P.NextComment();
} }
if at_line_begin && P.newl > 0 { if at_line_begin && P.newl > 0 {
P.newl--; P.newl--;
} }
if src_nl > P.newl {
P.newl = src_nl;
}
if P.newl > 2 {
P.newl = 2;
}
if P.newl > 0 { if P.newl > 0 {
P.Printf("\n"); P.Printf("\n");
if P.newl > 1 { if P.newl > 1 {
@ -141,7 +189,6 @@ func (P *Printer) String(pos int, s string) {
P.Printf("%s", s); P.Printf("%s", s);
P.lastpos = pos + len(s);
P.semi, P.newl = false, 0; P.semi, P.newl = false, 0;
} }
@ -151,11 +198,6 @@ func (P *Printer) Blank() {
} }
func (P *Printer) Tab() {
P.String(0, "\t");
}
func (P *Printer) Token(pos int, tok int) { func (P *Printer) Token(pos int, tok int) {
P.String(pos, Scanner.TokenString(tok)); P.String(pos, Scanner.TokenString(tok));
} }
@ -225,7 +267,7 @@ func (P *Printer) Fields(list *array.Array) {
} else if prev == x.tok { } else if prev == x.tok {
P.String(0, ", "); P.String(0, ", ");
} else { } else {
P.Tab(); P.String(0, "\t");
} }
} }
P.Expr(x); P.Expr(x);
@ -565,7 +607,7 @@ func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) {
} }
if d.val != nil { if d.val != nil {
P.Tab(); P.String(0, "\t");
if d.tok != Scanner.IMPORT { if d.tok != Scanner.IMPORT {
P.String(0, "= "); P.String(0, "= ");
} }
@ -603,30 +645,37 @@ func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) {
// Program // Program
func (P *Printer) Program(p *AST.Program) { func (P *Printer) Program(p *AST.Program) {
// TODO should initialize all fields?
padchar := byte(' ');
if usetabs.BVal() {
padchar = '\t';
}
P.writer = TabWriter.New(OS.Stdout, int(tabwidth.IVal()), 1, padchar, true);
P.clist = p.comments;
P.cindex = 0;
if p.comments.Len() > 0 {
P.cpos = p.comments.At(0).(*AST.Comment).pos;
} else {
P.cpos = 1000000000; // infinite
}
// Print package
P.String(p.pos, "package "); P.String(p.pos, "package ");
P.Expr(p.ident); P.Expr(p.ident);
P.newl = 2; P.newl = 2;
for i := 0; i < p.decls.Len(); i++ { for i := 0; i < p.decls.Len(); i++ {
P.Declaration(p.decls.At(i), false); P.Declaration(p.decls.At(i), false);
} }
P.newl = 2; // TODO we should be able to do this with 1 instead of 2
// but we are loosing the last buffer flush in that case // end program with '\n'
P.newl = 1;
P.String(0, ""); // flush buffer }
// ----------------------------------------------------------------------------
// External interface
export func Print(prog *AST.Program) {
// setup
padchar := byte(' ');
if usetabs.BVal() {
padchar = '\t';
}
writer := tabwriter.New(os.Stdout, int(tabwidth.IVal()), 1, padchar, true);
var P Printer;
P.Init(writer, prog.comments);
P.Program(prog);
// flush
P.String(0, "");
err := P.writer.Flush();
if err != nil {
panic("print error - exiting");
}
} }

View File

@ -17,10 +17,7 @@ export const (
STRING; STRING;
EOF; EOF;
COMMENT_BB; COMMENT;
COMMENT_BW;
COMMENT_WB;
COMMENT_WW;
ADD; ADD;
SUB; SUB;
@ -124,10 +121,7 @@ export func TokenString(tok int) string {
case STRING: return "STRING"; case STRING: return "STRING";
case EOF: return "EOF"; case EOF: return "EOF";
case COMMENT_BB: return "COMMENT_BB"; case COMMENT: return "COMMENT";
case COMMENT_BW: return "COMMENT_BW";
case COMMENT_WB: return "COMMENT_WB";
case COMMENT_WW: return "COMMENT_WW";
case ADD: return "+"; case ADD: return "+";
case SUB: return "-"; case SUB: return "-";
@ -285,10 +279,12 @@ export type ErrorHandler interface {
export type Scanner struct { export type Scanner struct {
// setup
err ErrorHandler; err ErrorHandler;
src string; // source
scan_comments bool;
// scanning // scanning
src string; // source
pos int; // current reading position pos int; // current reading position
ch int; // one char look-ahead ch int; // one char look-ahead
chpos int; // position of ch chpos int; // position of ch
@ -341,10 +337,11 @@ func (S *Scanner) ExpectNoErrors() {
} }
func (S *Scanner) Init(err ErrorHandler, src string, testmode bool) { func (S *Scanner) Init(err ErrorHandler, src string, scan_comments, testmode bool) {
S.err = err; S.err = err;
S.src = src; S.src = src;
S.scan_comments = scan_comments;
S.pos = 0; S.pos = 0;
S.linepos = 0; S.linepos = 0;
@ -379,41 +376,43 @@ func (S *Scanner) Expect(ch int) {
} }
// Returns true if a newline was seen, returns false otherwise. func (S *Scanner) SkipWhitespace() {
func (S *Scanner) SkipWhitespace() bool {
sawnl := S.chpos == 0; // file beginning is always start of a new line
for { for {
switch S.ch { switch S.ch {
case '\t', '\r', ' ': // nothing to do case '\t', '\r', ' ':
case '\n': sawnl = true; // nothing to do
default: return sawnl; case '\n':
if S.scan_comments {
return;
}
default:
return;
} }
S.Next(); S.Next();
} }
panic("UNREACHABLE"); panic("UNREACHABLE");
return false;
} }
func (S *Scanner) ScanComment(leading_ws bool) (tok int, val string) { func (S *Scanner) ScanComment() string {
// first '/' already consumed // first '/' already consumed
pos := S.chpos - 1; pos := S.chpos - 1;
if S.ch == '/' { if S.ch == '/' {
// comment //-style comment
S.Next(); S.Next();
for S.ch >= 0 { for S.ch >= 0 {
S.Next(); S.Next();
if S.ch == '\n' { if S.ch == '\n' {
// '\n' terminates comment but we do not include // '\n' terminates comment but we do not include
// it in the comment (otherwise we cannot see the // it in the comment (otherwise we don't see the
// start of a newline in SkipWhitespace()). // start of a newline in SkipWhitespace()).
goto exit; goto exit;
} }
} }
} else { } else {
/* comment */ /*-style comment */
S.Expect('*'); S.Expect('*');
for S.ch >= 0 { for S.ch >= 0 {
ch := S.ch; ch := S.ch;
@ -430,12 +429,6 @@ func (S *Scanner) ScanComment(leading_ws bool) (tok int, val string) {
exit: exit:
comment := S.src[pos : S.chpos]; comment := S.src[pos : S.chpos];
// skip whitespace but stop at line end
for S.ch == '\t' || S.ch == '\r' || S.ch == ' ' {
S.Next();
}
trailing_ws := S.ch == '\n';
if S.testmode { if S.testmode {
// interpret ERROR and SYNC comments // interpret ERROR and SYNC comments
oldpos := -1; oldpos := -1;
@ -457,21 +450,7 @@ exit:
} }
} }
if leading_ws { return comment;
if trailing_ws {
tok = COMMENT_WW;
} else {
tok = COMMENT_WB;
}
} else {
if trailing_ws {
tok = COMMENT_BW;
} else {
tok = COMMENT_BB;
}
}
return tok, comment;
} }
@ -700,7 +679,7 @@ func (S *Scanner) Select4(tok0, tok1, ch2, tok2, tok3 int) int {
func (S *Scanner) Scan() (pos, tok int, val string) { func (S *Scanner) Scan() (pos, tok int, val string) {
sawnl := S.SkipWhitespace(); L: S.SkipWhitespace();
pos, tok = S.chpos, ILLEGAL; pos, tok = S.chpos, ILLEGAL;
@ -711,6 +690,7 @@ func (S *Scanner) Scan() (pos, tok int, val string) {
S.Next(); // always make progress S.Next(); // always make progress
switch ch { switch ch {
case -1: tok = EOF; case -1: tok = EOF;
case '\n': tok, val = COMMENT, "\n";
case '"': tok, val = STRING, S.ScanString(); case '"': tok, val = STRING, S.ScanString();
case '\'': tok, val = INT, S.ScanChar(); case '\'': tok, val = INT, S.ScanChar();
case '`': tok, val = STRING, S.ScanRawString(); case '`': tok, val = STRING, S.ScanRawString();
@ -740,7 +720,10 @@ func (S *Scanner) Scan() (pos, tok int, val string) {
case '*': tok = S.Select2(MUL, MUL_ASSIGN); case '*': tok = S.Select2(MUL, MUL_ASSIGN);
case '/': case '/':
if S.ch == '/' || S.ch == '*' { if S.ch == '/' || S.ch == '*' {
tok, val = S.ScanComment(sawnl); tok, val = COMMENT, S.ScanComment();
if !S.scan_comments {
goto L;
}
} else { } else {
tok = S.Select2(QUO, QUO_ASSIGN); tok = S.Select2(QUO, QUO_ASSIGN);
} }