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