github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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 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 684 func (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 704 func (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 720 func (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 734 func (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 743 func (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 752 func (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 762 func 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 779 type printGroup struct { 780 node 781 Tok token 782 Decls []Decl 783 } 784 785 func (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 814 func (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 836 func (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 848 func (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 871 func (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 887 func (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 899 func (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 911 func (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 925 func (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 }