local t={}local f=require(script.Parent.LuaP)local X=require(script.Parent.LuaX)t.MAXSTACK=250 function t.ttisnumber(f,t)if t then return type(t.value)=="\110\117\109\098\101\114"else return false end end function t.nvalue(f,t)return t.value end function t.setnilvalue(f,t)t.value=nil end function t.setsvalue(X,t,f)t.value=f end t.setnvalue=t.setsvalue t.sethvalue=t.setsvalue t.setbvalue=t.setsvalue function t.numadd(X,t,f)return t+f end function t.numsub(X,t,f)return t-f end function t.nummul(X,t,f)return t*f end function t.numdiv(X,t,f)return t/f end function t.nummod(X,t,f)return t%f end function t.numpow(X,t,f)return t^f end function t.numunm(f,t)return-t end function t.numisnan(f,t)return t~=t end t.NO_JUMP=-1 t.BinOpr={OPR_ADD=0;OPR_SUB=1;OPR_MUL=2,OPR_DIV=3;OPR_MOD=4,OPR_POW=5,OPR_CONCAT=6,OPR_NE=7;OPR_EQ=8,OPR_LT=9,OPR_LE=10;OPR_GT=11;OPR_GE=12,OPR_AND=13;OPR_OR=14,OPR_NOBINOPR=15}t.UnOpr={OPR_MINUS=0,OPR_NOT=1,OPR_LEN=2,OPR_NOUNOPR=3}function t.getcode(X,t,f)return t.f.code[f.info]end function t.codeAsBx(I,t,X,S,U)return I:codeABx(t,X,S,U+f.MAXARG_sBx)end function t.setmultret(X,t,f)X:setreturns(t,f,luaY.LUA_MULTRET)end function t.hasjumps(f,t)return t.t~=t.f end function t.isnumeral(f,t)return t.k=="\086\075\078\085\077"and(t.t==f.NO_JUMP and t.f==f.NO_JUMP)end function t._nil(U,t,X,S)if t.pc>t.lasttarget then if t.pc==0 then if X>=t.nactvar then return end else local U=t.f.code[t.pc-1]if f:GET_OPCODE(U)=="\079\080\095\076\079\065\068\078\073\076"then local t=f:GETARG_A(U)local I=f:GETARG_B(U)if t<=X and X<=I+1 then if(X+S)-1>I then f:SETARG_B(U,(X+S)-1)end return end end end end U:codeABC(t,"\079\080\095\076\079\065\068\078\073\076",X,(X+S)-1,0)end function t.jump(f,t)local X=t.jpc t.jpc=f.NO_JUMP local S=f:codeAsBx(t,"\079\080\095\074\077\080",0,f.NO_JUMP)S=f:concat(t,S,X)return S end function t.ret(S,t,f,X)S:codeABC(t,"\079\080\095\082\069\084\085\082\078",f,X+1,0)end function t.condjump(I,t,f,X,S,U)I:codeABC(t,f,X,S,U)return I:jump(t)end function t.fixjump(I,t,S,U)local d=t.f.code[S]local h=U-(S+1)assert(U~=I.NO_JUMP)if math.abs(h)>f.MAXARG_sBx then X:syntaxerror(t.ls,"\099\111\110\116\114\111\108 \115\116\114\117\099\116\117\114\101 \116\111\111 \108\111\110\103")end f:SETARG_sBx(d,h)end function t.getlabel(f,t)t.lasttarget=t.pc return t.pc end function t.getjump(S,t,X)local U=f:GETARG_sBx(t.f.code[X])if U==S.NO_JUMP then return S.NO_JUMP else return(X+1)+U end end function t.getjumpcontrol(S,t,X)local U=t.f.code[X]local I=t.f.code[X-1]if X>=1 and f:testTMode(f:GET_OPCODE(I))~=0 then return I else return U end end function t.need_value(S,t,X)while X~=S.NO_JUMP do local U=S:getjumpcontrol(t,X)if f:GET_OPCODE(U)~="\079\080\095\084\069\083\084\083\069\084"then return true end X=S:getjump(t,X)end return false end function t.patchtestreg(U,t,X,S)local I=U:getjumpcontrol(t,X)if f:GET_OPCODE(I)~="\079\080\095\084\069\083\084\083\069\084"then return false end if S~=f.NO_REG and S~=f:GETARG_B(I)then f:SETARG_A(I,S)else f:SET_OPCODE(I,"\079\080\095\084\069\083\084")local t=f:GETARG_B(I)f:SETARG_A(I,t)f:SETARG_B(I,0)end return true end function t.removevalues(S,t,X)while X~=S.NO_JUMP do S:patchtestreg(t,X,f.NO_REG)X=S:getjump(t,X)end end function t.patchlistaux(I,t,f,X,S,U)while f~=I.NO_JUMP do local d=I:getjump(t,f)if I:patchtestreg(t,f,S)then I:fixjump(t,f,X)else I:fixjump(t,f,U)end f=d end end function t.dischargejpc(X,t)X:patchlistaux(t,t.jpc,t.pc,f.NO_REG,t.pc)t.jpc=X.NO_JUMP end function t.patchlist(U,t,X,S)if S==t.pc then U:patchtohere(t,X)else assert(S<t.pc)U:patchlistaux(t,X,S,f.NO_REG,S)end end function t.patchtohere(X,t,f)X:getlabel(t)t.jpc=X:concat(t,t.jpc,f)end function t.concat(S,t,f,X)if X==S.NO_JUMP then return f elseif f==S.NO_JUMP then return X else local U=f local I=S:getjump(t,U)while I~=S.NO_JUMP do U=I I=S:getjump(t,U)end S:fixjump(t,U,X)end return f end function t.checkstack(S,t,f)local U=t.freereg+f if U>t.f.maxstacksize then if U>=S.MAXSTACK then X:syntaxerror(t.ls,"\102\117\110\099\116\105\111\110 \111\114 \101\120\112\114\101\115\115\105\111\110 \116\111\111 \099\111\109\112\108\101\120")end t.f.maxstacksize=U end end function t.reserveregs(X,t,f)X:checkstack(t,f)t.freereg=t.freereg+f end function t.freereg(S,t,X)if not f:ISK(X)and X>=t.nactvar then t.freereg=t.freereg-1 assert(X==t.freereg)end end function t.freeexp(X,t,f)if f.k=="\086\078\079\078\082\069\076\079\067"then X:freereg(t,f.info)end end function t.addk(U,t,X,S)local I=t.L local d=t.h[X.value]local h=t.f if U:ttisnumber(d)then return U:nvalue(d)else d={}U:setnvalue(d,t.nk)t.h[X.value]=d luaY:growvector(I,h.k,t.nk,h.sizek,nil,f.MAXARG_Bx,"\099\111\110\115\116\097\110\116 \116\097\098\108\101 \111\118\101\114\102\108\111\119")h.k[t.nk]=S local T=t.nk t.nk=t.nk+1 return T end end function t.stringK(X,t,f)local S={}X:setsvalue(S,f)return X:addk(t,S,S)end function t.numberK(X,t,f)local S={}X:setnvalue(S,f)return X:addk(t,S,S)end function t.boolK(X,t,f)local S={}X:setbvalue(S,f)return X:addk(t,S,S)end function t.nilK(f,t)local X,S={},{}f:setnilvalue(S)f:sethvalue(X,t.h)return f:addk(t,X,S)end function t.setreturns(I,X,S,U)if S.k=="\086\067\065\076\076"then f:SETARG_C(I:getcode(X,S),U+1)elseif S.k=="\086\086\065\082\065\082\071"then f:SETARG_B(I:getcode(X,S),U+1)f:SETARG_A(I:getcode(X,S),X.freereg)t:reserveregs(X,1)end end function t.setoneret(S,t,X)if X.k=="\086\067\065\076\076"then X.k="\086\078\079\078\082\069\076\079\067"X.info=f:GETARG_A(S:getcode(t,X))elseif X.k=="\086\086\065\082\065\082\071"then f:SETARG_B(S:getcode(t,X),2)X.k="\086\082\069\076\079\067\065\066\076\069"end end function t.dischargevars(X,t,f)local S=f.k if S=="\086\076\079\067\065\076"then f.k="\086\078\079\078\082\069\076\079\067"elseif S=="\086\085\080\086\065\076"then f.info=X:codeABC(t,"\079\080\095\071\069\084\085\080\086\065\076",0,f.info,0)f.k="\086\082\069\076\079\067\065\066\076\069"elseif S=="\086\071\076\079\066\065\076"then f.info=X:codeABx(t,"\079\080\095\071\069\084\071\076\079\066\065\076",0,f.info)f.k="\086\082\069\076\079\067\065\066\076\069"elseif S=="\086\073\078\068\069\088\069\068"then X:freereg(t,f.aux)X:freereg(t,f.info)f.info=X:codeABC(t,"\079\080\095\071\069\084\084\065\066\076\069",0,f.info,f.aux)f.k="\086\082\069\076\079\067\065\066\076\069"elseif S=="\086\086\065\082\065\082\071"or S=="\086\067\065\076\076"then X:setoneret(t,f)else end end function t.code_label(U,t,f,X,S)U:getlabel(t)return U:codeABC(t,"\079\080\095\076\079\065\068\066\079\079\076",f,X,S)end function t.discharge2reg(U,t,X,S)U:dischargevars(t,X)local I=X.k if I=="\086\078\073\076"then U:_nil(t,S,1)elseif I=="\086\070\065\076\083\069"or I=="\086\084\082\085\069"then U:codeABC(t,"\079\080\095\076\079\065\068\066\079\079\076",S,X.k=="\086\084\082\085\069"and 1 or 0,0)elseif I=="\086\075"then U:codeABx(t,"\079\080\095\076\079\065\068\075",S,X.info)elseif I=="\086\075\078\085\077"then U:codeABx(t,"\079\080\095\076\079\065\068\075",S,U:numberK(t,X.nval))elseif I=="\086\082\069\076\079\067\065\066\076\069"then local I=U:getcode(t,X)f:SETARG_A(I,S)elseif I=="\086\078\079\078\082\069\076\079\067"then if S~=X.info then U:codeABC(t,"\079\080\095\077\079\086\069",S,X.info,0)end else assert(X.k=="\086\086\079\073\068"or X.k=="\086\074\077\080")return end X.info=S X.k="\086\078\079\078\082\069\076\079\067"end function t.discharge2anyreg(X,t,f)if f.k~="\086\078\079\078\082\069\076\079\067"then X:reserveregs(t,1)X:discharge2reg(t,f,t.freereg-1)end end function t.exp2reg(S,t,f,X)S:discharge2reg(t,f,X)if f.k=="\086\074\077\080"then f.t=S:concat(t,f.t,f.info)end if S:hasjumps(f)then local U local I=S.NO_JUMP local d=S.NO_JUMP if S:need_value(t,f.t)or S:need_value(t,f.f)then local U=f.k=="\086\074\077\080"and S.NO_JUMP or S:jump(t)I=S:code_label(t,X,0,1)d=S:code_label(t,X,1,0)S:patchtohere(t,U)end U=S:getlabel(t)S:patchlistaux(t,f.f,U,X,I)S:patchlistaux(t,f.t,U,X,d)end f.f,f.t=S.NO_JUMP,S.NO_JUMP f.info=X f.k="\086\078\079\078\082\069\076\079\067"end function t.exp2nextreg(X,t,f)X:dischargevars(t,f)X:freeexp(t,f)X:reserveregs(t,1)X:exp2reg(t,f,t.freereg-1)end function t.exp2anyreg(X,t,f)X:dischargevars(t,f)if f.k=="\086\078\079\078\082\069\076\079\067"then if not X:hasjumps(f)then return f.info end if f.info>=t.nactvar then X:exp2reg(t,f,f.info)return f.info end end X:exp2nextreg(t,f)return f.info end function t.exp2val(X,t,f)if X:hasjumps(f)then X:exp2anyreg(t,f)else X:dischargevars(t,f)end end function t.exp2RK(S,t,X)S:exp2val(t,X)local U=X.k if U=="\086\075\078\085\077"or U=="\086\084\082\085\069"or U=="\086\070\065\076\083\069"or U=="\086\078\073\076"then if t.nk<=f.MAXINDEXRK then if X.k=="\086\078\073\076"then X.info=S:nilK(t)else X.info=X.k=="\086\075\078\085\077"and S:numberK(t,X.nval)or S:boolK(t,X.k=="\086\084\082\085\069")end X.k="\086\075"return f:RKASK(X.info)end elseif U=="\086\075"then if X.info<=f.MAXINDEXRK then return f:RKASK(X.info)end else end return S:exp2anyreg(t,X)end function t.storevar(S,t,f,X)local U=f.k if U=="\086\076\079\067\065\076"then S:freeexp(t,X)S:exp2reg(t,X,f.info)return elseif U=="\086\085\080\086\065\076"then local U=S:exp2anyreg(t,X)S:codeABC(t,"\079\080\095\083\069\084\085\080\086\065\076",U,f.info,0)elseif U=="\086\071\076\079\066\065\076"then local U=S:exp2anyreg(t,X)S:codeABx(t,"\079\080\095\083\069\084\071\076\079\066\065\076",U,f.info)elseif U=="\086\073\078\068\069\088\069\068"then local U=S:exp2RK(t,X)S:codeABC(t,"\079\080\095\083\069\084\084\065\066\076\069",f.info,f.aux,U)else assert(0)end S:freeexp(t,X)end function t._self(S,t,f,X)S:exp2anyreg(t,f)S:freeexp(t,f)local U=t.freereg S:reserveregs(t,2)S:codeABC(t,"\079\080\095\083\069\076\070",U,f.info,S:exp2RK(t,X))S:freeexp(t,X)f.info=U f.k="\086\078\079\078\082\069\076\079\067"end function t.invertjump(S,t,X)local U=S:getjumpcontrol(t,X.info)assert(f:testTMode(f:GET_OPCODE(U))~=0 and(f:GET_OPCODE(U)~="\079\080\095\084\069\083\084\083\069\084"and f:GET_OPCODE(U)~="\079\080\095\084\069\083\084"))f:SETARG_A(U,f:GETARG_A(U)==0 and 1 or 0)end function t.jumponcond(U,t,X,S)if X.k=="\086\082\069\076\079\067\065\066\076\069"then local I=U:getcode(t,X)if f:GET_OPCODE(I)=="\079\080\095\078\079\084"then t.pc=t.pc-1 return U:condjump(t,"\079\080\095\084\069\083\084",f:GETARG_B(I),0,S and 0 or 1)end end U:discharge2anyreg(t,X)U:freeexp(t,X)return U:condjump(t,"\079\080\095\084\069\083\084\083\069\084",f.NO_REG,X.info,S and 1 or 0)end function t.goiftrue(X,t,f)local S X:dischargevars(t,f)local U=f.k if U=="\086\075"or U=="\086\075\078\085\077"or U=="\086\084\082\085\069"then S=X.NO_JUMP elseif U=="\086\070\065\076\083\069"then S=X:jump(t)elseif U=="\086\074\077\080"then X:invertjump(t,f)S=f.info else S=X:jumponcond(t,f,false)end f.f=X:concat(t,f.f,S)X:patchtohere(t,f.t)f.t=X.NO_JUMP end function t.goiffalse(X,t,f)local S X:dischargevars(t,f)local U=f.k if U=="\086\078\073\076"or U=="\086\070\065\076\083\069"then S=X.NO_JUMP elseif U=="\086\084\082\085\069"then S=X:jump(t)elseif U=="\086\074\077\080"then S=f.info else S=X:jumponcond(t,f,true)end f.t=X:concat(t,f.t,S)X:patchtohere(t,f.f)f.f=X.NO_JUMP end function t.codenot(X,t,f)X:dischargevars(t,f)local S=f.k if S=="\086\078\073\076"or S=="\086\070\065\076\083\069"then f.k="\086\084\082\085\069"elseif S=="\086\075"or S=="\086\075\078\085\077"or S=="\086\084\082\085\069"then f.k="\086\070\065\076\083\069"elseif S=="\086\074\077\080"then X:invertjump(t,f)elseif S=="\086\082\069\076\079\067\065\066\076\069"or S=="\086\078\079\078\082\069\076\079\067"then X:discharge2anyreg(t,f)X:freeexp(t,f)f.info=X:codeABC(t,"\079\080\095\078\079\084",0,f.info,0)f.k="\086\082\069\076\079\067\065\066\076\069"else assert(0)end f.f,f.t=f.t,f.f X:removevalues(t,f.f)X:removevalues(t,f.t)end function t.indexed(S,t,f,X)f.aux=S:exp2RK(t,X)f.k="\086\073\078\068\069\088\069\068"end function t.constfolding(S,t,f,X)local U if not S:isnumeral(f)or not S:isnumeral(X)then return false end local I=f.nval local d=X.nval if t=="\079\080\095\065\068\068"then U=S:numadd(I,d)elseif t=="\079\080\095\083\085\066"then U=S:numsub(I,d)elseif t=="\079\080\095\077\085\076"then U=S:nummul(I,d)elseif t=="\079\080\095\068\073\086"then if d==0 then return false end U=S:numdiv(I,d)elseif t=="\079\080\095\077\079\068"then if d==0 then return false end U=S:nummod(I,d)elseif t=="\079\080\095\080\079\087"then U=S:numpow(I,d)elseif t=="\079\080\095\085\078\077"then U=S:numunm(I)elseif t=="\079\080\095\076\069\078"then return false else assert(0)U=0 end if S:numisnan(U)then return false end f.nval=U return true end function t.codearith(U,t,f,X,S)if U:constfolding(f,X,S)then return else local I=(f~="\079\080\095\085\078\077"and f~="\079\080\095\076\069\078")and U:exp2RK(t,S)or 0 local d=U:exp2RK(t,X)if d>I then U:freeexp(t,X)U:freeexp(t,S)else U:freeexp(t,S)U:freeexp(t,X)end X.info=U:codeABC(t,f,0,d,I)X.k="\086\082\069\076\079\067\065\066\076\069"end end function t.codecomp(I,t,f,X,S,U)local d=I:exp2RK(t,S)local h=I:exp2RK(t,U)I:freeexp(t,U)I:freeexp(t,S)if X==0 and f~="\079\080\095\069\081"then d,h=h,d X=1 end S.info=I:condjump(t,f,X,d,h)S.k="\086\074\077\080"end function t.prefix(S,t,f,X)local U={}U.t,U.f=S.NO_JUMP,S.NO_JUMP U.k="\086\075\078\085\077"U.nval=0 if f=="\079\080\082\095\077\073\078\085\083"then if not S:isnumeral(X)then S:exp2anyreg(t,X)end S:codearith(t,"\079\080\095\085\078\077",X,U)elseif f=="\079\080\082\095\078\079\084"then S:codenot(t,X)elseif f=="\079\080\082\095\076\069\078"then S:exp2anyreg(t,X)S:codearith(t,"\079\080\095\076\069\078",X,U)else assert(0)end end function t.infix(S,t,f,X)if f=="\079\080\082\095\065\078\068"then S:goiftrue(t,X)elseif f=="\079\080\082\095\079\082"then S:goiffalse(t,X)elseif f=="\079\080\082\095\067\079\078\067\065\084"then S:exp2nextreg(t,X)elseif f=="\079\080\082\095\065\068\068"or f=="\079\080\082\095\083\085\066"or f=="\079\080\082\095\077\085\076"or f=="\079\080\082\095\068\073\086"or f=="\079\080\082\095\077\079\068"or f=="\079\080\082\095\080\079\087"then if not S:isnumeral(X)then S:exp2RK(t,X)end else S:exp2RK(t,X)end end t.arith_op={OPR_ADD="\079\080\095\065\068\068";OPR_SUB="\079\080\095\083\085\066";OPR_MUL="\079\080\095\077\085\076";OPR_DIV="\079\080\095\068\073\086";OPR_MOD="\079\080\095\077\079\068",OPR_POW="\079\080\095\080\079\087"}t.comp_op={OPR_EQ="\079\080\095\069\081",OPR_NE="\079\080\095\069\081";OPR_LT="\079\080\095\076\084";OPR_LE="\079\080\095\076\069",OPR_GT="\079\080\095\076\084";OPR_GE="\079\080\095\076\069"}t.comp_cond={OPR_EQ=1;OPR_NE=0,OPR_LT=1;OPR_LE=1,OPR_GT=0,OPR_GE=0}function t.posfix(I,t,X,S,U)local function d(t,f)t.k=f.k t.info=f.info t.aux=f.aux t.nval=f.nval t.t=f.t t.f=f.f end if X=="\079\080\082\095\065\078\068"then assert(S.t==I.NO_JUMP)I:dischargevars(t,U)U.f=I:concat(t,U.f,S.f)d(S,U)elseif X=="\079\080\082\095\079\082"then assert(S.f==I.NO_JUMP)I:dischargevars(t,U)U.t=I:concat(t,U.t,S.t)d(S,U)elseif X=="\079\080\082\095\067\079\078\067\065\084"then I:exp2val(t,U)if U.k=="\086\082\069\076\079\067\065\066\076\069"and f:GET_OPCODE(I:getcode(t,U))=="\079\080\095\067\079\078\067\065\084"then assert(S.info==f:GETARG_B(I:getcode(t,U))-1)I:freeexp(t,S)f:SETARG_B(I:getcode(t,U),S.info)S.k="\086\082\069\076\079\067\065\066\076\069"S.info=U.info else I:exp2nextreg(t,U)I:codearith(t,"\079\080\095\067\079\078\067\065\084",S,U)end else local f=I.arith_op[X]if f then I:codearith(t,f,S,U)else local f=I.comp_op[X]if f then I:codecomp(t,f,I.comp_cond[X],S,U)else assert(0)end end end end function t.fixline(X,t,f)t.f.lineinfo[t.pc-1]=f end function t.code(S,t,f,X)local U=t.f S:dischargejpc(t)luaY:growvector(t.L,U.code,t.pc,U.sizecode,nil,luaY.MAX_INT,"\099\111\100\101 \115\105\122\101 \111\118\101\114\102\108\111\119")U.code[t.pc]=f luaY:growvector(t.L,U.lineinfo,t.pc,U.sizelineinfo,nil,luaY.MAX_INT,"\099\111\100\101 \115\105\122\101 \111\118\101\114\102\108\111\119")U.lineinfo[t.pc]=X local I=t.pc t.pc=t.pc+1 return I end function t.codeABC(d,t,X,S,U,I)assert(f:getOpMode(X)==f.OpMode.iABC)assert(f:getBMode(X)~=f.OpArgMask.OpArgN or U==0)assert(f:getCMode(X)~=f.OpArgMask.OpArgN or I==0)return d:code(t,f:CREATE_ABC(X,S,U,I),t.ls.lastline)end function t.codeABx(I,t,X,S,U)assert(f:getOpMode(X)==f.OpMode.iABx or f:getOpMode(X)==f.OpMode.iAsBx)assert(f:getCMode(X)==f.OpArgMask.OpArgN)return I:code(t,f:CREATE_ABx(X,S,U),t.ls.lastline)end function t.setlist(I,t,X,S,U)local d=math.floor((S-1)/f.LFIELDS_PER_FLUSH)+1 local h=U==luaY.LUA_MULTRET and 0 or U assert(U~=0)if d<=f.MAXARG_C then I:codeABC(t,"\079\080\095\083\069\084\076\073\083\084",X,h,d)else I:codeABC(t,"\079\080\095\083\069\084\076\073\083\084",X,h,0)I:code(t,f:CREATE_Inst(d),t.ls.lastline)end t.freereg=X+1 end return function(f)luaY=f return t end
--[[--------------------------------------------------------------------

  lcode.lua
  Lua 5 code generator in Lua
  This file is part of Yueliang.

  Copyright (c) 2005-2007 Kein-Hong Man <khman@users.sf.net>
  The COPYRIGHT file describes the conditions
  under which this software may be distributed.

  See the ChangeLog for more information.

----------------------------------------------------------------------]]

--[[--------------------------------------------------------------------
-- Notes:
-- * one function manipulate a pointer argument with a simple data type
--   (can't be emulated by a table, ambiguous), now returns that value:
--   luaK:concat(fs, l1, l2)
-- * luaM_growvector uses the faux luaY:growvector, for limit checking
-- * some function parameters changed to boolean, additional code
--   translates boolean back to 1/0 for instruction fields
--
-- Not implemented:
-- * NOTE there is a failed assert in luaK:addk, a porting problem
--
-- Added:
-- * constant MAXSTACK from llimits.h
-- * luaK:ttisnumber(o) (from lobject.h)
-- * luaK:nvalue(o) (from lobject.h)
-- * luaK:setnilvalue(o) (from lobject.h)
-- * luaK:setnvalue(o, x) (from lobject.h)
-- * luaK:setbvalue(o, x) (from lobject.h)
-- * luaK:sethvalue(o, x) (from lobject.h), parameter L deleted
-- * luaK:setsvalue(o, x) (from lobject.h), parameter L deleted
-- * luaK:numadd, luaK:numsub, luaK:nummul, luaK:numdiv, luaK:nummod,
--   luaK:numpow, luaK:numunm, luaK:numisnan (from luaconf.h)
-- * copyexp(e1, e2) added in luaK:posfix to copy expdesc struct
--
-- Changed in 5.1.x:
-- * enum BinOpr has a new entry, OPR_MOD
-- * enum UnOpr has a new entry, OPR_LEN
-- * binopistest, unused in 5.0.x, has been deleted
-- * macro setmultret is new
-- * functions isnumeral, luaK_ret, boolK are new
-- * funcion nilK was named nil_constant in 5.0.x
-- * function interface changed: need_value, patchtestreg, concat
-- * TObject now a TValue
-- * functions luaK_setreturns, luaK_setoneret are new
-- * function luaK:setcallreturns deleted, to be replaced by:
--   luaK:setmultret, luaK:ret, luaK:setreturns, luaK:setoneret
-- * functions constfolding, codearith, codecomp are new
-- * luaK:codebinop has been deleted
-- * function luaK_setlist is new
-- * OPR_MULT renamed to OPR_MUL
----------------------------------------------------------------------]]

-- requires luaP, luaX, luaY
local luaK = {}
local luaP = require(script.Parent.LuaP)
local luaX = require(script.Parent.LuaX)

------------------------------------------------------------------------
-- constants used by code generator
------------------------------------------------------------------------
-- maximum stack for a Lua function
luaK.MAXSTACK = 250  -- (from llimits.h)

--[[--------------------------------------------------------------------
-- other functions
----------------------------------------------------------------------]]

------------------------------------------------------------------------
-- emulation of TValue macros (these are from lobject.h)
-- * TValue is a table since lcode passes references around
-- * tt member field removed, using Lua's type() instead
-- * for setsvalue, sethvalue, parameter L (deleted here) in lobject.h
--   is used in an assert for testing, see checkliveness(g,obj)
------------------------------------------------------------------------
function luaK:ttisnumber(o)
  if o then return type(o.value) == "number" else return false end
end
function luaK:nvalue(o) return o.value end
function luaK:setnilvalue(o) o.value = nil end
function luaK:setsvalue(o, x) o.value = x end
luaK.setnvalue = luaK.setsvalue
luaK.sethvalue = luaK.setsvalue
luaK.setbvalue = luaK.setsvalue

------------------------------------------------------------------------
-- The luai_num* macros define the primitive operations over numbers.
-- * this is not the entire set of primitive operations from luaconf.h
-- * used in luaK:constfolding()
------------------------------------------------------------------------
function luaK:numadd(a, b) return a + b end
function luaK:numsub(a, b) return a - b end
function luaK:nummul(a, b) return a * b end
function luaK:numdiv(a, b) return a / b end
function luaK:nummod(a, b) return a % b end
  -- ((a) - floor((a)/(b))*(b)) /* actual, for reference */
function luaK:numpow(a, b) return a ^ b end
function luaK:numunm(a) return -a end
function luaK:numisnan(a) return a ~= a end
  -- a NaN cannot equal another NaN

--[[--------------------------------------------------------------------
-- code generator functions
----------------------------------------------------------------------]]

------------------------------------------------------------------------
-- Marks the end of a patch list. It is an invalid value both as an absolute
-- address, and as a list link (would link an element to itself).
------------------------------------------------------------------------
luaK.NO_JUMP = -1

------------------------------------------------------------------------
-- grep "ORDER OPR" if you change these enums
------------------------------------------------------------------------
luaK.BinOpr = {
  OPR_ADD = 0, OPR_SUB = 1, OPR_MUL = 2, OPR_DIV = 3, OPR_MOD = 4, OPR_POW = 5,
  OPR_CONCAT = 6,
  OPR_NE = 7, OPR_EQ = 8,
  OPR_LT = 9, OPR_LE = 10, OPR_GT = 11, OPR_GE = 12,
  OPR_AND = 13, OPR_OR = 14,
  OPR_NOBINOPR = 15,
}

-- * UnOpr is used by luaK:prefix's op argument, but not directly used
--   because the function receives the symbols as strings, e.g. "OPR_NOT"
luaK.UnOpr = {
  OPR_MINUS = 0, OPR_NOT = 1, OPR_LEN = 2, OPR_NOUNOPR = 3
}

------------------------------------------------------------------------
-- returns the instruction object for given e (expdesc), was a macro
------------------------------------------------------------------------
function luaK:getcode(fs, e)
  return fs.f.code[e.info]
end

------------------------------------------------------------------------
-- codes an instruction with a signed Bx (sBx) field, was a macro
-- * used in luaK:jump(), (lparser) luaY:forbody()
------------------------------------------------------------------------
function luaK:codeAsBx(fs, o, A, sBx)
  return self:codeABx(fs, o, A, sBx + luaP.MAXARG_sBx)
end

------------------------------------------------------------------------
-- set the expdesc e instruction for multiple returns, was a macro
------------------------------------------------------------------------
function luaK:setmultret(fs, e)
  self:setreturns(fs, e, luaY.LUA_MULTRET)
end

------------------------------------------------------------------------
-- there is a jump if patch lists are not identical, was a macro
-- * used in luaK:exp2reg(), luaK:exp2anyreg(), luaK:exp2val()
------------------------------------------------------------------------
function luaK:hasjumps(e)
  return e.t ~= e.f
end

------------------------------------------------------------------------
-- true if the expression is a constant number (for constant folding)
-- * used in constfolding(), infix()
------------------------------------------------------------------------
function luaK:isnumeral(e)
  return e.k == "VKNUM" and e.t == self.NO_JUMP and e.f == self.NO_JUMP
end

------------------------------------------------------------------------
-- codes loading of nil, optimization done if consecutive locations
-- * used in luaK:discharge2reg(), (lparser) luaY:adjust_assign()
------------------------------------------------------------------------
function luaK:_nil(fs, from, n)
  if fs.pc > fs.lasttarget then  -- no jumps to current position?
    if fs.pc == 0 then  -- function start?
      if from >= fs.nactvar then
        return  -- positions are already clean
      end
    else
      local previous = fs.f.code[fs.pc - 1]
      if luaP:GET_OPCODE(previous) == "OP_LOADNIL" then
        local pfrom = luaP:GETARG_A(previous)
        local pto = luaP:GETARG_B(previous)
        if pfrom <= from and from <= pto + 1 then  -- can connect both?
          if from + n - 1 > pto then
            luaP:SETARG_B(previous, from + n - 1)
          end
          return
        end
      end
    end
  end
  self:codeABC(fs, "OP_LOADNIL", from, from + n - 1, 0)  -- else no optimization
end

------------------------------------------------------------------------
--
-- * used in multiple locations
------------------------------------------------------------------------
function luaK:jump(fs)
  local jpc = fs.jpc  -- save list of jumps to here
  fs.jpc = self.NO_JUMP
  local j = self:codeAsBx(fs, "OP_JMP", 0, self.NO_JUMP)
  j = self:concat(fs, j, jpc)  -- keep them on hold
  return j
end

------------------------------------------------------------------------
-- codes a RETURN instruction
-- * used in luaY:close_func(), luaY:retstat()
------------------------------------------------------------------------
function luaK:ret(fs, first, nret)
  self:codeABC(fs, "OP_RETURN", first, nret + 1, 0)
end

------------------------------------------------------------------------
--
-- * used in luaK:jumponcond(), luaK:codecomp()
------------------------------------------------------------------------
function luaK:condjump(fs, op, A, B, C)
  self:codeABC(fs, op, A, B, C)
  return self:jump(fs)
end

------------------------------------------------------------------------
--
-- * used in luaK:patchlistaux(), luaK:concat()
------------------------------------------------------------------------
function luaK:fixjump(fs, pc, dest)
  local jmp = fs.f.code[pc]
  local offset = dest - (pc + 1)
  assert(dest ~= self.NO_JUMP)
  if math.abs(offset) > luaP.MAXARG_sBx then
    luaX:syntaxerror(fs.ls, "control structure too long")
  end
  luaP:SETARG_sBx(jmp, offset)
end

------------------------------------------------------------------------
-- returns current 'pc' and marks it as a jump target (to avoid wrong
-- optimizations with consecutive instructions not in the same basic block).
-- * used in multiple locations
-- * fs.lasttarget tested only by luaK:_nil() when optimizing OP_LOADNIL
------------------------------------------------------------------------
function luaK:getlabel(fs)
  fs.lasttarget = fs.pc
  return fs.pc
end

------------------------------------------------------------------------
--
-- * used in luaK:need_value(), luaK:removevalues(), luaK:patchlistaux(),
--   luaK:concat()
------------------------------------------------------------------------
function luaK:getjump(fs, pc)
  local offset = luaP:GETARG_sBx(fs.f.code[pc])
  if offset == self.NO_JUMP then  -- point to itself represents end of list
    return self.NO_JUMP  -- end of list
  else
    return (pc + 1) + offset  -- turn offset into absolute position
  end
end

------------------------------------------------------------------------
--
-- * used in luaK:need_value(), luaK:patchtestreg(), luaK:invertjump()
------------------------------------------------------------------------
function luaK:getjumpcontrol(fs, pc)
  local pi = fs.f.code[pc]
  local ppi = fs.f.code[pc - 1]
  if pc >= 1 and luaP:testTMode(luaP:GET_OPCODE(ppi)) ~= 0 then
    return ppi
  else
    return pi
  end
end

------------------------------------------------------------------------
-- check whether list has any jump that do not produce a value
-- (or produce an inverted value)
-- * return value changed to boolean
-- * used only in luaK:exp2reg()
------------------------------------------------------------------------
function luaK:need_value(fs, list)
  while list ~= self.NO_JUMP do
    local i = self:getjumpcontrol(fs, list)
    if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then return true end
    list = self:getjump(fs, list)
  end
  return false  -- not found
end

------------------------------------------------------------------------
--
-- * used in luaK:removevalues(), luaK:patchlistaux()
------------------------------------------------------------------------
function luaK:patchtestreg(fs, node, reg)
  local i = self:getjumpcontrol(fs, node)
  if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then
    return false  -- cannot patch other instructions
  end
  if reg ~= luaP.NO_REG and reg ~= luaP:GETARG_B(i) then
    luaP:SETARG_A(i, reg)
  else  -- no register to put value or register already has the value
    -- due to use of a table as i, i cannot be replaced by another table
    -- so the following is required; there is no change to ARG_C
    luaP:SET_OPCODE(i, "OP_TEST")
    local b = luaP:GETARG_B(i)
    luaP:SETARG_A(i, b)
    luaP:SETARG_B(i, 0)
    -- *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); /* C */
  end
  return true
end

------------------------------------------------------------------------
--
-- * used only in luaK:codenot()
------------------------------------------------------------------------
function luaK:removevalues(fs, list)
  while list ~= self.NO_JUMP do
    self:patchtestreg(fs, list, luaP.NO_REG)
    list = self:getjump(fs, list)
  end
end

------------------------------------------------------------------------
--
-- * used in luaK:dischargejpc(), luaK:patchlist(), luaK:exp2reg()
------------------------------------------------------------------------
function luaK:patchlistaux(fs, list, vtarget, reg, dtarget)
  while list ~= self.NO_JUMP do
    local _next = self:getjump(fs, list)
    if self:patchtestreg(fs, list, reg) then
      self:fixjump(fs, list, vtarget)
    else
      self:fixjump(fs, list, dtarget)  -- jump to default target
    end
    list = _next
  end
end

------------------------------------------------------------------------
--
-- * used only in luaK:code()
------------------------------------------------------------------------
function luaK:dischargejpc(fs)
  self:patchlistaux(fs, fs.jpc, fs.pc, luaP.NO_REG, fs.pc)
  fs.jpc = self.NO_JUMP
end

------------------------------------------------------------------------
--
-- * used in (lparser) luaY:whilestat(), luaY:repeatstat(), luaY:forbody()
------------------------------------------------------------------------
function luaK:patchlist(fs, list, target)
  if target == fs.pc then
    self:patchtohere(fs, list)
  else
    assert(target < fs.pc)
    self:patchlistaux(fs, list, target, luaP.NO_REG, target)
  end
end

------------------------------------------------------------------------
--
-- * used in multiple locations
------------------------------------------------------------------------
function luaK:patchtohere(fs, list)
  self:getlabel(fs)
  fs.jpc = self:concat(fs, fs.jpc, list)
end

------------------------------------------------------------------------
-- * l1 was a pointer, now l1 is returned and callee assigns the value
-- * used in multiple locations
------------------------------------------------------------------------
function luaK:concat(fs, l1, l2)
  if l2 == self.NO_JUMP then return l1
  elseif l1 == self.NO_JUMP then
    return l2
  else
    local list = l1
    local _next = self:getjump(fs, list)
    while _next ~= self.NO_JUMP do  -- find last element
      list = _next
      _next = self:getjump(fs, list)
    end
    self:fixjump(fs, list, l2)
  end
  return l1
end

------------------------------------------------------------------------
--
-- * used in luaK:reserveregs(), (lparser) luaY:forlist()
------------------------------------------------------------------------
function luaK:checkstack(fs, n)
  local newstack = fs.freereg + n
  if newstack > fs.f.maxstacksize then
    if newstack >= self.MAXSTACK then
      luaX:syntaxerror(fs.ls, "function or expression too complex")
    end
    fs.f.maxstacksize = newstack
  end
end

------------------------------------------------------------------------
--
-- * used in multiple locations
------------------------------------------------------------------------
function luaK:reserveregs(fs, n)
  self:checkstack(fs, n)
  fs.freereg = fs.freereg + n
end

------------------------------------------------------------------------
--
-- * used in luaK:freeexp(), luaK:dischargevars()
------------------------------------------------------------------------
function luaK:freereg(fs, reg)
  if not luaP:ISK(reg) and reg >= fs.nactvar then
    fs.freereg = fs.freereg - 1
    assert(reg == fs.freereg)
  end
end

------------------------------------------------------------------------
--
-- * used in multiple locations
------------------------------------------------------------------------
function luaK:freeexp(fs, e)
  if e.k == "VNONRELOC" then
    self:freereg(fs, e.info)
  end
end

------------------------------------------------------------------------
-- * TODO NOTE implementation is not 100% correct, since the assert fails
-- * luaH_set, setobj deleted; direct table access used instead
-- * used in luaK:stringK(), luaK:numberK(), luaK:boolK(), luaK:nilK()
------------------------------------------------------------------------
function luaK:addk(fs, k, v)
  local L = fs.L
  local idx = fs.h[k.value]
  --TValue *idx = luaH_set(L, fs->h, k); /* C */
  local f = fs.f
  if self:ttisnumber(idx) then
    --TODO this assert currently FAILS (last tested for 5.0.2)
    --assert(fs.f.k[self:nvalue(idx)] == v)
    --assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); /* C */
    return self:nvalue(idx)
  else -- constant not found; create a new entry
    idx = {}
    self:setnvalue(idx, fs.nk)
    fs.h[k.value] = idx
    -- setnvalue(idx, cast_num(fs->nk)); /* C */
    luaY:growvector(L, f.k, fs.nk, f.sizek, nil,
                    luaP.MAXARG_Bx, "constant table overflow")
    -- loop to initialize empty f.k positions not required
    f.k[fs.nk] = v
    -- setobj(L, &f->k[fs->nk], v); /* C */
    -- luaC_barrier(L, f, v); /* GC */
    local nk = fs.nk
    fs.nk = fs.nk + 1
    return nk
  end

end

------------------------------------------------------------------------
-- creates and sets a string object
-- * used in (lparser) luaY:codestring(), luaY:singlevar()
------------------------------------------------------------------------
function luaK:stringK(fs, s)
  local o = {}  -- TValue
  self:setsvalue(o, s)
  return self:addk(fs, o, o)
end

------------------------------------------------------------------------
-- creates and sets a number object
-- * used in luaK:prefix() for negative (or negation of) numbers
-- * used in (lparser) luaY:simpleexp(), luaY:fornum()
------------------------------------------------------------------------
function luaK:numberK(fs, r)
  local o = {}  -- TValue
  self:setnvalue(o, r)
  return self:addk(fs, o, o)
end

------------------------------------------------------------------------
-- creates and sets a boolean object
-- * used only in luaK:exp2RK()
------------------------------------------------------------------------
function luaK:boolK(fs, b)
  local o = {}  -- TValue
  self:setbvalue(o, b)
  return self:addk(fs, o, o)
end

------------------------------------------------------------------------
-- creates and sets a nil object
-- * used only in luaK:exp2RK()
------------------------------------------------------------------------
function luaK:nilK(fs)
  local k, v = {}, {}  -- TValue
  self:setnilvalue(v)
  -- cannot use nil as key; instead use table itself to represent nil
  self:sethvalue(k, fs.h)
  return self:addk(fs, k, v)
end

------------------------------------------------------------------------
--
-- * used in luaK:setmultret(), (lparser) luaY:adjust_assign()
------------------------------------------------------------------------
function luaK:setreturns(fs, e, nresults)
  if e.k == "VCALL" then  -- expression is an open function call?
    luaP:SETARG_C(self:getcode(fs, e), nresults + 1)
  elseif e.k == "VVARARG" then
    luaP:SETARG_B(self:getcode(fs, e), nresults + 1);
    luaP:SETARG_A(self:getcode(fs, e), fs.freereg);
    luaK:reserveregs(fs, 1)
  end
end

------------------------------------------------------------------------
--
-- * used in luaK:dischargevars(), (lparser) luaY:assignment()
------------------------------------------------------------------------
function luaK:setoneret(fs, e)
  if e.k == "VCALL" then  -- expression is an open function call?
    e.k = "VNONRELOC"
    e.info = luaP:GETARG_A(self:getcode(fs, e))
  elseif e.k == "VVARARG" then
    luaP:SETARG_B(self:getcode(fs, e), 2)
    e.k = "VRELOCABLE"  -- can relocate its simple result
  end
end

------------------------------------------------------------------------
--
-- * used in multiple locations
------------------------------------------------------------------------
function luaK:dischargevars(fs, e)
  local k = e.k
  if k == "VLOCAL" then
    e.k = "VNONRELOC"
  elseif k == "VUPVAL" then
    e.info = self:codeABC(fs, "OP_GETUPVAL", 0, e.info, 0)
    e.k = "VRELOCABLE"
  elseif k == "VGLOBAL" then
    e.info = self:codeABx(fs, "OP_GETGLOBAL", 0, e.info)
    e.k = "VRELOCABLE"
  elseif k == "VINDEXED" then
    self:freereg(fs, e.aux)
    self:freereg(fs, e.info)
    e.info = self:codeABC(fs, "OP_GETTABLE", 0, e.info, e.aux)
    e.k = "VRELOCABLE"
  elseif k == "VVARARG" or k == "VCALL" then
    self:setoneret(fs, e)
  else
    -- there is one value available (somewhere)
  end
end

------------------------------------------------------------------------
--
-- * used only in luaK:exp2reg()
------------------------------------------------------------------------
function luaK:code_label(fs, A, b, jump)
  self:getlabel(fs)  -- those instructions may be jump targets
  return self:codeABC(fs, "OP_LOADBOOL", A, b, jump)
end

------------------------------------------------------------------------
--
-- * used in luaK:discharge2anyreg(), luaK:exp2reg()
------------------------------------------------------------------------
function luaK:discharge2reg(fs, e, reg)
  self:dischargevars(fs, e)
  local k = e.k
  if k == "VNIL" then
    self:_nil(fs, reg, 1)
  elseif k == "VFALSE" or k == "VTRUE" then
    self:codeABC(fs, "OP_LOADBOOL", reg, (e.k == "VTRUE") and 1 or 0, 0)
  elseif k == "VK" then
    self:codeABx(fs, "OP_LOADK", reg, e.info)
  elseif k == "VKNUM" then
    self:codeABx(fs, "OP_LOADK", reg, self:numberK(fs, e.nval))
  elseif k == "VRELOCABLE" then
    local pc = self:getcode(fs, e)
    luaP:SETARG_A(pc, reg)
  elseif k == "VNONRELOC" then
    if reg ~= e.info then
      self:codeABC(fs, "OP_MOVE", reg, e.info, 0)
    end
  else
    assert(e.k == "VVOID" or e.k == "VJMP")
    return  -- nothing to do...
  end
  e.info = reg
  e.k = "VNONRELOC"
end

------------------------------------------------------------------------
--
-- * used in luaK:jumponcond(), luaK:codenot()
------------------------------------------------------------------------
function luaK:discharge2anyreg(fs, e)
  if e.k ~= "VNONRELOC" then
    self:reserveregs(fs, 1)
    self:discharge2reg(fs, e, fs.freereg - 1)
  end
end

------------------------------------------------------------------------
--
-- * used in luaK:exp2nextreg(), luaK:exp2anyreg(), luaK:storevar()
------------------------------------------------------------------------
function luaK:exp2reg(fs, e, reg)
  self:discharge2reg(fs, e, reg)
  if e.k == "VJMP" then
    e.t = self:concat(fs, e.t, e.info)  -- put this jump in 't' list
  end
  if self:hasjumps(e) then
    local final  -- position after whole expression
    local p_f = self.NO_JUMP  -- position of an eventual LOAD false
    local p_t = self.NO_JUMP  -- position of an eventual LOAD true
    if self:need_value(fs, e.t) or self:need_value(fs, e.f) then
      local fj = (e.k == "VJMP") and self.NO_JUMP or self:jump(fs)
      p_f = self:code_label(fs, reg, 0, 1)
      p_t = self:code_label(fs, reg, 1, 0)
      self:patchtohere(fs, fj)
    end
    final = self:getlabel(fs)
    self:patchlistaux(fs, e.f, final, reg, p_f)
    self:patchlistaux(fs, e.t, final, reg, p_t)
  end
  e.f, e.t = self.NO_JUMP, self.NO_JUMP
  e.info = reg
  e.k = "VNONRELOC"
end

------------------------------------------------------------------------
--
-- * used in multiple locations
------------------------------------------------------------------------
function luaK:exp2nextreg(fs, e)
  self:dischargevars(fs, e)
  self:freeexp(fs, e)
  self:reserveregs(fs, 1)
  self:exp2reg(fs, e, fs.freereg - 1)
end

------------------------------------------------------------------------
--
-- * used in multiple locations
------------------------------------------------------------------------
function luaK:exp2anyreg(fs, e)
  self:dischargevars(fs, e)
  if e.k == "VNONRELOC" then
    if not self:hasjumps(e) then  -- exp is already in a register
      return e.info
    end
    if e.info >= fs.nactvar then  -- reg. is not a local?
      self:exp2reg(fs, e, e.info)  -- put value on it
      return e.info
    end
  end
  self:exp2nextreg(fs, e)  -- default
  return e.info
end

------------------------------------------------------------------------
--
-- * used in luaK:exp2RK(), luaK:prefix(), luaK:posfix()
-- * used in (lparser) luaY:yindex()
------------------------------------------------------------------------
function luaK:exp2val(fs, e)
  if self:hasjumps(e) then
    self:exp2anyreg(fs, e)
  else
    self:dischargevars(fs, e)
  end
end

------------------------------------------------------------------------
--
-- * used in multiple locations
------------------------------------------------------------------------
function luaK:exp2RK(fs, e)
  self:exp2val(fs, e)
  local k = e.k
  if k == "VKNUM" or k == "VTRUE" or k == "VFALSE" or k == "VNIL" then
    if fs.nk <= luaP.MAXINDEXRK then  -- constant fit in RK operand?
      -- converted from a 2-deep ternary operator expression
      if e.k == "VNIL" then
        e.info = self:nilK(fs)
      else
        e.info = (e.k == "VKNUM") and self:numberK(fs, e.nval)
                                  or self:boolK(fs, e.k == "VTRUE")
      end
      e.k = "VK"
      return luaP:RKASK(e.info)
    end
  elseif k == "VK" then
    if e.info <= luaP.MAXINDEXRK then  -- constant fit in argC?
      return luaP:RKASK(e.info)
    end
  else
    -- default
  end
  -- not a constant in the right range: put it in a register
  return self:exp2anyreg(fs, e)
end

------------------------------------------------------------------------
--
-- * used in (lparser) luaY:assignment(), luaY:localfunc(), luaY:funcstat()
------------------------------------------------------------------------
function luaK:storevar(fs, var, ex)
  local k = var.k
  if k == "VLOCAL" then
    self:freeexp(fs, ex)
    self:exp2reg(fs, ex, var.info)
    return
  elseif k == "VUPVAL" then
    local e = self:exp2anyreg(fs, ex)
    self:codeABC(fs, "OP_SETUPVAL", e, var.info, 0)
  elseif k == "VGLOBAL" then
    local e = self:exp2anyreg(fs, ex)
    self:codeABx(fs, "OP_SETGLOBAL", e, var.info)
  elseif k == "VINDEXED" then
    local e = self:exp2RK(fs, ex)
    self:codeABC(fs, "OP_SETTABLE", var.info, var.aux, e)
  else
    assert(0)  -- invalid var kind to store
  end
  self:freeexp(fs, ex)
end

------------------------------------------------------------------------
--
-- * used only in (lparser) luaY:primaryexp()
------------------------------------------------------------------------
function luaK:_self(fs, e, key)
  self:exp2anyreg(fs, e)
  self:freeexp(fs, e)
  local func = fs.freereg
  self:reserveregs(fs, 2)
  self:codeABC(fs, "OP_SELF", func, e.info, self:exp2RK(fs, key))
  self:freeexp(fs, key)
  e.info = func
  e.k = "VNONRELOC"
end

------------------------------------------------------------------------
--
-- * used in luaK:goiftrue(), luaK:codenot()
------------------------------------------------------------------------
function luaK:invertjump(fs, e)
  local pc = self:getjumpcontrol(fs, e.info)
  assert(luaP:testTMode(luaP:GET_OPCODE(pc)) ~= 0 and
             luaP:GET_OPCODE(pc) ~= "OP_TESTSET" and
             luaP:GET_OPCODE(pc) ~= "OP_TEST")
  luaP:SETARG_A(pc, (luaP:GETARG_A(pc) == 0) and 1 or 0)
end

------------------------------------------------------------------------
--
-- * used in luaK:goiftrue(), luaK:goiffalse()
------------------------------------------------------------------------
function luaK:jumponcond(fs, e, cond)
  if e.k == "VRELOCABLE" then
    local ie = self:getcode(fs, e)
    if luaP:GET_OPCODE(ie) == "OP_NOT" then
      fs.pc = fs.pc - 1  -- remove previous OP_NOT
      return self:condjump(fs, "OP_TEST", luaP:GETARG_B(ie), 0, cond and 0 or 1)
    end
    -- else go through
  end
  self:discharge2anyreg(fs, e)
  self:freeexp(fs, e)
  return self:condjump(fs, "OP_TESTSET", luaP.NO_REG, e.info, cond and 1 or 0)
end

------------------------------------------------------------------------
--
-- * used in luaK:infix(), (lparser) luaY:cond()
------------------------------------------------------------------------
function luaK:goiftrue(fs, e)
  local pc  -- pc of last jump
  self:dischargevars(fs, e)
  local k = e.k
  if k == "VK" or k == "VKNUM" or k == "VTRUE" then
    pc = self.NO_JUMP  -- always true; do nothing
  elseif k == "VFALSE" then
    pc = self:jump(fs)  -- always jump
  elseif k == "VJMP" then
    self:invertjump(fs, e)
    pc = e.info
  else
    pc = self:jumponcond(fs, e, false)
  end
  e.f = self:concat(fs, e.f, pc)  -- insert last jump in `f' list
  self:patchtohere(fs, e.t)
  e.t = self.NO_JUMP
end

------------------------------------------------------------------------
--
-- * used in luaK:infix()
------------------------------------------------------------------------
function luaK:goiffalse(fs, e)
  local pc  -- pc of last jump
  self:dischargevars(fs, e)
  local k = e.k
  if k == "VNIL" or k == "VFALSE"then
    pc = self.NO_JUMP  -- always false; do nothing
  elseif k == "VTRUE" then
    pc = self:jump(fs)  -- always jump
  elseif k == "VJMP" then
    pc = e.info
  else
    pc = self:jumponcond(fs, e, true)
  end
  e.t = self:concat(fs, e.t, pc)  -- insert last jump in `t' list
  self:patchtohere(fs, e.f)
  e.f = self.NO_JUMP
end

------------------------------------------------------------------------
--
-- * used only in luaK:prefix()
------------------------------------------------------------------------
function luaK:codenot(fs, e)
  self:dischargevars(fs, e)
  local k = e.k
  if k == "VNIL" or k == "VFALSE" then
    e.k = "VTRUE"
  elseif k == "VK" or k == "VKNUM" or k == "VTRUE" then
    e.k = "VFALSE"
  elseif k == "VJMP" then
    self:invertjump(fs, e)
  elseif k == "VRELOCABLE" or k == "VNONRELOC" then
    self:discharge2anyreg(fs, e)
    self:freeexp(fs, e)
    e.info = self:codeABC(fs, "OP_NOT", 0, e.info, 0)
    e.k = "VRELOCABLE"
  else
    assert(0)  -- cannot happen
  end
  -- interchange true and false lists
  e.f, e.t = e.t, e.f
  self:removevalues(fs, e.f)
  self:removevalues(fs, e.t)
end

------------------------------------------------------------------------
--
-- * used in (lparser) luaY:field(), luaY:primaryexp()
------------------------------------------------------------------------
function luaK:indexed(fs, t, k)
  t.aux = self:exp2RK(fs, k)
  t.k = "VINDEXED"
end

------------------------------------------------------------------------
--
-- * used only in luaK:codearith()
------------------------------------------------------------------------
function luaK:constfolding(op, e1, e2)
  local r
  if not self:isnumeral(e1) or not self:isnumeral(e2) then return false end
  local v1 = e1.nval
  local v2 = e2.nval
  if op == "OP_ADD" then
    r = self:numadd(v1, v2)
  elseif op == "OP_SUB" then
    r = self:numsub(v1, v2)
  elseif op == "OP_MUL" then
    r = self:nummul(v1, v2)
  elseif op == "OP_DIV" then
    if v2 == 0 then return false end  -- do not attempt to divide by 0
    r = self:numdiv(v1, v2)
  elseif op == "OP_MOD" then
    if v2 == 0 then return false end  -- do not attempt to divide by 0
    r = self:nummod(v1, v2)
  elseif op == "OP_POW" then
    r = self:numpow(v1, v2)
  elseif op == "OP_UNM" then
    r = self:numunm(v1)
  elseif op == "OP_LEN" then
    return false  -- no constant folding for 'len'
  else
    assert(0)
    r = 0
  end
  if self:numisnan(r) then return false end  -- do not attempt to produce NaN
  e1.nval = r
  return true
end

------------------------------------------------------------------------
--
-- * used in luaK:prefix(), luaK:posfix()
------------------------------------------------------------------------
function luaK:codearith(fs, op, e1, e2)
  if self:constfolding(op, e1, e2) then
    return
  else
    local o2 = (op ~= "OP_UNM" and op ~= "OP_LEN") and self:exp2RK(fs, e2) or 0
    local o1 = self:exp2RK(fs, e1)
    if o1 > o2 then
      self:freeexp(fs, e1)
      self:freeexp(fs, e2)
    else
      self:freeexp(fs, e2)
      self:freeexp(fs, e1)
    end
    e1.info = self:codeABC(fs, op, 0, o1, o2)
    e1.k = "VRELOCABLE"
  end
end

------------------------------------------------------------------------
--
-- * used only in luaK:posfix()
------------------------------------------------------------------------
function luaK:codecomp(fs, op, cond, e1, e2)
  local o1 = self:exp2RK(fs, e1)
  local o2 = self:exp2RK(fs, e2)
  self:freeexp(fs, e2)
  self:freeexp(fs, e1)
  if cond == 0 and op ~= "OP_EQ" then
    -- exchange args to replace by `<' or `<='
    o1, o2 = o2, o1  -- o1 <==> o2
    cond = 1
  end
  e1.info = self:condjump(fs, op, cond, o1, o2)
  e1.k = "VJMP"
end

------------------------------------------------------------------------
--
-- * used only in (lparser) luaY:subexpr()
------------------------------------------------------------------------
function luaK:prefix(fs, op, e)
  local e2 = {}  -- expdesc
  e2.t, e2.f = self.NO_JUMP, self.NO_JUMP
  e2.k = "VKNUM"
  e2.nval = 0
  if op == "OPR_MINUS" then
    if not self:isnumeral(e) then
      self:exp2anyreg(fs, e)  -- cannot operate on non-numeric constants
    end
    self:codearith(fs, "OP_UNM", e, e2)
  elseif op == "OPR_NOT" then
    self:codenot(fs, e)
  elseif op == "OPR_LEN" then
    self:exp2anyreg(fs, e)  -- cannot operate on constants
    self:codearith(fs, "OP_LEN", e, e2)
  else
    assert(0)
  end
end

------------------------------------------------------------------------
--
-- * used only in (lparser) luaY:subexpr()
------------------------------------------------------------------------
function luaK:infix(fs, op, v)
  if op == "OPR_AND" then
    self:goiftrue(fs, v)
  elseif op == "OPR_OR" then
    self:goiffalse(fs, v)
  elseif op == "OPR_CONCAT" then
    self:exp2nextreg(fs, v)  -- operand must be on the 'stack'
  elseif op == "OPR_ADD" or op == "OPR_SUB" or
         op == "OPR_MUL" or op == "OPR_DIV" or
         op == "OPR_MOD" or op == "OPR_POW" then
    if not self:isnumeral(v) then self:exp2RK(fs, v) end
  else
    self:exp2RK(fs, v)
  end
end

------------------------------------------------------------------------
--
-- * used only in (lparser) luaY:subexpr()
------------------------------------------------------------------------
-- table lookups to simplify testing
luaK.arith_op = {
  OPR_ADD = "OP_ADD", OPR_SUB = "OP_SUB", OPR_MUL = "OP_MUL",
  OPR_DIV = "OP_DIV", OPR_MOD = "OP_MOD", OPR_POW = "OP_POW",
}
luaK.comp_op = {
  OPR_EQ = "OP_EQ", OPR_NE = "OP_EQ", OPR_LT = "OP_LT",
  OPR_LE = "OP_LE", OPR_GT = "OP_LT", OPR_GE = "OP_LE",
}
luaK.comp_cond = {
  OPR_EQ = 1, OPR_NE = 0, OPR_LT = 1,
  OPR_LE = 1, OPR_GT = 0, OPR_GE = 0,
}
function luaK:posfix(fs, op, e1, e2)
  -- needed because e1 = e2 doesn't copy values...
  -- * in 5.0.x, only k/info/aux/t/f copied, t for AND, f for OR
  --   but here, all elements are copied for completeness' sake
  local function copyexp(e1, e2)
    e1.k = e2.k
    e1.info = e2.info; e1.aux = e2.aux
    e1.nval = e2.nval
    e1.t = e2.t; e1.f = e2.f
  end
  if op == "OPR_AND" then
    assert(e1.t == self.NO_JUMP)  -- list must be closed
    self:dischargevars(fs, e2)
    e2.f = self:concat(fs, e2.f, e1.f)
    copyexp(e1, e2)
  elseif op == "OPR_OR" then
    assert(e1.f == self.NO_JUMP)  -- list must be closed
    self:dischargevars(fs, e2)
    e2.t = self:concat(fs, e2.t, e1.t)
    copyexp(e1, e2)
  elseif op == "OPR_CONCAT" then
    self:exp2val(fs, e2)
    if e2.k == "VRELOCABLE" and luaP:GET_OPCODE(self:getcode(fs, e2)) == "OP_CONCAT" then
      assert(e1.info == luaP:GETARG_B(self:getcode(fs, e2)) - 1)
      self:freeexp(fs, e1)
      luaP:SETARG_B(self:getcode(fs, e2), e1.info)
      e1.k = "VRELOCABLE"
      e1.info = e2.info
    else
      self:exp2nextreg(fs, e2)  -- operand must be on the 'stack'
      self:codearith(fs, "OP_CONCAT", e1, e2)
    end
  else
    -- the following uses a table lookup in place of conditionals
    local arith = self.arith_op[op]
    if arith then
      self:codearith(fs, arith, e1, e2)
    else
      local comp = self.comp_op[op]
      if comp then
        self:codecomp(fs, comp, self.comp_cond[op], e1, e2)
      else
        assert(0)
      end
    end--if arith
  end--if op
end

------------------------------------------------------------------------
-- adjusts debug information for last instruction written, in order to
-- change the line where item comes into existence
-- * used in (lparser) luaY:funcargs(), luaY:forbody(), luaY:funcstat()
------------------------------------------------------------------------
function luaK:fixline(fs, line)
  fs.f.lineinfo[fs.pc - 1] = line
end

------------------------------------------------------------------------
-- general function to write an instruction into the instruction buffer,
-- sets debug information too
-- * used in luaK:codeABC(), luaK:codeABx()
-- * called directly by (lparser) luaY:whilestat()
------------------------------------------------------------------------
function luaK:code(fs, i, line)
  local f = fs.f
  self:dischargejpc(fs)  -- 'pc' will change
  -- put new instruction in code array
  luaY:growvector(fs.L, f.code, fs.pc, f.sizecode, nil,
                  luaY.MAX_INT, "code size overflow")
  f.code[fs.pc] = i
  -- save corresponding line information
  luaY:growvector(fs.L, f.lineinfo, fs.pc, f.sizelineinfo, nil,
                  luaY.MAX_INT, "code size overflow")
  f.lineinfo[fs.pc] = line
  local pc = fs.pc
  fs.pc = fs.pc + 1
  return pc
end

------------------------------------------------------------------------
-- writes an instruction of type ABC
-- * calls luaK:code()
------------------------------------------------------------------------
function luaK:codeABC(fs, o, a, b, c)
  assert(luaP:getOpMode(o) == luaP.OpMode.iABC)
  assert(luaP:getBMode(o) ~= luaP.OpArgMask.OpArgN or b == 0)
  assert(luaP:getCMode(o) ~= luaP.OpArgMask.OpArgN or c == 0)
  return self:code(fs, luaP:CREATE_ABC(o, a, b, c), fs.ls.lastline)
end

------------------------------------------------------------------------
-- writes an instruction of type ABx
-- * calls luaK:code(), called by luaK:codeAsBx()
------------------------------------------------------------------------
function luaK:codeABx(fs, o, a, bc)
  assert(luaP:getOpMode(o) == luaP.OpMode.iABx or
             luaP:getOpMode(o) == luaP.OpMode.iAsBx)
  assert(luaP:getCMode(o) == luaP.OpArgMask.OpArgN)
  return self:code(fs, luaP:CREATE_ABx(o, a, bc), fs.ls.lastline)
end

------------------------------------------------------------------------
--
-- * used in (lparser) luaY:closelistfield(), luaY:lastlistfield()
------------------------------------------------------------------------
function luaK:setlist(fs, base, nelems, tostore)
  local c = math.floor((nelems - 1)/luaP.LFIELDS_PER_FLUSH) + 1
  local b = (tostore == luaY.LUA_MULTRET) and 0 or tostore
  assert(tostore ~= 0)
  if c <= luaP.MAXARG_C then
    self:codeABC(fs, "OP_SETLIST", base, b, c)
  else
    self:codeABC(fs, "OP_SETLIST", base, b, 0)
    self:code(fs, luaP:CREATE_Inst(c), fs.ls.lastline)
  end
  fs.freereg = base + 1  -- free registers with list values
end

return function(a) luaY = a return luaK end