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, ®, &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, ®, 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, ®, &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, ®, &treg) 1121 kreg = loadRk(context, ®, 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, ®, &b) 1291 c := reg 1292 compileExprWithKMVPropagation(context, ex.Key, ®, &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, ®, &b) 1483 c := reg 1484 compileExprWithKMVPropagation(context, field.Value, ®, &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, ®, &b) 1540 c := reg 1541 compileExprWithKMVPropagation(context, expr.Rhs, ®, &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, ®, &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, ®, &b) 1637 c := reg 1638 compileExprWithKMVPropagation(context, expr.Rhs, ®, &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, ®, &b) 1841 c := loadRk(context, ®, 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 } // }}}