Commit 6d5bba51 authored by Robert Griesemer's avatar Robert Griesemer

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
parent a9c1a3b6
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"fmt"; "fmt";
"ast"; "ast";
"token";
"astprinter"; "astprinter";
"template"; "template";
) )
...@@ -37,15 +38,26 @@ func hasExportedNames(names []*ast.Ident) bool { ...@@ -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 { type constDoc struct {
decl *ast.ConstDecl; decl *ast.DeclList;
} }
type varDoc struct { type varDoc struct {
decl *ast.VarDecl; decl *ast.DeclList;
} }
...@@ -63,10 +75,10 @@ type typeDoc struct { ...@@ -63,10 +75,10 @@ type typeDoc struct {
type PackageDoc struct { type PackageDoc struct {
name string; // package name name string; // package name
imports map[string] string; doc ast.Comments; // package documentation, if any
consts map[string] *constDoc; 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; types map[string] *typeDoc;
vars map[string] *varDoc;
funcs map[string] *funcDoc; funcs map[string] *funcDoc;
} }
...@@ -77,10 +89,9 @@ type PackageDoc struct { ...@@ -77,10 +89,9 @@ type PackageDoc struct {
// //
func (doc *PackageDoc) Init(name string) { func (doc *PackageDoc) Init(name string) {
doc.name = name; doc.name = name;
doc.imports = make(map[string] string); doc.consts = vector.New(0);
doc.consts = make(map[string] *constDoc);
doc.types = make(map[string] *typeDoc); doc.types = make(map[string] *typeDoc);
doc.vars = make(map[string] *varDoc); doc.vars = vector.New(0);
doc.funcs = make(map[string] *funcDoc); doc.funcs = make(map[string] *funcDoc);
} }
...@@ -105,6 +116,13 @@ func (doc *PackageDoc) lookupTypeDoc(typ ast.Expr) *typeDoc { ...@@ -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) { func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) {
name := string(fun.Name.Lit); name := string(fun.Name.Lit);
fdoc := &funcDoc{fun}; fdoc := &funcDoc{fun};
...@@ -142,21 +160,19 @@ func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) { ...@@ -142,21 +160,19 @@ func (doc *PackageDoc) addFunc(fun *ast.FuncDecl) {
func (doc *PackageDoc) addDecl(decl ast.Decl) { func (doc *PackageDoc) addDecl(decl ast.Decl) {
switch d := decl.(type) { switch d := decl.(type) {
case *ast.ImportDecl:
case *ast.ConstDecl: case *ast.ConstDecl:
if hasExportedNames(d.Names) { if hasExportedNames(d.Names) {
// TODO
} }
case *ast.TypeDecl: case *ast.TypeDecl:
if isExported(d.Name) { if isExported(d.Name) {
// TODO only add if not there already - or ignore? doc.addType(d);
name := string(d.Name.Lit);
tdoc := &typeDoc{d, make(map[string] *funcDoc), make(map[string] *funcDoc)};
doc.types[name] = tdoc;
} }
case *ast.VarDecl: case *ast.VarDecl:
if hasExportedNames(d.Names) { if hasExportedNames(d.Names) {
// TODO
} }
case *ast.FuncDecl: case *ast.FuncDecl:
...@@ -165,24 +181,41 @@ func (doc *PackageDoc) addDecl(decl ast.Decl) { ...@@ -165,24 +181,41 @@ func (doc *PackageDoc) addDecl(decl ast.Decl) {
} }
case *ast.DeclList: case *ast.DeclList:
for i, decl := range d.List { switch d.Tok {
doc.addDecl(decl); 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 // AddProgram adds the AST of a source file belonging to the same
// package. The package names must match. If the package was added // package. The package names must match. If the source was added
// before, AddPackage is a no-op. // before, AddProgram is a no-op.
// //
func (doc *PackageDoc) AddProgram(pak *ast.Program) { func (doc *PackageDoc) AddProgram(prog *ast.Program) {
if doc.name != string(pak.Name.Lit) { if doc.name != string(prog.Name.Lit) {
panic("package names don't match"); 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 // add all declarations
for i, decl := range pak.Decls { for i, decl := range prog.Decls {
doc.addDecl(decl); doc.addDecl(decl);
} }
} }
...@@ -191,33 +224,58 @@ func (doc *PackageDoc) AddProgram(pak *ast.Program) { ...@@ -191,33 +224,58 @@ func (doc *PackageDoc) AddProgram(pak *ast.Program) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Printing // Printing
func htmlEscape(s string) string { func htmlEscape(s []byte) []byte {
var esc string; var buf io.ByteBuffer;
i0 := 0;
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {
var esc string;
switch s[i] { switch s[i] {
case '<': esc = "&lt;"; case '<': esc = "&lt;";
case '&': esc = "&amp;"; case '&': esc = "&amp;";
default: continue; 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; return s;
} }
// Reduce contiguous sequences of '\t' in a string to a single '\t'. // 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++ { for i := 0; i < len(s); i++ {
if s[i] == '\t' { if s[i] == '\t' {
j := i; i++; // include '\t'
for j < len(s) && s[j] == '\t' { buf.Write(s[i0 : i]);
j++; // skip additional tabs
} for i < len(s) && s[i] == '\t' {
if j-i > 1 { // more then one tab i++;
return s[0 : i+1] + untabify(s[j : len(s)]);
} }
i0 := i;
} else {
i++;
} }
} }
// write the rest
if i0 > 0 {
buf.Write(s[i0 : len(s)]);
s = buf.Data();
}
return s; return s;
} }
...@@ -234,40 +292,108 @@ func stripWhiteSpace(s []byte) []byte { ...@@ -234,40 +292,108 @@ func stripWhiteSpace(s []byte) []byte {
} }
func cleanComment(s []byte) []byte { func stripCommentDelimiters(s []byte) []byte {
switch s[1] { switch s[1] {
case '/': s = s[2 : len(s)-1]; case '/': return s[2 : len(s)-1];
case '*': s = s[2 : len(s)-2]; case '*': return s[2 : len(s)-2];
default : panic("illegal comment");
} }
return stripWhiteSpace(s); panic();
return nil;
} }
func printComment(p *astPrinter.Printer, comment ast.Comments) { const /* formatting mode */ (
in_paragraph := false; in_gap = iota;
for i, c := range comment { in_paragraph;
s := cleanComment(c.Text); in_preformatted;
if len(s) > 0 { )
if !in_paragraph {
p.Printf("<p>\n"); func printLine(p *astPrinter.Printer, line []byte, mode int) int {
in_paragraph = true; indented := len(line) > 0 && line[0] == '\t';
line = stripWhiteSpace(line);
if len(line) == 0 {
// empty line
switch mode {
case in_paragraph:
p.Printf("</p>\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("<pre>\n");
case in_paragraph:
p.Printf("</p>\n");
p.Printf("<pre>\n");
} }
p.Printf("%s\n", htmlEscape(untabify(string(s)))); mode = in_preformatted;
} else { } else {
if in_paragraph { switch mode {
p.Printf("</p>\n"); case in_gap:
in_paragraph = false; p.Printf("<p>\n");
case in_preformatted:
p.Printf("</pre>\n");
p.Printf("<p>\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("</p>\n"); p.Printf("</p>\n");
case in_preformatted:
p.Printf("</pre>\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("<pre>");
p.DoDeclList(c.decl);
p.Printf("</pre>\n");
}
func (c *varDoc) print(p *astPrinter.Printer) {
printComments(p, c.decl.Doc);
p.Printf("<pre>");
p.DoDeclList(c.decl);
p.Printf("</pre>\n");
} }
...@@ -283,9 +409,7 @@ func (f *funcDoc) print(p *astPrinter.Printer, hsize int) { ...@@ -283,9 +409,7 @@ func (f *funcDoc) print(p *astPrinter.Printer, hsize int) {
p.Printf("<p><code>"); p.Printf("<p><code>");
p.DoFuncDecl(d); p.DoFuncDecl(d);
p.Printf("</code></p>\n"); p.Printf("</code></p>\n");
if d.Doc != nil { printComments(p, d.Doc);
printComment(p, d.Doc);
}
} }
...@@ -295,9 +419,7 @@ func (t *typeDoc) print(p *astPrinter.Printer) { ...@@ -295,9 +419,7 @@ func (t *typeDoc) print(p *astPrinter.Printer) {
p.Printf("<p><pre>"); p.Printf("<p><pre>");
p.DoTypeDecl(d); p.DoTypeDecl(d);
p.Printf("</pre></p>\n"); p.Printf("</pre></p>\n");
if d.Doc != nil { printComments(p, d.Doc);
printComment(p, d.Doc);
}
// print associated methods, if any // print associated methods, if any
for name, m := range t.factories { for name, m := range t.factories {
...@@ -310,47 +432,6 @@ func (t *typeDoc) print(p *astPrinter.Printer) { ...@@ -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("<h2>Constants</h2>\n");
P.Printf("<p><pre>");
P.DoConstDecl(d);
P.String(nopos, "");
P.Printf("</pre></p>\n");
if d.Doc != nil {
P.printComment(d.Doc);
}
}
case *ast.VarDecl:
if hasExportedNames(d.Names) {
P.Printf("<h2>Variables</h2>\n");
P.Printf("<p><pre>");
P.DoVarDecl(d);
P.String(nopos, "");
P.Printf("</pre></p>\n");
if d.Doc != nil {
P.printComment(d.Doc);
}
}
case *ast.DeclList:
}
}
}
*/
// TODO make this a parameter for Init or Print? // TODO make this a parameter for Init or Print?
var templ = template.NewTemplateOrDie("template.html"); var templ = template.NewTemplateOrDie("template.html");
...@@ -367,29 +448,47 @@ func (doc *PackageDoc) Print(writer io.Write) { ...@@ -367,29 +448,47 @@ func (doc *PackageDoc) Print(writer io.Write) {
"PROGRAM_HEADER-->": "PROGRAM_HEADER-->":
func() { func() {
fmt.Fprintf(writer, "<p><code>import \"%s\"</code></p>\n", doc.name);
printComments(&p, doc.doc);
}, },
"CONSTANTS-->" : "CONSTANTS-->" :
func() { func() {
if doc.consts.Len() > 0 {
fmt.Fprintln(writer, "<hr />");
fmt.Fprintln(writer, "<h2>Constants</h2>");
for i := 0; i < doc.consts.Len(); i++ {
doc.consts.At(i).(*constDoc).print(&p);
}
}
}, },
"TYPES-->" : "TYPES-->" :
func() { func() {
for name, t := range doc.types { for name, t := range doc.types {
p.Printf("<hr />\n"); fmt.Fprintln(writer, "<hr />");
t.print(&p); t.print(&p);
} }
}, },
"VARIABLES-->" : "VARIABLES-->" :
func() { func() {
if doc.vars.Len() > 0 {
fmt.Fprintln(writer, "<hr />");
fmt.Fprintln(writer, "<h2>Variables</h2>");
for i := 0; i < doc.vars.Len(); i++ {
doc.vars.At(i).(*varDoc).print(&p);
}
}
}, },
"FUNCTIONS-->" : "FUNCTIONS-->" :
func() { func() {
p.Printf("<hr />\n"); if len(doc.funcs) > 0 {
for name, f := range doc.funcs { fmt.Fprintln(writer, "<hr />");
f.print(&p, 2); for name, f := range doc.funcs {
f.print(&p, 2);
}
} }
}, },
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment