github.com/euank/go@v0.0.0-20160829210321-495514729181/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  	// 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  }