github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/compiler/ast/printer/source_printer.go (about)

     1  package printer
     2  
     3  import (
     4  	"io"
     5  	"strings"
     6  	"text/tabwriter"
     7  
     8  	"github.com/hirochachacha/plua/compiler/ast"
     9  	"github.com/hirochachacha/plua/compiler/token"
    10  	"github.com/hirochachacha/plua/internal/strconv"
    11  	"github.com/hirochachacha/plua/position"
    12  )
    13  
    14  const (
    15  	indent = "  "
    16  )
    17  
    18  type mode uint
    19  
    20  const (
    21  	noBlank mode = 1 << iota
    22  	escape
    23  	noParen
    24  	compact
    25  	insertSemi
    26  )
    27  
    28  type printer struct {
    29  	w          *tabwriter.Writer
    30  	depth      int // indent depth
    31  	doIndent   bool
    32  	formfeed   bool
    33  	stmtEnd    bool
    34  	lastPos    position.Position
    35  	commentPos position.Position
    36  	cindex     int
    37  	comments   []*ast.CommentGroup
    38  	comment    *ast.CommentGroup
    39  	err        error
    40  }
    41  
    42  var initPos = position.Position{Line: 1}
    43  
    44  func newPrinter(w io.Writer) *printer {
    45  	return &printer{
    46  		w:       tabwriter.NewWriter(w, 2, 2, 1, ' ', tabwriter.DiscardEmptyColumns|tabwriter.StripEscape),
    47  		lastPos: initPos,
    48  	}
    49  }
    50  
    51  // Nodes
    52  
    53  func (p *printer) printNode(node ast.Node) {
    54  	switch node := node.(type) {
    55  	case *ast.BadExpr:
    56  		p.print(node.Pos(), "BadExpr", 0)
    57  	case *ast.Name:
    58  		p.printName(node, 0)
    59  	case *ast.Vararg:
    60  		p.printVararg(node, 0)
    61  	case *ast.BasicLit:
    62  		p.printBasicLit(node, 0)
    63  	case *ast.FuncLit:
    64  		p.printFuncLit(node, 0)
    65  	case *ast.TableLit:
    66  		p.printTableLit(node, 0)
    67  	case *ast.ParenExpr:
    68  		p.printParenExpr(node, 0)
    69  	case *ast.SelectorExpr:
    70  		p.printSelectorExpr(node, 0)
    71  	case *ast.IndexExpr:
    72  		p.printIndexExpr(node, 0)
    73  	case *ast.CallExpr:
    74  		p.printCallExpr(node, 0)
    75  	case *ast.UnaryExpr:
    76  		p.printUnaryExpr(node, 0)
    77  	case *ast.BinaryExpr:
    78  		p.printBinaryExpr(node, token.HighestPrec, 0)
    79  	case *ast.KeyValueExpr:
    80  		p.printKeyValueExpr(node, 0)
    81  	case *ast.BadStmt:
    82  		p.print(node.Pos(), "BadStmt", 0)
    83  	case *ast.EmptyStmt:
    84  		// skip this
    85  	case *ast.LocalAssignStmt:
    86  		p.printLocalAssignStmt(node)
    87  	case *ast.LocalFuncStmt:
    88  		p.printLocalFuncStmt(node)
    89  	case *ast.FuncStmt:
    90  		p.printFuncStmt(node)
    91  	case *ast.LabelStmt:
    92  		p.printLabelStmt(node)
    93  	case *ast.ExprStmt:
    94  		p.printExprStmt(node)
    95  	case *ast.AssignStmt:
    96  		p.printAssignStmt(node)
    97  	case *ast.GotoStmt:
    98  		p.printGotoStmt(node)
    99  	case *ast.BreakStmt:
   100  		p.printBreakStmt(node)
   101  	case *ast.IfStmt:
   102  		p.printIfStmt(node)
   103  	case *ast.DoStmt:
   104  		p.printDoStmt(node)
   105  	case *ast.WhileStmt:
   106  		p.printWhileStmt(node)
   107  	case *ast.RepeatStmt:
   108  		p.printRepeatStmt(node)
   109  	case *ast.ReturnStmt:
   110  		p.printReturnStmt(node)
   111  	case *ast.ForStmt:
   112  		p.printForStmt(node)
   113  	case *ast.ForEachStmt:
   114  		p.printForEachStmt(node)
   115  	case *ast.File:
   116  		p.printFile(node)
   117  	case *ast.Block:
   118  		p.printBlock(node)
   119  	case *ast.FuncBody:
   120  		p.printFuncBody(node)
   121  	case *ast.Comment:
   122  	case *ast.CommentGroup:
   123  	case *ast.ParamList:
   124  		p.printParams(node)
   125  
   126  	default:
   127  		panic("unreachable")
   128  	}
   129  }
   130  
   131  // Statements
   132  
   133  func (p *printer) printStmt(stmt ast.Stmt) {
   134  	switch stmt := stmt.(type) {
   135  	case *ast.BadStmt:
   136  		p.print(stmt.Pos(), "BadStmt", 0)
   137  	case *ast.EmptyStmt:
   138  		// skip this
   139  	case *ast.LocalAssignStmt:
   140  		p.printLocalAssignStmt(stmt)
   141  	case *ast.LocalFuncStmt:
   142  		p.printLocalFuncStmt(stmt)
   143  	case *ast.FuncStmt:
   144  		p.printFuncStmt(stmt)
   145  	case *ast.LabelStmt:
   146  		p.printLabelStmt(stmt)
   147  	case *ast.ExprStmt:
   148  		p.printExprStmt(stmt)
   149  	case *ast.AssignStmt:
   150  		p.printAssignStmt(stmt)
   151  	case *ast.GotoStmt:
   152  		p.printGotoStmt(stmt)
   153  	case *ast.BreakStmt:
   154  		p.printBreakStmt(stmt)
   155  	case *ast.IfStmt:
   156  		p.printIfStmt(stmt)
   157  	case *ast.DoStmt:
   158  		p.printDoStmt(stmt)
   159  	case *ast.WhileStmt:
   160  		p.printWhileStmt(stmt)
   161  	case *ast.RepeatStmt:
   162  		p.printRepeatStmt(stmt)
   163  	case *ast.ReturnStmt:
   164  		p.printReturnStmt(stmt)
   165  	case *ast.ForStmt:
   166  		p.printForStmt(stmt)
   167  	case *ast.ForEachStmt:
   168  		p.printForEachStmt(stmt)
   169  	default:
   170  		panic("unreachable")
   171  	}
   172  
   173  	p.stmtEnd = true
   174  }
   175  
   176  func (p *printer) printLocalAssignStmt(stmt *ast.LocalAssignStmt) {
   177  	p.print(stmt.Local, "local", insertSemi)
   178  	p.printNames(stmt.LHS, 0)
   179  	if stmt.Equal.IsValid() {
   180  		p.print(stmt.Equal, "=", 0)
   181  		p.indentWith(stmt.Equal, stmt.End(), func() {
   182  			p.printExprs(stmt.RHS, noParen)
   183  		})
   184  	}
   185  }
   186  
   187  func (p *printer) printLocalFuncStmt(stmt *ast.LocalFuncStmt) {
   188  	p.print(stmt.Local, "local", insertSemi)
   189  	p.print(stmt.Func, "function", 0)
   190  	p.printName(stmt.Name, 0)
   191  	p.printFuncBody(stmt.Body)
   192  	p.print(stmt.EndPos, "end", 0)
   193  }
   194  
   195  func (p *printer) printFuncStmt(stmt *ast.FuncStmt) {
   196  	p.print(stmt.Func, "function", insertSemi)
   197  	if len(stmt.PathList) > 0 {
   198  		if len(stmt.PathList) == 1 {
   199  			path := stmt.PathList[0]
   200  			p.print(path.Pos(), path.Name, 0)
   201  		} else {
   202  			path := stmt.PathList[0]
   203  			p.print(path.Pos(), path.Name+".", 0)
   204  			for _, path := range stmt.PathList[1 : len(stmt.PathList)-1] {
   205  				p.print(path.Pos(), path.Name+".", noBlank)
   206  			}
   207  			path = stmt.PathList[len(stmt.PathList)-1]
   208  			p.print(path.Pos(), path.Name, noBlank)
   209  		}
   210  		p.print(stmt.AccessPos, stmt.AccessTok.String(), noBlank)
   211  		p.printName(stmt.Name, noBlank)
   212  	} else {
   213  		p.printName(stmt.Name, 0)
   214  	}
   215  	p.printFuncBody(stmt.Body)
   216  	p.print(stmt.EndPos, "end", 0)
   217  }
   218  
   219  func (p *printer) printLabelStmt(stmt *ast.LabelStmt) {
   220  	p.print(stmt.Label, "::", insertSemi)
   221  	p.print(stmt.Name.Pos(), stmt.Name.Name, noBlank)
   222  	p.print(stmt.EndLabel, "::", noBlank)
   223  }
   224  
   225  func (p *printer) printExprStmt(stmt *ast.ExprStmt) {
   226  	p.printCallExpr(stmt.X, insertSemi)
   227  }
   228  
   229  func (p *printer) printAssignStmt(stmt *ast.AssignStmt) {
   230  	p.printExprs(stmt.LHS, noParen|insertSemi)
   231  	p.print(stmt.Equal, "=", 0)
   232  	p.indentWith(stmt.Equal, stmt.End(), func() {
   233  		p.printExprs(stmt.RHS, noParen)
   234  	})
   235  }
   236  
   237  func (p *printer) printGotoStmt(stmt *ast.GotoStmt) {
   238  	p.print(stmt.Goto, "goto", insertSemi)
   239  	p.printName(stmt.Label, 0)
   240  }
   241  
   242  func (p *printer) printBreakStmt(stmt *ast.BreakStmt) {
   243  	p.print(stmt.Break, "break", insertSemi)
   244  }
   245  
   246  func (p *printer) printIfStmt(stmt *ast.IfStmt) {
   247  	p.print(stmt.If, "if", insertSemi)
   248  	p.printExpr(stmt.Cond, noParen)
   249  	p.print(stmt.Then, "then", 0)
   250  	p.printBlock(stmt.Body)
   251  	for _, e := range stmt.ElseIfList {
   252  		p.print(e.If, "elseif", 0)
   253  		p.printExpr(e.Cond, noParen)
   254  		p.print(e.Then, "then", 0)
   255  		p.printBlock(e.Body)
   256  	}
   257  	if stmt.ElseBody != nil {
   258  		p.print(stmt.Else, "else", 0)
   259  		p.printBlock(stmt.ElseBody)
   260  	}
   261  	p.print(stmt.EndPos, "end", 0)
   262  }
   263  
   264  func (p *printer) printDoStmt(stmt *ast.DoStmt) {
   265  	p.print(stmt.Do, "do", insertSemi)
   266  	p.printBlock(stmt.Body)
   267  	p.print(stmt.EndPos, "end", 0)
   268  }
   269  
   270  func (p *printer) printWhileStmt(stmt *ast.WhileStmt) {
   271  	p.print(stmt.While, "while", insertSemi)
   272  	p.printExpr(stmt.Cond, noParen)
   273  	p.print(stmt.Do, "do", 0)
   274  	p.printBlock(stmt.Body)
   275  	p.print(stmt.EndPos, "end", 0)
   276  }
   277  
   278  func (p *printer) printRepeatStmt(stmt *ast.RepeatStmt) {
   279  	p.print(stmt.Repeat, "repeat", insertSemi)
   280  	p.printBlock(stmt.Body)
   281  	p.print(stmt.Until, "until", 0)
   282  	p.printExpr(stmt.Cond, noParen)
   283  }
   284  
   285  func (p *printer) printReturnStmt(stmt *ast.ReturnStmt) {
   286  	p.print(stmt.Return, "return", insertSemi)
   287  	p.printExprs(stmt.Results, noParen)
   288  }
   289  
   290  func (p *printer) printForStmt(stmt *ast.ForStmt) {
   291  	p.print(stmt.For, "for", insertSemi)
   292  	p.printName(stmt.Name, 0)
   293  	p.print(stmt.Equal, "=", 0)
   294  	p.printExpr(stmt.Start, noParen)
   295  	p.print(p.lastPos, ",", noBlank)
   296  	if stmt.Step != nil {
   297  		p.printExpr(stmt.Finish, noParen)
   298  		p.print(p.lastPos, ",", noBlank)
   299  		p.printExpr(stmt.Step, noParen)
   300  	} else {
   301  		p.printExpr(stmt.Finish, noParen)
   302  	}
   303  	p.print(stmt.Do, "do", 0)
   304  	p.printBlock(stmt.Body)
   305  	p.print(stmt.EndPos, "end", 0)
   306  }
   307  
   308  func (p *printer) printForEachStmt(stmt *ast.ForEachStmt) {
   309  	p.print(stmt.For, "for", insertSemi)
   310  	p.printNames(stmt.Names, 0)
   311  	p.print(stmt.In, "in", 0)
   312  	p.printExprs(stmt.Exprs, noParen)
   313  	p.print(stmt.Do, "do", 0)
   314  	p.printBlock(stmt.Body)
   315  	p.print(stmt.EndPos, "end", 0)
   316  }
   317  
   318  // Expression
   319  
   320  func (p *printer) printExpr(expr ast.Expr, mode mode) {
   321  	if mode&noParen != 0 {
   322  		for { // ((expr)) => expr
   323  			paren, ok := expr.(*ast.ParenExpr)
   324  			if !ok {
   325  				break
   326  			}
   327  			expr = paren.X
   328  		}
   329  		mode &^= noParen
   330  	}
   331  
   332  	switch expr := expr.(type) {
   333  	case *ast.BadExpr:
   334  		p.print(expr.Pos(), "BadExpr", mode)
   335  	case *ast.Name:
   336  		p.printName(expr, mode)
   337  	case *ast.Vararg:
   338  		p.printVararg(expr, mode)
   339  	case *ast.BasicLit:
   340  		p.printBasicLit(expr, mode)
   341  	case *ast.FuncLit:
   342  		p.printFuncLit(expr, mode)
   343  	case *ast.TableLit:
   344  		p.printTableLit(expr, mode)
   345  	case *ast.ParenExpr:
   346  		p.printParenExpr(expr, mode)
   347  	case *ast.SelectorExpr:
   348  		p.printSelectorExpr(expr, mode)
   349  	case *ast.IndexExpr:
   350  		p.printIndexExpr(expr, mode)
   351  	case *ast.CallExpr:
   352  		p.printCallExpr(expr, mode)
   353  	case *ast.UnaryExpr:
   354  		p.printUnaryExpr(expr, mode)
   355  	case *ast.BinaryExpr:
   356  		p.printBinaryExpr(expr, token.HighestPrec, mode)
   357  	case *ast.KeyValueExpr:
   358  		p.printKeyValueExpr(expr, mode)
   359  	default:
   360  		panic("unreachable")
   361  	}
   362  }
   363  
   364  func (p *printer) printName(expr *ast.Name, mode mode) {
   365  	p.print(expr.Pos(), expr.Name, mode)
   366  }
   367  
   368  func (p *printer) printVararg(expr *ast.Vararg, mode mode) {
   369  	p.print(expr.Ellipsis, "...", mode)
   370  }
   371  
   372  func (p *printer) printBasicLit(expr *ast.BasicLit, mode mode) {
   373  	if expr.Token.Type == token.STRING {
   374  		p.print(expr.Token.Pos, expr.Token.Lit, mode|escape)
   375  	} else {
   376  		p.print(expr.Token.Pos, expr.Token.Lit, mode)
   377  	}
   378  }
   379  
   380  func (p *printer) printFuncLit(expr *ast.FuncLit, mode mode) {
   381  	p.print(expr.Func, "function", mode)
   382  	p.printFuncBody(expr.Body)
   383  	p.print(expr.EndPos, "end", 0)
   384  }
   385  
   386  func (p *printer) printTableLit(expr *ast.TableLit, mode mode) {
   387  	p.print(expr.Lbrace, "{", mode)
   388  	p.indentWith(expr.Lbrace, expr.Rbrace, func() {
   389  		p.printExprs(expr.Fields, noBlank|noParen)
   390  		if len(expr.Fields) > 0 {
   391  			if expr.Rbrace.Line-p.lastPos.Line > 0 {
   392  				p.writeByte(',')
   393  			}
   394  		}
   395  	})
   396  	p.print(expr.Rbrace, "}", noBlank)
   397  }
   398  
   399  func (p *printer) printParenExpr(expr *ast.ParenExpr, mode mode) {
   400  	p.print(expr.Lparen, "(", mode)
   401  	p.indentWith(expr.Lparen, expr.Rparen, func() {
   402  		p.printExpr(expr.X, noBlank|noParen|compact)
   403  	})
   404  	p.print(expr.Rparen, ")", noBlank)
   405  }
   406  
   407  func (p *printer) printSelectorExpr(expr *ast.SelectorExpr, mode mode) {
   408  	p.printExpr(expr.X, mode)
   409  	p.print(expr.Period, ".", noBlank)
   410  	p.printName(expr.Sel, noBlank)
   411  }
   412  
   413  func (p *printer) printIndexExpr(expr *ast.IndexExpr, mode mode) {
   414  	p.printExpr(expr.X, mode)
   415  	p.print(expr.Lbrack, "[", noBlank)
   416  	p.indentWith(expr.Lbrack, expr.Rbrack, func() {
   417  		p.printExpr(expr.Index, noBlank|noParen|compact)
   418  	})
   419  	p.print(expr.Rbrack, "]", noBlank)
   420  }
   421  
   422  func (p *printer) printCallExpr(expr *ast.CallExpr, mode mode) {
   423  	p.printExpr(expr.X, mode)
   424  	if expr.Colon != position.NoPos {
   425  		p.print(expr.Colon, ":", noBlank)
   426  		p.printName(expr.Name, noBlank)
   427  	}
   428  	if expr.Lparen != position.NoPos {
   429  		p.print(expr.Lparen, "(", noBlank)
   430  		p.indentWith(expr.Lparen, expr.Rparen, func() {
   431  			p.printExprs(expr.Args, noBlank|noParen)
   432  		})
   433  		p.print(expr.Rparen, ")", noBlank)
   434  	} else {
   435  		p.printExprs(expr.Args, noParen)
   436  	}
   437  }
   438  
   439  func (p *printer) printUnaryExpr(expr *ast.UnaryExpr, mode mode) {
   440  	p.print(expr.OpPos, expr.Op.String(), mode)
   441  	switch x := expr.X.(type) {
   442  	case *ast.UnaryExpr:
   443  		// - - 6 => -(-6)
   444  		// not not true => not (not true)
   445  		if expr.Op != token.NOT {
   446  			p.print(x.Pos(), "(", noBlank)
   447  		} else {
   448  			p.print(x.Pos(), "(", 0)
   449  		}
   450  		p.printUnaryExpr(x, noBlank)
   451  		p.print(x.End(), ")", noBlank)
   452  	case *ast.BinaryExpr:
   453  		if expr.Op != token.NOT {
   454  			p.printBinaryExpr(x, token.HighestPrec, noBlank)
   455  		} else {
   456  			p.printBinaryExpr(x, token.HighestPrec, 0)
   457  		}
   458  	default:
   459  		if expr.Op != token.NOT {
   460  			p.printExpr(expr.X, noBlank)
   461  		} else {
   462  			p.printExpr(expr.X, 0)
   463  		}
   464  	}
   465  }
   466  
   467  func (p *printer) printBinaryExpr(expr *ast.BinaryExpr, prec1 int, mode mode) {
   468  	prec, _ := expr.Op.Precedence()
   469  
   470  	if (mode&compact != 0 || prec > prec1) && prec > 3 { // should cutoff?
   471  		// (1 + 8 * 9) => (1+8*9)
   472  		// 1 + 2 * 3 +4/5 +6 ^ 7 +8 => 1 + 2*3 + 4/5 + 6^7 + 8
   473  
   474  		if x, ok := expr.X.(*ast.BinaryExpr); ok {
   475  			p.printBinaryExpr(x, prec1, mode)
   476  		} else {
   477  			if x, ok := expr.X.(*ast.BasicLit); ok && (x.Token.Type == token.INT || x.Token.Type == token.FLOAT) && expr.Op == token.CONCAT {
   478  				// 2 .. 3 > "22" => "2"..3 > "22"
   479  				p.print(x.Token.Pos, strconv.Quote(x.Token.Lit), mode)
   480  			} else {
   481  				p.printExpr(expr.X, mode)
   482  			}
   483  		}
   484  
   485  		p.print(expr.OpPos, expr.Op.String(), noBlank)
   486  
   487  		switch y := expr.Y.(type) {
   488  		case *ast.UnaryExpr:
   489  			switch expr.Op.String() + y.Op.String() {
   490  			case "--", "~~":
   491  				// 1 - - 2 => 1-(-2)
   492  				// 1 ~ ~ 2 => 1~(~2)
   493  				p.print(y.Pos(), "(", noBlank)
   494  				p.printUnaryExpr(y, noBlank)
   495  				p.print(y.End(), ")", noBlank)
   496  			default:
   497  				p.printUnaryExpr(y, noBlank)
   498  			}
   499  		case *ast.BinaryExpr:
   500  			p.printBinaryExpr(y, prec1, noBlank)
   501  		default:
   502  			p.printExpr(expr.Y, noBlank)
   503  		}
   504  	} else {
   505  		if x, ok := expr.X.(*ast.BinaryExpr); ok {
   506  			p.printBinaryExpr(x, prec, mode)
   507  		} else {
   508  			p.printExpr(expr.X, mode)
   509  		}
   510  
   511  		p.print(expr.OpPos, expr.Op.String(), 0)
   512  
   513  		switch y := expr.Y.(type) {
   514  		case *ast.UnaryExpr:
   515  			switch expr.Op.String() + y.Op.String() {
   516  			case "--", "~~":
   517  				// 1 - - 2 => 1 - (-2)
   518  				// 1 ~ ~ 2 => 1 ~ (~2)
   519  				p.print(y.Pos(), "(", 0)
   520  				p.printUnaryExpr(y, noBlank)
   521  				p.print(y.End(), ")", noBlank)
   522  			default:
   523  				p.printUnaryExpr(y, 0)
   524  			}
   525  		case *ast.BinaryExpr:
   526  			p.printBinaryExpr(y, prec, 0)
   527  		default:
   528  			p.printExpr(expr.Y, 0)
   529  		}
   530  	}
   531  }
   532  
   533  func (p *printer) printKeyValueExpr(expr *ast.KeyValueExpr, mode mode) {
   534  	if expr.Lbrack != position.NoPos {
   535  		p.print(expr.Lbrack, "[", mode)
   536  		p.indentWith(expr.Lbrack, expr.Rbrack, func() {
   537  			p.printExpr(expr.Key, noBlank|noParen|compact)
   538  		})
   539  		p.print(expr.Rbrack, "]", noBlank)
   540  		p.print(expr.Equal, "=", 0)
   541  		p.indentWith(expr.Equal, expr.End(), func() {
   542  			p.printExpr(expr.Value, noParen)
   543  		})
   544  	} else {
   545  		p.printExpr(expr.Key, mode)
   546  		p.print(expr.Equal, "=", 0)
   547  		p.indentWith(expr.Equal, expr.End(), func() {
   548  			p.printExpr(expr.Value, noParen)
   549  		})
   550  	}
   551  }
   552  
   553  func (p *printer) nextComment() {
   554  	if p.cindex == len(p.comments) {
   555  		p.comment = nil
   556  		p.commentPos = position.Position{Line: 1 << 30}
   557  	} else {
   558  		p.comment = p.comments[p.cindex]
   559  		p.commentPos = p.comment.Pos()
   560  		p.cindex++
   561  	}
   562  }
   563  
   564  // Other
   565  
   566  func replaceEscape(s string) string {
   567  	if i := strings.IndexByte(s, tabwriter.Escape); i != -1 {
   568  		bs := make([]byte, len(s))
   569  		for i := range bs {
   570  			c := s[i]
   571  			if c == tabwriter.Escape {
   572  				bs[i] = 0x00
   573  			} else {
   574  				bs[i] = s[i]
   575  			}
   576  		}
   577  		return string(bs)
   578  	}
   579  	return s
   580  }
   581  
   582  func (p *printer) printFile(file *ast.File) {
   583  	if file.Shebang != "" {
   584  		p.writeByte(tabwriter.Escape)
   585  		p.writeString(replaceEscape(file.Shebang))
   586  		p.writeByte(tabwriter.Escape)
   587  	}
   588  
   589  	p.comments = file.Comments
   590  
   591  	p.nextComment()
   592  
   593  	for _, stmt := range file.Chunk {
   594  		p.printStmt(stmt)
   595  	}
   596  
   597  	p.insertComment(file.End())
   598  }
   599  
   600  func (p *printer) printFuncBody(body *ast.FuncBody) {
   601  	p.printParams(body.Params)
   602  	p.printBlock(body.Body)
   603  }
   604  
   605  func (p *printer) printParams(params *ast.ParamList) {
   606  	p.print(params.Lparen, "(", noBlank)
   607  	p.indentWith(params.Lparen, params.Rparen, func() {
   608  		p.printNames(params.List, noBlank)
   609  		if params.Ellipsis != position.NoPos {
   610  			if len(params.List) > 0 {
   611  				p.print(p.lastPos, ",", noBlank)
   612  			}
   613  			p.print(params.Ellipsis, "...", 0)
   614  		}
   615  	})
   616  	p.print(params.Rparen, ")", noBlank)
   617  }
   618  
   619  func (p *printer) printBlock(block *ast.Block) {
   620  	p.indentWith(block.Opening, block.Closing, func() {
   621  		for _, stmt := range block.List {
   622  			p.printStmt(stmt)
   623  		}
   624  	})
   625  }
   626  
   627  func (p *printer) printNames(names []*ast.Name, mode mode) {
   628  	if len(names) == 0 {
   629  		return
   630  	}
   631  	p.printName(names[0], mode)
   632  	for _, name := range names[1:] {
   633  		p.print(p.lastPos, ",", noBlank)
   634  		p.printName(name, 0)
   635  	}
   636  }
   637  
   638  func (p *printer) printExprs(exprs []ast.Expr, mode mode) {
   639  	switch len(exprs) {
   640  	case 0:
   641  		return
   642  	case 1:
   643  		p.printExpr(exprs[0], mode)
   644  	default:
   645  		p.printExpr(exprs[0], mode|compact)
   646  		for _, expr := range exprs[1:] {
   647  			p.print(p.lastPos, ",", noBlank)
   648  			p.printExpr(expr, noParen|compact)
   649  		}
   650  	}
   651  }
   652  
   653  func (p *printer) indentWith(pos, end position.Position, fn func()) {
   654  	if pos.Line == end.Line {
   655  		fn()
   656  	} else {
   657  		p.insertComment(pos)
   658  
   659  		p.formfeed = true
   660  		p.doIndent = true
   661  
   662  		depth := p.depth
   663  
   664  		fn()
   665  
   666  		p.insertComment(end)
   667  
   668  		p.depth = depth
   669  
   670  		p.doIndent = false
   671  		p.formfeed = true
   672  	}
   673  }
   674  
   675  func (p *printer) insertComment(pos position.Position) {
   676  	for p.commentPos.LessThan(pos) {
   677  		for i, c := range p.comment.List {
   678  			d := c.Pos().Line - p.lastPos.Line
   679  
   680  			switch {
   681  			case d == 0:
   682  				if p.lastPos != initPos {
   683  					p.writeByte('\t')
   684  				}
   685  			case d > 0:
   686  				if i == 0 {
   687  					p.writeByte('\f')
   688  				} else {
   689  					if p.formfeed {
   690  						p.writeByte('\f')
   691  					} else {
   692  						p.writeByte('\n')
   693  					}
   694  				}
   695  				if d > 1 {
   696  					p.writeByte('\f')
   697  				}
   698  				for i := 0; i < p.depth; i++ {
   699  					p.writeString(indent)
   700  				}
   701  				p.formfeed = false
   702  			default:
   703  				panic("unexpected")
   704  			}
   705  
   706  			p.writeByte(tabwriter.Escape)
   707  			text := replaceEscape(strings.TrimRight(c.Text, "\t\n\v\f\r "))
   708  			for {
   709  				i := strings.IndexByte(text, '\n')
   710  				if i == -1 {
   711  					p.writeString(text)
   712  
   713  					break
   714  				}
   715  
   716  				p.writeString(trimRightCR(text[:i]))
   717  				p.writeByte('\n')
   718  
   719  				text = text[i+1:]
   720  			}
   721  			p.writeByte(tabwriter.Escape)
   722  
   723  			p.lastPos = c.End()
   724  		}
   725  
   726  		p.nextComment()
   727  	}
   728  }
   729  
   730  func (p *printer) print(pos position.Position, s string, mode mode) {
   731  	if p.doIndent {
   732  		if pos.Line-p.lastPos.Line > 0 {
   733  			p.depth++
   734  		}
   735  
   736  		p.doIndent = false
   737  	}
   738  
   739  	p.insertComment(pos)
   740  
   741  	d := pos.Line - p.lastPos.Line
   742  
   743  	switch {
   744  	case d == 0:
   745  		if mode&noBlank == 0 {
   746  			if p.lastPos != initPos {
   747  				if p.stmtEnd && mode&insertSemi != 0 {
   748  					p.writeByte(';')
   749  				}
   750  				p.writeByte(' ')
   751  			}
   752  		}
   753  	case d > 0:
   754  		if p.formfeed {
   755  			p.writeByte('\f')
   756  		} else {
   757  			p.writeByte('\n')
   758  		}
   759  		if d > 1 {
   760  			p.writeByte('\f')
   761  		}
   762  		for i := 0; i < p.depth; i++ {
   763  			p.writeString(indent)
   764  		}
   765  		p.formfeed = false
   766  	default:
   767  		panic("unexpected")
   768  	}
   769  
   770  	if mode&escape != 0 {
   771  		p.writeByte(tabwriter.Escape)
   772  		p.writeString(strconv.Escape(s))
   773  		p.writeByte(tabwriter.Escape)
   774  	} else {
   775  		p.writeString(s)
   776  	}
   777  
   778  	p.lastPos = pos.Offset(s)
   779  
   780  	p.stmtEnd = false
   781  }
   782  
   783  func (p *printer) writeByte(c byte) {
   784  	if p.err != nil {
   785  		return
   786  	}
   787  	_, p.err = p.w.Write([]byte{c})
   788  }
   789  
   790  func (p *printer) writeString(s string) {
   791  	if p.err != nil {
   792  		return
   793  	}
   794  	_, p.err = p.w.Write([]byte(s))
   795  }
   796  
   797  func trimRightCR(s string) string {
   798  	if len(s) > 0 && s[len(s)-1] == '\r' {
   799  		s = s[:len(s)-1]
   800  	}
   801  	return s
   802  }