1
2
3
4
5
6 package printer
7
8 import (
9 "bytes"
10 "fmt"
11 "go/ast"
12 "go/token"
13 "io"
14 "os"
15 "path/filepath"
16 "runtime"
17 "tabwriter"
18 )
19
20 const debug = false
21
22
23 type whiteSpace int
24
25 const (
26 ignore = whiteSpace(0)
27 blank = whiteSpace(' ')
28 vtab = whiteSpace('\v')
29 newline = whiteSpace('\n')
30 formfeed = whiteSpace('\f')
31 indent = whiteSpace('>')
32 unindent = whiteSpace('<')
33 )
34
35 var (
36 esc = []byte{tabwriter.Escape}
37 htab = []byte{'\t'}
38 htabs = []byte("\t\t\t\t\t\t\t\t")
39 newlines = []byte("\n\n\n\n\n\n\n\n")
40 formfeeds = []byte("\f\f\f\f\f\f\f\f")
41 )
42
43
44 var noPos token.Position
45 var infinity = 1 << 30
46
47
48 var ignoreMultiLine = new(bool)
49
50
51 type pmode int
52
53 const (
54 inLiteral pmode = 1 << iota
55 noExtraLinebreak
56 )
57
58 type printer struct {
59
60 output io.Writer
61 Config
62 fset *token.FileSet
63 errors chan os.Error
64
65
66 written int
67 indent int
68 mode pmode
69 lastTok token.Token
70
71
72 wsbuf []whiteSpace
73 litbuf bytes.Buffer
74
75
76
77
78
79 pos token.Position
80
81
82
83 last token.Position
84
85
86 comments []*ast.CommentGroup
87 cindex int
88 useNodeComments bool
89
90
91 nodeSizes map[ast.Node]int
92 }
93
94 func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {
95 p.output = output
96 p.Config = *cfg
97 p.fset = fset
98 p.errors = make(chan os.Error)
99 p.wsbuf = make([]whiteSpace, 0, 16)
100 p.nodeSizes = nodeSizes
101 }
102
103 func (p *printer) internalError(msg ...interface{}) {
104 if debug {
105 fmt.Print(p.pos.String() + ": ")
106 fmt.Println(msg...)
107 panic("go/printer")
108 }
109 }
110
111
112
113
114
115
116 func (p *printer) escape(s string) string {
117 p.litbuf.Reset()
118 p.litbuf.WriteByte(tabwriter.Escape)
119 p.litbuf.WriteString(s)
120 p.litbuf.WriteByte(tabwriter.Escape)
121 return p.litbuf.String()
122 }
123
124
125
126
127 func (p *printer) nlines(n, min int) int {
128 const max = 2
129 switch {
130 case n < min:
131 return min
132 case n > max:
133 return max
134 }
135 return n
136 }
137
138
139
140
141 func (p *printer) write0(data []byte) {
142 if len(data) > 0 {
143 n, err := p.output.Write(data)
144 p.written += n
145 if err != nil {
146 p.errors <- err
147 runtime.Goexit()
148 }
149 }
150 }
151
152
153
154
155
156 func (p *printer) write(data []byte) {
157 i0 := 0
158 for i, b := range data {
159 switch b {
160 case '\n', '\f':
161
162 p.write0(data[i0 : i+1])
163
164
165 p.pos.Offset += i + 1 - i0
166 p.pos.Line++
167 p.pos.Column = 1
168
169 if p.mode&inLiteral == 0 {
170
171
172
173 j := p.indent
174 for ; j > len(htabs); j -= len(htabs) {
175 p.write0(htabs)
176 }
177 p.write0(htabs[0:j])
178
179
180 p.pos.Offset += p.indent
181 p.pos.Column += p.indent
182 }
183
184
185 i0 = i + 1
186
187 case tabwriter.Escape:
188 p.mode ^= inLiteral
189
190
191
192 p.pos.Offset--
193 p.pos.Column--
194 }
195 }
196
197
198 p.write0(data[i0:])
199
200
201 d := len(data) - i0
202 p.pos.Offset += d
203 p.pos.Column += d
204 }
205
206 func (p *printer) writeNewlines(n int, useFF bool) {
207 if n > 0 {
208 n = p.nlines(n, 0)
209 if useFF {
210 p.write(formfeeds[0:n])
211 } else {
212 p.write(newlines[0:n])
213 }
214 }
215 }
216
217
218
219
220
221
222
223 func (p *printer) writeItem(pos token.Position, data string) {
224 if pos.IsValid() {
225
226 if p.last.IsValid() && p.last.Filename != pos.Filename {
227
228
229
230 p.indent = 0
231 p.mode = 0
232 p.wsbuf = p.wsbuf[0:0]
233 }
234 p.pos = pos
235 }
236 if debug {
237
238 _, filename := filepath.Split(pos.Filename)
239 p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column)))
240 }
241 p.write([]byte(data))
242 p.last = p.pos
243 }
244
245
246
247
248
249
250
251
252
253 func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment, isKeyword bool) {
254 if p.written == 0 {
255
256 return
257 }
258
259 if pos.IsValid() && pos.Filename != p.last.Filename {
260
261 p.writeNewlines(10, true)
262 return
263 }
264
265 if pos.Line == p.last.Line && (prev == nil || prev.Text[1] != '/') {
266
267
268 hasSep := false
269 if prev == nil {
270
271 j := 0
272 for i, ch := range p.wsbuf {
273 switch ch {
274 case blank:
275
276 p.wsbuf[i] = ignore
277 continue
278 case vtab:
279
280
281 hasSep = true
282 continue
283 case indent:
284
285 continue
286 }
287 j = i
288 break
289 }
290 p.writeWhitespace(j)
291 }
292
293 if !hasSep {
294 if pos.Line == next.Line {
295
296
297
298 p.write([]byte{' '})
299 } else {
300 p.write(htab)
301 }
302 }
303
304 } else {
305
306
307 if prev == nil {
308
309 j := 0
310 for i, ch := range p.wsbuf {
311 switch ch {
312 case blank, vtab:
313
314 p.wsbuf[i] = ignore
315 continue
316 case indent:
317
318 continue
319 case unindent:
320
321
322
323
324
325
326 if isKeyword && pos.Column == next.Column {
327 continue
328 }
329 case newline, formfeed:
330
331 p.wsbuf[i] = ignore
332 }
333 j = i
334 break
335 }
336 p.writeWhitespace(j)
337 }
338
339
340
341
342
343 n := pos.Line - p.last.Line
344 if n <= 0 && prev != nil && prev.Text[1] == '/' {
345 n = 1
346 }
347 p.writeNewlines(n, true)
348 }
349 }
350
351
352
353
354
355 func split(text []byte) [][]byte {
356
357 n := 1
358 for _, c := range text {
359 if c == '\n' {
360 n++
361 }
362 }
363
364
365 lines := make([][]byte, n)
366 n = 0
367 i := 0
368 for j, c := range text {
369 if c == '\n' {
370 lines[n] = text[i:j]
371 i = j + 1
372 n++
373 }
374 }
375 lines[n] = text[i:]
376
377 return lines
378 }
379
380 func isBlank(s []byte) bool {
381 for _, b := range s {
382 if b > ' ' {
383 return false
384 }
385 }
386 return true
387 }
388
389 func commonPrefix(a, b []byte) []byte {
390 i := 0
391 for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') {
392 i++
393 }
394 return a[0:i]
395 }
396
397 func stripCommonPrefix(lines [][]byte) {
398 if len(lines) < 2 {
399 return
400 }
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421 var prefix []byte
422 if len(lines) > 2 {
423 for i, line := range lines[1 : len(lines)-1] {
424 switch {
425 case isBlank(line):
426 lines[1+i] = nil
427 case prefix == nil:
428 prefix = commonPrefix(line, line)
429 default:
430 prefix = commonPrefix(prefix, line)
431 }
432 }
433 } else {
434 line := lines[1]
435 prefix = commonPrefix(line, line)
436 }
437
438 439 440
441 lineOfStars := false
442 if i := bytes.Index(prefix, []byte{'*'}); i >= 0 {
443
444 if i > 0 && prefix[i-1] == ' ' {
445 i--
446 }
447 prefix = prefix[0:i]
448 lineOfStars = true
449 } else {
450
451
452
453
454
455
456
457 first := lines[0]
458 if isBlank(first[2:]) {
459
460
461
462
463
464 i := len(prefix)
465 for n := 0; n < 3 && i > 0 && prefix[i-1] == ' '; n++ {
466 i--
467 }
468 if i == len(prefix) && i > 0 && prefix[i-1] == '\t' {
469 i--
470 }
471 prefix = prefix[0:i]
472 } else {
473
474 suffix := make([]byte, len(first))
475 n := 2
476 for n < len(first) && first[n] <= ' ' {
477 suffix[n] = first[n]
478 n++
479 }
480 if n > 2 && suffix[2] == '\t' {
481
482 suffix = suffix[2:n]
483 } else {
484
485 suffix[0], suffix[1] = ' ', ' '
486 suffix = suffix[0:n]
487 }
488
489
490 if bytes.HasSuffix(prefix, suffix) {
491 prefix = prefix[0 : len(prefix)-len(suffix)]
492 }
493 }
494 }
495
496
497
498
499 last := lines[len(lines)-1]
500 closing := []byte("*/")
501 i := bytes.Index(last, closing)
502 if isBlank(last[0:i]) {
503
504 var sep []byte
505 if lineOfStars {
506
507 sep = []byte{' '}
508 }
509 lines[len(lines)-1] = bytes.Join([][]byte{prefix, closing}, sep)
510 } else {
511
512
513 prefix = commonPrefix(prefix, last)
514 }
515
516
517 for i, line := range lines[1:] {
518 if len(line) != 0 {
519 lines[1+i] = line[len(prefix):]
520 }
521 }
522 }
523
524 func (p *printer) writeComment(comment *ast.Comment) {
525 text := comment.Text
526
527
528 if text[1] == '/' {
529 p.writeItem(p.fset.Position(comment.Pos()), p.escape(text))
530 return
531 }
532
533
534
535 lines := split([]byte(text))
536 stripCommonPrefix(lines)
537
538
539
540 linebreak := formfeeds[0:1]
541 pos := p.fset.Position(comment.Pos())
542 for i, line := range lines {
543 if i > 0 {
544 p.write(linebreak)
545 pos = p.pos
546 }
547 if len(line) > 0 {
548 p.writeItem(pos, p.escape(string(line)))
549 }
550 }
551 }
552
553
554
555
556
557
558
559 func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
560 for i, ch := range p.wsbuf {
561 switch ch {
562 case blank, vtab:
563
564 p.wsbuf[i] = ignore
565 case indent, unindent:
566
567 case newline, formfeed:
568
569
570 if needsLinebreak {
571 needsLinebreak = false
572 } else {
573 if ch == formfeed {
574 droppedFF = true
575 }
576 p.wsbuf[i] = ignore
577 }
578 }
579 }
580 p.writeWhitespace(len(p.wsbuf))
581
582
583 if needsLinebreak {
584 p.write([]byte{'\n'})
585 }
586
587 return
588 }
589
590
591
592
593
594
595
596 func (p *printer) intersperseComments(next token.Position, tok token.Token) (droppedFF bool) {
597 var last *ast.Comment
598 for ; p.commentBefore(next); p.cindex++ {
599 for _, c := range p.comments[p.cindex].List {
600 p.writeCommentPrefix(p.fset.Position(c.Pos()), next, last, tok.IsKeyword())
601 p.writeComment(c)
602 last = c
603 }
604 }
605
606 if last != nil {
607 if last.Text[1] == '*' && p.fset.Position(last.Pos()).Line == next.Line {
608
609
610 p.write([]byte{' '})
611 }
612
613
614 needsLinebreak :=
615 last.Text[1] == '/' ||
616 tok == token.RBRACE && p.mode&noExtraLinebreak == 0 ||
617 tok == token.EOF
618 return p.writeCommentSuffix(needsLinebreak)
619 }
620
621
622
623 p.internalError("intersperseComments called without pending comments")
624 return false
625 }
626
627
628 func (p *printer) writeWhitespace(n int) {
629
630 var data [1]byte
631 for i := 0; i < n; i++ {
632 switch ch := p.wsbuf[i]; ch {
633 case ignore:
634
635 case indent:
636 p.indent++
637 case unindent:
638 p.indent--
639 if p.indent < 0 {
640 p.internalError("negative indentation:", p.indent)
641 p.indent = 0
642 }
643 case newline, formfeed:
644
645
646
647
648
649
650 if i+1 < n && p.wsbuf[i+1] == unindent {
651
652
653
654
655
656 p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed
657 i--
658 continue
659 }
660 fallthrough
661 default:
662 data[0] = byte(ch)
663 p.write(data[0:])
664 }
665 }
666
667
668 i := 0
669 for ; n < len(p.wsbuf); n++ {
670 p.wsbuf[i] = p.wsbuf[n]
671 i++
672 }
673 p.wsbuf = p.wsbuf[0:i]
674 }
675
676
677
678
679
680 func mayCombine(prev token.Token, next byte) (b bool) {
681 switch prev {
682 case token.INT:
683 b = next == '.'
684 case token.ADD:
685 b = next == '+'
686 case token.SUB:
687 b = next == '-'
688 case token.QUO:
689 b = next == '*'
690 case token.LSS:
691 b = next == '-' || next == '<'
692 case token.AND:
693 b = next == '&' || next == '^'
694 }
695 return
696 }
697
698
699
700
701
702
703
704
705
706
707
708
709 func (p *printer) print(args ...interface{}) {
710 for _, f := range args {
711 next := p.pos
712 var data string
713 var tok token.Token
714
715 switch x := f.(type) {
716 case pmode:
717
718 p.mode ^= x
719 case whiteSpace:
720 if x == ignore {
721
722
723
724 break
725 }
726 i := len(p.wsbuf)
727 if i == cap(p.wsbuf) {
728
729
730
731 p.writeWhitespace(i)
732 i = 0
733 }
734 p.wsbuf = p.wsbuf[0 : i+1]
735 p.wsbuf[i] = x
736 case *ast.Ident:
737 data = x.Name
738 tok = token.IDENT
739 case *ast.BasicLit:
740 data = p.escape(x.Value)
741 tok = x.Kind
742 case token.Token:
743 s := x.String()
744 if mayCombine(p.lastTok, s[0]) {
745
746
747
748
749
750
751 if len(p.wsbuf) != 0 {
752 p.internalError("whitespace buffer not empty")
753 }
754 p.wsbuf = p.wsbuf[0:1]
755 p.wsbuf[0] = ' '
756 }
757 data = s
758 tok = x
759 case token.Pos:
760 if x.IsValid() {
761 next = p.fset.Position(x)
762 }
763 tok = p.lastTok
764 default:
765 fmt.Fprintf(os.Stderr, "print: unsupported argument type %T\n", f)
766 panic("go/printer type")
767 }
768 p.lastTok = tok
769 p.pos = next
770
771 if data != "" {
772 droppedFF := p.flush(next, tok)
773
774
775
776
777
778 p.writeNewlines(next.Line-p.pos.Line, droppedFF)
779
780 p.writeItem(next, data)
781 }
782 }
783 }
784
785
786
787
788 func (p *printer) commentBefore(next token.Position) bool {
789 return p.cindex < len(p.comments) && p.fset.Position(p.comments[p.cindex].List[0].Pos()).Offset < next.Offset
790 }
791
792
793
794
795
796
797
798 func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
799 if p.commentBefore(next) {
800
801 droppedFF = p.intersperseComments(next, tok)
802 } else {
803
804 p.writeWhitespace(len(p.wsbuf))
805 }
806 return
807 }
808
809
810
811
812
813
814
815
816
817
818 type trimmer struct {
819 output io.Writer
820 state int
821 space bytes.Buffer
822 }
823
824
825
826 const (
827 inSpace = iota
828 inEscape
829 inText
830 )
831
832
833
834
835
836
837
838 func (p *trimmer) Write(data []byte) (n int, err os.Error) {
839
840
841
842
843
844 m := 0
845 var b byte
846 for n, b = range data {
847 if b == '\v' {
848 b = '\t'
849 }
850 switch p.state {
851 case inSpace:
852 switch b {
853 case '\t', ' ':
854 p.space.WriteByte(b)
855 case '\n', '\f':
856 p.space.Reset()
857 _, err = p.output.Write(newlines[0:1])
858 case tabwriter.Escape:
859 _, err = p.output.Write(p.space.Bytes())
860 p.state = inEscape
861 m = n + 1
862 default:
863 _, err = p.output.Write(p.space.Bytes())
864 p.state = inText
865 m = n
866 }
867 case inEscape:
868 if b == tabwriter.Escape {
869 _, err = p.output.Write(data[m:n])
870 p.state = inSpace
871 p.space.Reset()
872 }
873 case inText:
874 switch b {
875 case '\t', ' ':
876 _, err = p.output.Write(data[m:n])
877 p.state = inSpace
878 p.space.Reset()
879 p.space.WriteByte(b)
880 case '\n', '\f':
881 _, err = p.output.Write(data[m:n])
882 p.state = inSpace
883 p.space.Reset()
884 _, err = p.output.Write(newlines[0:1])
885 case tabwriter.Escape:
886 _, err = p.output.Write(data[m:n])
887 p.state = inEscape
888 m = n + 1
889 }
890 default:
891 panic("unreachable")
892 }
893 if err != nil {
894 return
895 }
896 }
897 n = len(data)
898
899 switch p.state {
900 case inEscape, inText:
901 _, err = p.output.Write(data[m:n])
902 p.state = inSpace
903 p.space.Reset()
904 }
905
906 return
907 }
908
909
910
911
912
913 const (
914 RawFormat uint = 1 << iota
915 TabIndent
916 UseSpaces
917 )
918
919
920 type Config struct {
921 Mode uint
922 Tabwidth int
923 }
924
925
926 func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (int, os.Error) {
927
928
929
930
931 output = &trimmer{output: output}
932
933
934 var tw *tabwriter.Writer
935 if cfg.Mode&RawFormat == 0 {
936 minwidth := cfg.Tabwidth
937
938 padchar := byte('\t')
939 if cfg.Mode&UseSpaces != 0 {
940 padchar = ' '
941 }
942
943 twmode := tabwriter.DiscardEmptyColumns
944 if cfg.Mode&TabIndent != 0 {
945 minwidth = 0
946 twmode |= tabwriter.TabIndent
947 }
948
949 tw = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padchar, twmode)
950 output = tw
951 }
952
953
954 var p printer
955 p.init(output, cfg, fset, nodeSizes)
956 go func() {
957 switch n := node.(type) {
958 case ast.Expr:
959 p.useNodeComments = true
960 p.expr(n, ignoreMultiLine)
961 case ast.Stmt:
962 p.useNodeComments = true
963
964
965 if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt {
966 p.indent = 1
967 }
968 p.stmt(n, false, ignoreMultiLine)
969 case ast.Decl:
970 p.useNodeComments = true
971 p.decl(n, ignoreMultiLine)
972 case ast.Spec:
973 p.useNodeComments = true
974 p.spec(n, 1, false, ignoreMultiLine)
975 case *ast.File:
976 p.comments = n.Comments
977 p.useNodeComments = n.Comments == nil
978 p.file(n)
979 default:
980 p.errors <- fmt.Errorf("printer.Fprint: unsupported node type %T", n)
981 runtime.Goexit()
982 }
983 p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF)
984 p.errors <- nil
985 }()
986 err := <-p.errors
987
988
989 if tw != nil {
990 tw.Flush()
991 }
992
993 return p.written, err
994 }
995
996
997
998
999
1000
1001
1002 func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) (int, os.Error) {
1003 return cfg.fprint(output, fset, node, make(map[ast.Node]int))
1004 }
1005
1006
1007
1008
1009 func Fprint(output io.Writer, fset *token.FileSet, node interface{}) os.Error {
1010 _, err := (&Config{Tabwidth: 8}).Fprint(output, fset, node)
1011 return err
1012 }