github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/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  }