mirror of
https://github.com/golang/go
synced 2024-10-02 22:21:20 -06:00
- 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
This commit is contained in:
parent
abc6ad427d
commit
8f52a82169
@ -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) {
|
|||||||
|
|
||||||
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) {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// 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 (
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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) {
|
|||||||
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) {
|
|||||||
// 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) {
|
|||||||
// 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 ...) {
|
|||||||
// 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 ...) {
|
|||||||
// 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 {
|
|||||||
|
|
||||||
// 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) {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// 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
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
// Fprint "pretty-prints" an AST node to output and returns the number of
|
// An HtmlTag specifies a start and end tag.
|
||||||
// bytes written, and an error, if any. The node type must be *ast.File,
|
type HtmlTag struct {
|
||||||
// or assignment-compatible to ast.Expr, ast.Decl, or ast.Stmt. Printing
|
Start, End string; // empty if tags are absent
|
||||||
// is controlled by the mode and tabwidth parameters.
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A Styler specifies formatting of line tags and elementary Go words.
|
||||||
|
// A format consists of text and a (possibly empty) surrounding HTML tag.
|
||||||
//
|
//
|
||||||
func Fprint(output io.Writer, node interface{}, mode uint, tabwidth int, style Styler) (int, os.Error) {
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A Config node controls the output of Fprint.
|
||||||
|
type Config struct {
|
||||||
|
Mode uint; // default: 0
|
||||||
|
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 (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
|
|||||||
|
|
||||||
// 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
|
|||||||
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
|
|||||||
|
|
||||||
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) {
|
|||||||
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();
|
||||||
|
Loading…
Reference in New Issue
Block a user