Commit 8f52a821 authored by Robert Griesemer's avatar Robert Griesemer

- make printer interface easily extensible w/o breaking clients (in the future)

- replacement for p4 CL 35999 (abandoned)

R=rsc
http://go/go-review/1012010
parent abc6ad42
...@@ -37,7 +37,7 @@ func (p *Prog) writeOutput(srcfile string) { ...@@ -37,7 +37,7 @@ func (p *Prog) writeOutput(srcfile string) {
// Write Go output: Go input with rewrites of C.xxx to _C_xxx. // Write Go output: Go input with rewrites of C.xxx to _C_xxx.
fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n"); fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n");
fmt.Fprintf(fgo1, "//line %s:1\n", srcfile); fmt.Fprintf(fgo1, "//line %s:1\n", srcfile);
printer.Fprint(fgo1, p.AST, 0, 8, nil); printer.Fprint(fgo1, p.AST);
// Write second Go output: definitions of _C_xxx. // Write second Go output: definitions of _C_xxx.
// In a separate file so that the import of "unsafe" does not // In a separate file so that the import of "unsafe" does not
...@@ -48,7 +48,7 @@ func (p *Prog) writeOutput(srcfile string) { ...@@ -48,7 +48,7 @@ func (p *Prog) writeOutput(srcfile string) {
for name, def := range p.Typedef { for name, def := range p.Typedef {
fmt.Fprintf(fgo2, "type %s ", name); fmt.Fprintf(fgo2, "type %s ", name);
printer.Fprint(fgo2, def, 0, 8, nil); printer.Fprint(fgo2, def);
fmt.Fprintf(fgo2, "\n"); fmt.Fprintf(fgo2, "\n");
} }
fmt.Fprintf(fgo2, "type _C_void [0]byte\n"); fmt.Fprintf(fgo2, "type _C_void [0]byte\n");
...@@ -63,7 +63,7 @@ func (p *Prog) writeOutput(srcfile string) { ...@@ -63,7 +63,7 @@ func (p *Prog) writeOutput(srcfile string) {
for name, def := range p.Vardef { for name, def := range p.Vardef {
fmt.Fprintf(fc, "#pragma dynld %s·_C_%s %s \"%s/%s_%s.so\"\n", p.Package, name, name, pkgroot, p.PackagePath, base); fmt.Fprintf(fc, "#pragma dynld %s·_C_%s %s \"%s/%s_%s.so\"\n", p.Package, name, name, pkgroot, p.PackagePath, base);
fmt.Fprintf(fgo2, "var _C_%s ", name); fmt.Fprintf(fgo2, "var _C_%s ", name);
printer.Fprint(fgo2, &ast.StarExpr{X: def.Go}, 0, 8, nil); printer.Fprint(fgo2, &ast.StarExpr{X: def.Go});
fmt.Fprintf(fgo2, "\n"); fmt.Fprintf(fgo2, "\n");
} }
fmt.Fprintf(fc, "\n"); fmt.Fprintf(fc, "\n");
...@@ -74,7 +74,7 @@ func (p *Prog) writeOutput(srcfile string) { ...@@ -74,7 +74,7 @@ func (p *Prog) writeOutput(srcfile string) {
Name: &ast.Ident{Value: "_C_"+name}, Name: &ast.Ident{Value: "_C_"+name},
Type: def.Go, Type: def.Go,
}; };
printer.Fprint(fgo2, d, 0, 8, nil); printer.Fprint(fgo2, d);
fmt.Fprintf(fgo2, "\n"); fmt.Fprintf(fgo2, "\n");
if name == "CString" || name == "GoString" { if name == "CString" || name == "GoString" {
......
...@@ -271,7 +271,7 @@ func writeNode(w io.Writer, node interface{}, html bool, style printer.Styler) { ...@@ -271,7 +271,7 @@ func writeNode(w io.Writer, node interface{}, html bool, style printer.Styler) {
if html { if html {
mode |= printer.GenHTML; mode |= printer.GenHTML;
} }
printer.Fprint(w, node, mode, *tabwidth, style); (&printer.Config{mode, *tabwidth, style}).Fprint(w, node);
} }
......
...@@ -91,7 +91,7 @@ func processFile(filename string) os.Error { ...@@ -91,7 +91,7 @@ func processFile(filename string) os.Error {
} }
var res bytes.Buffer; var res bytes.Buffer;
_, err = printer.Fprint(&res, file, printerMode(), *tabwidth, nil); _, err = (&printer.Config{printerMode(), *tabwidth, nil}).Fprint(&res, file);
if err != nil { if err != nil {
return err; return err;
} }
......
...@@ -26,16 +26,6 @@ const ( ...@@ -26,16 +26,6 @@ const (
) )
// Printing is controlled with these flags supplied
// to Fprint via the mode parameter.
//
const (
GenHTML uint = 1 << iota; // generate HTML
RawFormat; // do not use a tabwriter; if set, UseSpaces is ignored
UseSpaces; // use spaces instead of tabs for indentation and alignment
)
type whiteSpace int type whiteSpace int
const ( const (
...@@ -64,29 +54,10 @@ var ( ...@@ -64,29 +54,10 @@ var (
var noPos token.Position var noPos token.Position
// An HtmlTag specifies a start and end tag.
type HtmlTag struct {
Start, End string; // empty if tags are absent
}
// A Styler specifies the formatting line tags and elementary Go words.
// A format consists of text and a (possibly empty) surrounding HTML tag.
type Styler interface {
LineTag(line int) ([]byte, HtmlTag);
Comment(c *ast.Comment, line []byte) ([]byte, HtmlTag);
BasicLit(x *ast.BasicLit) ([]byte, HtmlTag);
Ident(id *ast.Ident) ([]byte, HtmlTag);
Token(tok token.Token) ([]byte, HtmlTag);
}
type printer struct { type printer struct {
// Configuration (does not change after initialization) // Configuration (does not change after initialization)
output io.Writer; output io.Writer;
mode uint; Config;
tabwidth int;
style Styler;
errors chan os.Error; errors chan os.Error;
// Current state // Current state
...@@ -115,11 +86,9 @@ type printer struct { ...@@ -115,11 +86,9 @@ type printer struct {
} }
func (p *printer) init(output io.Writer, mode uint, tabwidth int, style Styler) { func (p *printer) init(output io.Writer, cfg *Config) {
p.output = output; p.output = output;
p.mode = mode; p.Config = *cfg;
p.tabwidth = tabwidth;
p.style = style;
p.errors = make(chan os.Error); p.errors = make(chan os.Error);
p.buffer = make([]whiteSpace, 0, 16); // whitespace sequences are short p.buffer = make([]whiteSpace, 0, 16); // whitespace sequences are short
} }
...@@ -174,7 +143,7 @@ func (p *printer) write(data []byte) { ...@@ -174,7 +143,7 @@ func (p *printer) write(data []byte) {
i0 = i+1; i0 = i+1;
case '&', '<', '>': case '&', '<', '>':
if p.mode & GenHTML != 0 { if p.Mode & GenHTML != 0 {
// write segment ending in b // write segment ending in b
p.write0(data[i0 : i]); p.write0(data[i0 : i]);
...@@ -248,12 +217,12 @@ func (p *printer) writeItem(pos token.Position, data []byte, tag HtmlTag) { ...@@ -248,12 +217,12 @@ func (p *printer) writeItem(pos token.Position, data []byte, tag HtmlTag) {
// do not update p.pos - use write0 // do not update p.pos - use write0
p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column))); p.write0(strings.Bytes(fmt.Sprintf("[%d:%d]", pos.Line, pos.Column)));
} }
if p.mode & GenHTML != 0 { if p.Mode & GenHTML != 0 {
// write line tag if on a new line // write line tag if on a new line
// TODO(gri): should write line tags on each line at the start // TODO(gri): should write line tags on each line at the start
// will be more useful (e.g. to show line numbers) // will be more useful (e.g. to show line numbers)
if p.style != nil && pos.Line > p.lastTaggedLine { if p.Styler != nil && pos.Line > p.lastTaggedLine {
p.writeTaggedItem(p.style.LineTag(pos.Line)); p.writeTaggedItem(p.Styler.LineTag(pos.Line));
p.lastTaggedLine = pos.Line; p.lastTaggedLine = pos.Line;
} }
p.writeTaggedItem(data, tag); p.writeTaggedItem(data, tag);
...@@ -357,15 +326,15 @@ func (p *printer) writeComment(comment *ast.Comment) { ...@@ -357,15 +326,15 @@ func (p *printer) writeComment(comment *ast.Comment) {
// by the printer, reducing tab sequences to single tabs will yield the // by the printer, reducing tab sequences to single tabs will yield the
// original comment again after reformatting via the tabwriter. // original comment again after reformatting via the tabwriter.
text := comment.Text; text := comment.Text;
if p.mode & RawFormat == 0 { if p.Mode & RawFormat == 0 {
// tabwriter is used // tabwriter is used
text = collapseTabs(comment.Text); text = collapseTabs(comment.Text);
} }
// write comment // write comment
var tag HtmlTag; var tag HtmlTag;
if p.style != nil { if p.Styler != nil {
text, tag = p.style.Comment(comment, text); text, tag = p.Styler.Comment(comment, text);
} }
p.writeItem(comment.Pos(), text, tag); p.writeItem(comment.Pos(), text, tag);
} }
...@@ -519,14 +488,14 @@ func (p *printer) print(args ...) { ...@@ -519,14 +488,14 @@ func (p *printer) print(args ...) {
// handles comments correctly // handles comments correctly
data = strings.Bytes(x); data = strings.Bytes(x);
case *ast.Ident: case *ast.Ident:
if p.style != nil { if p.Styler != nil {
data, tag = p.style.Ident(x); data, tag = p.Styler.Ident(x);
} else { } else {
data = strings.Bytes(x.Value); data = strings.Bytes(x.Value);
} }
case *ast.BasicLit: case *ast.BasicLit:
if p.style != nil { if p.Styler != nil {
data, tag = p.style.BasicLit(x); data, tag = p.Styler.BasicLit(x);
} else { } else {
data = x.Value; data = x.Value;
} }
...@@ -536,8 +505,8 @@ func (p *printer) print(args ...) { ...@@ -536,8 +505,8 @@ func (p *printer) print(args ...) {
// TODO(gri): this this more efficiently. // TODO(gri): this this more efficiently.
data = strings.Bytes("\xff" + string(data) + "\xff"); data = strings.Bytes("\xff" + string(data) + "\xff");
case token.Token: case token.Token:
if p.style != nil { if p.Styler != nil {
data, tag = p.style.Token(x); data, tag = p.Styler.Token(x);
} else { } else {
data = strings.Bytes(x.String()); data = strings.Bytes(x.String());
} }
...@@ -1509,7 +1478,7 @@ func (p *printer) isOneLiner(b *ast.BlockStmt) bool { ...@@ -1509,7 +1478,7 @@ func (p *printer) isOneLiner(b *ast.BlockStmt) bool {
// test-print the statement and see if it would fit // test-print the statement and see if it would fit
var buf bytes.Buffer; var buf bytes.Buffer;
_, err := Fprint(&buf, b.List[0], p.mode, p.tabwidth, p.style); _, err := p.Config.Fprint(&buf, b.List[0]);
if err != nil { if err != nil {
return false; // don't try return false; // don't try
} }
...@@ -1715,15 +1684,46 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { ...@@ -1715,15 +1684,46 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Public interface // Public interface
var inf = token.Position{Offset: 1<<30, Line: 1<<30} // General printing is controlled with these Config.Mode flags.
const (
GenHTML uint = 1 << iota; // generate HTML
RawFormat; // do not use a tabwriter; if set, UseSpaces is ignored
UseSpaces; // use spaces instead of tabs for indentation and alignment
)
// An HtmlTag specifies a start and end tag.
type HtmlTag struct {
Start, End string; // empty if tags are absent
}
// A Styler specifies formatting of line tags and elementary Go words.
// A format consists of text and a (possibly empty) surrounding HTML tag.
//
type Styler interface {
LineTag(line int) ([]byte, HtmlTag);
Comment(c *ast.Comment, line []byte) ([]byte, HtmlTag);
BasicLit(x *ast.BasicLit) ([]byte, HtmlTag);
Ident(id *ast.Ident) ([]byte, HtmlTag);
Token(tok token.Token) ([]byte, HtmlTag);
}
// Fprint "pretty-prints" an AST node to output and returns the number of // A Config node controls the output of Fprint.
// bytes written, and an error, if any. The node type must be *ast.File, type Config struct {
// or assignment-compatible to ast.Expr, ast.Decl, or ast.Stmt. Printing Mode uint; // default: 0
// is controlled by the mode and tabwidth parameters. Tabwidth int; // default: 8
Styler Styler; // default: nil
}
// Fprint "pretty-prints" an AST node to output and returns the number
// of bytes written and an error (if any) for a given configuration cfg.
// The node type must be *ast.File, or assignment-compatible to ast.Expr,
// ast.Decl, or ast.Stmt.
// //
func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style Styler) (int, os.Error) { func (cfg *Config) Fprint(output io.Writer, node interface{}) (int, os.Error) {
// redirect output through a trimmer to eliminate trailing whitespace // redirect output through a trimmer to eliminate trailing whitespace
// (Input to a tabwriter must be untrimmed since trailing tabs provide // (Input to a tabwriter must be untrimmed since trailing tabs provide
// formatting information. The tabwriter could provide trimming // formatting information. The tabwriter could provide trimming
...@@ -1732,22 +1732,22 @@ func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style S ...@@ -1732,22 +1732,22 @@ func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style S
// setup tabwriter if needed and redirect output // setup tabwriter if needed and redirect output
var tw *tabwriter.Writer; var tw *tabwriter.Writer;
if mode & RawFormat == 0 { if cfg.Mode & RawFormat == 0 {
padchar := byte('\t'); padchar := byte('\t');
if mode & UseSpaces != 0 { if cfg.Mode & UseSpaces != 0 {
padchar = ' '; padchar = ' ';
} }
twmode := tabwriter.DiscardEmptyColumns; twmode := tabwriter.DiscardEmptyColumns;
if mode & GenHTML != 0 { if cfg.Mode & GenHTML != 0 {
twmode |= tabwriter.FilterHTML; twmode |= tabwriter.FilterHTML;
} }
tw = tabwriter.NewWriter(output, tabwidth, 1, padchar, twmode); tw = tabwriter.NewWriter(output, cfg.Tabwidth, 1, padchar, twmode);
output = tw; output = tw;
} }
// setup printer and print node // setup printer and print node
var p printer; var p printer;
p.init(output, mode, tabwidth, style); p.init(output, cfg);
go func() { go func() {
switch n := node.(type) { switch n := node.(type) {
case ast.Expr: case ast.Expr:
...@@ -1763,7 +1763,7 @@ func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style S ...@@ -1763,7 +1763,7 @@ func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style S
p.errors <- os.NewError(fmt.Sprintf("printer.Fprint: unsupported node type %T", n)); p.errors <- os.NewError(fmt.Sprintf("printer.Fprint: unsupported node type %T", n));
runtime.Goexit(); runtime.Goexit();
} }
p.flush(inf); p.flush(token.Position{Offset: 1<<30, Line: 1<<30}); // flush to "infinity"
p.errors <- nil; // no errors p.errors <- nil; // no errors
}(); }();
err := <-p.errors; // wait for completion of goroutine err := <-p.errors; // wait for completion of goroutine
...@@ -1775,3 +1775,12 @@ func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style S ...@@ -1775,3 +1775,12 @@ func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style S
return p.written, err; return p.written, err;
} }
// Fprint "pretty-prints" an AST node to output.
// It calls Config.Fprint with default settings.
//
func Fprint(output io.Writer, node interface{}) os.Error {
_, err := (&Config{Tabwidth: 8}).Fprint(output, node); // don't care about number of bytes written
return err;
}
...@@ -54,15 +54,15 @@ func check(t *testing.T, source, golden string, mode checkMode) { ...@@ -54,15 +54,15 @@ func check(t *testing.T, source, golden string, mode checkMode) {
prog.Comments = nil; // don't print comments that are not in AST prog.Comments = nil; // don't print comments that are not in AST
} }
// determine printer mode // determine printer configuration
var pmode uint; cfg := Config{Tabwidth: tabwidth};
if mode&rawFormat != 0 { if mode&rawFormat != 0 {
pmode |= RawFormat; cfg.Mode |= RawFormat;
} }
// format source // format source
var buf bytes.Buffer; var buf bytes.Buffer;
if _, err := Fprint(&buf, prog, pmode, tabwidth, nil); err != nil { if _, err := Fprint(&buf, prog, &cfg); err != nil {
t.Error(err); t.Error(err);
} }
res := buf.Bytes(); res := buf.Bytes();
......
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