From: Robert Griesemer Date: Wed, 1 Apr 2009 22:00:22 +0000 (-0700) Subject: More gds functionality: X-Git-Tag: weekly.2009-11-06~1915 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=6d5bba5148f57a28bc19d86f21ccb114e9aaf614;p=gostls13.git More gds functionality: - package headers - constants - variables - formatted comments Next steps: - sorted output - collection of all files belonging to a package - fine-tuning of output R=r OCL=26997 CL=26997 --- diff --git a/usr/gri/pretty/docprinter.go b/usr/gri/pretty/docprinter.go index f724f7d5f6..aa72687044 100644 --- a/usr/gri/pretty/docprinter.go +++ b/usr/gri/pretty/docprinter.go @@ -12,6 +12,7 @@ import ( "fmt"; "ast"; + "token"; "astprinter"; "template"; ) @@ -37,15 +38,26 @@ func hasExportedNames(names []*ast.Ident) bool { } +func hasExportedDecls(decl []ast.Decl) bool { + for i, d := range decl { + switch t := d.(type) { + case *ast.ConstDecl: + return hasExportedNames(t.Names); + } + } + return false; +} + + // ---------------------------------------------------------------------------- type constDoc struct { - decl *ast.ConstDecl; + decl *ast.DeclList; } type varDoc struct { - decl *ast.VarDecl; + decl *ast.DeclList; } @@ -63,10 +75,10 @@ type typeDoc struct { type PackageDoc struct { name string; // package name - imports map[string] string; - consts map[string] *constDoc; + doc ast.Comments; // package documentation, if any + consts *vector.Vector; // list of *ast.DeclList with Tok == token.CONST + vars *vector.Vector; // list of *ast.DeclList with Tok == token.CONST types map[string] *typeDoc; - vars map[string] *varDoc; funcs map[string] *funcDoc; } @@ -77,10 +89,9 @@ type PackageDoc struct { // func (doc *PackageDoc) Init(name string) { doc.name = name; - doc.imports = make(map[string] string); - doc.consts = make(map[string] *constDoc); + doc.consts = vector.New(0); doc.types = make(map[string] *typeDoc); - doc.vars = make(map[string] *varDoc); + doc.vars = vector.New(0); doc.funcs = make(map[string] *funcDoc); } @@ -105,6 +116,13 @@ func (doc *PackageDoc) lookupTypeDoc(typ ast.Expr) *typeDoc { } +func (doc *PackageDoc) addType(typ *ast.TypeDecl) { + name := string(typ.Name.Lit); + tdoc := &typeDoc{typ, make(map[string] *funcDoc), make(map[string] *funcDoc)}; + doc.types[name] = tdoc; +} + + func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) { name := string(fun.Name.Lit); fdoc := &funcDoc{fun}; @@ -142,21 +160,19 @@ func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) { func (doc *PackageDoc) addDecl(decl ast.Decl) { switch d := decl.(type) { - case *ast.ImportDecl: case *ast.ConstDecl: if hasExportedNames(d.Names) { + // TODO } case *ast.TypeDecl: if isExported(d.Name) { - // TODO only add if not there already - or ignore? - name := string(d.Name.Lit); - tdoc := &typeDoc{d, make(map[string] *funcDoc), make(map[string] *funcDoc)}; - doc.types[name] = tdoc; + doc.addType(d); } case *ast.VarDecl: if hasExportedNames(d.Names) { + // TODO } case *ast.FuncDecl: @@ -165,24 +181,41 @@ func (doc *PackageDoc) addDecl(decl ast.Decl) { } case *ast.DeclList: - for i, decl := range d.List { - doc.addDecl(decl); + switch d.Tok { + case token.IMPORT, token.TYPE: + for i, decl := range d.List { + doc.addDecl(decl); + } + case token.CONST: + if hasExportedDecls(d.List) { + doc.consts.Push(&constDoc{d}); + } + case token.VAR: + if hasExportedDecls(d.List) { + doc.consts.Push(&varDoc{d}); + } } } } // AddProgram adds the AST of a source file belonging to the same -// package. The package names must match. If the package was added -// before, AddPackage is a no-op. +// package. The package names must match. If the source was added +// before, AddProgram is a no-op. // -func (doc *PackageDoc) AddProgram(pak *ast.Program) { - if doc.name != string(pak.Name.Lit) { +func (doc *PackageDoc) AddProgram(prog *ast.Program) { + if doc.name != string(prog.Name.Lit) { panic("package names don't match"); } - + + // add package documentation + // TODO what to do if there are multiple files? + if prog.Doc != nil { + doc.doc = prog.Doc + } + // add all declarations - for i, decl := range pak.Decls { + for i, decl := range prog.Decls { doc.addDecl(decl); } } @@ -191,33 +224,58 @@ func (doc *PackageDoc) AddProgram(pak *ast.Program) { // ---------------------------------------------------------------------------- // Printing -func htmlEscape(s string) string { - var esc string; +func htmlEscape(s []byte) []byte { + var buf io.ByteBuffer; + + i0 := 0; for i := 0; i < len(s); i++ { + var esc string; switch s[i] { case '<': esc = "<"; case '&': esc = "&"; default: continue; } - return s[0 : i] + esc + htmlEscape(s[i+1 : len(s)]); + fmt.Fprintf(&buf, "%s%s", s[i0 : i], esc); + i0 := i+1; // skip escaped char + } + + // write the rest + if i0 > 0 { + buf.Write(s[i0 : len(s)]); + s = buf.Data(); } return s; } // Reduce contiguous sequences of '\t' in a string to a single '\t'. -func untabify(s string) string { +// This will produce better results when the string is printed via +// a tabwriter. +// TODO make this functionality optional. +// +func untabify(s []byte) []byte { + var buf io.ByteBuffer; + + i0 := 0; for i := 0; i < len(s); i++ { if s[i] == '\t' { - j := i; - for j < len(s) && s[j] == '\t' { - j++; - } - if j-i > 1 { // more then one tab - return s[0 : i+1] + untabify(s[j : len(s)]); + i++; // include '\t' + buf.Write(s[i0 : i]); + // skip additional tabs + for i < len(s) && s[i] == '\t' { + i++; } + i0 := i; + } else { + i++; } } + + // write the rest + if i0 > 0 { + buf.Write(s[i0 : len(s)]); + s = buf.Data(); + } return s; } @@ -234,40 +292,108 @@ func stripWhiteSpace(s []byte) []byte { } -func cleanComment(s []byte) []byte { +func stripCommentDelimiters(s []byte) []byte { switch s[1] { - case '/': s = s[2 : len(s)-1]; - case '*': s = s[2 : len(s)-2]; - default : panic("illegal comment"); + case '/': return s[2 : len(s)-1]; + case '*': return s[2 : len(s)-2]; } - return stripWhiteSpace(s); + panic(); + return nil; } -func printComment(p *astPrinter.Printer, comment ast.Comments) { - in_paragraph := false; - for i, c := range comment { - s := cleanComment(c.Text); - if len(s) > 0 { - if !in_paragraph { - p.Printf("

\n"); - in_paragraph = true; +const /* formatting mode */ ( + in_gap = iota; + in_paragraph; + in_preformatted; +) + +func printLine(p *astPrinter.Printer, line []byte, mode int) int { + indented := len(line) > 0 && line[0] == '\t'; + line = stripWhiteSpace(line); + if len(line) == 0 { + // empty line + switch mode { + case in_paragraph: + p.Printf("

\n"); + mode = in_gap; + case in_preformatted: + p.Printf("\n"); + // remain in preformatted + } + } else { + // non-empty line + if indented { + switch mode { + case in_gap: + p.Printf("
\n");
+			case in_paragraph:
+				p.Printf("

\n"); + p.Printf("
\n");
 			}
-			p.Printf("%s\n", htmlEscape(untabify(string(s))));
+			mode = in_preformatted;
 		} else {
-			if in_paragraph {
-				p.Printf("

\n"); - in_paragraph = false; + switch mode { + case in_gap: + p.Printf("

\n"); + case in_preformatted: + p.Printf("

\n"); + p.Printf("

\n"); } + mode = in_paragraph; } + // print line + p.Printf("%s\n", untabify(htmlEscape(line))); } - if in_paragraph { + return mode; +} + + +func closeMode(p *astPrinter.Printer, mode int) { + switch mode { + case in_paragraph: p.Printf("

\n"); + case in_preformatted: + p.Printf("
\n"); + } +} + + +func printComments(p *astPrinter.Printer, comment ast.Comments) { + mode := in_gap; + for i, c := range comment { + s := stripCommentDelimiters(c.Text); + + // split comment into lines and print the lines + i0 := 0; // beginning of current line + for i := 0; i < len(s); i++ { + if s[i] == '\n' { + // reached line end - print current line + mode = printLine(p, s[i0 : i], mode); + i0 = i + 1; // beginning of next line; skip '\n' + } + } + + // print last line + mode = printLine(p, s[i0 : len(s)], mode); } + closeMode(p, mode); } -func (c *constDoc) printConsts(p *astPrinter.Printer) { +func (c *constDoc) print(p *astPrinter.Printer) { + printComments(p, c.decl.Doc); + p.Printf("
");
+	p.DoDeclList(c.decl);
+	p.Printf("
\n"); +} + + +func (c *varDoc) print(p *astPrinter.Printer) { + printComments(p, c.decl.Doc); + p.Printf("
");
+	p.DoDeclList(c.decl);
+	p.Printf("
\n"); } @@ -283,9 +409,7 @@ func (f *funcDoc) print(p *astPrinter.Printer, hsize int) { p.Printf("

"); p.DoFuncDecl(d); p.Printf("

\n"); - if d.Doc != nil { - printComment(p, d.Doc); - } + printComments(p, d.Doc); } @@ -295,9 +419,7 @@ func (t *typeDoc) print(p *astPrinter.Printer) { p.Printf("

");
 	p.DoTypeDecl(d);
 	p.Printf("

\n"); - if d.Doc != nil { - printComment(p, d.Doc); - } + printComments(p, d.Doc); // print associated methods, if any for name, m := range t.factories { @@ -310,47 +432,6 @@ func (t *typeDoc) print(p *astPrinter.Printer) { } -func (v *varDoc) print(p *astPrinter.Printer) { -} - - -/* -func (P *Printer) Interface(p *ast.Program) { - P.full = false; - for i := 0; i < len(p.Decls); i++ { - switch d := p.Decls[i].(type) { - case *ast.ConstDecl: - if hasExportedNames(d.Names) { - P.Printf("

Constants

\n"); - P.Printf("

");
-				P.DoConstDecl(d);
-				P.String(nopos, "");
-				P.Printf("

\n"); - if d.Doc != nil { - P.printComment(d.Doc); - } - } - - case *ast.VarDecl: - if hasExportedNames(d.Names) { - P.Printf("

Variables

\n"); - P.Printf("

");
-				P.DoVarDecl(d);
-				P.String(nopos, "");
-				P.Printf("

\n"); - if d.Doc != nil { - P.printComment(d.Doc); - } - } - - case *ast.DeclList: - - } - } -} -*/ - - // TODO make this a parameter for Init or Print? var templ = template.NewTemplateOrDie("template.html"); @@ -367,29 +448,47 @@ func (doc *PackageDoc) Print(writer io.Write) { "PROGRAM_HEADER-->": func() { + fmt.Fprintf(writer, "

import \"%s\"

\n", doc.name); + printComments(&p, doc.doc); }, "CONSTANTS-->" : func() { + if doc.consts.Len() > 0 { + fmt.Fprintln(writer, "
"); + fmt.Fprintln(writer, "

Constants

"); + for i := 0; i < doc.consts.Len(); i++ { + doc.consts.At(i).(*constDoc).print(&p); + } + } }, "TYPES-->" : func() { for name, t := range doc.types { - p.Printf("
\n"); + fmt.Fprintln(writer, "
"); t.print(&p); } }, "VARIABLES-->" : func() { + if doc.vars.Len() > 0 { + fmt.Fprintln(writer, "
"); + fmt.Fprintln(writer, "

Variables

"); + for i := 0; i < doc.vars.Len(); i++ { + doc.vars.At(i).(*varDoc).print(&p); + } + } }, "FUNCTIONS-->" : func() { - p.Printf("
\n"); - for name, f := range doc.funcs { - f.print(&p, 2); + if len(doc.funcs) > 0 { + fmt.Fprintln(writer, "
"); + for name, f := range doc.funcs { + f.print(&p, 2); + } } }, });