// Program
export type Comment struct {
- pos int;
+ pos, tok int;
text string;
}
-export func NewComment(pos int, text string) *Comment {
+export func NewComment(pos, tok int, text string) *Comment {
c := new(Comment);
- c.pos, c.text = pos, text;
+ c.pos, c.tok, c.text = pos, tok, text;
return c;
}
func (P *Parser) Next() {
- for P.Next0(); P.tok == Scanner.COMMENT; P.Next0() {
- P.comments.Add(AST.NewComment(P.pos, P.val));
+ // TODO This is too expensive for every token - fix
+ for P.Next0();
+ P.tok == Scanner.COMMENT_WW ||
+ P.tok == Scanner.COMMENT_WB ||
+ P.tok == Scanner.COMMENT_BW ||
+ P.tok == Scanner.COMMENT_BB ;
+ P.Next0()
+ {
+ P.comments.Add(AST.NewComment(P.pos, P.tok, P.val));
}
}
assert(len(text) >= 3); // classification char + "//" or "/*"
// classify comment
- switch text[0] {
- case ' ':
- // not only white space before comment on the same line
- // - put into next cell if //-style comment
- // - preceed with a space if /*-style comment
- //print("[case a][", text[1 : len(text)], "]");
- if text[2] == '/' {
- P.buf.Tab();
- } else {
- P.buf.Print(" ");
- }
-
- /*
- case '\n':
- // comment starts at beginning of line
- // - reproduce exactly
- //print("[case b][", text[1 : len(text)], "]");
- if !P.buf.AtLineBegin() {
- P.buf.Newline();
- }
- */
+ switch comment.tok {
+ case Scanner.COMMENT_BB:
+ // black space before and after comment on the same line
+ // - print surrounded by blanks
+ P.buf.Print(" ");
+ P.buf.Print(text);
+ P.buf.Print(" ");
+
+ case Scanner.COMMENT_BW:
+ // only white space after comment on the same line
+ // - put into next cell
+ P.buf.Tab();
+ P.buf.Print(text);
- case '\n', '\t':
+ case Scanner.COMMENT_WW, Scanner.COMMENT_WB:
// only white space before comment on the same line
// - indent
- //print("[case c][", text[1 : len(text)], "]");
if !P.buf.EmptyLine() {
P.buf.Newline();
}
for i := P.indent; i > 0; i-- {
P.buf.Tab();
}
+ P.buf.Print(text);
default:
panic("UNREACHABLE");
}
- P.buf.Print(text[1 : len(text)]);
- if text[2] == '/' {
+ if text[1] == '/' {
// line comments must end in newline
// TODO should we set P.newl instead?
P.buf.Newline();
INT;
FLOAT;
STRING;
- COMMENT;
EOF;
+ COMMENT_BB;
+ COMMENT_BW;
+ COMMENT_WB;
+ COMMENT_WW;
+
ADD;
SUB;
MUL;
case INT: return "INT";
case FLOAT: return "FLOAT";
case STRING: return "STRING";
- case COMMENT: return "COMMENT";
case EOF: return "EOF";
+ case COMMENT_BB: return "COMMENT_BB";
+ case COMMENT_BW: return "COMMENT_BW";
+ case COMMENT_WB: return "COMMENT_WB";
+ case COMMENT_WW: return "COMMENT_WW";
+
case ADD: return "+";
case SUB: return "-";
case MUL: return "*";
}
-func (S *Scanner) SkipWhitespace() int {
- pos := -1; // no new line position yet
-
- if S.chpos == 0 {
- // file beginning is always start of a new line
- pos = 0;
- }
-
+// Returns true if a newline was seen, returns false otherwise.
+func (S *Scanner) SkipWhitespace() bool {
+ sawnl := S.chpos == 0; // file beginning is always start of a new line
for {
switch S.ch {
case '\t', '\r', ' ': // nothing to do
- case '\n': pos = S.pos; // remember start of new line
- default: goto exit;
+ case '\n': sawnl = true;
+ default: return sawnl;
}
S.Next();
}
-
-exit:
- return pos;
+ panic("UNREACHABLE");
+ return false;
}
-func (S *Scanner) ScanComment(nlpos int) string {
+func (S *Scanner) ScanComment(leading_ws bool) (tok int, val string) {
// first '/' already consumed
pos := S.chpos - 1;
exit:
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 {
// interpret ERROR and SYNC comments
oldpos := -1;
S.ErrorMsg(oldpos, "ERROR not found");
}
}
-
- if nlpos < 0 {
- // not only whitespace before comment on this line
- comment = " " + comment;
- } else if nlpos == pos {
- // comment starts at the beginning of the line
- comment = "\n" + comment;
+
+ if leading_ws {
+ if trailing_ws {
+ tok = COMMENT_WW;
+ } else {
+ tok = COMMENT_WB;
+ }
} else {
- // only whitespace before comment on this line
- comment = "\t" + comment;
+ if trailing_ws {
+ tok = COMMENT_BW;
+ } else {
+ tok = COMMENT_BB;
+ }
}
- return comment;
+
+ return tok, comment;
}
func (S *Scanner) Scan() (pos, tok int, val string) {
- nlpos := S.SkipWhitespace();
+ sawnl := S.SkipWhitespace();
pos, tok = S.chpos, ILLEGAL;
case '*': tok = S.Select2(MUL, MUL_ASSIGN);
case '/':
if S.ch == '/' || S.ch == '*' {
- tok, val = COMMENT, S.ScanComment(nlpos);
+ tok, val = S.ScanComment(sawnl);
} else {
tok = S.Select2(QUO, QUO_ASSIGN);
}