github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/src/cmd/compile/internal/syntax/printer.go (about) 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 7 package syntax 8 9 import ( 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 19 func 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 38 func 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 47 type ctrlSymbol int 48 49 const ( 50 none ctrlSymbol = iota 51 semi 52 blank 53 newline 54 indent 55 outdent 56 // comment 57 // eolComment 58 ) 59 60 type whitespace struct { 61 last token 62 kind ctrlSymbol 63 //text string // comment text (possibly ""); valid if kind == comment 64 } 65 66 type 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. 80 func (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 88 var ( 89 tabBytes = []byte("\t\t\t\t\t\t\t\t") 90 newlineByte = []byte("\n") 91 blankByte = []byte(" ") 92 ) 93 94 func (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 111 func (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. 118 func 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 130 func lineComment(text string) bool { 131 return strings.HasPrefix(text, "//") 132 } 133 134 func (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 145 func (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 224 func 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 243 func (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 309 func (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 342 func (p *printer) printRawNode(n Node) { 343 switch n := n.(type) { 344 // expressions and types 345 case *Name: 346 p.print(_Name, n.Value) // _Name requires actual value following immediately 347 348 case *BasicLit: 349 p.print(_Name, n.Value) // _Name requires actual value following immediately 350 351 case *FuncLit: 352 p.print(n.Type, blank) 353 p.printBody(n.Body) 354 355 case *CompositeLit: 356 if n.Type != nil { 357 p.print(n.Type) 358 } 359 p.print(_Lbrace) 360 if n.NKeys > 0 && n.NKeys == len(n.ElemList) { 361 p.printExprLines(n.ElemList) 362 } else { 363 p.printExprList(n.ElemList) 364 } 365 p.print(_Rbrace) 366 367 case *ParenExpr: 368 p.print(_Lparen, n.X, _Rparen) 369 370 case *SelectorExpr: 371 p.print(n.X, _Dot, n.Sel) 372 373 case *IndexExpr: 374 p.print(n.X, _Lbrack, n.Index, _Rbrack) 375 376 case *SliceExpr: 377 p.print(n.X, _Lbrack) 378 if i := n.Index[0]; i != nil { 379 p.printNode(i) 380 } 381 p.print(_Colon) 382 if j := n.Index[1]; j != nil { 383 p.printNode(j) 384 } 385 if k := n.Index[2]; k != nil { 386 p.print(_Colon, k) 387 } 388 p.print(_Rbrack) 389 390 case *AssertExpr: 391 p.print(n.X, _Dot, _Lparen) 392 if n.Type != nil { 393 p.printNode(n.Type) 394 } else { 395 p.print(_Type) 396 } 397 p.print(_Rparen) 398 399 case *CallExpr: 400 p.print(n.Fun, _Lparen) 401 p.printExprList(n.ArgList) 402 if n.HasDots { 403 p.print(_DotDotDot) 404 } 405 p.print(_Rparen) 406 407 case *Operation: 408 if n.Y == nil { 409 // unary expr 410 p.print(n.Op) 411 // if n.Op == lexical.Range { 412 // p.print(blank) 413 // } 414 p.print(n.X) 415 } else { 416 // binary expr 417 // TODO(gri) eventually take precedence into account 418 // to control possibly missing parentheses 419 p.print(n.X, blank, n.Op, blank, n.Y) 420 } 421 422 case *KeyValueExpr: 423 p.print(n.Key, _Colon, blank, n.Value) 424 425 case *ListExpr: 426 p.printExprList(n.ElemList) 427 428 case *ArrayType: 429 var len interface{} = _DotDotDot 430 if n.Len != nil { 431 len = n.Len 432 } 433 p.print(_Lbrack, len, _Rbrack, n.Elem) 434 435 case *SliceType: 436 p.print(_Lbrack, _Rbrack, n.Elem) 437 438 case *DotsType: 439 p.print(_DotDotDot, n.Elem) 440 441 case *StructType: 442 p.print(_Struct) 443 if len(n.FieldList) > 0 && p.linebreaks { 444 p.print(blank) 445 } 446 p.print(_Lbrace) 447 if len(n.FieldList) > 0 { 448 p.print(newline, indent) 449 p.printFieldList(n.FieldList, n.TagList) 450 p.print(outdent, newline) 451 } 452 p.print(_Rbrace) 453 454 case *FuncType: 455 p.print(_Func) 456 p.printSignature(n) 457 458 case *InterfaceType: 459 p.print(_Interface) 460 if len(n.MethodList) > 0 && p.linebreaks { 461 p.print(blank) 462 } 463 p.print(_Lbrace) 464 if len(n.MethodList) > 0 { 465 p.print(newline, indent) 466 p.printMethodList(n.MethodList) 467 p.print(outdent, newline) 468 } 469 p.print(_Rbrace) 470 471 case *MapType: 472 p.print(_Map, _Lbrack, n.Key, _Rbrack, n.Value) 473 474 case *ChanType: 475 if n.Dir == RecvOnly { 476 p.print(_Arrow) 477 } 478 p.print(_Chan) 479 if n.Dir == SendOnly { 480 p.print(_Arrow) 481 } 482 p.print(blank, n.Elem) 483 484 // statements 485 case *DeclStmt: 486 p.printDecl(n.DeclList) 487 488 case *EmptyStmt: 489 // nothing to print 490 491 case *LabeledStmt: 492 p.print(outdent, n.Label, _Colon, indent, newline, n.Stmt) 493 494 case *ExprStmt: 495 p.print(n.X) 496 497 case *SendStmt: 498 p.print(n.Chan, blank, _Arrow, blank, n.Value) 499 500 case *AssignStmt: 501 p.print(n.Lhs) 502 if n.Rhs == ImplicitOne { 503 // TODO(gri) This is going to break the mayCombine 504 // check once we enable that again. 505 p.print(n.Op, n.Op) // ++ or -- 506 } else { 507 p.print(blank, n.Op, _Assign, blank) 508 p.print(n.Rhs) 509 } 510 511 case *CallStmt: 512 p.print(n.Tok, blank, n.Call) 513 514 case *ReturnStmt: 515 p.print(_Return) 516 if n.Results != nil { 517 p.print(blank, n.Results) 518 } 519 520 case *BranchStmt: 521 p.print(n.Tok) 522 if n.Label != nil { 523 p.print(blank, n.Label) 524 } 525 526 case *BlockStmt: 527 p.printBody(n.Body) 528 529 case *IfStmt: 530 p.print(_If, blank) 531 if n.Init != nil { 532 p.print(n.Init, _Semi, blank) 533 } 534 p.print(n.Cond, blank) 535 p.printBody(n.Then) 536 if n.Else != nil { 537 p.print(blank, _Else, blank, n.Else) 538 } 539 540 case *SwitchStmt: 541 p.print(_Switch, blank) 542 if n.Init != nil { 543 p.print(n.Init, _Semi, blank) 544 } 545 if n.Tag != nil { 546 p.print(n.Tag, blank) 547 } 548 p.printSwitchBody(n.Body) 549 550 case *TypeSwitchGuard: 551 if n.Lhs != nil { 552 p.print(n.Lhs, blank, _Define, blank) 553 } 554 p.print(n.X, _Dot, _Lparen, _Type, _Rparen) 555 556 case *SelectStmt: 557 p.print(_Select, blank) // for now 558 p.printSelectBody(n.Body) 559 560 case *RangeClause: 561 if n.Lhs != nil { 562 tok := _Assign 563 if n.Def { 564 tok = _Define 565 } 566 p.print(n.Lhs, blank, tok, blank) 567 } 568 p.print(_Range, blank, n.X) 569 570 case *ForStmt: 571 p.print(_For, blank) 572 if n.Init == nil && n.Post == nil { 573 if n.Cond != nil { 574 p.print(n.Cond, blank) 575 } 576 } else { 577 if n.Init != nil { 578 p.print(n.Init) 579 // TODO(gri) clean this up 580 if _, ok := n.Init.(*RangeClause); ok { 581 p.print(blank) 582 p.printBody(n.Body) 583 break 584 } 585 } 586 p.print(_Semi, blank) 587 if n.Cond != nil { 588 p.print(n.Cond) 589 } 590 p.print(_Semi, blank) 591 if n.Post != nil { 592 p.print(n.Post, blank) 593 } 594 } 595 p.printBody(n.Body) 596 597 case *ImportDecl: 598 if n.Group == nil { 599 p.print(_Import, blank) 600 } 601 if n.LocalPkgName != nil { 602 p.print(n.LocalPkgName, blank) 603 } 604 p.print(n.Path) 605 606 case *ConstDecl: 607 if n.Group == nil { 608 p.print(_Const, blank) 609 } 610 p.printNameList(n.NameList) 611 if n.Type != nil { 612 p.print(blank, n.Type) 613 } 614 if n.Values != nil { 615 p.print(blank, _Assign, blank, n.Values) 616 } 617 618 case *TypeDecl: 619 if n.Group == nil { 620 p.print(_Type, blank) 621 } 622 p.print(n.Name, blank, n.Type) 623 624 case *VarDecl: 625 if n.Group == nil { 626 p.print(_Var, blank) 627 } 628 p.printNameList(n.NameList) 629 if n.Type != nil { 630 p.print(blank, n.Type) 631 } 632 if n.Values != nil { 633 p.print(blank, _Assign, blank, n.Values) 634 } 635 636 case *FuncDecl: 637 p.print(_Func, blank) 638 if r := n.Recv; r != nil { 639 p.print(_Lparen) 640 if r.Name != nil { 641 p.print(r.Name, blank) 642 } 643 p.printNode(r.Type) 644 p.print(_Rparen, blank) 645 } 646 p.print(n.Name) 647 p.printSignature(n.Type) 648 if n.Body != nil { 649 p.print(blank) 650 p.printBody(n.Body) 651 } 652 653 case *printGroup: 654 p.print(n.Tok, blank, _Lparen) 655 if len(n.Decls) > 0 { 656 p.print(newline, indent) 657 for _, d := range n.Decls { 658 p.printNode(d) 659 p.print(_Semi, newline) 660 } 661 p.print(outdent) 662 } 663 p.print(_Rparen) 664 665 // files 666 case *File: 667 p.print(_Package, blank, n.PkgName) 668 if len(n.DeclList) > 0 { 669 p.print(_Semi, newline, newline) 670 p.printDeclList(n.DeclList) 671 } 672 673 default: 674 panic(fmt.Sprintf("syntax.Iterate: unexpected node type %T", n)) 675 } 676 } 677 678 func (p *printer) printFields(fields []*Field, tags []*BasicLit, i, j int) { 679 if i+1 == j && fields[i].Name == nil { 680 // anonymous field 681 p.printNode(fields[i].Type) 682 } else { 683 for k, f := range fields[i:j] { 684 if k > 0 { 685 p.print(_Comma, blank) 686 } 687 p.printNode(f.Name) 688 } 689 p.print(blank) 690 p.printNode(fields[i].Type) 691 } 692 if i < len(tags) && tags[i] != nil { 693 p.print(blank) 694 p.printNode(tags[i]) 695 } 696 } 697 698 func (p *printer) printFieldList(fields []*Field, tags []*BasicLit) { 699 i0 := 0 700 var typ Expr 701 for i, f := range fields { 702 if f.Name == nil || f.Type != typ { 703 if i0 < i { 704 p.printFields(fields, tags, i0, i) 705 p.print(_Semi, newline) 706 i0 = i 707 } 708 typ = f.Type 709 } 710 } 711 p.printFields(fields, tags, i0, len(fields)) 712 } 713 714 func (p *printer) printMethodList(methods []*Field) { 715 for i, m := range methods { 716 if i > 0 { 717 p.print(_Semi, newline) 718 } 719 if m.Name != nil { 720 p.printNode(m.Name) 721 p.printSignature(m.Type.(*FuncType)) 722 } else { 723 p.printNode(m.Type) 724 } 725 } 726 } 727 728 func (p *printer) printNameList(list []*Name) { 729 for i, x := range list { 730 if i > 0 { 731 p.print(_Comma, blank) 732 } 733 p.printNode(x) 734 } 735 } 736 737 func (p *printer) printExprList(list []Expr) { 738 for i, x := range list { 739 if i > 0 { 740 p.print(_Comma, blank) 741 } 742 p.printNode(x) 743 } 744 } 745 746 func (p *printer) printExprLines(list []Expr) { 747 if len(list) > 0 { 748 p.print(newline, indent) 749 for _, x := range list { 750 p.print(x, _Comma, newline) 751 } 752 p.print(outdent) 753 } 754 } 755 756 func groupFor(d Decl) (token, *Group) { 757 switch d := d.(type) { 758 case *ImportDecl: 759 return _Import, d.Group 760 case *ConstDecl: 761 return _Const, d.Group 762 case *TypeDecl: 763 return _Type, d.Group 764 case *VarDecl: 765 return _Var, d.Group 766 case *FuncDecl: 767 return _Func, nil 768 default: 769 panic("unreachable") 770 } 771 } 772 773 type printGroup struct { 774 node 775 Tok token 776 Decls []Decl 777 } 778 779 func (p *printer) printDecl(list []Decl) { 780 tok, group := groupFor(list[0]) 781 782 if group == nil { 783 if len(list) != 1 { 784 panic("unreachable") 785 } 786 p.printNode(list[0]) 787 return 788 } 789 790 // if _, ok := list[0].(*EmptyDecl); ok { 791 // if len(list) != 1 { 792 // panic("unreachable") 793 // } 794 // // TODO(gri) if there are comments inside the empty 795 // // group, we may need to keep the list non-nil 796 // list = nil 797 // } 798 799 // printGroup is here for consistent comment handling 800 // (this is not yet used) 801 var pg printGroup 802 // *pg.Comments() = *group.Comments() 803 pg.Tok = tok 804 pg.Decls = list 805 p.printNode(&pg) 806 } 807 808 func (p *printer) printDeclList(list []Decl) { 809 i0 := 0 810 var tok token 811 var group *Group 812 for i, x := range list { 813 if s, g := groupFor(x); g == nil || g != group { 814 if i0 < i { 815 p.printDecl(list[i0:i]) 816 p.print(_Semi, newline) 817 // print empty line between different declaration groups, 818 // different kinds of declarations, or between functions 819 if g != group || s != tok || s == _Func { 820 p.print(newline) 821 } 822 i0 = i 823 } 824 tok, group = s, g 825 } 826 } 827 p.printDecl(list[i0:]) 828 } 829 830 func (p *printer) printSignature(sig *FuncType) { 831 p.printParameterList(sig.ParamList) 832 if list := sig.ResultList; list != nil { 833 p.print(blank) 834 if len(list) == 1 && list[0].Name == nil { 835 p.printNode(list[0].Type) 836 } else { 837 p.printParameterList(list) 838 } 839 } 840 } 841 842 func (p *printer) printParameterList(list []*Field) { 843 p.print(_Lparen) 844 if len(list) > 0 { 845 for i, f := range list { 846 if i > 0 { 847 p.print(_Comma, blank) 848 } 849 if f.Name != nil { 850 p.printNode(f.Name) 851 if i+1 < len(list) { 852 f1 := list[i+1] 853 if f1.Name != nil && f1.Type == f.Type { 854 continue // no need to print type 855 } 856 } 857 p.print(blank) 858 } 859 p.printNode(f.Type) 860 } 861 } 862 p.print(_Rparen) 863 } 864 865 func (p *printer) printStmtList(list []Stmt, braces bool) { 866 for i, x := range list { 867 p.print(x, _Semi) 868 if i+1 < len(list) { 869 p.print(newline) 870 } else if braces { 871 // Print an extra semicolon if the last statement is 872 // an empty statement and we are in a braced block 873 // because one semicolon is automatically removed. 874 if _, ok := x.(*EmptyStmt); ok { 875 p.print(x, _Semi) 876 } 877 } 878 } 879 } 880 881 func (p *printer) printBody(list []Stmt) { 882 p.print(_Lbrace) 883 if len(list) > 0 { 884 p.print(newline, indent) 885 p.printStmtList(list, true) 886 p.print(outdent, newline) 887 } 888 p.print(_Rbrace) 889 } 890 891 func (p *printer) printSwitchBody(list []*CaseClause) { 892 p.print(_Lbrace) 893 if len(list) > 0 { 894 p.print(newline) 895 for i, c := range list { 896 p.printCaseClause(c, i+1 == len(list)) 897 p.print(newline) 898 } 899 } 900 p.print(_Rbrace) 901 } 902 903 func (p *printer) printSelectBody(list []*CommClause) { 904 p.print(_Lbrace) 905 if len(list) > 0 { 906 p.print(newline) 907 for i, c := range list { 908 p.printCommClause(c, i+1 == len(list)) 909 p.print(newline) 910 } 911 } 912 p.print(_Rbrace) 913 } 914 915 func (p *printer) printCaseClause(c *CaseClause, braces bool) { 916 if c.Cases != nil { 917 p.print(_Case, blank, c.Cases) 918 } else { 919 p.print(_Default) 920 } 921 p.print(_Colon) 922 if len(c.Body) > 0 { 923 p.print(newline, indent) 924 p.printStmtList(c.Body, braces) 925 p.print(outdent) 926 } 927 } 928 929 func (p *printer) printCommClause(c *CommClause, braces bool) { 930 if c.Comm != nil { 931 p.print(_Case, blank) 932 p.print(c.Comm) 933 } else { 934 p.print(_Default) 935 } 936 p.print(_Colon) 937 if len(c.Body) > 0 { 938 p.print(newline, indent) 939 p.printStmtList(c.Body, braces) 940 p.print(outdent) 941 } 942 }