github.com/yuin/gopher-lua@v1.1.2-0.20231212122839-2348fd042596/compile.go (about)

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