lp: added __parseNewTable(), better table pseudo-code

This commit is contained in:
CPunch 2022-08-16 00:12:26 -05:00
parent a248cc4807
commit 19bed999ee
2 changed files with 86 additions and 35 deletions

View File

@ -12,48 +12,69 @@ Lua has a relatively small instruction set (only 38 different opcodes!). This ma
```sh ```sh
> cat example.lua && luac5.1 -o example.luac example.lua > cat example.lua && luac5.1 -o example.luac example.lua
local tbl = {"Hello", "World"} local tbl = {"He", "llo", " ", "Wo", "rld", "!"}
local str = ""
print(tbl[1] .. " " .. tbl[2] .. ": " .. 2.5) for i = 1, #tbl do
str = str .. tbl[i]
end
print(str)
> python main.py example.luac > python main.py example.luac
example.luac example.luac
==== [[example.lua's constants]] ==== ==== [[example.lua's constants]] ====
0: [STRING] Hello 0: [STRING] He
1: [STRING] World 1: [STRING] llo
2: [STRING] print 2: [STRING]
3: [NUMBER] 1.0 3: [STRING] Wo
4: [STRING] 4: [STRING] rld
5: [NUMBER] 2.0 5: [STRING] !
6: [STRING] : 6: [STRING]
7: [NUMBER] 2.5 7: [NUMBER] 1.0
8: [STRING] print
==== [[example.lua's locals]] ==== ==== [[example.lua's locals]] ====
R[0]: tbl R[0]: tbl
R[1]: str
R[2]: (for index)
R[3]: (for limit)
R[4]: (for step)
R[5]: i
==== [[example.lua's dissassembly]] ==== ==== [[example.lua's dissassembly]] ====
[ 0] NEWTABLE : 0 2 0 ; [ 0] NEWTABLE : 0 6 0 ;
[ 1] LOADK : R[1] K[0] ; load "Hello" into R[1] [ 1] LOADK : R[1] K[0] ; load "He" into R[1]
[ 2] LOADK : R[2] K[1] ; load "World" into R[2] [ 2] LOADK : R[2] K[1] ; load "llo" into R[2]
[ 3] SETLIST : 0 2 1 ; [ 3] LOADK : R[3] K[2] ; load " " into R[3]
[ 4] GETGLOBAL : R[1] K[2] ; move _G["print"] into R[1] [ 4] LOADK : R[4] K[3] ; load "Wo" into R[4]
[ 5] GETTABLE : R[2] 0 K[3] ; [ 5] LOADK : R[5] K[4] ; load "rld" into R[5]
[ 6] LOADK : R[3] K[4] ; load " " into R[3] [ 6] LOADK : R[6] K[5] ; load "!" into R[6]
[ 7] GETTABLE : R[4] 0 K[5] ; [ 7] SETLIST : 0 6 1 ;
[ 8] LOADK : R[5] K[6] ; load ": " into R[5] [ 8] LOADK : R[1] K[6] ; load "" into R[1]
[ 9] LOADK : R[6] K[7] ; load 2.5 into R[6] [ 9] LOADK : R[2] K[7] ; load 1 into R[2]
[ 10] CONCAT : 2 2 6 ; concat 5 values from R[2] to R[6], store into R[2] [ 10] LEN : 3 0 0 ;
[ 11] CALL : 1 2 1 ; [ 11] LOADK : R[4] K[7] ; load 1 into R[4]
[ 12] RETURN : 0 1 0 ; [ 12] FORPREP : R[2] 3 ;
[ 13] MOVE : 6 1 0 ; move R[1] into R[6]
[ 14] GETTABLE : R[7] 0 R[5] ;
[ 15] CONCAT : 1 6 7 ; concat 2 values from R[6] to R[7], store into R[1]
[ 16] FORLOOP : R[2] -4 ;
[ 17] GETGLOBAL : R[2] K[8] ; move _G["print"] into R[2]
[ 18] MOVE : 3 1 0 ; move R[1] into R[3]
[ 19] CALL : 2 2 1 ;
[ 20] RETURN : 0 1 0 ;
==== [[example.lua's pseudo-code]] ==== ==== [[example.lua's pseudo-code]] ====
local tbl = {} local tbl = {"He", "llo", " ", "Wo", "rld", "!", }
tbl[1] = "Hello" for i = 1, #tbl, 1 do
tbl[2] = "World" local str = str .. tbl[i]
print(tbl[1] .. " " .. tbl[2] .. ": " .. 2.5) end
print(str)
``` ```

View File

@ -29,7 +29,12 @@ class _Line:
self.scope = scope self.scope = scope
def isValidLocal(ident: str) -> bool: def isValidLocal(ident: str) -> bool:
for c in ident: # has to start with an alpha or _
if ident[0] not in "abcdefghijklmnopqrstuvwxyz_":
return False
# then it can be alphanum or _
for c in ident[1:]:
if c not in "abcdefghijklmnopqrstuvwxyz1234567890_": if c not in "abcdefghijklmnopqrstuvwxyz1234567890_":
return False return False
@ -200,6 +205,9 @@ class LuaDecomp:
self.__addExpr("end") self.__addExpr("end")
self.scope.pop() self.scope.pop()
self.__endStatement()
# =====================================[[ Instructions ]]====================================== # =====================================[[ Instructions ]]======================================
def __emitOperand(self, a: int, b: str, c: str, op: str) -> None: def __emitOperand(self, a: int, b: str, c: str, op: str) -> None:
@ -254,6 +262,33 @@ class LuaDecomp:
else: else:
return self.__getReg(rk) return self.__getReg(rk)
# walk & peak ahead NEWTABLE
def __parseNewTable(self, indx: int):
# TODO: parse SETTABLE too?
tblOps = [Opcodes.LOADK, Opcodes.SETLIST]
instr = self.__getNextInstr()
cachedRegs = self.top
tbl = "{"
while instr.opcode in tblOps:
if instr.opcode == Opcodes.LOADK: # operate on registers
cachedRegs[instr.A] = self.chunk.getConstant(instr.B).toCode()
elif instr.opcode == Opcodes.SETLIST:
numElems = instr.B
for i in range(numElems):
tbl += "%s, " % cachedRegs[instr.A + i + 1]
self.pc += 1
instr = self.__getNextInstr()
tbl += "}"
# i use forceLocal here even though i don't know *for sure* that the register is a local.
# this does help later though if the table is reused (which is 99% of the time). the other 1%
# only affects syntax and may look a little weird but is fine and equivalent non-the-less
self.__setReg(indx, tbl, forceLocal=True)
self.__endStatement()
def parseInstr(self): def parseInstr(self):
instr = self.__getCurrInstr() instr = self.__getCurrInstr()
@ -279,12 +314,7 @@ class LuaDecomp:
self.__addExpr(self.__getReg(instr.A) + "[" + self.__readRK(instr.B) + "] = " + self.__readRK(instr.C)) self.__addExpr(self.__getReg(instr.A) + "[" + self.__readRK(instr.B) + "] = " + self.__readRK(instr.C))
self.__endStatement() self.__endStatement()
elif instr.opcode == Opcodes.NEWTABLE: elif instr.opcode == Opcodes.NEWTABLE:
# i use forceLocal here even though i don't know *for sure* that the register is a local. self.__parseNewTable(instr.A)
# this does help later though if the table is populated (which is 99% of the time). the other 1%
# only affects syntax and may look a little weird but is fine and equivalent non-the-less
# TODO: make this better
self.__setReg(instr.A, "{}", forceLocal=True)
elif instr.opcode == Opcodes.ADD: elif instr.opcode == Opcodes.ADD:
self.__emitOperand(instr.A, self.__readRK(instr.B), self.__readRK(instr.C), " + ") self.__emitOperand(instr.A, self.__readRK(instr.B), self.__readRK(instr.C), " + ")
elif instr.opcode == Opcodes.SUB: elif instr.opcode == Opcodes.SUB:
@ -302,7 +332,7 @@ class LuaDecomp:
elif instr.opcode == Opcodes.NOT: elif instr.opcode == Opcodes.NOT:
self.__setReg(instr.A, "not " + self.__getReg(instr.B)) self.__setReg(instr.A, "not " + self.__getReg(instr.B))
elif instr.opcode == Opcodes.LEN: elif instr.opcode == Opcodes.LEN:
self.__setReg(instr.A, "#" + self.__getCurrInstr(instr.B)) self.__setReg(instr.A, "#" + self.__getReg(instr.B))
elif instr.opcode == Opcodes.CONCAT: elif instr.opcode == Opcodes.CONCAT:
count = instr.C-instr.B+1 count = instr.C-instr.B+1
concatStr = "" concatStr = ""