github.com/enotodden/gopher-lua@v1.1.2/compile.go (about)

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