github.com/xmx/lua@v0.0.0-20230324063450-8a298e091302/compile.go (about)

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