Home | History | Annotate | Download | only in syntax
      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 }
    943