github.com/epfl-dcsl/gotee@v0.0.0-20200909122901-014b35f5e5e9/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 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) 397 if n.Type != nil { 398 p.printNode(n.Type) 399 } else { 400 p.print(_Type) 401 } 402 p.print(_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 *TypeSwitchGuard: 561 if n.Lhs != nil { 562 p.print(n.Lhs, blank, _Define, blank) 563 } 564 p.print(n.X, _Dot, _Lparen, _Type, _Rparen) 565 566 case *SelectStmt: 567 p.print(_Select, blank) // for now 568 p.printSelectBody(n.Body) 569 570 case *RangeClause: 571 if n.Lhs != nil { 572 tok := _Assign 573 if n.Def { 574 tok = _Define 575 } 576 p.print(n.Lhs, blank, tok, blank) 577 } 578 p.print(_Range, blank, n.X) 579 580 case *ForStmt: 581 p.print(_For, blank) 582 if n.Init == nil && n.Post == nil { 583 if n.Cond != nil { 584 p.print(n.Cond, blank) 585 } 586 } else { 587 if n.Init != nil { 588 p.print(n.Init) 589 // TODO(gri) clean this up 590 if _, ok := n.Init.(*RangeClause); ok { 591 p.print(blank, n.Body) 592 break 593 } 594 } 595 p.print(_Semi, blank) 596 if n.Cond != nil { 597 p.print(n.Cond) 598 } 599 p.print(_Semi, blank) 600 if n.Post != nil { 601 p.print(n.Post, blank) 602 } 603 } 604 p.print(n.Body) 605 606 case *ImportDecl: 607 if n.Group == nil { 608 p.print(_Import, blank) 609 } 610 if n.LocalPkgName != nil { 611 p.print(n.LocalPkgName, blank) 612 } 613 p.print(n.Path) 614 615 case *ConstDecl: 616 if n.Group == nil { 617 p.print(_Const, blank) 618 } 619 p.printNameList(n.NameList) 620 if n.Type != nil { 621 p.print(blank, n.Type) 622 } 623 if n.Values != nil { 624 p.print(blank, _Assign, blank, n.Values) 625 } 626 627 case *TypeDecl: 628 if n.Group == nil { 629 p.print(_Type, blank) 630 } 631 p.print(n.Name, blank) 632 if n.Alias { 633 p.print(_Assign, blank) 634 } 635 p.print(n.Type) 636 637 case *VarDecl: 638 if n.Group == nil { 639 p.print(_Var, blank) 640 } 641 p.printNameList(n.NameList) 642 if n.Type != nil { 643 p.print(blank, n.Type) 644 } 645 if n.Values != nil { 646 p.print(blank, _Assign, blank, n.Values) 647 } 648 649 case *FuncDecl: 650 p.print(_Func, blank) 651 if r := n.Recv; r != nil { 652 p.print(_Lparen) 653 if r.Name != nil { 654 p.print(r.Name, blank) 655 } 656 p.printNode(r.Type) 657 p.print(_Rparen, blank) 658 } 659 p.print(n.Name) 660 p.printSignature(n.Type) 661 if n.Body != nil { 662 p.print(blank, n.Body) 663 } 664 665 case *printGroup: 666 p.print(n.Tok, blank, _Lparen) 667 if len(n.Decls) > 0 { 668 p.print(newline, indent) 669 for _, d := range n.Decls { 670 p.printNode(d) 671 p.print(_Semi, newline) 672 } 673 p.print(outdent) 674 } 675 p.print(_Rparen) 676 677 // files 678 case *File: 679 p.print(_Package, blank, n.PkgName) 680 if len(n.DeclList) > 0 { 681 p.print(_Semi, newline, newline) 682 p.printDeclList(n.DeclList) 683 } 684 685 default: 686 panic(fmt.Sprintf("syntax.Iterate: unexpected node type %T", n)) 687 } 688 } 689 690 func (p *printer) printFields(fields []*Field, tags []*BasicLit, i, j int) { 691 if i+1 == j && fields[i].Name == nil { 692 // anonymous field 693 p.printNode(fields[i].Type) 694 } else { 695 for k, f := range fields[i:j] { 696 if k > 0 { 697 p.print(_Comma, blank) 698 } 699 p.printNode(f.Name) 700 } 701 p.print(blank) 702 p.printNode(fields[i].Type) 703 } 704 if i < len(tags) && tags[i] != nil { 705 p.print(blank) 706 p.printNode(tags[i]) 707 } 708 } 709 710 func (p *printer) printFieldList(fields []*Field, tags []*BasicLit) { 711 i0 := 0 712 var typ Expr 713 for i, f := range fields { 714 if f.Name == nil || f.Type != typ { 715 if i0 < i { 716 p.printFields(fields, tags, i0, i) 717 p.print(_Semi, newline) 718 i0 = i 719 } 720 typ = f.Type 721 } 722 } 723 p.printFields(fields, tags, i0, len(fields)) 724 } 725 726 func (p *printer) printMethodList(methods []*Field) { 727 for i, m := range methods { 728 if i > 0 { 729 p.print(_Semi, newline) 730 } 731 if m.Name != nil { 732 p.printNode(m.Name) 733 p.printSignature(m.Type.(*FuncType)) 734 } else { 735 p.printNode(m.Type) 736 } 737 } 738 } 739 740 func (p *printer) printNameList(list []*Name) { 741 for i, x := range list { 742 if i > 0 { 743 p.print(_Comma, blank) 744 } 745 p.printNode(x) 746 } 747 } 748 749 func (p *printer) printExprList(list []Expr) { 750 for i, x := range list { 751 if i > 0 { 752 p.print(_Comma, blank) 753 } 754 p.printNode(x) 755 } 756 } 757 758 func (p *printer) printExprLines(list []Expr) { 759 if len(list) > 0 { 760 p.print(newline, indent) 761 for _, x := range list { 762 p.print(x, _Comma, newline) 763 } 764 p.print(outdent) 765 } 766 } 767 768 func groupFor(d Decl) (token, *Group) { 769 switch d := d.(type) { 770 case *ImportDecl: 771 return _Import, d.Group 772 case *ConstDecl: 773 return _Const, d.Group 774 case *TypeDecl: 775 return _Type, d.Group 776 case *VarDecl: 777 return _Var, d.Group 778 case *FuncDecl: 779 return _Func, nil 780 default: 781 panic("unreachable") 782 } 783 } 784 785 type printGroup struct { 786 node 787 Tok token 788 Decls []Decl 789 } 790 791 func (p *printer) printDecl(list []Decl) { 792 tok, group := groupFor(list[0]) 793 794 if group == nil { 795 if len(list) != 1 { 796 panic("unreachable") 797 } 798 p.printNode(list[0]) 799 return 800 } 801 802 // if _, ok := list[0].(*EmptyDecl); ok { 803 // if len(list) != 1 { 804 // panic("unreachable") 805 // } 806 // // TODO(gri) if there are comments inside the empty 807 // // group, we may need to keep the list non-nil 808 // list = nil 809 // } 810 811 // printGroup is here for consistent comment handling 812 // (this is not yet used) 813 var pg printGroup 814 // *pg.Comments() = *group.Comments() 815 pg.Tok = tok 816 pg.Decls = list 817 p.printNode(&pg) 818 } 819 820 func (p *printer) printDeclList(list []Decl) { 821 i0 := 0 822 var tok token 823 var group *Group 824 for i, x := range list { 825 if s, g := groupFor(x); g == nil || g != group { 826 if i0 < i { 827 p.printDecl(list[i0:i]) 828 p.print(_Semi, newline) 829 // print empty line between different declaration groups, 830 // different kinds of declarations, or between functions 831 if g != group || s != tok || s == _Func { 832 p.print(newline) 833 } 834 i0 = i 835 } 836 tok, group = s, g 837 } 838 } 839 p.printDecl(list[i0:]) 840 } 841 842 func (p *printer) printSignature(sig *FuncType) { 843 p.printParameterList(sig.ParamList) 844 if list := sig.ResultList; list != nil { 845 p.print(blank) 846 if len(list) == 1 && list[0].Name == nil { 847 p.printNode(list[0].Type) 848 } else { 849 p.printParameterList(list) 850 } 851 } 852 } 853 854 func (p *printer) printParameterList(list []*Field) { 855 p.print(_Lparen) 856 if len(list) > 0 { 857 for i, f := range list { 858 if i > 0 { 859 p.print(_Comma, blank) 860 } 861 if f.Name != nil { 862 p.printNode(f.Name) 863 if i+1 < len(list) { 864 f1 := list[i+1] 865 if f1.Name != nil && f1.Type == f.Type { 866 continue // no need to print type 867 } 868 } 869 p.print(blank) 870 } 871 p.printNode(f.Type) 872 } 873 } 874 p.print(_Rparen) 875 } 876 877 func (p *printer) printStmtList(list []Stmt, braces bool) { 878 for i, x := range list { 879 p.print(x, _Semi) 880 if i+1 < len(list) { 881 p.print(newline) 882 } else if braces { 883 // Print an extra semicolon if the last statement is 884 // an empty statement and we are in a braced block 885 // because one semicolon is automatically removed. 886 if _, ok := x.(*EmptyStmt); ok { 887 p.print(x, _Semi) 888 } 889 } 890 } 891 } 892 893 func (p *printer) printSwitchBody(list []*CaseClause) { 894 p.print(_Lbrace) 895 if len(list) > 0 { 896 p.print(newline) 897 for i, c := range list { 898 p.printCaseClause(c, i+1 == len(list)) 899 p.print(newline) 900 } 901 } 902 p.print(_Rbrace) 903 } 904 905 func (p *printer) printSelectBody(list []*CommClause) { 906 p.print(_Lbrace) 907 if len(list) > 0 { 908 p.print(newline) 909 for i, c := range list { 910 p.printCommClause(c, i+1 == len(list)) 911 p.print(newline) 912 } 913 } 914 p.print(_Rbrace) 915 } 916 917 func (p *printer) printCaseClause(c *CaseClause, braces bool) { 918 if c.Cases != nil { 919 p.print(_Case, blank, c.Cases) 920 } else { 921 p.print(_Default) 922 } 923 p.print(_Colon) 924 if len(c.Body) > 0 { 925 p.print(newline, indent) 926 p.printStmtList(c.Body, braces) 927 p.print(outdent) 928 } 929 } 930 931 func (p *printer) printCommClause(c *CommClause, braces bool) { 932 if c.Comm != nil { 933 p.print(_Case, blank) 934 p.print(c.Comm) 935 } else { 936 p.print(_Default) 937 } 938 p.print(_Colon) 939 if len(c.Body) > 0 { 940 p.print(newline, indent) 941 p.printStmtList(c.Body, braces) 942 p.print(outdent) 943 } 944 }