# Copyright (c) 2023 Peter McGoron # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 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, signed=False): """ 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) if signed: return creole.from_2c(self.cenv.stk[stk]) else: return self.cenv.stk[stk] def pop(self): if stk == 0: return None stk = stk - 1 return 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)))