diff options
Diffstat (limited to 'libgo/go/go/printer/printer.go')
-rw-r--r-- | libgo/go/go/printer/printer.go | 165 |
1 files changed, 65 insertions, 100 deletions
diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go index a43e4a12c77..697a83fa866 100644 --- a/libgo/go/go/printer/printer.go +++ b/libgo/go/go/printer/printer.go @@ -34,12 +34,6 @@ const ( ) -const ( - esc2 = '\xfe' // an escape byte that cannot occur in regular UTF-8 - _ = 1 / (esc2 - tabwriter.Escape) // cause compiler error if esc2 == tabwriter.Escape -) - - var ( esc = []byte{tabwriter.Escape} htab = []byte{'\t'} @@ -81,8 +75,9 @@ type printer struct { mode pmode // current printer mode lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace) - // Buffered whitespace - buffer []whiteSpace + // Reused buffers + wsbuf []whiteSpace // delayed white space + litbuf bytes.Buffer // for creation of escaped literals and comments // The (possibly estimated) position in the generated output; // in AST space (i.e., pos is set whenever a token position is @@ -109,7 +104,7 @@ func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeS p.Config = *cfg p.fset = fset p.errors = make(chan os.Error) - p.buffer = make([]whiteSpace, 0, 16) // whitespace sequences are short + p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short p.nodeSizes = nodeSizes } @@ -123,6 +118,20 @@ func (p *printer) internalError(msg ...interface{}) { } +// escape escapes string s by bracketing it with tabwriter.Escape. +// Escaped strings pass through tabwriter unchanged. (Note that +// valid Go programs cannot contain tabwriter.Escape bytes since +// they do not appear in legal UTF-8 sequences). +// +func (p *printer) escape(s string) string { + p.litbuf.Reset() + p.litbuf.WriteByte(tabwriter.Escape) + p.litbuf.WriteString(s) + p.litbuf.WriteByte(tabwriter.Escape) + return p.litbuf.String() +} + + // nlines returns the adjusted number of linebreaks given the desired number // of breaks n such that min <= result <= max where max depends on the current // nesting level. @@ -230,7 +239,7 @@ func (p *printer) writeNewlines(n int, useFF bool) { // source text. writeItem updates p.last to the position immediately following // the data. // -func (p *printer) writeItem(pos token.Position, data []byte) { +func (p *printer) writeItem(pos token.Position, data string) { if pos.IsValid() { // continue with previous position if we don't have a valid pos if p.last.IsValid() && p.last.Filename != pos.Filename { @@ -239,7 +248,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) { // e.g., the result of ast.MergePackageFiles) p.indent = 0 p.mode = 0 - p.buffer = p.buffer[0:0] + p.wsbuf = p.wsbuf[0:0] } p.pos = pos } @@ -248,7 +257,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) { _, filename := filepath.Split(pos.Filename) p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column))) } - p.write(data) + p.write([]byte(data)) p.last = p.pos } @@ -280,11 +289,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment if prev == nil { // first comment of a comment group j := 0 - for i, ch := range p.buffer { + for i, ch := range p.wsbuf { switch ch { case blank: // ignore any blanks before a comment - p.buffer[i] = ignore + p.wsbuf[i] = ignore continue case vtab: // respect existing tabs - important @@ -318,11 +327,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment if prev == nil { // first comment of a comment group j := 0 - for i, ch := range p.buffer { + for i, ch := range p.wsbuf { switch ch { case blank, vtab: // ignore any horizontal whitespace before line breaks - p.buffer[i] = ignore + p.wsbuf[i] = ignore continue case indent: // apply pending indentation @@ -339,7 +348,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment } case newline, formfeed: // TODO(gri): may want to keep formfeed info in some cases - p.buffer[i] = ignore + p.wsbuf[i] = ignore } j = i break @@ -360,12 +369,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment } -func (p *printer) writeCommentLine(comment *ast.Comment, pos token.Position, line []byte) { - // line must pass through unchanged, bracket it with tabwriter.Escape - line = bytes.Join([][]byte{esc, line, esc}, nil) - p.writeItem(pos, line) -} - +// TODO(gri): It should be possible to convert the code below from using +// []byte to string and in the process eliminate some conversions. // Split comment text into lines func split(text []byte) [][]byte { @@ -546,13 +551,13 @@ func (p *printer) writeComment(comment *ast.Comment) { // shortcut common case of //-style comments if text[1] == '/' { - p.writeCommentLine(comment, p.fset.Position(comment.Pos()), text) + p.writeItem(p.fset.Position(comment.Pos()), p.escape(text)) return } // for /*-style comments, print line by line and let the // write function take care of the proper indentation - lines := split(text) + lines := split([]byte(text)) stripCommonPrefix(lines) // write comment lines, separated by formfeed, @@ -565,7 +570,7 @@ func (p *printer) writeComment(comment *ast.Comment) { pos = p.pos } if len(line) > 0 { - p.writeCommentLine(comment, pos, line) + p.writeItem(pos, p.escape(string(line))) } } } @@ -578,11 +583,11 @@ func (p *printer) writeComment(comment *ast.Comment) { // formfeed was dropped from the whitespace buffer. // func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { - for i, ch := range p.buffer { + for i, ch := range p.wsbuf { switch ch { case blank, vtab: // ignore trailing whitespace - p.buffer[i] = ignore + p.wsbuf[i] = ignore case indent, unindent: // don't loose indentation information case newline, formfeed: @@ -594,11 +599,11 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) { if ch == formfeed { droppedFF = true } - p.buffer[i] = ignore + p.wsbuf[i] = ignore } } } - p.writeWhitespace(len(p.buffer)) + p.writeWhitespace(len(p.wsbuf)) // make sure we have a line break if needsLinebreak { @@ -652,7 +657,7 @@ func (p *printer) writeWhitespace(n int) { // write entries var data [1]byte for i := 0; i < n; i++ { - switch ch := p.buffer[i]; ch { + switch ch := p.wsbuf[i]; ch { case ignore: // ignore! case indent: @@ -670,13 +675,13 @@ func (p *printer) writeWhitespace(n int) { // the line break and the label, the unindent is not // part of the comment whitespace prefix and the comment // will be positioned correctly indented. - if i+1 < n && p.buffer[i+1] == unindent { + if i+1 < n && p.wsbuf[i+1] == unindent { // Use a formfeed to terminate the current section. // Otherwise, a long label name on the next line leading // to a wide column may increase the indentation column // of lines before the label; effectively leading to wrong // indentation. - p.buffer[i], p.buffer[i+1] = unindent, formfeed + p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed i-- // do it again continue } @@ -689,11 +694,11 @@ func (p *printer) writeWhitespace(n int) { // shift remaining entries down i := 0 - for ; n < len(p.buffer); n++ { - p.buffer[i] = p.buffer[n] + for ; n < len(p.wsbuf); n++ { + p.wsbuf[i] = p.wsbuf[n] i++ } - p.buffer = p.buffer[0:i] + p.wsbuf = p.wsbuf[0:i] } @@ -734,7 +739,7 @@ func mayCombine(prev token.Token, next byte) (b bool) { func (p *printer) print(args ...interface{}) { for _, f := range args { next := p.pos // estimated position of next item - var data []byte + var data string var tok token.Token switch x := f.(type) { @@ -748,42 +753,22 @@ func (p *printer) print(args ...interface{}) { // LabeledStmt) break } - i := len(p.buffer) - if i == cap(p.buffer) { + i := len(p.wsbuf) + if i == cap(p.wsbuf) { // Whitespace sequences are very short so this should // never happen. Handle gracefully (but possibly with // bad comment placement) if it does happen. p.writeWhitespace(i) i = 0 } - p.buffer = p.buffer[0 : i+1] - p.buffer[i] = x + p.wsbuf = p.wsbuf[0 : i+1] + p.wsbuf[i] = x case *ast.Ident: - data = []byte(x.Name) + data = x.Name tok = token.IDENT case *ast.BasicLit: - // escape all literals so they pass through unchanged - // (note that valid Go programs cannot contain - // tabwriter.Escape bytes since they do not appear in - // legal UTF-8 sequences) - data = make([]byte, 0, len(x.Value)+2) - data = append(data, tabwriter.Escape) - data = append(data, x.Value...) - data = append(data, tabwriter.Escape) + data = p.escape(x.Value) tok = x.Kind - // If we have a raw string that spans multiple lines and - // the opening quote (`) is on a line preceded only by - // indentation, we don't want to write that indentation - // because the following lines of the raw string are not - // indented. It's easiest to correct the output at the end - // via the trimmer (because of the complex handling of - // white space). - // Mark multi-line raw strings by replacing the opening - // quote with esc2 and have the trimmer take care of fixing - // it up. (Do this _after_ making a copy of data!) - if data[1] == '`' && bytes.IndexByte(data, '\n') > 0 { - data[1] = esc2 - } case token.Token: s := x.String() if mayCombine(p.lastTok, s[0]) { @@ -793,13 +778,13 @@ func (p *printer) print(args ...interface{}) { // (except for token.INT followed by a '.' this // should never happen because it is taken care // of via binary expression formatting) - if len(p.buffer) != 0 { + if len(p.wsbuf) != 0 { p.internalError("whitespace buffer not empty") } - p.buffer = p.buffer[0:1] - p.buffer[0] = ' ' + p.wsbuf = p.wsbuf[0:1] + p.wsbuf[0] = ' ' } - data = []byte(s) + data = s tok = x case token.Pos: if x.IsValid() { @@ -813,7 +798,7 @@ func (p *printer) print(args ...interface{}) { p.lastTok = tok p.pos = next - if data != nil { + if data != "" { droppedFF := p.flush(next, tok) // intersperse extra newlines if present in the source @@ -848,7 +833,7 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { droppedFF = p.intersperseComments(next, tok) } else { // otherwise, write any leftover whitespace - p.writeWhitespace(len(p.buffer)) + p.writeWhitespace(len(p.wsbuf)) } return } @@ -864,10 +849,9 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) { // through unchanged. // type trimmer struct { - output io.Writer - state int - space bytes.Buffer - hasText bool + output io.Writer + state int + space bytes.Buffer } @@ -875,15 +859,11 @@ type trimmer struct { // It can be in one of the following states: const ( inSpace = iota // inside space - atEscape // inside space and the last char was an opening tabwriter.Escape inEscape // inside text bracketed by tabwriter.Escapes inText // inside text ) -var backquote = []byte{'`'} - - // Design note: It is tempting to eliminate extra blanks occurring in // whitespace in this function as it could simplify some // of the blanks logic in the node printing functions. @@ -892,9 +872,8 @@ var backquote = []byte{'`'} func (p *trimmer) Write(data []byte) (n int, err os.Error) { // invariants: - // p.state == inSpace, atEscape: + // p.state == inSpace: // p.space is unwritten - // p.hasText indicates if there is any text on this line // p.state == inEscape, inText: // data[m:n] is unwritten m := 0 @@ -911,32 +890,20 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { case '\n', '\f': p.space.Reset() // discard trailing space _, err = p.output.Write(newlines[0:1]) // write newline - p.hasText = false case tabwriter.Escape: - p.state = atEscape + _, err = p.output.Write(p.space.Bytes()) + p.state = inEscape + m = n + 1 // +1: skip tabwriter.Escape default: _, err = p.output.Write(p.space.Bytes()) p.state = inText m = n } - case atEscape: - // discard indentation if we have a multi-line raw string - // (see printer.print for details) - if b != esc2 || p.hasText { - _, err = p.output.Write(p.space.Bytes()) - } - p.state = inEscape - m = n - if b == esc2 { - _, err = p.output.Write(backquote) // convert back - m++ - } case inEscape: if b == tabwriter.Escape { _, err = p.output.Write(data[m:n]) p.state = inSpace p.space.Reset() - p.hasText = true } case inText: switch b { @@ -945,19 +912,18 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { p.state = inSpace p.space.Reset() p.space.WriteByte(b) // WriteByte returns no errors - p.hasText = true case '\n', '\f': _, err = p.output.Write(data[m:n]) p.state = inSpace p.space.Reset() _, err = p.output.Write(newlines[0:1]) // write newline - p.hasText = false case tabwriter.Escape: _, err = p.output.Write(data[m:n]) - p.state = atEscape - p.space.Reset() - p.hasText = true + p.state = inEscape + m = n + 1 // +1: skip tabwriter.Escape } + default: + panic("unreachable") } if err != nil { return @@ -970,7 +936,6 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) { _, err = p.output.Write(data[m:n]) p.state = inSpace p.space.Reset() - p.hasText = true } return |