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 	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 }
    945