github.com/panjjo/go@v0.0.0-20161104043856-d62b31386338/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(_Larrow)
   477  		}
   478  		p.print(_Chan)
   479  		if n.Dir == SendOnly {
   480  			p.print(_Larrow)
   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, _Larrow, 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 *AliasDecl:
   607  		if n.Group == nil {
   608  			p.print(n.Tok, blank)
   609  		}
   610  		p.print(n.Name, blank, _Rarrow, blank, n.Orig)
   611  
   612  	case *ConstDecl:
   613  		if n.Group == nil {
   614  			p.print(_Const, blank)
   615  		}
   616  		p.printNameList(n.NameList)
   617  		if n.Type != nil {
   618  			p.print(blank, n.Type)
   619  		}
   620  		if n.Values != nil {
   621  			p.print(blank, _Assign, blank, n.Values)
   622  		}
   623  
   624  	case *TypeDecl:
   625  		if n.Group == nil {
   626  			p.print(_Type, blank)
   627  		}
   628  		p.print(n.Name, blank, n.Type)
   629  
   630  	case *VarDecl:
   631  		if n.Group == nil {
   632  			p.print(_Var, blank)
   633  		}
   634  		p.printNameList(n.NameList)
   635  		if n.Type != nil {
   636  			p.print(blank, n.Type)
   637  		}
   638  		if n.Values != nil {
   639  			p.print(blank, _Assign, blank, n.Values)
   640  		}
   641  
   642  	case *FuncDecl:
   643  		p.print(_Func, blank)
   644  		if r := n.Recv; r != nil {
   645  			p.print(_Lparen)
   646  			if r.Name != nil {
   647  				p.print(r.Name, blank)
   648  			}
   649  			p.printNode(r.Type)
   650  			p.print(_Rparen, blank)
   651  		}
   652  		p.print(n.Name)
   653  		p.printSignature(n.Type)
   654  		if n.Body != nil {
   655  			p.print(blank)
   656  			p.printBody(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 *AliasDecl:
   767  		return d.Tok, d.Group
   768  	case *ConstDecl:
   769  		return _Const, d.Group
   770  	case *TypeDecl:
   771  		return _Type, d.Group
   772  	case *VarDecl:
   773  		return _Var, d.Group
   774  	case *FuncDecl:
   775  		return _Func, nil
   776  	default:
   777  		panic("unreachable")
   778  	}
   779  }
   780  
   781  type printGroup struct {
   782  	node
   783  	Tok   token
   784  	Decls []Decl
   785  }
   786  
   787  func (p *printer) printDecl(list []Decl) {
   788  	tok, group := groupFor(list[0])
   789  
   790  	if group == nil {
   791  		if len(list) != 1 {
   792  			panic("unreachable")
   793  		}
   794  		p.printNode(list[0])
   795  		return
   796  	}
   797  
   798  	// if _, ok := list[0].(*EmptyDecl); ok {
   799  	// 	if len(list) != 1 {
   800  	// 		panic("unreachable")
   801  	// 	}
   802  	// 	// TODO(gri) if there are comments inside the empty
   803  	// 	// group, we may need to keep the list non-nil
   804  	// 	list = nil
   805  	// }
   806  
   807  	// printGroup is here for consistent comment handling
   808  	// (this is not yet used)
   809  	var pg printGroup
   810  	// *pg.Comments() = *group.Comments()
   811  	pg.Tok = tok
   812  	pg.Decls = list
   813  	p.printNode(&pg)
   814  }
   815  
   816  func (p *printer) printDeclList(list []Decl) {
   817  	i0 := 0
   818  	var tok token
   819  	var group *Group
   820  	for i, x := range list {
   821  		if s, g := groupFor(x); g == nil || g != group {
   822  			if i0 < i {
   823  				p.printDecl(list[i0:i])
   824  				p.print(_Semi, newline)
   825  				// print empty line between different declaration groups,
   826  				// different kinds of declarations, or between functions
   827  				if g != group || s != tok || s == _Func {
   828  					p.print(newline)
   829  				}
   830  				i0 = i
   831  			}
   832  			tok, group = s, g
   833  		}
   834  	}
   835  	p.printDecl(list[i0:])
   836  }
   837  
   838  func (p *printer) printSignature(sig *FuncType) {
   839  	p.printParameterList(sig.ParamList)
   840  	if list := sig.ResultList; list != nil {
   841  		p.print(blank)
   842  		if len(list) == 1 && list[0].Name == nil {
   843  			p.printNode(list[0].Type)
   844  		} else {
   845  			p.printParameterList(list)
   846  		}
   847  	}
   848  }
   849  
   850  func (p *printer) printParameterList(list []*Field) {
   851  	p.print(_Lparen)
   852  	if len(list) > 0 {
   853  		for i, f := range list {
   854  			if i > 0 {
   855  				p.print(_Comma, blank)
   856  			}
   857  			if f.Name != nil {
   858  				p.printNode(f.Name)
   859  				if i+1 < len(list) {
   860  					f1 := list[i+1]
   861  					if f1.Name != nil && f1.Type == f.Type {
   862  						continue // no need to print type
   863  					}
   864  				}
   865  				p.print(blank)
   866  			}
   867  			p.printNode(f.Type)
   868  		}
   869  	}
   870  	p.print(_Rparen)
   871  }
   872  
   873  func (p *printer) printStmtList(list []Stmt, braces bool) {
   874  	for i, x := range list {
   875  		p.print(x, _Semi)
   876  		if i+1 < len(list) {
   877  			p.print(newline)
   878  		} else if braces {
   879  			// Print an extra semicolon if the last statement is
   880  			// an empty statement and we are in a braced block
   881  			// because one semicolon is automatically removed.
   882  			if _, ok := x.(*EmptyStmt); ok {
   883  				p.print(x, _Semi)
   884  			}
   885  		}
   886  	}
   887  }
   888  
   889  func (p *printer) printBody(list []Stmt) {
   890  	p.print(_Lbrace)
   891  	if len(list) > 0 {
   892  		p.print(newline, indent)
   893  		p.printStmtList(list, true)
   894  		p.print(outdent, newline)
   895  	}
   896  	p.print(_Rbrace)
   897  }
   898  
   899  func (p *printer) printSwitchBody(list []*CaseClause) {
   900  	p.print(_Lbrace)
   901  	if len(list) > 0 {
   902  		p.print(newline)
   903  		for i, c := range list {
   904  			p.printCaseClause(c, i+1 == len(list))
   905  			p.print(newline)
   906  		}
   907  	}
   908  	p.print(_Rbrace)
   909  }
   910  
   911  func (p *printer) printSelectBody(list []*CommClause) {
   912  	p.print(_Lbrace)
   913  	if len(list) > 0 {
   914  		p.print(newline)
   915  		for i, c := range list {
   916  			p.printCommClause(c, i+1 == len(list))
   917  			p.print(newline)
   918  		}
   919  	}
   920  	p.print(_Rbrace)
   921  }
   922  
   923  func (p *printer) printCaseClause(c *CaseClause, braces bool) {
   924  	if c.Cases != nil {
   925  		p.print(_Case, blank, c.Cases)
   926  	} else {
   927  		p.print(_Default)
   928  	}
   929  	p.print(_Colon)
   930  	if len(c.Body) > 0 {
   931  		p.print(newline, indent)
   932  		p.printStmtList(c.Body, braces)
   933  		p.print(outdent)
   934  	}
   935  }
   936  
   937  func (p *printer) printCommClause(c *CommClause, braces bool) {
   938  	if c.Comm != nil {
   939  		p.print(_Case, blank)
   940  		p.print(c.Comm)
   941  	} else {
   942  		p.print(_Default)
   943  	}
   944  	p.print(_Colon)
   945  	if len(c.Body) > 0 {
   946  		p.print(newline, indent)
   947  		p.printStmtList(c.Body, braces)
   948  		p.print(outdent)
   949  	}
   950  }