github.com/assemblaj/gopher-lua@v0.0.0-20221116224352-d57295a0d9e8/compile.go (about)

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