1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// This file implements printing of syntax trees in source format.
6
7package syntax
8
9import (
10	"bytes"
11	"fmt"
12	"io"
13	"strings"
14)
15
16// TODO(gri) Consider removing the linebreaks flag from this signature.
17// Its likely rarely used in common cases.
18
19func Fprint(w io.Writer, x Node, linebreaks bool) (n int, err error) {
20	p := printer{
21		output:     w,
22		linebreaks: linebreaks,
23	}
24
25	defer func() {
26		n = p.written
27		if e := recover(); e != nil {
28			err = e.(localError).err // re-panics if it's not a localError
29		}
30	}()
31
32	p.print(x)
33	p.flush(_EOF)
34
35	return
36}
37
38func String(n Node) string {
39	var buf bytes.Buffer
40	_, err := Fprint(&buf, n, false)
41	if err != nil {
42		panic(err) // TODO(gri) print something sensible into buf instead
43	}
44	return buf.String()
45}
46
47type ctrlSymbol int
48
49const (
50	none ctrlSymbol = iota
51	semi
52	blank
53	newline
54	indent
55	outdent
56	// comment
57	// eolComment
58)
59
60type whitespace struct {
61	last token
62	kind ctrlSymbol
63	//text string // comment text (possibly ""); valid if kind == comment
64}
65
66type printer struct {
67	output     io.Writer
68	written    int  // number of bytes written
69	linebreaks bool // print linebreaks instead of semis
70
71	indent  int // current indentation level
72	nlcount int // number of consecutive newlines
73
74	pending []whitespace // pending whitespace
75	lastTok token        // last token (after any pending semi) processed by print
76}
77
78// write is a thin wrapper around p.output.Write
79// that takes care of accounting and error handling.
80func (p *printer) write(data []byte) {
81	n, err := p.output.Write(data)
82	p.written += n
83	if err != nil {
84		panic(localError{err})
85	}
86}
87
88var (
89	tabBytes    = []byte("\t\t\t\t\t\t\t\t")
90	newlineByte = []byte("\n")
91	blankByte   = []byte(" ")
92)
93
94func (p *printer) writeBytes(data []byte) {
95	if len(data) == 0 {
96		panic("expected non-empty []byte")
97	}
98	if p.nlcount > 0 && p.indent > 0 {
99		// write indentation
100		n := p.indent
101		for n > len(tabBytes) {
102			p.write(tabBytes)
103			n -= len(tabBytes)
104		}
105		p.write(tabBytes[:n])
106	}
107	p.write(data)
108	p.nlcount = 0
109}
110
111func (p *printer) writeString(s string) {
112	p.writeBytes([]byte(s))
113}
114
115// If impliesSemi returns true for a non-blank line's final token tok,
116// a semicolon is automatically inserted. Vice versa, a semicolon may
117// be omitted in those cases.
118func impliesSemi(tok token) bool {
119	switch tok {
120	case _Name,
121		_Break, _Continue, _Fallthrough, _Return,
122		/*_Inc, _Dec,*/ _Rparen, _Rbrack, _Rbrace: // TODO(gri) fix this
123		return true
124	}
125	return false
126}
127
128// TODO(gri) provide table of []byte values for all tokens to avoid repeated string conversion
129
130func lineComment(text string) bool {
131	return strings.HasPrefix(text, "//")
132}
133
134func (p *printer) addWhitespace(kind ctrlSymbol, text string) {
135	p.pending = append(p.pending, whitespace{p.lastTok, kind /*text*/})
136	switch kind {
137	case semi:
138		p.lastTok = _Semi
139	case newline:
140		p.lastTok = 0
141		// TODO(gri) do we need to handle /*-style comments containing newlines here?
142	}
143}
144
145func (p *printer) flush(next token) {
146	// eliminate semis and redundant whitespace
147	sawNewline := next == _EOF
148	sawParen := next == _Rparen || next == _Rbrace
149	for i := len(p.pending) - 1; i >= 0; i-- {
150		switch p.pending[i].kind {
151		case semi:
152			k := semi
153			if sawParen {
154				sawParen = false
155				k = none // eliminate semi
156			} else if sawNewline && impliesSemi(p.pending[i].last) {
157				sawNewline = false
158				k = none // eliminate semi
159			}
160			p.pending[i].kind = k
161		case newline:
162			sawNewline = true
163		case blank, indent, outdent:
164			// nothing to do
165		// case comment:
166		// 	// A multi-line comment acts like a newline; and a ""
167		// 	// comment implies by definition at least one newline.
168		// 	if text := p.pending[i].text; strings.HasPrefix(text, "/*") && strings.ContainsRune(text, '\n') {
169		// 		sawNewline = true
170		// 	}
171		// case eolComment:
172		// 	// TODO(gri) act depending on sawNewline
173		default:
174			panic("unreachable")
175		}
176	}
177
178	// print pending
179	prev := none
180	for i := range p.pending {
181		switch p.pending[i].kind {
182		case none:
183			// nothing to do
184		case semi:
185			p.writeString(";")
186			p.nlcount = 0
187			prev = semi
188		case blank:
189			if prev != blank {
190				// at most one blank
191				p.writeBytes(blankByte)
192				p.nlcount = 0
193				prev = blank
194			}
195		case newline:
196			const maxEmptyLines = 1
197			if p.nlcount <= maxEmptyLines {
198				p.write(newlineByte)
199				p.nlcount++
200				prev = newline
201			}
202		case indent:
203			p.indent++
204		case outdent:
205			p.indent--
206			if p.indent < 0 {
207				panic("negative indentation")
208			}
209		// case comment:
210		// 	if text := p.pending[i].text; text != "" {
211		// 		p.writeString(text)
212		// 		p.nlcount = 0
213		// 		prev = comment
214		// 	}
215		// 	// TODO(gri) should check that line comments are always followed by newline
216		default:
217			panic("unreachable")
218		}
219	}
220
221	p.pending = p.pending[:0] // re-use underlying array
222}
223
224func mayCombine(prev token, next byte) (b bool) {
225	return // for now
226	// switch prev {
227	// case lexical.Int:
228	// 	b = next == '.' // 1.
229	// case lexical.Add:
230	// 	b = next == '+' // ++
231	// case lexical.Sub:
232	// 	b = next == '-' // --
233	// case lexical.Quo:
234	// 	b = next == '*' // /*
235	// case lexical.Lss:
236	// 	b = next == '-' || next == '<' // <- or <<
237	// case lexical.And:
238	// 	b = next == '&' || next == '^' // && or &^
239	// }
240	// return
241}
242
243func (p *printer) print(args ...interface{}) {
244	for i := 0; i < len(args); i++ {
245		switch x := args[i].(type) {
246		case nil:
247			// we should not reach here but don't crash
248
249		case Node:
250			p.printNode(x)
251
252		case token:
253			// _Name implies an immediately following string
254			// argument which is the actual value to print.
255			var s string
256			if x == _Name {
257				i++
258				if i >= len(args) {
259					panic("missing string argument after _Name")
260				}
261				s = args[i].(string)
262			} else {
263				s = x.String()
264			}
265
266			// TODO(gri) This check seems at the wrong place since it doesn't
267			//           take into account pending white space.
268			if mayCombine(p.lastTok, s[0]) {
269				panic("adjacent tokens combine without whitespace")
270			}
271
272			if x == _Semi {
273				// delay printing of semi
274				p.addWhitespace(semi, "")
275			} else {
276				p.flush(x)
277				p.writeString(s)
278				p.nlcount = 0
279				p.lastTok = x
280			}
281
282		case Operator:
283			if x != 0 {
284				p.flush(_Operator)
285				p.writeString(x.String())
286			}
287
288		case ctrlSymbol:
289			switch x {
290			case none, semi /*, comment*/ :
291				panic("unreachable")
292			case newline:
293				// TODO(gri) need to handle mandatory newlines after a //-style comment
294				if !p.linebreaks {
295					x = blank
296				}
297			}
298			p.addWhitespace(x, "")
299
300		// case *Comment: // comments are not Nodes
301		// 	p.addWhitespace(comment, x.Text)
302
303		default:
304			panic(fmt.Sprintf("unexpected argument %v (%T)", x, x))
305		}
306	}
307}
308
309func (p *printer) printNode(n Node) {
310	// ncom := *n.Comments()
311	// if ncom != nil {
312	// 	// TODO(gri) in general we cannot make assumptions about whether
313	// 	// a comment is a /*- or a //-style comment since the syntax
314	// 	// tree may have been manipulated. Need to make sure the correct
315	// 	// whitespace is emitted.
316	// 	for _, c := range ncom.Alone {
317	// 		p.print(c, newline)
318	// 	}
319	// 	for _, c := range ncom.Before {
320	// 		if c.Text == "" || lineComment(c.Text) {
321	// 			panic("unexpected empty line or //-style 'before' comment")
322	// 		}
323	// 		p.print(c, blank)
324	// 	}
325	// }
326
327	p.printRawNode(n)
328
329	// if ncom != nil && len(ncom.After) > 0 {
330	// 	for i, c := range ncom.After {
331	// 		if i+1 < len(ncom.After) {
332	// 			if c.Text == "" || lineComment(c.Text) {
333	// 				panic("unexpected empty line or //-style non-final 'after' comment")
334	// 			}
335	// 		}
336	// 		p.print(blank, c)
337	// 	}
338	// 	//p.print(newline)
339	// }
340}
341
342func (p *printer) printRawNode(n Node) {
343	switch n := n.(type) {
344	case nil:
345		// we should not reach here but don't crash
346
347	// expressions and types
348	case *BadExpr:
349		p.print(_Name, "<bad expr>")
350
351	case *Name:
352		p.print(_Name, n.Value) // _Name requires actual value following immediately
353
354	case *BasicLit:
355		p.print(_Name, n.Value) // _Name requires actual value following immediately
356
357	case *FuncLit:
358		p.print(n.Type, blank, n.Body)
359
360	case *CompositeLit:
361		if n.Type != nil {
362			p.print(n.Type)
363		}
364		p.print(_Lbrace)
365		if n.NKeys > 0 && n.NKeys == len(n.ElemList) {
366			p.printExprLines(n.ElemList)
367		} else {
368			p.printExprList(n.ElemList)
369		}
370		p.print(_Rbrace)
371
372	case *ParenExpr:
373		p.print(_Lparen, n.X, _Rparen)
374
375	case *SelectorExpr:
376		p.print(n.X, _Dot, n.Sel)
377
378	case *IndexExpr:
379		p.print(n.X, _Lbrack, n.Index, _Rbrack)
380
381	case *SliceExpr:
382		p.print(n.X, _Lbrack)
383		if i := n.Index[0]; i != nil {
384			p.printNode(i)
385		}
386		p.print(_Colon)
387		if j := n.Index[1]; j != nil {
388			p.printNode(j)
389		}
390		if k := n.Index[2]; k != nil {
391			p.print(_Colon, k)
392		}
393		p.print(_Rbrack)
394
395	case *AssertExpr:
396		p.print(n.X, _Dot, _Lparen, n.Type, _Rparen)
397
398	case *TypeSwitchGuard:
399		if n.Lhs != nil {
400			p.print(n.Lhs, blank, _Define, blank)
401		}
402		p.print(n.X, _Dot, _Lparen, _Type, _Rparen)
403
404	case *CallExpr:
405		p.print(n.Fun, _Lparen)
406		p.printExprList(n.ArgList)
407		if n.HasDots {
408			p.print(_DotDotDot)
409		}
410		p.print(_Rparen)
411
412	case *Operation:
413		if n.Y == nil {
414			// unary expr
415			p.print(n.Op)
416			// if n.Op == lexical.Range {
417			// 	p.print(blank)
418			// }
419			p.print(n.X)
420		} else {
421			// binary expr
422			// TODO(gri) eventually take precedence into account
423			// to control possibly missing parentheses
424			p.print(n.X, blank, n.Op, blank, n.Y)
425		}
426
427	case *KeyValueExpr:
428		p.print(n.Key, _Colon, blank, n.Value)
429
430	case *ListExpr:
431		p.printExprList(n.ElemList)
432
433	case *ArrayType:
434		var len interface{} = _DotDotDot
435		if n.Len != nil {
436			len = n.Len
437		}
438		p.print(_Lbrack, len, _Rbrack, n.Elem)
439
440	case *SliceType:
441		p.print(_Lbrack, _Rbrack, n.Elem)
442
443	case *DotsType:
444		p.print(_DotDotDot, n.Elem)
445
446	case *StructType:
447		p.print(_Struct)
448		if len(n.FieldList) > 0 && p.linebreaks {
449			p.print(blank)
450		}
451		p.print(_Lbrace)
452		if len(n.FieldList) > 0 {
453			p.print(newline, indent)
454			p.printFieldList(n.FieldList, n.TagList)
455			p.print(outdent, newline)
456		}
457		p.print(_Rbrace)
458
459	case *FuncType:
460		p.print(_Func)
461		p.printSignature(n)
462
463	case *InterfaceType:
464		p.print(_Interface)
465		if len(n.MethodList) > 0 && p.linebreaks {
466			p.print(blank)
467		}
468		p.print(_Lbrace)
469		if len(n.MethodList) > 0 {
470			p.print(newline, indent)
471			p.printMethodList(n.MethodList)
472			p.print(outdent, newline)
473		}
474		p.print(_Rbrace)
475
476	case *MapType:
477		p.print(_Map, _Lbrack, n.Key, _Rbrack, n.Value)
478
479	case *ChanType:
480		if n.Dir == RecvOnly {
481			p.print(_Arrow)
482		}
483		p.print(_Chan)
484		if n.Dir == SendOnly {
485			p.print(_Arrow)
486		}
487		p.print(blank, n.Elem)
488
489	// statements
490	case *DeclStmt:
491		p.printDecl(n.DeclList)
492
493	case *EmptyStmt:
494		// nothing to print
495
496	case *LabeledStmt:
497		p.print(outdent, n.Label, _Colon, indent, newline, n.Stmt)
498
499	case *ExprStmt:
500		p.print(n.X)
501
502	case *SendStmt:
503		p.print(n.Chan, blank, _Arrow, blank, n.Value)
504
505	case *AssignStmt:
506		p.print(n.Lhs)
507		if n.Rhs == ImplicitOne {
508			// TODO(gri) This is going to break the mayCombine
509			//           check once we enable that again.
510			p.print(n.Op, n.Op) // ++ or --
511		} else {
512			p.print(blank, n.Op, _Assign, blank)
513			p.print(n.Rhs)
514		}
515
516	case *CallStmt:
517		p.print(n.Tok, blank, n.Call)
518
519	case *ReturnStmt:
520		p.print(_Return)
521		if n.Results != nil {
522			p.print(blank, n.Results)
523		}
524
525	case *BranchStmt:
526		p.print(n.Tok)
527		if n.Label != nil {
528			p.print(blank, n.Label)
529		}
530
531	case *BlockStmt:
532		p.print(_Lbrace)
533		if len(n.List) > 0 {
534			p.print(newline, indent)
535			p.printStmtList(n.List, true)
536			p.print(outdent, newline)
537		}
538		p.print(_Rbrace)
539
540	case *IfStmt:
541		p.print(_If, blank)
542		if n.Init != nil {
543			p.print(n.Init, _Semi, blank)
544		}
545		p.print(n.Cond, blank, n.Then)
546		if n.Else != nil {
547			p.print(blank, _Else, blank, n.Else)
548		}
549
550	case *SwitchStmt:
551		p.print(_Switch, blank)
552		if n.Init != nil {
553			p.print(n.Init, _Semi, blank)
554		}
555		if n.Tag != nil {
556			p.print(n.Tag, blank)
557		}
558		p.printSwitchBody(n.Body)
559
560	case *SelectStmt:
561		p.print(_Select, blank) // for now
562		p.printSelectBody(n.Body)
563
564	case *RangeClause:
565		if n.Lhs != nil {
566			tok := _Assign
567			if n.Def {
568				tok = _Define
569			}
570			p.print(n.Lhs, blank, tok, blank)
571		}
572		p.print(_Range, blank, n.X)
573
574	case *ForStmt:
575		p.print(_For, blank)
576		if n.Init == nil && n.Post == nil {
577			if n.Cond != nil {
578				p.print(n.Cond, blank)
579			}
580		} else {
581			if n.Init != nil {
582				p.print(n.Init)
583				// TODO(gri) clean this up
584				if _, ok := n.Init.(*RangeClause); ok {
585					p.print(blank, n.Body)
586					break
587				}
588			}
589			p.print(_Semi, blank)
590			if n.Cond != nil {
591				p.print(n.Cond)
592			}
593			p.print(_Semi, blank)
594			if n.Post != nil {
595				p.print(n.Post, blank)
596			}
597		}
598		p.print(n.Body)
599
600	case *ImportDecl:
601		if n.Group == nil {
602			p.print(_Import, blank)
603		}
604		if n.LocalPkgName != nil {
605			p.print(n.LocalPkgName, blank)
606		}
607		p.print(n.Path)
608
609	case *ConstDecl:
610		if n.Group == nil {
611			p.print(_Const, blank)
612		}
613		p.printNameList(n.NameList)
614		if n.Type != nil {
615			p.print(blank, n.Type)
616		}
617		if n.Values != nil {
618			p.print(blank, _Assign, blank, n.Values)
619		}
620
621	case *TypeDecl:
622		if n.Group == nil {
623			p.print(_Type, blank)
624		}
625		p.print(n.Name, blank)
626		if n.Alias {
627			p.print(_Assign, blank)
628		}
629		p.print(n.Type)
630
631	case *VarDecl:
632		if n.Group == nil {
633			p.print(_Var, blank)
634		}
635		p.printNameList(n.NameList)
636		if n.Type != nil {
637			p.print(blank, n.Type)
638		}
639		if n.Values != nil {
640			p.print(blank, _Assign, blank, n.Values)
641		}
642
643	case *FuncDecl:
644		p.print(_Func, blank)
645		if r := n.Recv; r != nil {
646			p.print(_Lparen)
647			if r.Name != nil {
648				p.print(r.Name, blank)
649			}
650			p.printNode(r.Type)
651			p.print(_Rparen, blank)
652		}
653		p.print(n.Name)
654		p.printSignature(n.Type)
655		if n.Body != nil {
656			p.print(blank, n.Body)
657		}
658
659	case *printGroup:
660		p.print(n.Tok, blank, _Lparen)
661		if len(n.Decls) > 0 {
662			p.print(newline, indent)
663			for _, d := range n.Decls {
664				p.printNode(d)
665				p.print(_Semi, newline)
666			}
667			p.print(outdent)
668		}
669		p.print(_Rparen)
670
671	// files
672	case *File:
673		p.print(_Package, blank, n.PkgName)
674		if len(n.DeclList) > 0 {
675			p.print(_Semi, newline, newline)
676			p.printDeclList(n.DeclList)
677		}
678
679	default:
680		panic(fmt.Sprintf("syntax.Iterate: unexpected node type %T", n))
681	}
682}
683
684func (p *printer) printFields(fields []*Field, tags []*BasicLit, i, j int) {
685	if i+1 == j && fields[i].Name == nil {
686		// anonymous field
687		p.printNode(fields[i].Type)
688	} else {
689		for k, f := range fields[i:j] {
690			if k > 0 {
691				p.print(_Comma, blank)
692			}
693			p.printNode(f.Name)
694		}
695		p.print(blank)
696		p.printNode(fields[i].Type)
697	}
698	if i < len(tags) && tags[i] != nil {
699		p.print(blank)
700		p.printNode(tags[i])
701	}
702}
703
704func (p *printer) printFieldList(fields []*Field, tags []*BasicLit) {
705	i0 := 0
706	var typ Expr
707	for i, f := range fields {
708		if f.Name == nil || f.Type != typ {
709			if i0 < i {
710				p.printFields(fields, tags, i0, i)
711				p.print(_Semi, newline)
712				i0 = i
713			}
714			typ = f.Type
715		}
716	}
717	p.printFields(fields, tags, i0, len(fields))
718}
719
720func (p *printer) printMethodList(methods []*Field) {
721	for i, m := range methods {
722		if i > 0 {
723			p.print(_Semi, newline)
724		}
725		if m.Name != nil {
726			p.printNode(m.Name)
727			p.printSignature(m.Type.(*FuncType))
728		} else {
729			p.printNode(m.Type)
730		}
731	}
732}
733
734func (p *printer) printNameList(list []*Name) {
735	for i, x := range list {
736		if i > 0 {
737			p.print(_Comma, blank)
738		}
739		p.printNode(x)
740	}
741}
742
743func (p *printer) printExprList(list []Expr) {
744	for i, x := range list {
745		if i > 0 {
746			p.print(_Comma, blank)
747		}
748		p.printNode(x)
749	}
750}
751
752func (p *printer) printExprLines(list []Expr) {
753	if len(list) > 0 {
754		p.print(newline, indent)
755		for _, x := range list {
756			p.print(x, _Comma, newline)
757		}
758		p.print(outdent)
759	}
760}
761
762func groupFor(d Decl) (token, *Group) {
763	switch d := d.(type) {
764	case *ImportDecl:
765		return _Import, d.Group
766	case *ConstDecl:
767		return _Const, d.Group
768	case *TypeDecl:
769		return _Type, d.Group
770	case *VarDecl:
771		return _Var, d.Group
772	case *FuncDecl:
773		return _Func, nil
774	default:
775		panic("unreachable")
776	}
777}
778
779type printGroup struct {
780	node
781	Tok   token
782	Decls []Decl
783}
784
785func (p *printer) printDecl(list []Decl) {
786	tok, group := groupFor(list[0])
787
788	if group == nil {
789		if len(list) != 1 {
790			panic("unreachable")
791		}
792		p.printNode(list[0])
793		return
794	}
795
796	// if _, ok := list[0].(*EmptyDecl); ok {
797	// 	if len(list) != 1 {
798	// 		panic("unreachable")
799	// 	}
800	// 	// TODO(gri) if there are comments inside the empty
801	// 	// group, we may need to keep the list non-nil
802	// 	list = nil
803	// }
804
805	// printGroup is here for consistent comment handling
806	// (this is not yet used)
807	var pg printGroup
808	// *pg.Comments() = *group.Comments()
809	pg.Tok = tok
810	pg.Decls = list
811	p.printNode(&pg)
812}
813
814func (p *printer) printDeclList(list []Decl) {
815	i0 := 0
816	var tok token
817	var group *Group
818	for i, x := range list {
819		if s, g := groupFor(x); g == nil || g != group {
820			if i0 < i {
821				p.printDecl(list[i0:i])
822				p.print(_Semi, newline)
823				// print empty line between different declaration groups,
824				// different kinds of declarations, or between functions
825				if g != group || s != tok || s == _Func {
826					p.print(newline)
827				}
828				i0 = i
829			}
830			tok, group = s, g
831		}
832	}
833	p.printDecl(list[i0:])
834}
835
836func (p *printer) printSignature(sig *FuncType) {
837	p.printParameterList(sig.ParamList)
838	if list := sig.ResultList; list != nil {
839		p.print(blank)
840		if len(list) == 1 && list[0].Name == nil {
841			p.printNode(list[0].Type)
842		} else {
843			p.printParameterList(list)
844		}
845	}
846}
847
848func (p *printer) printParameterList(list []*Field) {
849	p.print(_Lparen)
850	if len(list) > 0 {
851		for i, f := range list {
852			if i > 0 {
853				p.print(_Comma, blank)
854			}
855			if f.Name != nil {
856				p.printNode(f.Name)
857				if i+1 < len(list) {
858					f1 := list[i+1]
859					if f1.Name != nil && f1.Type == f.Type {
860						continue // no need to print type
861					}
862				}
863				p.print(blank)
864			}
865			p.printNode(f.Type)
866		}
867	}
868	p.print(_Rparen)
869}
870
871func (p *printer) printStmtList(list []Stmt, braces bool) {
872	for i, x := range list {
873		p.print(x, _Semi)
874		if i+1 < len(list) {
875			p.print(newline)
876		} else if braces {
877			// Print an extra semicolon if the last statement is
878			// an empty statement and we are in a braced block
879			// because one semicolon is automatically removed.
880			if _, ok := x.(*EmptyStmt); ok {
881				p.print(x, _Semi)
882			}
883		}
884	}
885}
886
887func (p *printer) printSwitchBody(list []*CaseClause) {
888	p.print(_Lbrace)
889	if len(list) > 0 {
890		p.print(newline)
891		for i, c := range list {
892			p.printCaseClause(c, i+1 == len(list))
893			p.print(newline)
894		}
895	}
896	p.print(_Rbrace)
897}
898
899func (p *printer) printSelectBody(list []*CommClause) {
900	p.print(_Lbrace)
901	if len(list) > 0 {
902		p.print(newline)
903		for i, c := range list {
904			p.printCommClause(c, i+1 == len(list))
905			p.print(newline)
906		}
907	}
908	p.print(_Rbrace)
909}
910
911func (p *printer) printCaseClause(c *CaseClause, braces bool) {
912	if c.Cases != nil {
913		p.print(_Case, blank, c.Cases)
914	} else {
915		p.print(_Default)
916	}
917	p.print(_Colon)
918	if len(c.Body) > 0 {
919		p.print(newline, indent)
920		p.printStmtList(c.Body, braces)
921		p.print(outdent)
922	}
923}
924
925func (p *printer) printCommClause(c *CommClause, braces bool) {
926	if c.Comm != nil {
927		p.print(_Case, blank)
928		p.print(c.Comm)
929	} else {
930		p.print(_Default)
931	}
932	p.print(_Colon)
933	if len(c.Body) > 0 {
934		p.print(newline, indent)
935		p.printStmtList(c.Body, braces)
936		p.print(outdent)
937	}
938}
939