...

Source file src/pkg/cmd/compile/internal/syntax/printer.go

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

View as plain text