- various parser fixes to match updated spec (&&, &^=, label decls, const decls)
- using html template for directory and error page in doc server
- show compile errors inplace in the source
- cleanups
R=rsc
OCL=26287
CL=26287
};
Field struct {
- Idents []*Ident;
+ Names []*Ident;
Typ Expr;
Tag Expr; // nil = no tag
Comment CommentGroup;
Loc scanner.Location;
};
- LabelDecl struct {
+ LabeledStat struct {
Loc scanner.Location; // location of ":"
Label *Ident;
+ Stat Stat;
};
DeclarationStat struct {
type StatVisitor interface {
DoBadStat(s *BadStat);
- DoLabelDecl(s *LabelDecl);
+ DoLabeledStat(s *LabeledStat);
DoDeclarationStat(s *DeclarationStat);
DoExpressionStat(s *ExpressionStat);
DoCompositeStat(s *CompositeStat);
func (s *BadStat) Visit(v StatVisitor) { v.DoBadStat(s); }
-func (s *LabelDecl) Visit(v StatVisitor) { v.DoLabelDecl(s); }
+func (s *LabeledStat) Visit(v StatVisitor) { v.DoLabeledStat(s); }
func (s *DeclarationStat) Visit(v StatVisitor) { v.DoDeclarationStat(s); }
func (s *ExpressionStat) Visit(v StatVisitor) { v.DoExpressionStat(s); }
func (s *CompositeStat) Visit(v StatVisitor) { v.DoCompositeStat(s); }
ImportDecl struct {
Loc scanner.Location; // if > 0: position of "import"
- Ident *Ident;
+ Name *Ident;
Path Expr;
};
ConstDecl struct {
Loc scanner.Location; // if > 0: position of "const"
- Idents []*Ident;
+ Names []*Ident;
Typ Expr;
Vals Expr;
Comment CommentGroup;
TypeDecl struct {
Loc scanner.Location; // if > 0: position of "type"
- Ident *Ident;
+ Name *Ident;
Typ Expr;
Comment CommentGroup;
};
VarDecl struct {
Loc scanner.Location; // if > 0: position of "var"
- Idents []*Ident;
+ Names []*Ident;
Typ Expr;
Vals Expr;
Comment CommentGroup;
FuncDecl struct {
Loc scanner.Location; // location of "func"
Recv *Field;
- Ident *Ident;
+ Name *Ident;
Sig *Signature;
Body *Block;
Comment CommentGroup;
// TODO rename to Package
type Program struct {
Loc scanner.Location; // tok is token.PACKAGE
- Ident *Ident;
+ Name *Ident;
Decls []Decl;
Comment CommentGroup;
Comments []CommentGroup;
"utf8";
"fmt";
"os";
- Utils "utils";
- Platform "platform";
+ "utils";
+ "platform";
"scanner";
Parser "parser";
- AST "ast";
- TypeChecker "typechecker";
+ "ast";
+ "typechecker";
+ "sort";
)
}
+type Error struct {
+ Loc scanner.Location;
+ Msg string;
+}
+
+
+type ErrorList []Error
+
+func (list ErrorList) Len() int { return len(list); }
+func (list ErrorList) Less(i, j int) bool { return list[i].Loc.Pos < list[j].Loc.Pos; }
+func (list ErrorList) Swap(i, j int) { list[i], list[j] = list[j], list[i]; }
+
+
type errorHandler struct {
filename string;
src []byte;
columns bool;
errline int;
- nerrors int;
+ errors vector.Vector;
}
h.filename = filename;
h.src = src;
h.columns = columns;
+ h.errors.Init(0);
}
-/*
-// Compute (line, column) information for a given source position.
-func (h *errorHandler) LineCol(pos int) (line, col int) {
- line = 1;
- lpos := 0;
-
- src := h.src;
- if pos > len(src) {
- pos = len(src);
- }
-
- for i := 0; i < pos; i++ {
- if src[i] == '\n' {
- line++;
- lpos = i;
- }
+func (h *errorHandler) Error(loc scanner.Location, msg string) {
+ // only report errors that are on a new line
+ // in the hope to avoid most follow-up errors
+ if loc.Line == h.errline {
+ return;
}
- return line, utf8.RuneCount(src[lpos : pos]);
-}
-*/
-
-
-func (h *errorHandler) ErrorMsg(loc scanner.Location, msg string) {
+ // report error
fmt.Printf("%s:%d:", h.filename, loc.Line);
if h.columns {
fmt.Printf("%d:", loc.Col);
}
fmt.Printf(" %s\n", msg);
+ // collect the error
+ h.errors.Push(Error{loc, msg});
h.errline = loc.Line;
-
- h.nerrors++;
- if h.nerrors >= 10 {
- sys.Exit(1);
- }
}
-func (h *errorHandler) Error(loc scanner.Location, msg string) {
- // only report errors that are on a new line
- // in the hope to avoid most follow-up errors
- if loc.Line != h.errline {
- h.ErrorMsg(loc, msg);
- }
-}
-
-
-func Compile(src_file string, flags *Flags) (*AST.Program, int) {
+func Compile(src_file string, flags *Flags) (*ast.Program, ErrorList) {
src, ok := Platform.ReadSourceFile(src_file);
if !ok {
print("cannot open ", src_file, "\n");
- return nil, 1;
+ return nil, nil;
}
var err errorHandler;
prog := parser.Parse(Parser.ParseEntirePackage);
- if err.nerrors == 0 {
+ if err.errors.Len() == 0 {
TypeChecker.CheckProgram(&err, prog);
}
+
+ // convert error list and sort it
+ errors := make(ErrorList, err.errors.Len());
+ for i := 0; i < err.errors.Len(); i++ {
+ errors[i] = err.errors.At(i).(Error);
+ }
+ sort.Sort(errors);
- return prog, err.nerrors;
+ return prog, errors;
}
}
/*
-func printDep(localset map [string] bool, wset *vector.Vector, decl AST.Decl2) {
- src := decl.Val.(*AST.BasicLit).Val;
+func printDep(localset map [string] bool, wset *vector.Vector, decl ast.Decl2) {
+ src := decl.Val.(*ast.BasicLit).Val;
src = src[1 : len(src) - 1]; // strip "'s
// ignore files when they are seen a 2nd time
if !found {
globalset[src_file] = true;
- prog, nerrors := Compile(src_file, flags);
- if nerrors > 0 {
+ prog, errors := Compile(src_file, flags);
+ if errors == nil || len(errors) > 0 {
return;
}
printDep(localset, wset, decl);
} else {
for j := 0; j < decl.List.Len(); j++ {
- printDep(localset, wset, decl.List.At(j).(*AST.Decl));
+ printDep(localset, wset, decl.List.At(j).(*ast.Decl));
}
}
*/
--- /dev/null
+
+<h1><!--PATH--></h1>
+
+<h2>Directories</h2>
+<!--DIRECTORIES-->
+
+<h2>Go files</h2>
+<!--GO FILES-->
+
+<h2>Other files</h2>
+<font color=grey>
+<!--OTHER FILES-->
+</font>
+
+</div> <!-- content -->
+</body>
+</html>
--- /dev/null
+
+<font color=red>THIS SECTION IS CURRENTLY UNDER CONSTRUCTION</font>
+
+<h1>Compilation errors in <!--FILE_NAME--></h1>
+
+<pre>
+<!--ERRORS-->
+</pre>
+
+</div> <!-- content -->
+</body>
+</html>
"os";
"sort";
"log";
+ "template";
- Utils "utils";
- Platform "platform";
- Compilation "compilation";
- Printer "printer";
+ "utils";
+ "platform";
+ "compilation";
+ "printer";
)
func isGoFile(dir *os.Dir) bool {
- ext := ".go"; // TODO 6g bug - should be const
+ const ext = ".go";
return dir.IsRegular() && Utils.Contains(dir.Name, ext, len(dir.Name) - len(ext));
}
func printLink(c *http.Conn, path, name string) {
- fmt.Fprintf(c, "<a href=\"%s\">%s</a><br>\n", path + name, name);
+ fmt.Fprintf(c, "<a href=\"%s\">%s</a><br />\n", path + name, name);
}
+var dir_template = template.NewTemplateOrDie("dir_template.html");
+
func serveDir(c *http.Conn, dirname string) {
fd, err1 := os.Open(*root + dirname, os.O_RDONLY, 0);
if err1 != nil {
c.SetHeader("content-type", "text/html; charset=utf-8");
path := dirname + "/";
- fmt.Fprintf(c, "<b>%s</b>\n", path);
// Print contents in 3 sections: directories, go files, everything else
- // 1) directories
- fmt.Fprintln(c, "<p>");
- for i, entry := range list {
- if entry.IsDirectory() {
- printLink(c, path, entry.Name);
+ // TODO handle Apply errors
+ dir_template.Apply(c, "<!--", template.Substitution {
+ "PATH-->" : func() {
+ fmt.Fprintf(c, "%s", path);
+ },
+
+ "DIRECTORIES-->" : func() {
+ for i, entry := range list {
+ if entry.IsDirectory() {
+ printLink(c, path, entry.Name);
+ }
+ }
+ },
+
+ "GO FILES-->" : func() {
+ for i, entry := range list {
+ if isGoFile(&entry) {
+ printLink(c, path, entry.Name);
+ }
+ }
+ },
+
+ "OTHER FILES-->" : func() {
+ for i, entry := range list {
+ if !entry.IsDirectory() && !isGoFile(&entry) {
+ fmt.Fprintf(c, "%s<br />\n", entry.Name);
+ }
+ }
}
- }
+ });
+}
- // 2) .go files
- fmt.Fprintln(c, "<p>");
- for i, entry := range list {
- if isGoFile(&entry) {
- printLink(c, path, entry.Name);
- }
- }
- // 3) everything else
- fmt.Fprintln(c, "<p>");
- for i, entry := range list {
- if !entry.IsDirectory() && !isGoFile(&entry) {
- fmt.Fprintf(c, "<font color=grey>%s</font><br>\n", entry.Name);
+var error_template = template.NewTemplateOrDie("error_template.html");
+
+func printErrors(c *http.Conn, filename string, errors Compilation.ErrorList) {
+ // TODO factor code - shouldn't do this here and in Compilation
+ src, ok := Platform.ReadSourceFile(*root + filename);
+
+ // TODO handle Apply errors
+ error_template.Apply(c, "<!--", template.Substitution {
+ "FILE_NAME-->" : func() {
+ fmt.Fprintf(c, "%s", filename);
+ },
+
+ "ERRORS-->" : func () {
+ if ok == false /* 6g bug139 */ {
+ fmt.Fprintf(c, "could not read file %s\n", *root + filename);
+ return;
+ }
+ pos := 0;
+ for i, e := range errors {
+ if 0 <= e.Loc.Pos && e.Loc.Pos <= len(src) {
+ // TODO handle Write errors
+ c.Write(src[pos : e.Loc.Pos]);
+ // TODO this should be done using a .css file
+ fmt.Fprintf(c, "<b><font color=red>%s >>></font></b>", e.Msg);
+ pos = e.Loc.Pos;
+ } else {
+ log.Stdoutf("error position %d out of bounds (len = %d)", e.Loc.Pos, len(src));
+ }
+ }
+ // TODO handle Write errors
+ c.Write(src[pos : len(src)]);
}
- }
+ });
}
func serveFile(c *http.Conn, filename string) {
var flags Compilation.Flags;
- prog, nerrors := Compilation.Compile(*root + filename, &flags);
- if nerrors > 0 {
+ prog, errors := Compilation.Compile(*root + filename, &flags);
+ if errors == nil {
c.WriteHeader(http.StatusNotFound);
- fmt.Fprintf(c, "Error: File has compilation errors (%s)\n", filename);
+ fmt.Fprintf(c, "Error: could not read file (%s)\n", filename);
+ return;
+ }
+
+ if len(errors) > 0 {
+ c.SetHeader("content-type", "text/html; charset=utf-8");
+ printErrors(c, filename, errors);
return;
}
*root = Utils.SanitizePath(*root);
dir, err1 := os.Stat(*root);
if err1 != nil || !dir.IsDirectory() {
- log.Exitf("root not found or not a directory: ", *root);
+ log.Exitf("root not found or not a directory: %s", *root);
}
if *verbose {
// ----------------------------------------------------------------------------
// Statements
-func (P *Parser) parseSimpleStat(range_ok bool) ast.Stat {
+const /* mode */ (
+ label_ok = 1 << iota;
+ range_ok;
+)
+
+func (P *Parser) parseSimpleStat(mode int) ast.Stat {
if P.trace {
defer un(trace(P, "SimpleStat"));
}
switch P.tok {
case token.COLON:
- // label declaration
+ // labeled statement
loc := P.loc;
P.next(); // consume ":"
P.opt_semi = true;
- if ast.ExprLen(x) == 1 {
+ if mode & label_ok != 0 && ast.ExprLen(x) == 1 {
if label, is_ident := x.(*ast.Ident); is_ident {
- return &ast.LabelDecl{loc, label};
+ return &ast.LabeledStat{loc, label, P.parseStatement()};
}
}
P.error(x.Loc(), "illegal label declaration");
loc, tok := P.loc, P.tok;
P.next();
var y ast.Expr;
- if range_ok && P.tok == token.RANGE {
+ if mode & range_ok != 0 && P.tok == token.RANGE {
range_loc := P.loc;
P.next();
y = &ast.UnaryExpr{range_loc, token.RANGE, P.parseExpression(1)};
prev_lev := P.expr_lev;
P.expr_lev = -1;
if P.tok != token.SEMICOLON {
- init = P.parseSimpleStat(isForStat);
+ mode := 0;
+ if isForStat {
+ mode = range_ok;
+ }
+ init = P.parseSimpleStat(mode);
// TODO check for range clause and exit if found
}
if P.tok == token.SEMICOLON {
if isForStat {
P.expect(token.SEMICOLON);
if P.tok != token.LBRACE {
- post = P.parseSimpleStat(false);
+ post = P.parseSimpleStat(0);
}
}
} else {
switch P.tok {
case token.CONST, token.TYPE, token.VAR:
return &ast.DeclarationStat{P.parseDeclaration()};
- case token.FUNC:
- // for now we do not allow local function declarations,
- // instead we assume this starts a function literal
- fallthrough;
case
- // only the tokens that are legal top-level expression starts
- token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING, token.LPAREN, // operand
+ // tokens that may start a top-level expression
+ token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operand
token.LBRACK, token.STRUCT, // composite type
- token.MUL, token.AND, token.ARROW: // unary
- return P.parseSimpleStat(false);
+ token.MUL, token.AND, token.ARROW: // unary operators
+ return P.parseSimpleStat(label_ok);
case token.GO, token.DEFER:
return P.parseInvocationStat(P.tok);
case token.RETURN:
return P.parseSwitchStat();
case token.SELECT:
return P.parseSelectStat();
- case token.SEMICOLON:
+ case token.SEMICOLON, token.RBRACE:
// don't consume the ";", it is the separator following the empty statement
return &ast.EmptyStat{P.loc};
}
idents := P.parseIdentList2(nil);
typ := P.tryType();
-
var vals ast.Expr;
- if P.tok == token.ASSIGN {
- P.next();
+ if typ != nil || P.tok == token.ASSIGN {
+ P.expect(token.ASSIGN);
vals = P.parseExpressionList();
}
}
idents := P.parseIdentList2(nil);
- var typ ast.Expr;
+ typ := P.tryType();
var vals ast.Expr;
- if P.tok == token.ASSIGN {
- P.next();
+ if typ == nil || P.tok == token.ASSIGN {
+ P.expect(token.ASSIGN);
vals = P.parseExpressionList();
- } else {
- typ = P.parseVarType();
- if P.tok == token.ASSIGN {
- P.next();
- vals = P.parseExpressionList();
- }
}
return &ast.VarDecl{loc, idents, typ, vals, comment};
loc := P.loc;
P.expect(token.PACKAGE);
name := P.parseIdent();
+ if P.tok == token.SEMICOLON {
+ // common error
+ P.error(P.loc, "extra semicolon");
+ P.next();
+ }
+
+
var decls []ast.Decl;
-
if mode <= ParseImportDeclsOnly {
// import decls
list := vector.New(0);
Compilation.ComputeDeps(src_file, &flags);
} else {
- prog, nerrors := Compilation.Compile(src_file, &flags);
- if nerrors > 0 {
+ prog, errors := Compilation.Compile(src_file, &flags);
+ if errors == nil || len(errors) > 0 {
sys.Exit(1);
}
if !*silent {
if i > 0 {
P.separator = comma;
}
- n := P.Idents(par.Idents, true);
+ n := P.Idents(par.Names, true);
if n > 0 {
P.separator = blank
};
if sig.Result != nil {
P.separator = blank;
- if len(sig.Result) == 1 && sig.Result[0].Idents == nil {
+ if len(sig.Result) == 1 && sig.Result[0].Names == nil {
// single anonymous result
// => no parentheses needed unless it's a function type
fld := sig.Result[0];
P.separator = semicolon;
P.newlines = 1;
}
- n := P.Idents(fld.Idents, P.full);
+ n := P.Idents(fld.Names, P.full);
if n > 0 {
// at least one identifier
P.separator = tab
};
- if n > 0 || len(fld.Idents) == 0 {
+ if n > 0 || len(fld.Names) == 0 {
// at least one identifier or anonymous field
if is_interface {
if ftyp, is_ftyp := fld.Typ.(*ast.FunctionType); is_ftyp {
}
-func (P *Printer) DoLabelDecl(s *ast.LabelDecl) {
+func (P *Printer) DoLabeledStat(s *ast.LabeledStat) {
P.indentation--;
P.Expr(s.Label);
P.Token(s.Loc, token.COLON);
- // TODO not quite correct:
- // - we must not print this optional semicolon, as it may invalidate code.
- // - this will change once the AST reflects the LabelStatement change
- P.opt_semi = true;
P.indentation++;
+ // TODO be more clever if s.Stat is a labeled stat as well
+ P.separator = tab;
+ P.Stat(s.Stat);
}
P.Token(d.Loc, token.IMPORT);
P.separator = blank;
}
- if d.Ident != nil {
- P.Expr(d.Ident);
+ if d.Name != nil {
+ P.Expr(d.Name);
} else {
P.String(d.Path.Loc(), ""); // flush pending ';' separator/newlines
}
P.Token(d.Loc, token.CONST);
P.separator = blank;
}
- P.Idents(d.Idents, P.full);
+ P.Idents(d.Names, P.full);
if d.Typ != nil {
P.separator = blank; // TODO switch to tab? (indentation problem with structs)
P.Expr(d.Typ);
P.Token(d.Loc, token.TYPE);
P.separator = blank;
}
- P.Expr(d.Ident);
+ P.Expr(d.Name);
P.separator = blank; // TODO switch to tab? (but indentation problem with structs)
P.Expr(d.Typ);
P.newlines = 2;
P.Token(d.Loc, token.VAR);
P.separator = blank;
}
- P.Idents(d.Idents, P.full);
+ P.Idents(d.Names, P.full);
if d.Typ != nil {
P.separator = blank; // TODO switch to tab? (indentation problem with structs)
P.Expr(d.Typ);
if recv := d.Recv; recv != nil {
// method: print receiver
P.Token(noloc, token.LPAREN);
- if len(recv.Idents) > 0 {
- P.Expr(recv.Idents[0]);
+ if len(recv.Names) > 0 {
+ P.Expr(recv.Names[0]);
P.separator = blank;
}
P.Expr(recv.Typ);
P.Token(noloc, token.RPAREN);
P.separator = blank;
}
- P.Expr(d.Ident);
+ P.Expr(d.Name);
P.Signature(d.Sig);
if P.full && d.Body != nil {
P.separator = blank;
for i := 0; i < len(p.Decls); i++ {
switch d := p.Decls[i].(type) {
case *ast.ConstDecl:
- if hasExportedNames(d.Idents) {
+ if hasExportedNames(d.Names) {
P.Printf("<h2>Constants</h2>\n");
P.Printf("<p><pre>");
P.DoConstDecl(d);
}
case *ast.TypeDecl:
- if isExported(d.Ident) {
- P.Printf("<h2>type %s</h2>\n", d.Ident.Str);
+ if isExported(d.Name) {
+ P.Printf("<h2>type %s</h2>\n", d.Name.Str);
P.Printf("<p><pre>");
P.DoTypeDecl(d);
P.String(noloc, "");
}
case *ast.VarDecl:
- if hasExportedNames(d.Idents) {
+ if hasExportedNames(d.Names) {
P.Printf("<h2>Variables</h2>\n");
P.Printf("<p><pre>");
P.DoVarDecl(d);
}
case *ast.FuncDecl:
- if isExported(d.Ident) {
+ if isExported(d.Name) {
if d.Recv != nil {
P.Printf("<h3>func (");
P.Expr(d.Recv.Typ);
- P.Printf(") %s</h3>\n", d.Ident.Str);
+ P.Printf(") %s</h3>\n", d.Name.Str);
} else {
- P.Printf("<h2>func %s</h2>\n", d.Ident.Str);
+ P.Printf("<h2>func %s</h2>\n", d.Name.Str);
}
P.Printf("<p><code>");
P.DoFuncDecl(d);
P.full = true;
P.Token(p.Loc, token.PACKAGE);
P.separator = blank;
- P.Expr(p.Ident);
+ P.Expr(p.Name);
P.newlines = 1;
for i := 0; i < len(p.Decls); i++ {
P.Decl(p.Decls[i]);
// ----------------------------------------------------------------------------
// External interface
-var templ template.Template;
-
-func init() {
- templ.Init("template.html");
-}
+var templ = template.NewTemplateOrDie("template.html");
func Print(writer io.Write, prog *ast.Program, html bool) {
if P.html {
err := templ.Apply(text, "<!--", template.Substitution {
- "PACKAGE_NAME-->" : func() { P.Printf("%s", prog.Ident.Str); },
+ "PACKAGE_NAME-->" : func() { P.Printf("%s", prog.Name.Str); },
"PACKAGE_COMMENT-->": func() { P.printComment(prog.Comment); },
"PACKAGE_INTERFACE-->" : func() { P.Interface(prog); },
"PACKAGE_BODY-->" : func() { P.Program(prog); },
// Returns true if buf starts with s, returns false otherwise.
-
+//
func match(buf []byte, s string) bool {
if len(buf) < len(s) {
return false;
// Find the position of string s in buf, starting at i.
// Returns a value < 0 if not found.
-
+//
func find(buf []byte, s string, i int) int {
if s == "" {
return i;
len, err := w.Write(T.template[i0 : len(T.template)]); // TODO handle errors
return err;
}
+
+
+func NewTemplate(filename string) *Template {
+ t := new(Template);
+ if t.Init(filename) != nil {
+ return nil;
+ }
+ return t;
+}
+
+
+func NewTemplateOrDie(filename string) *Template {
+ t := NewTemplate(filename);
+ if t == nil {
+ panic("could not read template");
+ }
+ return t;
+}
case `basename $F` in
# files with errors (skip them)
# the following have semantic errors: bug039.go | bug040.go
- calc.go | method1.go | selftest1.go | func3.go | \
+ test_errors.go | calc.go | method1.go | selftest1.go | func3.go | const2.go | \
bug014.go | bug025.go | bug029.go | bug032.go | bug039.go | bug040.go | bug050.go | bug068.go | \
bug088.go | bug083.go | bug106.go | bug121.go | bug125.go | bug126.go | bug132.go | bug133.go | bug134.go ) ;;
* ) $1 $2; count $F;;