gitee.com/yfmps/gopher-lua@v0.0.3/compile.go (about)

     1  package lua
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/yuin/gopher-lua/ast"
     6  	"math"
     7  	"reflect"
     8  )
     9  
    10  /* internal constants & structs  {{{ */
    11  
    12  const maxRegisters = 200
    13  
    14  type expContextType int
    15  
    16  const (
    17  	ecGlobal expContextType = iota
    18  	ecUpvalue
    19  	ecLocal
    20  	ecTable
    21  	ecVararg
    22  	ecMethod
    23  	ecNone
    24  )
    25  
    26  const regNotDefined = opMaxArgsA + 1
    27  const labelNoJump = 0
    28  
    29  type expcontext struct {
    30  	ctype expContextType
    31  	reg   int
    32  	// varargopt >= 0: wants varargopt+1 results, i.e  a = func()
    33  	// varargopt = -1: ignore results             i.e  func()
    34  	// varargopt = -2: receive all results        i.e  a = {func()}
    35  	varargopt int
    36  }
    37  
    38  type assigncontext struct {
    39  	ec       *expcontext
    40  	keyrk    int
    41  	valuerk  int
    42  	keyks    bool
    43  	needmove bool
    44  }
    45  
    46  type lblabels struct {
    47  	t int
    48  	f int
    49  	e int
    50  	b bool
    51  }
    52  
    53  type constLValueExpr struct {
    54  	ast.ExprBase
    55  
    56  	Value LValue
    57  }
    58  
    59  // }}}
    60  
    61  /* utilities {{{ */
    62  var _ecnone0 = &expcontext{ecNone, regNotDefined, 0}
    63  var _ecnonem1 = &expcontext{ecNone, regNotDefined, -1}
    64  var _ecnonem2 = &expcontext{ecNone, regNotDefined, -2}
    65  var ecfuncdef = &expcontext{ecMethod, regNotDefined, 0}
    66  
    67  func ecupdate(ec *expcontext, ctype expContextType, reg, varargopt int) {
    68  	if ec == _ecnone0 || ec == _ecnonem1 || ec == _ecnonem2 {
    69  		panic("can not update ec cache")
    70  	}
    71  	ec.ctype = ctype
    72  	ec.reg = reg
    73  	ec.varargopt = varargopt
    74  }
    75  
    76  func ecnone(varargopt int) *expcontext {
    77  	switch varargopt {
    78  	case 0:
    79  		return _ecnone0
    80  	case -1:
    81  		return _ecnonem1
    82  	case -2:
    83  		return _ecnonem2
    84  	}
    85  	return &expcontext{ecNone, regNotDefined, varargopt}
    86  }
    87  
    88  func shouldmove(ec *expcontext, reg int) bool {
    89  	return ec.ctype == ecLocal && ec.reg != regNotDefined && ec.reg != reg
    90  }
    91  
    92  func sline(pos ast.PositionHolder) int {
    93  	return pos.Line()
    94  }
    95  
    96  func eline(pos ast.PositionHolder) int {
    97  	return pos.LastLine()
    98  }
    99  
   100  func savereg(ec *expcontext, reg int) int {
   101  	if ec.ctype != ecLocal || ec.reg == regNotDefined {
   102  		return reg
   103  	}
   104  	return ec.reg
   105  }
   106  
   107  func raiseCompileError(context *funcContext, line int, format string, args ...interface{}) {
   108  	msg := fmt.Sprintf(format, args...)
   109  	panic(&CompileError{context: context, Line: line, Message: msg})
   110  }
   111  
   112  func isVarArgReturnExpr(expr ast.Expr) bool {
   113  	switch ex := expr.(type) {
   114  	case *ast.FuncCallExpr:
   115  		return !ex.AdjustRet
   116  	case *ast.Comma3Expr:
   117  		return true
   118  	}
   119  	return false
   120  }
   121  
   122  func lnumberValue(expr ast.Expr) (LNumber, bool) {
   123  	if ex, ok := expr.(*ast.NumberExpr); ok {
   124  		lv, err := parseNumber(ex.Value)
   125  		if err != nil {
   126  			lv = LNumber(math.NaN())
   127  		}
   128  		return lv, true
   129  	} else if ex, ok := expr.(*constLValueExpr); ok {
   130  		return ex.Value.(LNumber), true
   131  	}
   132  	return 0, false
   133  }
   134  
   135  /* utilities }}} */
   136  
   137  type CompileError struct { // {{{
   138  	context *funcContext
   139  	Line    int
   140  	Message string
   141  }
   142  
   143  func (e *CompileError) Error() string {
   144  	return fmt.Sprintf("compile error near line(%v) %v: %v", e.Line, e.context.Proto.SourceName, e.Message)
   145  } // }}}
   146  
   147  type codeStore struct { // {{{
   148  	codes []uint32
   149  	lines []int
   150  	pc    int
   151  }
   152  
   153  func (cd *codeStore) Add(inst uint32, line int) {
   154  	if l := len(cd.codes); l <= 0 || cd.pc == l {
   155  		cd.codes = append(cd.codes, inst)
   156  		cd.lines = append(cd.lines, line)
   157  	} else {
   158  		cd.codes[cd.pc] = inst
   159  		cd.lines[cd.pc] = line
   160  	}
   161  	cd.pc++
   162  }
   163  
   164  func (cd *codeStore) AddABC(op int, a int, b int, c int, line int) {
   165  	cd.Add(opCreateABC(op, a, b, c), line)
   166  }
   167  
   168  func (cd *codeStore) AddABx(op int, a int, bx int, line int) {
   169  	cd.Add(opCreateABx(op, a, bx), line)
   170  }
   171  
   172  func (cd *codeStore) AddASbx(op int, a int, sbx int, line int) {
   173  	cd.Add(opCreateASbx(op, a, sbx), line)
   174  }
   175  
   176  func (cd *codeStore) PropagateKMV(top int, save *int, reg *int, inc int) {
   177  	lastinst := cd.Last()
   178  	if opGetArgA(lastinst) >= top {
   179  		switch opGetOpCode(lastinst) {
   180  		case OP_LOADK:
   181  			cindex := opGetArgBx(lastinst)
   182  			if cindex <= opMaxIndexRk {
   183  				cd.Pop()
   184  				*save = opRkAsk(cindex)
   185  				return
   186  			}
   187  		case OP_MOVE:
   188  			cd.Pop()
   189  			*save = opGetArgB(lastinst)
   190  			return
   191  		}
   192  	}
   193  	*save = *reg
   194  	*reg = *reg + inc
   195  }
   196  
   197  func (cd *codeStore) PropagateMV(top int, save *int, reg *int, inc int) {
   198  	lastinst := cd.Last()
   199  	if opGetArgA(lastinst) >= top {
   200  		switch opGetOpCode(lastinst) {
   201  		case OP_MOVE:
   202  			cd.Pop()
   203  			*save = opGetArgB(lastinst)
   204  			return
   205  		}
   206  	}
   207  	*save = *reg
   208  	*reg = *reg + inc
   209  }
   210  
   211  func (cd *codeStore) AddLoadNil(a, b, line int) {
   212  	last := cd.Last()
   213  	if opGetOpCode(last) == OP_LOADNIL && (opGetArgA(last)+opGetArgB(last)) == a {
   214  		cd.SetB(cd.LastPC(), b)
   215  	} else {
   216  		cd.AddABC(OP_LOADNIL, a, b, 0, line)
   217  	}
   218  }
   219  
   220  func (cd *codeStore) SetOpCode(pc int, v int) {
   221  	opSetOpCode(&cd.codes[pc], v)
   222  }
   223  
   224  func (cd *codeStore) SetA(pc int, v int) {
   225  	opSetArgA(&cd.codes[pc], v)
   226  }
   227  
   228  func (cd *codeStore) SetB(pc int, v int) {
   229  	opSetArgB(&cd.codes[pc], v)
   230  }
   231  
   232  func (cd *codeStore) SetC(pc int, v int) {
   233  	opSetArgC(&cd.codes[pc], v)
   234  }
   235  
   236  func (cd *codeStore) SetBx(pc int, v int) {
   237  	opSetArgBx(&cd.codes[pc], v)
   238  }
   239  
   240  func (cd *codeStore) SetSbx(pc int, v int) {
   241  	opSetArgSbx(&cd.codes[pc], v)
   242  }
   243  
   244  func (cd *codeStore) At(pc int) uint32 {
   245  	return cd.codes[pc]
   246  }
   247  
   248  func (cd *codeStore) List() []uint32 {
   249  	return cd.codes[:cd.pc]
   250  }
   251  
   252  func (cd *codeStore) PosList() []int {
   253  	return cd.lines[:cd.pc]
   254  }
   255  
   256  func (cd *codeStore) LastPC() int {
   257  	return cd.pc - 1
   258  }
   259  
   260  func (cd *codeStore) Last() uint32 {
   261  	if cd.pc == 0 {
   262  		return opInvalidInstruction
   263  	}
   264  	return cd.codes[cd.pc-1]
   265  }
   266  
   267  func (cd *codeStore) Pop() {
   268  	cd.pc--
   269  } /* }}} Code */
   270  
   271  /* {{{ VarNamePool */
   272  
   273  type varNamePoolValue struct {
   274  	Index int
   275  	Name  string
   276  }
   277  
   278  type varNamePool struct {
   279  	names       []string
   280  	globalIndex []int
   281  	offset      int
   282  }
   283  
   284  func newVarNamePool(offset int) *varNamePool {
   285  	return &varNamePool{make([]string, 0, 16), make([]int, 0, 16), offset}
   286  }
   287  
   288  func (vp *varNamePool) Names() []string {
   289  	return vp.names
   290  }
   291  
   292  func (vp *varNamePool) List() []varNamePoolValue {
   293  	result := make([]varNamePoolValue, len(vp.names), len(vp.names))
   294  	for i, name := range vp.names {
   295  		result[i].Index = i + vp.offset
   296  		result[i].Name = name
   297  	}
   298  	return result
   299  }
   300  
   301  func (vp *varNamePool) ListGlobalIndex() []varNamePoolValue {
   302  	result := make([]varNamePoolValue, len(vp.names), len(vp.names))
   303  	for i, name := range vp.names {
   304  		result[i].Index = vp.globalIndex[i]
   305  		result[i].Name = name
   306  	}
   307  	return result
   308  }
   309  
   310  func (vp *varNamePool) LastIndex() int {
   311  	return vp.offset + len(vp.names)
   312  }
   313  
   314  func (vp *varNamePool) Find(name string) int {
   315  	for i := len(vp.names) - 1; i >= 0; i-- {
   316  		if vp.names[i] == name {
   317  			return i + vp.offset
   318  		}
   319  	}
   320  	return -1
   321  }
   322  
   323  func (vp *varNamePool) RegisterUnique(name string) int {
   324  	index := vp.Find(name)
   325  	if index < 0 {
   326  		return vp.Register(name, -1)
   327  	}
   328  	return index
   329  }
   330  
   331  func (vp *varNamePool) Register(name string, globalIndex int) int {
   332  	vp.names = append(vp.names, name)
   333  	vp.globalIndex = append(vp.globalIndex, globalIndex)
   334  	return len(vp.names) - 1 + vp.offset
   335  }
   336  
   337  /* }}} VarNamePool */
   338  
   339  /* FuncContext {{{ */
   340  
   341  type codeBlock struct {
   342  	LocalVars  *varNamePool
   343  	BreakLabel int
   344  	Parent     *codeBlock
   345  	RefUpvalue bool
   346  	LineStart  int
   347  	LastLine   int
   348  }
   349  
   350  func newCodeBlock(localvars *varNamePool, blabel int, parent *codeBlock, pos ast.PositionHolder) *codeBlock {
   351  	bl := &codeBlock{localvars, blabel, parent, false, 0, 0}
   352  	if pos != nil {
   353  		bl.LineStart = pos.Line()
   354  		bl.LastLine = pos.LastLine()
   355  	}
   356  	return bl
   357  }
   358  
   359  type funcContext struct {
   360  	Proto    *FunctionProto
   361  	Code     *codeStore
   362  	Parent   *funcContext
   363  	Upvalues *varNamePool
   364  	Block    *codeBlock
   365  	Blocks   []*codeBlock
   366  	regTop   int
   367  	labelId  int
   368  	labelPc  map[int]int
   369  }
   370  
   371  func newFuncContext(sourcename string, parent *funcContext) *funcContext {
   372  	fc := &funcContext{
   373  		Proto:    newFunctionProto(sourcename),
   374  		Code:     &codeStore{make([]uint32, 0, 1024), make([]int, 0, 1024), 0},
   375  		Parent:   parent,
   376  		Upvalues: newVarNamePool(0),
   377  		Block:    newCodeBlock(newVarNamePool(0), labelNoJump, nil, nil),
   378  		regTop:   0,
   379  		labelId:  1,
   380  		labelPc:  map[int]int{},
   381  	}
   382  	fc.Blocks = []*codeBlock{fc.Block}
   383  	return fc
   384  }
   385  
   386  func (fc *funcContext) NewLabel() int {
   387  	ret := fc.labelId
   388  	fc.labelId++
   389  	return ret
   390  }
   391  
   392  func (fc *funcContext) SetLabelPc(label int, pc int) {
   393  	fc.labelPc[label] = pc
   394  }
   395  
   396  func (fc *funcContext) GetLabelPc(label int) int {
   397  	return fc.labelPc[label]
   398  }
   399  
   400  func (fc *funcContext) ConstIndex(value LValue) int {
   401  	ctype := value.Type()
   402  	for i, lv := range fc.Proto.Constants {
   403  		if lv.Type() == ctype && lv == value {
   404  			return i
   405  		}
   406  	}
   407  	fc.Proto.Constants = append(fc.Proto.Constants, value)
   408  	v := len(fc.Proto.Constants) - 1
   409  	if v > opMaxArgBx {
   410  		raiseCompileError(fc, fc.Proto.LineDefined, "too many constants")
   411  	}
   412  	return v
   413  }
   414  
   415  func (fc *funcContext) RegisterLocalVar(name string) int {
   416  	ret := fc.Block.LocalVars.Register(name, len(fc.Proto.DbgLocals))
   417  	fc.Proto.DbgLocals = append(fc.Proto.DbgLocals, &DbgLocalInfo{Name: name, StartPc: fc.Code.pc})
   418  	fc.SetRegTop(fc.RegTop() + 1)
   419  	return ret
   420  }
   421  
   422  func (fc *funcContext) FindLocalVarAndBlock(name string) (int, *codeBlock) {
   423  	for block := fc.Block; block != nil; block = block.Parent {
   424  		if index := block.LocalVars.Find(name); index > -1 {
   425  			return index, block
   426  		}
   427  	}
   428  	return -1, nil
   429  }
   430  
   431  func (fc *funcContext) FindLocalVar(name string) int {
   432  	idx, _ := fc.FindLocalVarAndBlock(name)
   433  	return idx
   434  }
   435  
   436  func (fc *funcContext) LocalVars() []varNamePoolValue {
   437  	result := make([]varNamePoolValue, 0, 32)
   438  	for _, block := range fc.Blocks {
   439  		result = append(result, block.LocalVars.List()...)
   440  	}
   441  	return result
   442  }
   443  
   444  func (fc *funcContext) EnterBlock(blabel int, pos ast.PositionHolder) {
   445  	fc.Block = newCodeBlock(newVarNamePool(fc.RegTop()), blabel, fc.Block, pos)
   446  	fc.Blocks = append(fc.Blocks, fc.Block)
   447  }
   448  
   449  func (fc *funcContext) CloseUpvalues() int {
   450  	n := -1
   451  	if fc.Block.RefUpvalue {
   452  		n = fc.Block.Parent.LocalVars.LastIndex()
   453  		fc.Code.AddABC(OP_CLOSE, n, 0, 0, fc.Block.LastLine)
   454  	}
   455  	return n
   456  }
   457  
   458  func (fc *funcContext) LeaveBlock() int {
   459  	closed := fc.CloseUpvalues()
   460  	fc.EndScope()
   461  	fc.Block = fc.Block.Parent
   462  	fc.SetRegTop(fc.Block.LocalVars.LastIndex())
   463  	return closed
   464  }
   465  
   466  func (fc *funcContext) EndScope() {
   467  	for _, vr := range fc.Block.LocalVars.ListGlobalIndex() {
   468  		fc.Proto.DbgLocals[vr.Index].EndPc = fc.Code.LastPC()
   469  	}
   470  
   471  	//for _, dbLocal := range fc.Proto.DbgLocals {
   472  	//	if fc.Block.LocalVars.Find(dbLocal.Name) != -1 {
   473  	//		dbLocal.EndPc = fc.Code.LastPC()
   474  	//	}
   475  	//}
   476  }
   477  
   478  func (fc *funcContext) SetRegTop(top int) {
   479  	if top > maxRegisters {
   480  		raiseCompileError(fc, fc.Proto.LineDefined, "too many local variables")
   481  	}
   482  	fc.regTop = top
   483  }
   484  
   485  func (fc *funcContext) RegTop() int {
   486  	return fc.regTop
   487  }
   488  
   489  /* FuncContext }}} */
   490  
   491  func compileChunk(context *funcContext, chunk []ast.Stmt) { // {{{
   492  	for _, stmt := range chunk {
   493  		compileStmt(context, stmt)
   494  	}
   495  } // }}}
   496  
   497  func compileBlock(context *funcContext, chunk []ast.Stmt) { // {{{
   498  	if len(chunk) == 0 {
   499  		return
   500  	}
   501  	ph := &ast.Node{}
   502  	ph.SetLine(sline(chunk[0]))
   503  	ph.SetLastLine(eline(chunk[len(chunk)-1]))
   504  	context.EnterBlock(labelNoJump, ph)
   505  	for _, stmt := range chunk {
   506  		compileStmt(context, stmt)
   507  	}
   508  	context.LeaveBlock()
   509  } // }}}
   510  
   511  func compileStmt(context *funcContext, stmt ast.Stmt) { // {{{
   512  	switch st := stmt.(type) {
   513  	case *ast.AssignStmt:
   514  		compileAssignStmt(context, st)
   515  	case *ast.LocalAssignStmt:
   516  		compileLocalAssignStmt(context, st)
   517  	case *ast.FuncCallStmt:
   518  		compileFuncCallExpr(context, context.RegTop(), st.Expr.(*ast.FuncCallExpr), ecnone(-1))
   519  	case *ast.DoBlockStmt:
   520  		context.EnterBlock(labelNoJump, st)
   521  		compileChunk(context, st.Stmts)
   522  		context.LeaveBlock()
   523  	case *ast.WhileStmt:
   524  		compileWhileStmt(context, st)
   525  	case *ast.RepeatStmt:
   526  		compileRepeatStmt(context, st)
   527  	case *ast.FuncDefStmt:
   528  		compileFuncDefStmt(context, st)
   529  	case *ast.ReturnStmt:
   530  		compileReturnStmt(context, st)
   531  	case *ast.IfStmt:
   532  		compileIfStmt(context, st)
   533  	case *ast.BreakStmt:
   534  		compileBreakStmt(context, st)
   535  	case *ast.NumberForStmt:
   536  		compileNumberForStmt(context, st)
   537  	case *ast.GenericForStmt:
   538  		compileGenericForStmt(context, st)
   539  	}
   540  } // }}}
   541  
   542  func compileAssignStmtLeft(context *funcContext, stmt *ast.AssignStmt) (int, []*assigncontext) { // {{{
   543  	reg := context.RegTop()
   544  	acs := make([]*assigncontext, 0, len(stmt.Lhs))
   545  	for i, lhs := range stmt.Lhs {
   546  		islast := i == len(stmt.Lhs)-1
   547  		switch st := lhs.(type) {
   548  		case *ast.IdentExpr:
   549  			identtype := getIdentRefType(context, context, st)
   550  			ec := &expcontext{identtype, regNotDefined, 0}
   551  			switch identtype {
   552  			case ecGlobal:
   553  				context.ConstIndex(LString(st.Value))
   554  			case ecUpvalue:
   555  				context.Upvalues.RegisterUnique(st.Value)
   556  			case ecLocal:
   557  				if islast {
   558  					ec.reg = context.FindLocalVar(st.Value)
   559  				}
   560  			}
   561  			acs = append(acs, &assigncontext{ec, 0, 0, false, false})
   562  		case *ast.AttrGetExpr:
   563  			ac := &assigncontext{&expcontext{ecTable, regNotDefined, 0}, 0, 0, false, false}
   564  			compileExprWithKMVPropagation(context, st.Object, &reg, &ac.ec.reg)
   565  			ac.keyrk = reg
   566  			reg += compileExpr(context, reg, st.Key, ecnone(0))
   567  			if _, ok := st.Key.(*ast.StringExpr); ok {
   568  				ac.keyks = true
   569  			}
   570  			acs = append(acs, ac)
   571  
   572  		default:
   573  			panic("invalid left expression.")
   574  		}
   575  	}
   576  	return reg, acs
   577  } // }}}
   578  
   579  func compileAssignStmtRight(context *funcContext, stmt *ast.AssignStmt, reg int, acs []*assigncontext) (int, []*assigncontext) { // {{{
   580  	lennames := len(stmt.Lhs)
   581  	lenexprs := len(stmt.Rhs)
   582  	namesassigned := 0
   583  
   584  	for namesassigned < lennames {
   585  		ac := acs[namesassigned]
   586  		ec := ac.ec
   587  		var expr ast.Expr = nil
   588  		if namesassigned >= lenexprs {
   589  			expr = &ast.NilExpr{}
   590  			expr.SetLine(sline(stmt.Lhs[namesassigned]))
   591  			expr.SetLastLine(eline(stmt.Lhs[namesassigned]))
   592  		} else if isVarArgReturnExpr(stmt.Rhs[namesassigned]) && (lenexprs-namesassigned-1) <= 0 {
   593  			varargopt := lennames - namesassigned - 1
   594  			regstart := reg
   595  			reginc := compileExpr(context, reg, stmt.Rhs[namesassigned], ecnone(varargopt))
   596  			reg += reginc
   597  			for i := namesassigned; i < namesassigned+int(reginc); i++ {
   598  				acs[i].needmove = true
   599  				if acs[i].ec.ctype == ecTable {
   600  					acs[i].valuerk = regstart + (i - namesassigned)
   601  				}
   602  			}
   603  			namesassigned = lennames
   604  			continue
   605  		}
   606  
   607  		if expr == nil {
   608  			expr = stmt.Rhs[namesassigned]
   609  		}
   610  		idx := reg
   611  		reginc := compileExpr(context, reg, expr, ec)
   612  		if ec.ctype == ecTable {
   613  			if _, ok := expr.(*ast.LogicalOpExpr); !ok {
   614  				context.Code.PropagateKMV(context.RegTop(), &ac.valuerk, &reg, reginc)
   615  			} else {
   616  				ac.valuerk = idx
   617  				reg += reginc
   618  			}
   619  		} else {
   620  			ac.needmove = reginc != 0
   621  			reg += reginc
   622  		}
   623  		namesassigned += 1
   624  	}
   625  
   626  	rightreg := reg - 1
   627  
   628  	// extra right exprs
   629  	for i := namesassigned; i < lenexprs; i++ {
   630  		varargopt := -1
   631  		if i != lenexprs-1 {
   632  			varargopt = 0
   633  		}
   634  		reg += compileExpr(context, reg, stmt.Rhs[i], ecnone(varargopt))
   635  	}
   636  	return rightreg, acs
   637  } // }}}
   638  
   639  func compileAssignStmt(context *funcContext, stmt *ast.AssignStmt) { // {{{
   640  	code := context.Code
   641  	lennames := len(stmt.Lhs)
   642  	reg, acs := compileAssignStmtLeft(context, stmt)
   643  	reg, acs = compileAssignStmtRight(context, stmt, reg, acs)
   644  
   645  	for i := lennames - 1; i >= 0; i-- {
   646  		ex := stmt.Lhs[i]
   647  		switch acs[i].ec.ctype {
   648  		case ecLocal:
   649  			if acs[i].needmove {
   650  				code.AddABC(OP_MOVE, context.FindLocalVar(ex.(*ast.IdentExpr).Value), reg, 0, sline(ex))
   651  				reg -= 1
   652  			}
   653  		case ecGlobal:
   654  			code.AddABx(OP_SETGLOBAL, reg, context.ConstIndex(LString(ex.(*ast.IdentExpr).Value)), sline(ex))
   655  			reg -= 1
   656  		case ecUpvalue:
   657  			code.AddABC(OP_SETUPVAL, reg, context.Upvalues.RegisterUnique(ex.(*ast.IdentExpr).Value), 0, sline(ex))
   658  			reg -= 1
   659  		case ecTable:
   660  			opcode := OP_SETTABLE
   661  			if acs[i].keyks {
   662  				opcode = OP_SETTABLEKS
   663  			}
   664  			code.AddABC(opcode, acs[i].ec.reg, acs[i].keyrk, acs[i].valuerk, sline(ex))
   665  			if !opIsK(acs[i].valuerk) {
   666  				reg -= 1
   667  			}
   668  		}
   669  	}
   670  } // }}}
   671  
   672  func compileRegAssignment(context *funcContext, names []string, exprs []ast.Expr, reg int, nvars int, line int) { // {{{
   673  	lennames := len(names)
   674  	lenexprs := len(exprs)
   675  	namesassigned := 0
   676  	ec := &expcontext{}
   677  
   678  	for namesassigned < lennames && namesassigned < lenexprs {
   679  		if isVarArgReturnExpr(exprs[namesassigned]) && (lenexprs-namesassigned-1) <= 0 {
   680  
   681  			varargopt := nvars - namesassigned
   682  			ecupdate(ec, ecVararg, reg, varargopt-1)
   683  			compileExpr(context, reg, exprs[namesassigned], ec)
   684  			reg += varargopt
   685  			namesassigned = lennames
   686  		} else {
   687  			ecupdate(ec, ecLocal, reg, 0)
   688  			compileExpr(context, reg, exprs[namesassigned], ec)
   689  			reg += 1
   690  			namesassigned += 1
   691  		}
   692  	}
   693  
   694  	// extra left names
   695  	if lennames > namesassigned {
   696  		restleft := lennames - namesassigned - 1
   697  		context.Code.AddLoadNil(reg, reg+restleft, line)
   698  		reg += restleft
   699  	}
   700  
   701  	// extra right exprs
   702  	for i := namesassigned; i < lenexprs; i++ {
   703  		varargopt := -1
   704  		if i != lenexprs-1 {
   705  			varargopt = 0
   706  		}
   707  		ecupdate(ec, ecNone, reg, varargopt)
   708  		reg += compileExpr(context, reg, exprs[i], ec)
   709  	}
   710  } // }}}
   711  
   712  func compileLocalAssignStmt(context *funcContext, stmt *ast.LocalAssignStmt) { // {{{
   713  	reg := context.RegTop()
   714  	if len(stmt.Names) == 1 && len(stmt.Exprs) == 1 {
   715  		if _, ok := stmt.Exprs[0].(*ast.FunctionExpr); ok {
   716  			context.RegisterLocalVar(stmt.Names[0])
   717  			compileRegAssignment(context, stmt.Names, stmt.Exprs, reg, len(stmt.Names), sline(stmt))
   718  			return
   719  		}
   720  	}
   721  
   722  	compileRegAssignment(context, stmt.Names, stmt.Exprs, reg, len(stmt.Names), sline(stmt))
   723  	for _, name := range stmt.Names {
   724  		context.RegisterLocalVar(name)
   725  	}
   726  } // }}}
   727  
   728  func compileReturnStmt(context *funcContext, stmt *ast.ReturnStmt) { // {{{
   729  	lenexprs := len(stmt.Exprs)
   730  	code := context.Code
   731  	reg := context.RegTop()
   732  	a := reg
   733  	lastisvaarg := false
   734  
   735  	if lenexprs == 1 {
   736  		switch ex := stmt.Exprs[0].(type) {
   737  		case *ast.IdentExpr:
   738  			if idx := context.FindLocalVar(ex.Value); idx > -1 {
   739  				code.AddABC(OP_RETURN, idx, 2, 0, sline(stmt))
   740  				return
   741  			}
   742  		case *ast.FuncCallExpr:
   743  			reg += compileExpr(context, reg, ex, ecnone(-2))
   744  			code.SetOpCode(code.LastPC(), OP_TAILCALL)
   745  			code.AddABC(OP_RETURN, a, 0, 0, sline(stmt))
   746  			return
   747  		}
   748  	}
   749  
   750  	for i, expr := range stmt.Exprs {
   751  		if i == lenexprs-1 && isVarArgReturnExpr(expr) {
   752  			compileExpr(context, reg, expr, ecnone(-2))
   753  			lastisvaarg = true
   754  		} else {
   755  			reg += compileExpr(context, reg, expr, ecnone(0))
   756  		}
   757  	}
   758  	count := reg - a + 1
   759  	if lastisvaarg {
   760  		count = 0
   761  	}
   762  	context.Code.AddABC(OP_RETURN, a, count, 0, sline(stmt))
   763  } // }}}
   764  
   765  func compileIfStmt(context *funcContext, stmt *ast.IfStmt) { // {{{
   766  	thenlabel := context.NewLabel()
   767  	elselabel := context.NewLabel()
   768  	endlabel := context.NewLabel()
   769  
   770  	compileBranchCondition(context, context.RegTop(), stmt.Condition, thenlabel, elselabel, false)
   771  	context.SetLabelPc(thenlabel, context.Code.LastPC())
   772  	compileBlock(context, stmt.Then)
   773  	if len(stmt.Else) > 0 {
   774  		context.Code.AddASbx(OP_JMP, 0, endlabel, sline(stmt))
   775  	}
   776  	context.SetLabelPc(elselabel, context.Code.LastPC())
   777  	if len(stmt.Else) > 0 {
   778  		compileBlock(context, stmt.Else)
   779  		context.SetLabelPc(endlabel, context.Code.LastPC())
   780  	}
   781  
   782  } // }}}
   783  
   784  func compileBranchCondition(context *funcContext, reg int, expr ast.Expr, thenlabel, elselabel int, hasnextcond bool) { // {{{
   785  	// TODO folding constants?
   786  	code := context.Code
   787  	flip := 0
   788  	jumplabel := elselabel
   789  	if hasnextcond {
   790  		flip = 1
   791  		jumplabel = thenlabel
   792  	}
   793  
   794  	switch ex := expr.(type) {
   795  	case *ast.FalseExpr, *ast.NilExpr:
   796  		if !hasnextcond {
   797  			code.AddASbx(OP_JMP, 0, elselabel, sline(expr))
   798  			return
   799  		}
   800  	case *ast.TrueExpr, *ast.NumberExpr, *ast.StringExpr:
   801  		if !hasnextcond {
   802  			return
   803  		}
   804  	case *ast.UnaryNotOpExpr:
   805  		compileBranchCondition(context, reg, ex.Expr, elselabel, thenlabel, !hasnextcond)
   806  		return
   807  	case *ast.LogicalOpExpr:
   808  		switch ex.Operator {
   809  		case "and":
   810  			nextcondlabel := context.NewLabel()
   811  			compileBranchCondition(context, reg, ex.Lhs, nextcondlabel, elselabel, false)
   812  			context.SetLabelPc(nextcondlabel, context.Code.LastPC())
   813  			compileBranchCondition(context, reg, ex.Rhs, thenlabel, elselabel, hasnextcond)
   814  		case "or":
   815  			nextcondlabel := context.NewLabel()
   816  			compileBranchCondition(context, reg, ex.Lhs, thenlabel, nextcondlabel, true)
   817  			context.SetLabelPc(nextcondlabel, context.Code.LastPC())
   818  			compileBranchCondition(context, reg, ex.Rhs, thenlabel, elselabel, hasnextcond)
   819  		}
   820  		return
   821  	case *ast.RelationalOpExpr:
   822  		compileRelationalOpExprAux(context, reg, ex, flip, jumplabel)
   823  		return
   824  	}
   825  
   826  	a := reg
   827  	compileExprWithMVPropagation(context, expr, &reg, &a)
   828  	code.AddABC(OP_TEST, a, 0, 0^flip, sline(expr))
   829  	code.AddASbx(OP_JMP, 0, jumplabel, sline(expr))
   830  } // }}}
   831  
   832  func compileWhileStmt(context *funcContext, stmt *ast.WhileStmt) { // {{{
   833  	thenlabel := context.NewLabel()
   834  	elselabel := context.NewLabel()
   835  	condlabel := context.NewLabel()
   836  
   837  	context.SetLabelPc(condlabel, context.Code.LastPC())
   838  	compileBranchCondition(context, context.RegTop(), stmt.Condition, thenlabel, elselabel, false)
   839  	context.SetLabelPc(thenlabel, context.Code.LastPC())
   840  	context.EnterBlock(elselabel, stmt)
   841  	compileChunk(context, stmt.Stmts)
   842  	context.CloseUpvalues()
   843  	context.Code.AddASbx(OP_JMP, 0, condlabel, eline(stmt))
   844  	context.LeaveBlock()
   845  	context.SetLabelPc(elselabel, context.Code.LastPC())
   846  } // }}}
   847  
   848  func compileRepeatStmt(context *funcContext, stmt *ast.RepeatStmt) { // {{{
   849  	initlabel := context.NewLabel()
   850  	thenlabel := context.NewLabel()
   851  	elselabel := context.NewLabel()
   852  
   853  	context.SetLabelPc(initlabel, context.Code.LastPC())
   854  	context.SetLabelPc(elselabel, context.Code.LastPC())
   855  	context.EnterBlock(thenlabel, stmt)
   856  	compileChunk(context, stmt.Stmts)
   857  	compileBranchCondition(context, context.RegTop(), stmt.Condition, thenlabel, elselabel, false)
   858  
   859  	context.SetLabelPc(thenlabel, context.Code.LastPC())
   860  	n := context.LeaveBlock()
   861  
   862  	if n > -1 {
   863  		label := context.NewLabel()
   864  		context.Code.AddASbx(OP_JMP, 0, label, eline(stmt))
   865  		context.SetLabelPc(elselabel, context.Code.LastPC())
   866  		context.Code.AddABC(OP_CLOSE, n, 0, 0, eline(stmt))
   867  		context.Code.AddASbx(OP_JMP, 0, initlabel, eline(stmt))
   868  		context.SetLabelPc(label, context.Code.LastPC())
   869  	}
   870  
   871  } // }}}
   872  
   873  func compileBreakStmt(context *funcContext, stmt *ast.BreakStmt) { // {{{
   874  	for block := context.Block; block != nil; block = block.Parent {
   875  		if label := block.BreakLabel; label != labelNoJump {
   876  			if block.RefUpvalue {
   877  				context.Code.AddABC(OP_CLOSE, block.Parent.LocalVars.LastIndex(), 0, 0, sline(stmt))
   878  			}
   879  			context.Code.AddASbx(OP_JMP, 0, label, sline(stmt))
   880  			return
   881  		}
   882  	}
   883  	raiseCompileError(context, sline(stmt), "no loop to break")
   884  } // }}}
   885  
   886  func compileFuncDefStmt(context *funcContext, stmt *ast.FuncDefStmt) { // {{{
   887  	if stmt.Name.Func == nil {
   888  		reg := context.RegTop()
   889  		var treg, kreg int
   890  		compileExprWithKMVPropagation(context, stmt.Name.Receiver, &reg, &treg)
   891  		kreg = loadRk(context, &reg, stmt.Func, LString(stmt.Name.Method))
   892  		compileExpr(context, reg, stmt.Func, ecfuncdef)
   893  		context.Code.AddABC(OP_SETTABLE, treg, kreg, reg, sline(stmt.Name.Receiver))
   894  	} else {
   895  		astmt := &ast.AssignStmt{Lhs: []ast.Expr{stmt.Name.Func}, Rhs: []ast.Expr{stmt.Func}}
   896  		astmt.SetLine(sline(stmt.Func))
   897  		astmt.SetLastLine(eline(stmt.Func))
   898  		compileAssignStmt(context, astmt)
   899  	}
   900  } // }}}
   901  
   902  func compileNumberForStmt(context *funcContext, stmt *ast.NumberForStmt) { // {{{
   903  	code := context.Code
   904  	endlabel := context.NewLabel()
   905  	ec := &expcontext{}
   906  
   907  	context.EnterBlock(endlabel, stmt)
   908  	reg := context.RegTop()
   909  	rindex := context.RegisterLocalVar("(for index)")
   910  	ecupdate(ec, ecLocal, rindex, 0)
   911  	compileExpr(context, reg, stmt.Init, ec)
   912  
   913  	reg = context.RegTop()
   914  	rlimit := context.RegisterLocalVar("(for limit)")
   915  	ecupdate(ec, ecLocal, rlimit, 0)
   916  	compileExpr(context, reg, stmt.Limit, ec)
   917  
   918  	reg = context.RegTop()
   919  	rstep := context.RegisterLocalVar("(for step)")
   920  	if stmt.Step == nil {
   921  		stmt.Step = &ast.NumberExpr{Value: "1"}
   922  		stmt.Step.SetLine(sline(stmt.Init))
   923  	}
   924  	ecupdate(ec, ecLocal, rstep, 0)
   925  	compileExpr(context, reg, stmt.Step, ec)
   926  
   927  	code.AddASbx(OP_FORPREP, rindex, 0, sline(stmt))
   928  
   929  	context.RegisterLocalVar(stmt.Name)
   930  
   931  	bodypc := code.LastPC()
   932  	compileChunk(context, stmt.Stmts)
   933  
   934  	context.LeaveBlock()
   935  
   936  	flpc := code.LastPC()
   937  	code.AddASbx(OP_FORLOOP, rindex, bodypc-(flpc+1), sline(stmt))
   938  
   939  	context.SetLabelPc(endlabel, code.LastPC())
   940  	code.SetSbx(bodypc, flpc-bodypc)
   941  
   942  } // }}}
   943  
   944  func compileGenericForStmt(context *funcContext, stmt *ast.GenericForStmt) { // {{{
   945  	code := context.Code
   946  	endlabel := context.NewLabel()
   947  	bodylabel := context.NewLabel()
   948  	fllabel := context.NewLabel()
   949  	nnames := len(stmt.Names)
   950  
   951  	context.EnterBlock(endlabel, stmt)
   952  	rgen := context.RegisterLocalVar("(for generator)")
   953  	context.RegisterLocalVar("(for state)")
   954  	context.RegisterLocalVar("(for control)")
   955  
   956  	compileRegAssignment(context, stmt.Names, stmt.Exprs, context.RegTop()-3, 3, sline(stmt))
   957  
   958  	code.AddASbx(OP_JMP, 0, fllabel, sline(stmt))
   959  
   960  	for _, name := range stmt.Names {
   961  		context.RegisterLocalVar(name)
   962  	}
   963  
   964  	context.SetLabelPc(bodylabel, code.LastPC())
   965  	compileChunk(context, stmt.Stmts)
   966  
   967  	context.LeaveBlock()
   968  
   969  	context.SetLabelPc(fllabel, code.LastPC())
   970  	code.AddABC(OP_TFORLOOP, rgen, 0, nnames, sline(stmt))
   971  	code.AddASbx(OP_JMP, 0, bodylabel, sline(stmt))
   972  
   973  	context.SetLabelPc(endlabel, code.LastPC())
   974  } // }}}
   975  
   976  func compileExpr(context *funcContext, reg int, expr ast.Expr, ec *expcontext) int { // {{{
   977  	code := context.Code
   978  	sreg := savereg(ec, reg)
   979  	sused := 1
   980  	if sreg < reg {
   981  		sused = 0
   982  	}
   983  
   984  	switch ex := expr.(type) {
   985  	case *ast.StringExpr:
   986  		code.AddABx(OP_LOADK, sreg, context.ConstIndex(LString(ex.Value)), sline(ex))
   987  		return sused
   988  	case *ast.NumberExpr:
   989  		num, err := parseNumber(ex.Value)
   990  		if err != nil {
   991  			num = LNumber(math.NaN())
   992  		}
   993  		code.AddABx(OP_LOADK, sreg, context.ConstIndex(num), sline(ex))
   994  		return sused
   995  	case *constLValueExpr:
   996  		code.AddABx(OP_LOADK, sreg, context.ConstIndex(ex.Value), sline(ex))
   997  		return sused
   998  	case *ast.NilExpr:
   999  		code.AddLoadNil(sreg, sreg, sline(ex))
  1000  		return sused
  1001  	case *ast.FalseExpr:
  1002  		code.AddABC(OP_LOADBOOL, sreg, 0, 0, sline(ex))
  1003  		return sused
  1004  	case *ast.TrueExpr:
  1005  		code.AddABC(OP_LOADBOOL, sreg, 1, 0, sline(ex))
  1006  		return sused
  1007  	case *ast.IdentExpr:
  1008  		switch getIdentRefType(context, context, ex) {
  1009  		case ecGlobal:
  1010  			code.AddABx(OP_GETGLOBAL, sreg, context.ConstIndex(LString(ex.Value)), sline(ex))
  1011  		case ecUpvalue:
  1012  			code.AddABC(OP_GETUPVAL, sreg, context.Upvalues.RegisterUnique(ex.Value), 0, sline(ex))
  1013  		case ecLocal:
  1014  			b := context.FindLocalVar(ex.Value)
  1015  			code.AddABC(OP_MOVE, sreg, b, 0, sline(ex))
  1016  		}
  1017  		return sused
  1018  	case *ast.Comma3Expr:
  1019  		if context.Proto.IsVarArg == 0 {
  1020  			raiseCompileError(context, sline(ex), "cannot use '...' outside a vararg function")
  1021  		}
  1022  		context.Proto.IsVarArg &= ^VarArgNeedsArg
  1023  		code.AddABC(OP_VARARG, sreg, 2+ec.varargopt, 0, sline(ex))
  1024  		if context.RegTop() > (sreg+2+ec.varargopt) || ec.varargopt < -1 {
  1025  			return 0
  1026  		}
  1027  		return (sreg + 1 + ec.varargopt) - reg
  1028  	case *ast.AttrGetExpr:
  1029  		a := sreg
  1030  		b := reg
  1031  		compileExprWithMVPropagation(context, ex.Object, &reg, &b)
  1032  		c := reg
  1033  		compileExprWithKMVPropagation(context, ex.Key, &reg, &c)
  1034  		opcode := OP_GETTABLE
  1035  		if _, ok := ex.Key.(*ast.StringExpr); ok {
  1036  			opcode = OP_GETTABLEKS
  1037  		}
  1038  		code.AddABC(opcode, a, b, c, sline(ex))
  1039  		return sused
  1040  	case *ast.TableExpr:
  1041  		compileTableExpr(context, reg, ex, ec)
  1042  		return 1
  1043  	case *ast.ArithmeticOpExpr:
  1044  		compileArithmeticOpExpr(context, reg, ex, ec)
  1045  		return sused
  1046  	case *ast.StringConcatOpExpr:
  1047  		compileStringConcatOpExpr(context, reg, ex, ec)
  1048  		return sused
  1049  	case *ast.UnaryMinusOpExpr, *ast.UnaryNotOpExpr, *ast.UnaryLenOpExpr:
  1050  		compileUnaryOpExpr(context, reg, ex, ec)
  1051  		return sused
  1052  	case *ast.RelationalOpExpr:
  1053  		compileRelationalOpExpr(context, reg, ex, ec)
  1054  		return sused
  1055  	case *ast.LogicalOpExpr:
  1056  		compileLogicalOpExpr(context, reg, ex, ec)
  1057  		return sused
  1058  	case *ast.FuncCallExpr:
  1059  		return compileFuncCallExpr(context, reg, ex, ec)
  1060  	case *ast.FunctionExpr:
  1061  		childcontext := newFuncContext(context.Proto.SourceName, context)
  1062  		compileFunctionExpr(childcontext, ex, ec)
  1063  		protono := len(context.Proto.FunctionPrototypes)
  1064  		context.Proto.FunctionPrototypes = append(context.Proto.FunctionPrototypes, childcontext.Proto)
  1065  		code.AddABx(OP_CLOSURE, sreg, protono, sline(ex))
  1066  		for _, upvalue := range childcontext.Upvalues.List() {
  1067  			localidx, block := context.FindLocalVarAndBlock(upvalue.Name)
  1068  			if localidx > -1 {
  1069  				code.AddABC(OP_MOVE, 0, localidx, 0, sline(ex))
  1070  				block.RefUpvalue = true
  1071  			} else {
  1072  				upvalueidx := context.Upvalues.Find(upvalue.Name)
  1073  				if upvalueidx < 0 {
  1074  					upvalueidx = context.Upvalues.RegisterUnique(upvalue.Name)
  1075  				}
  1076  				code.AddABC(OP_GETUPVAL, 0, upvalueidx, 0, sline(ex))
  1077  			}
  1078  		}
  1079  		return sused
  1080  	default:
  1081  		panic(fmt.Sprintf("expr %v not implemented.", reflect.TypeOf(ex).Elem().Name()))
  1082  	}
  1083  
  1084  } // }}}
  1085  
  1086  func compileExprWithPropagation(context *funcContext, expr ast.Expr, reg *int, save *int, propergator func(int, *int, *int, int)) { // {{{
  1087  	reginc := compileExpr(context, *reg, expr, ecnone(0))
  1088  	if _, ok := expr.(*ast.LogicalOpExpr); ok {
  1089  		*save = *reg
  1090  		*reg = *reg + reginc
  1091  	} else {
  1092  		propergator(context.RegTop(), save, reg, reginc)
  1093  	}
  1094  } // }}}
  1095  
  1096  func compileExprWithKMVPropagation(context *funcContext, expr ast.Expr, reg *int, save *int) { // {{{
  1097  	compileExprWithPropagation(context, expr, reg, save, context.Code.PropagateKMV)
  1098  } // }}}
  1099  
  1100  func compileExprWithMVPropagation(context *funcContext, expr ast.Expr, reg *int, save *int) { // {{{
  1101  	compileExprWithPropagation(context, expr, reg, save, context.Code.PropagateMV)
  1102  } // }}}
  1103  
  1104  func constFold(exp ast.Expr) ast.Expr { // {{{
  1105  	switch expr := exp.(type) {
  1106  	case *ast.ArithmeticOpExpr:
  1107  		lvalue, lisconst := lnumberValue(constFold(expr.Lhs))
  1108  		rvalue, risconst := lnumberValue(constFold(expr.Rhs))
  1109  		if lisconst && risconst {
  1110  			switch expr.Operator {
  1111  			case "+":
  1112  				return &constLValueExpr{Value: lvalue + rvalue}
  1113  			case "-":
  1114  				return &constLValueExpr{Value: lvalue - rvalue}
  1115  			case "*":
  1116  				return &constLValueExpr{Value: lvalue * rvalue}
  1117  			case "/":
  1118  				return &constLValueExpr{Value: lvalue / rvalue}
  1119  			case "%":
  1120  				return &constLValueExpr{Value: luaModulo(lvalue, rvalue)}
  1121  			case "^":
  1122  				return &constLValueExpr{Value: LNumber(math.Pow(float64(lvalue), float64(rvalue)))}
  1123  			default:
  1124  				panic(fmt.Sprintf("unknown binop: %v", expr.Operator))
  1125  			}
  1126  		} else {
  1127  			return expr
  1128  		}
  1129  	case *ast.UnaryMinusOpExpr:
  1130  		expr.Expr = constFold(expr.Expr)
  1131  		if value, ok := lnumberValue(expr.Expr); ok {
  1132  			return &constLValueExpr{Value: LNumber(-value)}
  1133  		}
  1134  		return expr
  1135  	default:
  1136  
  1137  		return exp
  1138  	}
  1139  } // }}}
  1140  
  1141  func compileFunctionExpr(context *funcContext, funcexpr *ast.FunctionExpr, ec *expcontext) { // {{{
  1142  	context.Proto.LineDefined = sline(funcexpr)
  1143  	context.Proto.LastLineDefined = eline(funcexpr)
  1144  	if len(funcexpr.ParList.Names) > maxRegisters {
  1145  		raiseCompileError(context, context.Proto.LineDefined, "register overflow")
  1146  	}
  1147  	context.Proto.NumParameters = uint8(len(funcexpr.ParList.Names))
  1148  	if ec.ctype == ecMethod {
  1149  		context.Proto.NumParameters += 1
  1150  		context.RegisterLocalVar("self")
  1151  	}
  1152  	for _, name := range funcexpr.ParList.Names {
  1153  		context.RegisterLocalVar(name)
  1154  	}
  1155  	if funcexpr.ParList.HasVargs {
  1156  		if CompatVarArg {
  1157  			context.Proto.IsVarArg = VarArgHasArg | VarArgNeedsArg
  1158  			if context.Parent != nil {
  1159  				context.RegisterLocalVar("arg")
  1160  			}
  1161  		}
  1162  		context.Proto.IsVarArg |= VarArgIsVarArg
  1163  	}
  1164  
  1165  	compileChunk(context, funcexpr.Stmts)
  1166  
  1167  	context.Code.AddABC(OP_RETURN, 0, 1, 0, eline(funcexpr))
  1168  	context.EndScope()
  1169  	context.Proto.Code = context.Code.List()
  1170  	context.Proto.DbgSourcePositions = context.Code.PosList()
  1171  	context.Proto.DbgUpvalues = context.Upvalues.Names()
  1172  	context.Proto.NumUpvalues = uint8(len(context.Proto.DbgUpvalues))
  1173  	for _, clv := range context.Proto.Constants {
  1174  		sv := ""
  1175  		if slv, ok := clv.(LString); ok {
  1176  			sv = string(slv)
  1177  		}
  1178  		context.Proto.stringConstants = append(context.Proto.stringConstants, sv)
  1179  	}
  1180  	patchCode(context)
  1181  } // }}}
  1182  
  1183  func compileTableExpr(context *funcContext, reg int, ex *ast.TableExpr, ec *expcontext) { // {{{
  1184  	code := context.Code
  1185  	/*
  1186  		tablereg := savereg(ec, reg)
  1187  		if tablereg == reg {
  1188  			reg += 1
  1189  		}
  1190  	*/
  1191  	tablereg := reg
  1192  	reg++
  1193  	code.AddABC(OP_NEWTABLE, tablereg, 0, 0, sline(ex))
  1194  	tablepc := code.LastPC()
  1195  	regbase := reg
  1196  
  1197  	arraycount := 0
  1198  	lastvararg := false
  1199  	for i, field := range ex.Fields {
  1200  		islast := i == len(ex.Fields)-1
  1201  		if field.Key == nil {
  1202  			if islast && isVarArgReturnExpr(field.Value) {
  1203  				reg += compileExpr(context, reg, field.Value, ecnone(-2))
  1204  				lastvararg = true
  1205  			} else {
  1206  				reg += compileExpr(context, reg, field.Value, ecnone(0))
  1207  				arraycount += 1
  1208  			}
  1209  		} else {
  1210  			regorg := reg
  1211  			b := reg
  1212  			compileExprWithKMVPropagation(context, field.Key, &reg, &b)
  1213  			c := reg
  1214  			compileExprWithKMVPropagation(context, field.Value, &reg, &c)
  1215  			opcode := OP_SETTABLE
  1216  			if _, ok := field.Key.(*ast.StringExpr); ok {
  1217  				opcode = OP_SETTABLEKS
  1218  			}
  1219  			line := field.Value
  1220  			if field.Key != nil {
  1221  				line = field.Key
  1222  			}
  1223  			code.AddABC(opcode, tablereg, b, c, sline(line))
  1224  			reg = regorg
  1225  		}
  1226  		flush := arraycount % FieldsPerFlush
  1227  		if (arraycount != 0 && (flush == 0 || islast)) || lastvararg {
  1228  			reg = regbase
  1229  			num := flush
  1230  			if num == 0 {
  1231  				num = FieldsPerFlush
  1232  			}
  1233  			c := (arraycount-1)/FieldsPerFlush + 1
  1234  			b := num
  1235  			if islast && isVarArgReturnExpr(field.Value) {
  1236  				b = 0
  1237  			}
  1238  			line := field.Value
  1239  			if field.Key != nil {
  1240  				line = field.Key
  1241  			}
  1242  			if c > 511 {
  1243  				c = 0
  1244  			}
  1245  			code.AddABC(OP_SETLIST, tablereg, b, c, sline(line))
  1246  			if c == 0 {
  1247  				code.Add(uint32(c), sline(line))
  1248  			}
  1249  		}
  1250  	}
  1251  	code.SetB(tablepc, int2Fb(arraycount))
  1252  	code.SetC(tablepc, int2Fb(len(ex.Fields)-arraycount))
  1253  	if shouldmove(ec, tablereg) {
  1254  		code.AddABC(OP_MOVE, ec.reg, tablereg, 0, sline(ex))
  1255  	}
  1256  } // }}}
  1257  
  1258  func compileArithmeticOpExpr(context *funcContext, reg int, expr *ast.ArithmeticOpExpr, ec *expcontext) { // {{{
  1259  	exp := constFold(expr)
  1260  	if ex, ok := exp.(*constLValueExpr); ok {
  1261  		exp.SetLine(sline(expr))
  1262  		compileExpr(context, reg, ex, ec)
  1263  		return
  1264  	}
  1265  	expr, _ = exp.(*ast.ArithmeticOpExpr)
  1266  	a := savereg(ec, reg)
  1267  	b := reg
  1268  	compileExprWithKMVPropagation(context, expr.Lhs, &reg, &b)
  1269  	c := reg
  1270  	compileExprWithKMVPropagation(context, expr.Rhs, &reg, &c)
  1271  
  1272  	op := 0
  1273  	switch expr.Operator {
  1274  	case "+":
  1275  		op = OP_ADD
  1276  	case "-":
  1277  		op = OP_SUB
  1278  	case "*":
  1279  		op = OP_MUL
  1280  	case "/":
  1281  		op = OP_DIV
  1282  	case "%":
  1283  		op = OP_MOD
  1284  	case "^":
  1285  		op = OP_POW
  1286  	}
  1287  	context.Code.AddABC(op, a, b, c, sline(expr))
  1288  } // }}}
  1289  
  1290  func compileStringConcatOpExpr(context *funcContext, reg int, expr *ast.StringConcatOpExpr, ec *expcontext) { // {{{
  1291  	code := context.Code
  1292  	crange := 1
  1293  	for current := expr.Rhs; current != nil; {
  1294  		if ex, ok := current.(*ast.StringConcatOpExpr); ok {
  1295  			crange += 1
  1296  			current = ex.Rhs
  1297  		} else {
  1298  			current = nil
  1299  		}
  1300  	}
  1301  	a := savereg(ec, reg)
  1302  	basereg := reg
  1303  	reg += compileExpr(context, reg, expr.Lhs, ecnone(0))
  1304  	reg += compileExpr(context, reg, expr.Rhs, ecnone(0))
  1305  	for pc := code.LastPC(); pc != 0 && opGetOpCode(code.At(pc)) == OP_CONCAT; pc-- {
  1306  		code.Pop()
  1307  	}
  1308  	code.AddABC(OP_CONCAT, a, basereg, basereg+crange, sline(expr))
  1309  } // }}}
  1310  
  1311  func compileUnaryOpExpr(context *funcContext, reg int, expr ast.Expr, ec *expcontext) { // {{{
  1312  	opcode := 0
  1313  	code := context.Code
  1314  	var operandexpr ast.Expr
  1315  	switch ex := expr.(type) {
  1316  	case *ast.UnaryMinusOpExpr:
  1317  		exp := constFold(ex)
  1318  		if lvexpr, ok := exp.(*constLValueExpr); ok {
  1319  			exp.SetLine(sline(expr))
  1320  			compileExpr(context, reg, lvexpr, ec)
  1321  			return
  1322  		}
  1323  		ex, _ = exp.(*ast.UnaryMinusOpExpr)
  1324  		operandexpr = ex.Expr
  1325  		opcode = OP_UNM
  1326  	case *ast.UnaryNotOpExpr:
  1327  		switch ex.Expr.(type) {
  1328  		case *ast.TrueExpr:
  1329  			code.AddABC(OP_LOADBOOL, savereg(ec, reg), 0, 0, sline(expr))
  1330  			return
  1331  		case *ast.FalseExpr, *ast.NilExpr:
  1332  			code.AddABC(OP_LOADBOOL, savereg(ec, reg), 1, 0, sline(expr))
  1333  			return
  1334  		default:
  1335  			opcode = OP_NOT
  1336  			operandexpr = ex.Expr
  1337  		}
  1338  	case *ast.UnaryLenOpExpr:
  1339  		opcode = OP_LEN
  1340  		operandexpr = ex.Expr
  1341  	}
  1342  
  1343  	a := savereg(ec, reg)
  1344  	b := reg
  1345  	compileExprWithMVPropagation(context, operandexpr, &reg, &b)
  1346  	code.AddABC(opcode, a, b, 0, sline(expr))
  1347  } // }}}
  1348  
  1349  func compileRelationalOpExprAux(context *funcContext, reg int, expr *ast.RelationalOpExpr, flip int, label int) { // {{{
  1350  	code := context.Code
  1351  	b := reg
  1352  	compileExprWithKMVPropagation(context, expr.Lhs, &reg, &b)
  1353  	c := reg
  1354  	compileExprWithKMVPropagation(context, expr.Rhs, &reg, &c)
  1355  	switch expr.Operator {
  1356  	case "<":
  1357  		code.AddABC(OP_LT, 0^flip, b, c, sline(expr))
  1358  	case ">":
  1359  		code.AddABC(OP_LT, 0^flip, c, b, sline(expr))
  1360  	case "<=":
  1361  		code.AddABC(OP_LE, 0^flip, b, c, sline(expr))
  1362  	case ">=":
  1363  		code.AddABC(OP_LE, 0^flip, c, b, sline(expr))
  1364  	case "==":
  1365  		code.AddABC(OP_EQ, 0^flip, b, c, sline(expr))
  1366  	case "~=":
  1367  		code.AddABC(OP_EQ, 1^flip, b, c, sline(expr))
  1368  	}
  1369  	code.AddASbx(OP_JMP, 0, label, sline(expr))
  1370  } // }}}
  1371  
  1372  func compileRelationalOpExpr(context *funcContext, reg int, expr *ast.RelationalOpExpr, ec *expcontext) { // {{{
  1373  	a := savereg(ec, reg)
  1374  	code := context.Code
  1375  	jumplabel := context.NewLabel()
  1376  	compileRelationalOpExprAux(context, reg, expr, 1, jumplabel)
  1377  	code.AddABC(OP_LOADBOOL, a, 0, 1, sline(expr))
  1378  	context.SetLabelPc(jumplabel, code.LastPC())
  1379  	code.AddABC(OP_LOADBOOL, a, 1, 0, sline(expr))
  1380  } // }}}
  1381  
  1382  func compileLogicalOpExpr(context *funcContext, reg int, expr *ast.LogicalOpExpr, ec *expcontext) { // {{{
  1383  	a := savereg(ec, reg)
  1384  	code := context.Code
  1385  	endlabel := context.NewLabel()
  1386  	lb := &lblabels{context.NewLabel(), context.NewLabel(), endlabel, false}
  1387  	nextcondlabel := context.NewLabel()
  1388  	if expr.Operator == "and" {
  1389  		compileLogicalOpExprAux(context, reg, expr.Lhs, ec, nextcondlabel, endlabel, false, lb)
  1390  		context.SetLabelPc(nextcondlabel, code.LastPC())
  1391  		compileLogicalOpExprAux(context, reg, expr.Rhs, ec, endlabel, endlabel, false, lb)
  1392  	} else {
  1393  		compileLogicalOpExprAux(context, reg, expr.Lhs, ec, endlabel, nextcondlabel, true, lb)
  1394  		context.SetLabelPc(nextcondlabel, code.LastPC())
  1395  		compileLogicalOpExprAux(context, reg, expr.Rhs, ec, endlabel, endlabel, false, lb)
  1396  	}
  1397  
  1398  	if lb.b {
  1399  		context.SetLabelPc(lb.f, code.LastPC())
  1400  		code.AddABC(OP_LOADBOOL, a, 0, 1, sline(expr))
  1401  		context.SetLabelPc(lb.t, code.LastPC())
  1402  		code.AddABC(OP_LOADBOOL, a, 1, 0, sline(expr))
  1403  	}
  1404  
  1405  	lastinst := code.Last()
  1406  	if opGetOpCode(lastinst) == OP_JMP && opGetArgSbx(lastinst) == endlabel {
  1407  		code.Pop()
  1408  	}
  1409  
  1410  	context.SetLabelPc(endlabel, code.LastPC())
  1411  } // }}}
  1412  
  1413  func compileLogicalOpExprAux(context *funcContext, reg int, expr ast.Expr, ec *expcontext, thenlabel, elselabel int, hasnextcond bool, lb *lblabels) { // {{{
  1414  	// TODO folding constants?
  1415  	code := context.Code
  1416  	flip := 0
  1417  	jumplabel := elselabel
  1418  	if hasnextcond {
  1419  		flip = 1
  1420  		jumplabel = thenlabel
  1421  	}
  1422  
  1423  	switch ex := expr.(type) {
  1424  	case *ast.FalseExpr:
  1425  		if elselabel == lb.e {
  1426  			code.AddASbx(OP_JMP, 0, lb.f, sline(expr))
  1427  			lb.b = true
  1428  		} else {
  1429  			code.AddASbx(OP_JMP, 0, elselabel, sline(expr))
  1430  		}
  1431  		return
  1432  	case *ast.NilExpr:
  1433  		if elselabel == lb.e {
  1434  			compileExpr(context, reg, expr, ec)
  1435  			code.AddASbx(OP_JMP, 0, lb.e, sline(expr))
  1436  		} else {
  1437  			code.AddASbx(OP_JMP, 0, elselabel, sline(expr))
  1438  		}
  1439  		return
  1440  	case *ast.TrueExpr:
  1441  		if thenlabel == lb.e {
  1442  			code.AddASbx(OP_JMP, 0, lb.t, sline(expr))
  1443  			lb.b = true
  1444  		} else {
  1445  			code.AddASbx(OP_JMP, 0, thenlabel, sline(expr))
  1446  		}
  1447  		return
  1448  	case *ast.NumberExpr, *ast.StringExpr:
  1449  		if thenlabel == lb.e {
  1450  			compileExpr(context, reg, expr, ec)
  1451  			code.AddASbx(OP_JMP, 0, lb.e, sline(expr))
  1452  		} else {
  1453  			code.AddASbx(OP_JMP, 0, thenlabel, sline(expr))
  1454  		}
  1455  		return
  1456  	case *ast.LogicalOpExpr:
  1457  		switch ex.Operator {
  1458  		case "and":
  1459  			nextcondlabel := context.NewLabel()
  1460  			compileLogicalOpExprAux(context, reg, ex.Lhs, ec, nextcondlabel, elselabel, false, lb)
  1461  			context.SetLabelPc(nextcondlabel, context.Code.LastPC())
  1462  			compileLogicalOpExprAux(context, reg, ex.Rhs, ec, thenlabel, elselabel, hasnextcond, lb)
  1463  		case "or":
  1464  			nextcondlabel := context.NewLabel()
  1465  			compileLogicalOpExprAux(context, reg, ex.Lhs, ec, thenlabel, nextcondlabel, true, lb)
  1466  			context.SetLabelPc(nextcondlabel, context.Code.LastPC())
  1467  			compileLogicalOpExprAux(context, reg, ex.Rhs, ec, thenlabel, elselabel, hasnextcond, lb)
  1468  		}
  1469  		return
  1470  	case *ast.RelationalOpExpr:
  1471  		if thenlabel == elselabel {
  1472  			flip ^= 1
  1473  			jumplabel = lb.t
  1474  			lb.b = true
  1475  		} else if thenlabel == lb.e {
  1476  			jumplabel = lb.t
  1477  			lb.b = true
  1478  		} else if elselabel == lb.e {
  1479  			jumplabel = lb.f
  1480  			lb.b = true
  1481  		}
  1482  		compileRelationalOpExprAux(context, reg, ex, flip, jumplabel)
  1483  		return
  1484  	}
  1485  
  1486  	a := reg
  1487  	sreg := savereg(ec, a)
  1488  	if !hasnextcond && thenlabel == elselabel {
  1489  		reg += compileExpr(context, reg, expr, &expcontext{ec.ctype, intMax(a, sreg), ec.varargopt})
  1490  		last := context.Code.Last()
  1491  		if opGetOpCode(last) == OP_MOVE && opGetArgA(last) == a {
  1492  			context.Code.SetA(context.Code.LastPC(), sreg)
  1493  		} else {
  1494  			context.Code.AddABC(OP_MOVE, sreg, a, 0, sline(expr))
  1495  		}
  1496  	} else {
  1497  		reg += compileExpr(context, reg, expr, ecnone(0))
  1498  		if sreg == a {
  1499  			code.AddABC(OP_TEST, a, 0, 0^flip, sline(expr))
  1500  		} else {
  1501  			code.AddABC(OP_TESTSET, sreg, a, 0^flip, sline(expr))
  1502  		}
  1503  	}
  1504  	code.AddASbx(OP_JMP, 0, jumplabel, sline(expr))
  1505  } // }}}
  1506  
  1507  func compileFuncCallExpr(context *funcContext, reg int, expr *ast.FuncCallExpr, ec *expcontext) int { // {{{
  1508  	funcreg := reg
  1509  	if ec.ctype == ecLocal && ec.reg == (int(context.Proto.NumParameters)-1) {
  1510  		funcreg = ec.reg
  1511  		reg = ec.reg
  1512  	}
  1513  	argc := len(expr.Args)
  1514  	islastvararg := false
  1515  	name := "(anonymous)"
  1516  
  1517  	if expr.Func != nil { // hoge.func()
  1518  		reg += compileExpr(context, reg, expr.Func, ecnone(0))
  1519  		name = getExprName(context, expr.Func)
  1520  	} else { // hoge:method()
  1521  		b := reg
  1522  		compileExprWithMVPropagation(context, expr.Receiver, &reg, &b)
  1523  		c := loadRk(context, &reg, expr, LString(expr.Method))
  1524  		context.Code.AddABC(OP_SELF, funcreg, b, c, sline(expr))
  1525  		// increments a register for an implicit "self"
  1526  		reg = b + 1
  1527  		reg2 := funcreg + 2
  1528  		if reg2 > reg {
  1529  			reg = reg2
  1530  		}
  1531  		argc += 1
  1532  		name = string(expr.Method)
  1533  	}
  1534  
  1535  	for i, ar := range expr.Args {
  1536  		islastvararg = (i == len(expr.Args)-1) && isVarArgReturnExpr(ar)
  1537  		if islastvararg {
  1538  			compileExpr(context, reg, ar, ecnone(-2))
  1539  		} else {
  1540  			reg += compileExpr(context, reg, ar, ecnone(0))
  1541  		}
  1542  	}
  1543  	b := argc + 1
  1544  	if islastvararg {
  1545  		b = 0
  1546  	}
  1547  	context.Code.AddABC(OP_CALL, funcreg, b, ec.varargopt+2, sline(expr))
  1548  	context.Proto.DbgCalls = append(context.Proto.DbgCalls, DbgCall{Pc: context.Code.LastPC(), Name: name})
  1549  
  1550  	if ec.varargopt == 0 && shouldmove(ec, funcreg) {
  1551  		context.Code.AddABC(OP_MOVE, ec.reg, funcreg, 0, sline(expr))
  1552  		return 1
  1553  	}
  1554  	if context.RegTop() > (funcreg+2+ec.varargopt) || ec.varargopt < -1 {
  1555  		return 0
  1556  	}
  1557  	return ec.varargopt + 1
  1558  } // }}}
  1559  
  1560  func loadRk(context *funcContext, reg *int, expr ast.Expr, cnst LValue) int { // {{{
  1561  	cindex := context.ConstIndex(cnst)
  1562  	if cindex <= opMaxIndexRk {
  1563  		return opRkAsk(cindex)
  1564  	} else {
  1565  		ret := *reg
  1566  		*reg++
  1567  		context.Code.AddABx(OP_LOADK, ret, cindex, sline(expr))
  1568  		return ret
  1569  	}
  1570  } // }}}
  1571  
  1572  func getIdentRefType(context *funcContext, current *funcContext, expr *ast.IdentExpr) expContextType { // {{{
  1573  	if current == nil {
  1574  		return ecGlobal
  1575  	} else if current.FindLocalVar(expr.Value) > -1 {
  1576  		if current == context {
  1577  			return ecLocal
  1578  		}
  1579  		return ecUpvalue
  1580  	}
  1581  	return getIdentRefType(context, current.Parent, expr)
  1582  } // }}}
  1583  
  1584  func getExprName(context *funcContext, expr ast.Expr) string { // {{{
  1585  	switch ex := expr.(type) {
  1586  	case *ast.IdentExpr:
  1587  		return ex.Value
  1588  	case *ast.AttrGetExpr:
  1589  		switch kex := ex.Key.(type) {
  1590  		case *ast.StringExpr:
  1591  			return kex.Value
  1592  		}
  1593  		return "?"
  1594  	}
  1595  	return "?"
  1596  } // }}}
  1597  
  1598  func patchCode(context *funcContext) { // {{{
  1599  	maxreg := 1
  1600  	if np := int(context.Proto.NumParameters); np > 1 {
  1601  		maxreg = np
  1602  	}
  1603  	moven := 0
  1604  	code := context.Code.List()
  1605  	for pc := 0; pc < len(code); pc++ {
  1606  		inst := code[pc]
  1607  		curop := opGetOpCode(inst)
  1608  		switch curop {
  1609  		case OP_CLOSURE:
  1610  			pc += int(context.Proto.FunctionPrototypes[opGetArgBx(inst)].NumUpvalues)
  1611  			moven = 0
  1612  			continue
  1613  		case OP_SETGLOBAL, OP_SETUPVAL, OP_EQ, OP_LT, OP_LE, OP_TEST,
  1614  			OP_TAILCALL, OP_RETURN, OP_FORPREP, OP_FORLOOP, OP_TFORLOOP,
  1615  			OP_SETLIST, OP_CLOSE:
  1616  			/* nothing to do */
  1617  		case OP_CALL:
  1618  			if reg := opGetArgA(inst) + opGetArgC(inst) - 2; reg > maxreg {
  1619  				maxreg = reg
  1620  			}
  1621  		case OP_VARARG:
  1622  			if reg := opGetArgA(inst) + opGetArgB(inst) - 1; reg > maxreg {
  1623  				maxreg = reg
  1624  			}
  1625  		case OP_SELF:
  1626  			if reg := opGetArgA(inst) + 1; reg > maxreg {
  1627  				maxreg = reg
  1628  			}
  1629  		case OP_LOADNIL:
  1630  			if reg := opGetArgB(inst); reg > maxreg {
  1631  				maxreg = reg
  1632  			}
  1633  		case OP_JMP: // jump to jump optimization
  1634  			distance := 0
  1635  			count := 0 // avoiding infinite loops
  1636  			for jmp := inst; opGetOpCode(jmp) == OP_JMP && count < 5; jmp = context.Code.At(pc + distance + 1) {
  1637  				d := context.GetLabelPc(opGetArgSbx(jmp)) - pc
  1638  				if d > opMaxArgSbx {
  1639  					if distance == 0 {
  1640  						raiseCompileError(context, context.Proto.LineDefined, "too long to jump.")
  1641  					}
  1642  					break
  1643  				}
  1644  				distance = d
  1645  				count++
  1646  			}
  1647  			if distance == 0 {
  1648  				context.Code.SetOpCode(pc, OP_NOP)
  1649  			} else {
  1650  				context.Code.SetSbx(pc, distance)
  1651  			}
  1652  		default:
  1653  			if reg := opGetArgA(inst); reg > maxreg {
  1654  				maxreg = reg
  1655  			}
  1656  		}
  1657  
  1658  		// bulk move optimization(reducing op dipatch costs)
  1659  		if curop == OP_MOVE {
  1660  			moven++
  1661  		} else {
  1662  			if moven > 1 {
  1663  				context.Code.SetOpCode(pc-moven, OP_MOVEN)
  1664  				context.Code.SetC(pc-moven, intMin(moven-1, opMaxArgsC))
  1665  			}
  1666  			moven = 0
  1667  		}
  1668  	}
  1669  	maxreg++
  1670  	if maxreg > maxRegisters {
  1671  		raiseCompileError(context, context.Proto.LineDefined, "register overflow(too many local variables)")
  1672  	}
  1673  	context.Proto.NumUsedRegisters = uint8(maxreg)
  1674  } // }}}
  1675  
  1676  func Compile(chunk []ast.Stmt, name string) (proto *FunctionProto, err error) { // {{{
  1677  	defer func() {
  1678  		if rcv := recover(); rcv != nil {
  1679  			if _, ok := rcv.(*CompileError); ok {
  1680  				err = rcv.(error)
  1681  			} else {
  1682  				panic(rcv)
  1683  			}
  1684  		}
  1685  	}()
  1686  	err = nil
  1687  	parlist := &ast.ParList{HasVargs: true, Names: []string{}}
  1688  	funcexpr := &ast.FunctionExpr{ParList: parlist, Stmts: chunk}
  1689  	context := newFuncContext(name, nil)
  1690  	compileFunctionExpr(context, funcexpr, ecnone(0))
  1691  	proto = context.Proto
  1692  	return
  1693  } // }}}