github.com/hirochachacha/plua@v0.0.0-20170217012138-c82f520cc725/compiler/codegen/gen_stmt.go (about)

     1  package codegen
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hirochachacha/plua/compiler/ast"
     7  	"github.com/hirochachacha/plua/compiler/token"
     8  	"github.com/hirochachacha/plua/internal/version"
     9  	"github.com/hirochachacha/plua/object"
    10  	"github.com/hirochachacha/plua/opcode"
    11  )
    12  
    13  type immBool int
    14  
    15  const (
    16  	immUndefined immBool = iota
    17  	immTrue
    18  	immFalse
    19  )
    20  
    21  func (g *generator) genStmt(stmt ast.Stmt) {
    22  	switch stmt := stmt.(type) {
    23  	case *ast.BadStmt:
    24  		panic("bad stmt")
    25  	case *ast.EmptyStmt:
    26  		// do nothing
    27  	case *ast.LocalAssignStmt:
    28  		g.genLocalAssignStmt(stmt)
    29  	case *ast.LocalFuncStmt:
    30  		g.genLocalFuncStmt(stmt)
    31  	case *ast.FuncStmt:
    32  		g.genFuncStmt(stmt)
    33  	case *ast.LabelStmt:
    34  		g.genLabelStmt(stmt)
    35  	case *ast.ExprStmt:
    36  		g.genExprStmt(stmt)
    37  	case *ast.AssignStmt:
    38  		g.genAssignStmt(stmt)
    39  	case *ast.GotoStmt:
    40  		g.genGotoStmt(stmt)
    41  	case *ast.BreakStmt:
    42  		g.genBreakStmt(stmt)
    43  	case *ast.IfStmt:
    44  		g.genIfStmt(stmt)
    45  	case *ast.DoStmt:
    46  		g.genDoStmt(stmt)
    47  	case *ast.WhileStmt:
    48  		g.genWhileStmt(stmt)
    49  	case *ast.RepeatStmt:
    50  		g.genRepeatStmt(stmt)
    51  	case *ast.ReturnStmt:
    52  		g.genReturnStmt(stmt)
    53  	case *ast.ForStmt:
    54  		g.genForStmt(stmt)
    55  	case *ast.ForEachStmt:
    56  		g.genForEachStmt(stmt)
    57  	default:
    58  		panic("unreachable")
    59  	}
    60  }
    61  
    62  func (g *generator) genLocalAssignStmt(stmt *ast.LocalAssignStmt) {
    63  	sp := g.sp
    64  
    65  	g.locktmp = true
    66  
    67  	switch {
    68  	case len(stmt.LHS) > len(stmt.RHS):
    69  		if len(stmt.RHS) == 0 {
    70  			g.pushInst(opcode.AB(opcode.LOADNIL, g.sp, len(stmt.LHS)-1))
    71  		} else {
    72  			for _, e := range stmt.RHS[:len(stmt.RHS)-1] {
    73  				g.genExpr(e, genMove)
    74  			}
    75  
    76  			g.genExprN(stmt.RHS[len(stmt.RHS)-1], len(stmt.LHS)+1-len(stmt.RHS))
    77  		}
    78  	case len(stmt.LHS) < len(stmt.RHS):
    79  		for i := range stmt.LHS {
    80  			g.genExpr(stmt.RHS[i], genMove)
    81  		}
    82  
    83  		for i := len(stmt.LHS); i < len(stmt.RHS); i++ {
    84  			if _, ok := g.foldExpr(stmt.RHS[i]); !ok {
    85  				g.genExprN(stmt.RHS[i], 0)
    86  			}
    87  		}
    88  	default:
    89  		for i := range stmt.LHS {
    90  			g.genExpr(stmt.RHS[i], genMove)
    91  		}
    92  	}
    93  
    94  	g.locktmp = false
    95  
    96  	for i, name := range stmt.LHS {
    97  		g.declareLocalName(name, sp+i)
    98  	}
    99  
   100  	g.setSP(sp + len(stmt.LHS))
   101  }
   102  
   103  func (g *generator) genLocalFuncStmt(stmt *ast.LocalFuncStmt) {
   104  	name := stmt.Name
   105  
   106  	body := stmt.Body
   107  
   108  	endLine := stmt.End().Line
   109  
   110  	g.declareLocalName(name, g.sp) // declare before genFuncBody (for recursive function)
   111  
   112  	p := g.proto(body, false, endLine)
   113  
   114  	g.pushInstLine(opcode.ABx(opcode.CLOSURE, g.sp, p), endLine)
   115  
   116  	g.LocVars[len(g.LocVars)-1].StartPC++ // adjust StartPC (start from CLOSURE)
   117  
   118  	g.nextSP()
   119  }
   120  
   121  func (g *generator) genFuncStmt(stmt *ast.FuncStmt) {
   122  	sp := g.sp
   123  
   124  	name := stmt.Name
   125  	prefix := stmt.PathList
   126  
   127  	body := stmt.Body
   128  
   129  	endLine := stmt.End().Line
   130  
   131  	if prefix == nil {
   132  		l, ok := g.resolveName(name)
   133  
   134  		p := g.proto(body, false, endLine)
   135  
   136  		if ok {
   137  			switch l.kind {
   138  			case linkLocal:
   139  				g.pushInstLine(opcode.ABx(opcode.CLOSURE, l.index, p), endLine)
   140  			case linkUpval:
   141  				g.pushInstLine(opcode.ABx(opcode.CLOSURE, g.sp, p), endLine)
   142  
   143  				g.pushInst(opcode.AB(opcode.SETUPVAL, g.sp, l.index))
   144  			default:
   145  				panic("unreachable")
   146  			}
   147  		} else {
   148  			g.pushInstLine(opcode.ABx(opcode.CLOSURE, g.sp, p), endLine)
   149  
   150  			g.genSetGlobal(name, g.sp)
   151  		}
   152  	} else {
   153  		r := g.genPrefix(prefix)
   154  
   155  		rk := g.genName(name, genKey)
   156  
   157  		switch stmt.AccessTok {
   158  		case token.COLON:
   159  			p := g.proto(body, true, endLine)
   160  
   161  			g.pushInstLine(opcode.ABx(opcode.CLOSURE, g.sp, p), endLine)
   162  
   163  			g.pushInst(opcode.ABC(opcode.SETTABLE, r, rk, g.sp))
   164  		case token.PERIOD:
   165  			p := g.proto(body, false, endLine)
   166  
   167  			g.pushInstLine(opcode.ABx(opcode.CLOSURE, g.sp, p), endLine)
   168  
   169  			g.pushInst(opcode.ABC(opcode.SETTABLE, r, rk, g.sp))
   170  		default:
   171  			panic("unreachable")
   172  		}
   173  	}
   174  
   175  	// recover sp
   176  	g.sp = sp
   177  }
   178  
   179  func (g *generator) genLabelStmt(stmt *ast.LabelStmt) {
   180  	nameNode := stmt.Name
   181  	name := nameNode.Name
   182  	pos := nameNode.Pos()
   183  
   184  	if label, ok := g.labels[name]; ok {
   185  		g.error(pos, fmt.Errorf("label '%s' already defined on %s", name, label.pos))
   186  	}
   187  
   188  	g.declareLabelPos(name, pos)
   189  
   190  	g.lockpeep = true
   191  }
   192  
   193  func (g *generator) genExprStmt(stmt *ast.ExprStmt) {
   194  	sp := g.sp
   195  
   196  	g.genExprN(stmt.X, 0)
   197  
   198  	// recover sp
   199  	g.sp = sp
   200  }
   201  
   202  func (g *generator) genAssignStmt(stmt *ast.AssignStmt) {
   203  	sp := g.sp
   204  
   205  	g.locktmp = true
   206  
   207  	switch {
   208  	case len(stmt.LHS) > len(stmt.RHS):
   209  		if len(stmt.RHS) == 0 {
   210  			g.pushInst(opcode.AB(opcode.LOADNIL, sp, len(stmt.LHS)-1))
   211  		} else {
   212  			for _, e := range stmt.RHS[:len(stmt.RHS)-1] {
   213  				g.genExpr(e, genMove)
   214  			}
   215  
   216  			g.genExprN(stmt.RHS[len(stmt.RHS)-1], len(stmt.LHS)+1-len(stmt.RHS))
   217  		}
   218  	case len(stmt.LHS) < len(stmt.RHS):
   219  		for i := range stmt.LHS {
   220  			g.genExpr(stmt.RHS[i], genMove)
   221  		}
   222  
   223  		for i := len(stmt.LHS); i < len(stmt.RHS); i++ {
   224  			if _, ok := g.foldExpr(stmt.RHS[i]); !ok {
   225  				g.genExprN(stmt.RHS[i], 0)
   226  			}
   227  		}
   228  	default:
   229  		for i := range stmt.LHS {
   230  			g.genExpr(stmt.RHS[i], genMove)
   231  		}
   232  	}
   233  
   234  	g.locktmp = false
   235  
   236  	if len(stmt.LHS) == 1 { // fast path
   237  		g.genAssignSimple(stmt.LHS[0], sp)
   238  	} else {
   239  		g.genAssign(stmt.LHS, sp)
   240  	}
   241  
   242  	g.sp = sp
   243  }
   244  
   245  func (g *generator) genAssignSimple(lhs ast.Expr, r int) {
   246  	switch lhs := lhs.(type) {
   247  	case *ast.Name:
   248  		if l, ok := g.resolveName(lhs); ok {
   249  			switch l.kind {
   250  			case linkLocal:
   251  				g.pushInst(opcode.AB(opcode.MOVE, l.index, r))
   252  			case linkUpval:
   253  				g.pushInst(opcode.AB(opcode.SETUPVAL, r, l.index))
   254  			default:
   255  				panic("unreachable")
   256  			}
   257  		} else {
   258  			g.genSetGlobal(lhs, r)
   259  		}
   260  	case *ast.SelectorExpr:
   261  		x := g.genExpr(lhs.X, genR|genK)
   262  		y := g.genName(lhs.Sel, genKey)
   263  
   264  		g.pushInst(opcode.ABC(opcode.SETTABLE, x, y, r))
   265  	case *ast.IndexExpr:
   266  		x := g.genExpr(lhs.X, genR|genK)
   267  		y := g.genExpr(lhs.Index, genR|genK)
   268  
   269  		g.pushInst(opcode.ABC(opcode.SETTABLE, x, y, r))
   270  	default:
   271  		panic("unreachable")
   272  	}
   273  }
   274  
   275  func (g *generator) genAssign(LHS []ast.Expr, base int) {
   276  	assigns := make([]opcode.Instruction, len(LHS))
   277  
   278  	g.locktmp = true
   279  
   280  	var r int
   281  	for i, lhs := range LHS {
   282  		r = base + i
   283  
   284  		switch lhs := lhs.(type) {
   285  		case *ast.Name:
   286  			if l, ok := g.resolveName(lhs); ok {
   287  				switch l.kind {
   288  				case linkLocal:
   289  					assigns[i] = opcode.AB(opcode.MOVE, l.index, r)
   290  				case linkUpval:
   291  					assigns[i] = opcode.AB(opcode.SETUPVAL, r, l.index)
   292  				default:
   293  					panic("unreachable")
   294  				}
   295  			} else {
   296  				env := g.genName(tmpName(version.LUA_ENV), genR|genMove)
   297  
   298  				rk := g.markRK(g.constant(object.String(lhs.Name)), false)
   299  
   300  				assigns[i] = opcode.ABC(opcode.SETTABLE, env, rk, r)
   301  			}
   302  		case *ast.SelectorExpr:
   303  			x := g.genExpr(lhs.X, genR|genK|genMove)
   304  			y := g.genName(lhs.Sel, genKey)
   305  
   306  			assigns[i] = opcode.ABC(opcode.SETTABLE, x, y, r)
   307  		case *ast.IndexExpr:
   308  			x := g.genExpr(lhs.X, genR|genK|genMove)
   309  			y := g.genExpr(lhs.Index, genR|genK|genMove)
   310  
   311  			assigns[i] = opcode.ABC(opcode.SETTABLE, x, y, r)
   312  		default:
   313  			panic("unreachable")
   314  		}
   315  	}
   316  
   317  	for _, assign := range assigns[:len(assigns)-1] {
   318  		g.pushInst(assign)
   319  	}
   320  
   321  	g.locktmp = false
   322  
   323  	g.pushInst(assigns[len(assigns)-1])
   324  }
   325  
   326  func (g *generator) genGotoStmt(stmt *ast.GotoStmt) {
   327  	g.genSetJumpPoint(stmt.Label.Name, stmt.Label.Pos())
   328  }
   329  
   330  func (g *generator) genBreakStmt(stmt *ast.BreakStmt) {
   331  	g.genSetJumpPoint("@break", stmt.Break)
   332  }
   333  
   334  func (g *generator) genIfStmt(stmt *ast.IfStmt) {
   335  	if stmt.ElseIfList != nil {
   336  		elseBody := stmt.ElseBody
   337  		root := &ast.IfStmt{
   338  			Cond: stmt.Cond,
   339  			Body: stmt.Body,
   340  		}
   341  		leaf := root
   342  		for _, stmt := range stmt.ElseIfList {
   343  			nextLeaf := &ast.IfStmt{
   344  				Cond: stmt.Cond,
   345  				Body: stmt.Body,
   346  			}
   347  			leaf.ElseBody = &ast.Block{
   348  				List: []ast.Stmt{
   349  					nextLeaf,
   350  				},
   351  			}
   352  			leaf = nextLeaf
   353  		}
   354  		leaf.ElseBody = elseBody
   355  		stmt = root
   356  	}
   357  
   358  	g.openScope()
   359  
   360  	switch g.genTest(stmt.Cond, false) {
   361  	case immTrue:
   362  		if stmt.Body != nil {
   363  			g.genBlock(stmt.Body)
   364  		}
   365  	case immFalse:
   366  		if stmt.ElseBody != nil {
   367  			g.genBlock(stmt.ElseBody)
   368  		}
   369  	default:
   370  		elseJump := g.genJumpPoint()
   371  
   372  		if stmt.Body != nil {
   373  			g.openScope()
   374  
   375  			g.genBlock(stmt.Body)
   376  
   377  			g.closeScope()
   378  		}
   379  
   380  		if stmt.ElseBody != nil {
   381  			doneJump := g.genJumpPoint()
   382  
   383  			g.genJumpFrom(elseJump)
   384  
   385  			g.openScope()
   386  
   387  			g.genBlock(stmt.ElseBody)
   388  
   389  			g.closeScope()
   390  
   391  			g.genJumpFrom(doneJump)
   392  		} else {
   393  			g.genJumpFrom(elseJump)
   394  		}
   395  	}
   396  
   397  	g.closeScope()
   398  }
   399  
   400  func (g *generator) genDoStmt(stmt *ast.DoStmt) {
   401  	if stmt.Body != nil {
   402  		g.openScope()
   403  
   404  		g.genBlock(stmt.Body)
   405  
   406  		g.closeScope()
   407  	}
   408  }
   409  
   410  func (g *generator) genWhileStmt(stmt *ast.WhileStmt) {
   411  	g.openScope()
   412  
   413  	initLabel := g.newLabel()
   414  
   415  	switch g.genTest(stmt.Cond, false) {
   416  	case immTrue:
   417  		if stmt.Body != nil {
   418  			g.genBlock(stmt.Body)
   419  		}
   420  
   421  		g.closeScope()
   422  
   423  		g.genJumpTo(initLabel)
   424  
   425  		g.declareLabel("@break")
   426  	case immFalse:
   427  		g.closeScope()
   428  	default:
   429  		endJump := g.genJumpPoint()
   430  
   431  		if stmt.Body != nil {
   432  			g.genBlock(stmt.Body)
   433  		}
   434  
   435  		g.closeScope()
   436  
   437  		g.genJumpTo(initLabel)
   438  
   439  		g.genJumpFrom(endJump)
   440  
   441  		g.declareLabel("@break")
   442  	}
   443  }
   444  
   445  func (g *generator) genRepeatStmt(stmt *ast.RepeatStmt) {
   446  	g.openScope()
   447  
   448  	initLabel := g.newLabel()
   449  
   450  	if stmt.Body != nil {
   451  		g.genBlock(stmt.Body)
   452  	}
   453  
   454  	switch g.genTest(stmt.Cond, true) {
   455  	case immTrue:
   456  		g.genJumpTo(initLabel)
   457  
   458  		g.declareLabel("@break")
   459  	case immFalse:
   460  		g.declareLabel("@break")
   461  	default:
   462  		endJump := g.genJumpPoint()
   463  
   464  		g.genJumpTo(initLabel)
   465  
   466  		g.genJumpFrom(endJump)
   467  
   468  		g.declareLabel("@break")
   469  	}
   470  
   471  	g.closeScope()
   472  }
   473  
   474  func (g *generator) genReturnStmt(stmt *ast.ReturnStmt) {
   475  	if len(stmt.Results) == 0 {
   476  		g.pushInst(opcode.AB(opcode.RETURN, 0, 1))
   477  
   478  		return
   479  	}
   480  
   481  	sp := g.sp
   482  
   483  	if len(stmt.Results) == 1 {
   484  		if tail, ok := stmt.Results[len(stmt.Results)-1].(*ast.CallExpr); ok {
   485  			g.genCallExprN(tail, -1, true)
   486  
   487  			g.pushInst(opcode.AB(opcode.RETURN, sp, 0))
   488  
   489  			return
   490  		}
   491  	}
   492  
   493  	g.locktmp = true
   494  
   495  	var isVar bool
   496  	if len(stmt.Results) != 0 {
   497  		for _, e := range stmt.Results[:len(stmt.Results)-1] {
   498  			g.genExpr(e, genMove)
   499  		}
   500  
   501  		isVar = g.genExprN(stmt.Results[len(stmt.Results)-1], -1)
   502  	}
   503  
   504  	if isVar {
   505  		g.pushInst(opcode.AB(opcode.RETURN, sp, 0))
   506  	} else {
   507  		g.pushInst(opcode.AB(opcode.RETURN, sp, len(stmt.Results)+1))
   508  	}
   509  
   510  	g.locktmp = false
   511  }
   512  
   513  func (g *generator) genForStmt(stmt *ast.ForStmt) {
   514  	g.openScope()
   515  
   516  	forLine := stmt.For.Line
   517  
   518  	sp := g.sp
   519  
   520  	g.locktmp = true
   521  
   522  	g.genExpr(stmt.Start, genMove)
   523  	g.genExpr(stmt.Finish, genMove)
   524  	if stmt.Step != nil {
   525  		g.genExpr(stmt.Step, genMove)
   526  	} else {
   527  		g.genConst(object.Integer(1), genMove)
   528  	}
   529  
   530  	g.locktmp = false
   531  
   532  	g.declareLocal("(for index)", sp)
   533  	g.declareLocal("(for limit)", sp+1)
   534  	g.declareLocal("(for step)", sp+2)
   535  
   536  	forprep := g.pushTempLine(forLine)
   537  
   538  	g.openScope()
   539  
   540  	g.declareLocalName(stmt.Name, g.sp)
   541  
   542  	g.addSP(1)
   543  
   544  	if stmt.Body != nil {
   545  		g.genBlock(stmt.Body)
   546  	}
   547  
   548  	g.closeScope()
   549  
   550  	g.Code[forprep] = opcode.AsBx(opcode.FORPREP, sp, g.pc()-forprep-1)
   551  
   552  	g.pushInstLine(opcode.AsBx(opcode.FORLOOP, sp, forprep-g.pc()), forLine)
   553  
   554  	g.declareLabel("@break")
   555  
   556  	g.sp = sp
   557  
   558  	g.closeScope()
   559  }
   560  
   561  func (g *generator) genForEachStmt(stmt *ast.ForEachStmt) {
   562  	g.openScope()
   563  
   564  	forLine := stmt.For.Line
   565  
   566  	sp := g.sp
   567  
   568  	g.locktmp = true
   569  
   570  	switch {
   571  	case len(stmt.Exprs) > 3:
   572  		g.genExpr(stmt.Exprs[0], genMove)
   573  		g.genExpr(stmt.Exprs[1], genMove)
   574  		g.genExpr(stmt.Exprs[2], genMove)
   575  		for i := 3; i < len(stmt.Exprs); i++ {
   576  			if _, ok := g.foldExpr(stmt.Exprs[i]); !ok {
   577  				g.genExprN(stmt.Exprs[i], 0)
   578  			}
   579  		}
   580  	case len(stmt.Exprs) < 3:
   581  		switch len(stmt.Exprs) {
   582  		case 1:
   583  			g.genExprN(stmt.Exprs[0], 3)
   584  		case 2:
   585  			g.genExpr(stmt.Exprs[0], genMove)
   586  			g.genExprN(stmt.Exprs[1], 2)
   587  		default:
   588  			panic("unreachable")
   589  		}
   590  	default:
   591  		for _, e := range stmt.Exprs {
   592  			g.genExpr(e, genMove)
   593  		}
   594  	}
   595  
   596  	g.locktmp = false
   597  
   598  	g.declareLocal("(for generator)", sp)
   599  	g.declareLocal("(for generator)", sp+1)
   600  	g.declareLocal("(for control)", sp+2)
   601  
   602  	g.setSP(sp + 3)
   603  
   604  	loopJump := g.genJumpPoint()
   605  
   606  	g.openScope()
   607  
   608  	for i, name := range stmt.Names {
   609  		g.declareLocalName(name, g.sp+i)
   610  	}
   611  
   612  	g.addSP(len(stmt.Names))
   613  
   614  	init := g.pc()
   615  
   616  	if stmt.Body != nil {
   617  		g.genBlock(stmt.Body)
   618  	}
   619  
   620  	g.genJumpFrom(loopJump)
   621  
   622  	g.closeScope()
   623  
   624  	g.pushInstLine(opcode.AC(opcode.TFORCALL, sp, len(stmt.Names)), forLine)
   625  
   626  	g.pushInstLine(opcode.AsBx(opcode.TFORLOOP, sp+2, init-g.pc()-1), forLine)
   627  
   628  	g.declareLabel("@break")
   629  
   630  	g.closeScope()
   631  }