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