161 lines
3.6 KiB
Python
161 lines
3.6 KiB
Python
from ctypes import *
|
|
import creole
|
|
from enum import Enum
|
|
dll = CDLL("./libcreole.so")
|
|
|
|
class CompileRet(Enum):
|
|
OK = 0
|
|
OPCODE_READ_ERROR = 1
|
|
OPCODE_MALFORMED = 2
|
|
ARG_READ_ERROR = 3
|
|
ARG_MALFORMED = 4
|
|
LAST_READ_ERROR = 5
|
|
LAST_MALFORMED = 6
|
|
LABEL_OVERFLOW = 7
|
|
TYPE_ERROR = 8
|
|
CLEARED_INSTRUCTION = 9
|
|
PROGRAM_OVERFLOW = 10
|
|
|
|
class RunRet(Enum):
|
|
CONTINUE = 0
|
|
SYSCALL = 1
|
|
STOP = 2
|
|
STACK_OVERFLOW = 3
|
|
STACK_UNDERFLOW = 4
|
|
LABEL_OVERFLOW = 5
|
|
REGISTER_OVERFLOW = 6
|
|
UNKNOWN_OPCODE = 7
|
|
DIVIDE_BY_ZERO = 8
|
|
HIGH_BIT_MALFORMED = 9
|
|
|
|
def is_halt(self):
|
|
return not (self == RunRet.CONTINUE or self == RunRet.SYSCALL)
|
|
|
|
class CIns(Structure):
|
|
_fields_ = [("opcode", c_int),
|
|
("w_flags", c_ubyte * 3),
|
|
("w", c_uint * 3)]
|
|
|
|
class CReader(Structure):
|
|
_fields_ = [("p", POINTER(c_ubyte)),
|
|
("left", c_size_t)]
|
|
def make_uchar_buf(s):
|
|
buf = (c_ubyte * len(s))()
|
|
buf[:] = s[:]
|
|
return buf
|
|
def make_reader(s):
|
|
buf = make_uchar_buf(s)
|
|
return CReader(buf, len(s))
|
|
|
|
class CEnv(Structure):
|
|
_fields_ = [
|
|
("reg", POINTER(c_uint)),
|
|
("reglen", c_size_t),
|
|
("lab", POINTER(c_size_t)),
|
|
("lablen", c_size_t),
|
|
("stk", POINTER(c_uint)),
|
|
("stkptr", c_size_t),
|
|
("stklen", c_size_t),
|
|
("prg", POINTER(CIns)),
|
|
("prgptr", c_size_t),
|
|
("prgend", c_size_t),
|
|
("prglen", c_size_t)
|
|
]
|
|
|
|
class RegisterOverflowError(Exception):
|
|
def __init__(self, reg):
|
|
self.reg = reg
|
|
class StackOverflowError(Exception):
|
|
def __init__(self, pos):
|
|
self.pos = pos
|
|
class InvalidSyscallError(Exception):
|
|
def __init__(self, sc):
|
|
self.sc = sc
|
|
class CompileError(Exception):
|
|
def __init__(self, r):
|
|
self.r = r
|
|
|
|
class Environment:
|
|
def getreg(self, reg, signed=False):
|
|
""" Get the value at the register.
|
|
|
|
:param reg: Register number.
|
|
:param signed: If the register value should be interpreted
|
|
as signed.
|
|
"""
|
|
if reg >= self.cenv.reglen or reg < 0:
|
|
raise RegisterOverflowError(r)
|
|
if signed:
|
|
return creole.from_2c(self.cenv.reg[reg])
|
|
else:
|
|
return self.cenv.reg[reg]
|
|
def getstk(self, stk):
|
|
""" Get the value at the stack position.
|
|
|
|
:param reg: Register number.
|
|
:param signed: If the stack value should be interpreted
|
|
as signed.
|
|
"""
|
|
if stk >= self.cenv.stklen or stk < 0:
|
|
raise StackOverflowError(r)
|
|
return creole.from_2c(self.cenv.stk[stk])
|
|
|
|
def __init__(self, prog=None, reglen=32, lablen=32, stklen=4096, prglen=4096):
|
|
cenv = CEnv()
|
|
cenv.reglen = reglen
|
|
cenv.reg = (c_uint * reglen)()
|
|
|
|
cenv.lablen = lablen
|
|
cenv.lab = (c_size_t * lablen)()
|
|
|
|
cenv.stklen = stklen
|
|
cenv.stk = (c_uint * stklen)()
|
|
cenv.stkptr = 0
|
|
|
|
cenv.prglen = prglen
|
|
cenv.prg = (CIns * prglen)()
|
|
cenv.prgptr = 0
|
|
cenv.prgend = 0
|
|
|
|
self.cenv = cenv
|
|
if prog is not None:
|
|
r = self.load(prog)
|
|
if r is not CompileRet.OK:
|
|
raise CompileError(r)
|
|
|
|
def restart(self):
|
|
self.cenv.stkptr = 0
|
|
self.cenv.prgptr = 0
|
|
self.cenv.prgend = 0
|
|
|
|
def load(self, prog):
|
|
rd = make_reader(prog)
|
|
self.restart()
|
|
ret = dll.creole_compile(byref(self.cenv), byref(rd))
|
|
return CompileRet(ret)
|
|
|
|
def syscall(self, sc):
|
|
raise InvalidSyscallError(sc)
|
|
def __call__(self, debug=False):
|
|
sc = c_size_t()
|
|
ret = RunRet.CONTINUE
|
|
while not ret.is_halt():
|
|
ret = RunRet(dll.creole_step(byref(self.cenv), byref(sc)))
|
|
if ret == RunRet.SYSCALL:
|
|
self.syscall(sc)
|
|
if debug:
|
|
print(self.cenv.reg[0])
|
|
return ret
|
|
|
|
class CParseLineException(Exception):
|
|
pass
|
|
def parse_line(line):
|
|
rd = make_reader(line)
|
|
ins = CIns()
|
|
|
|
ret = dll.creole_parse_line(byref(ins), byref(rd))
|
|
|
|
if ret != CompileRet.OK.value:
|
|
raise CParseLineException(CompileRet(ret).name)
|
|
return (ins.opcode, list(zip(ins.w_flags, ins.w)))
|