diff --git a/asm/creole.py b/asm/creole.py index 8e87b58..c1045c5 100644 --- a/asm/creole.py +++ b/asm/creole.py @@ -24,16 +24,32 @@ class ArgType(Enum): def typecheck(self, s): t = ArgType.gettype(s) if self == ArgType.VAL: - return t[0] == ArgType.REG or t[0] == ArgType.IMM + if t[0] == ArgType.REG or t[0] == ArgType.IMM: + return t + else: + return None + elif t[0] == self: + return t else: - return t[0] == self + return None class OpcodeException(Exception): pass class TypecheckLenException(Exception): - pass + def __init__(self, opcode, insargs, argtypelen): + self.opcode = opcode + self.insargs = insargs + self.argtypelen = argtypelen + def __str__(self): + return f'arguments {insargs} to opcode {self.opcode} not length {self.argtypelen}' class TypecheckException(Exception): - pass + def __init__(self, argtype, sarg, i, opcode): + self.argtype = argtype + self.sarg = sarg + self.i = i + self.opcode = opcode + def __str__(self): + return f'opcode {self.opcode} has invalid value {self.sarg} (expected {self.argtype} in position {self.i}' class Instruction: def __init__(self, opcode, argtypes): if opcode > 0x7F or opcode < 0: @@ -44,12 +60,15 @@ class Instruction: def typecheck(self, sargs): rargs = [] if len(sargs) != len(self.argtypes): - raise TypecheckLenException(sargs, self.argtypes) + raise TypecheckLenException(self.opcode, sargs, + len(self.argtypes)) for i in range(0, len(sargs)): - if not self.argtypes[i].typecheck(sargs[i]): + t = self.argtypes[i].typecheck(sargs[i]) + if t is None: raise TypecheckException(self.argtypes[i], - sargs[i]) - rargs.append(ArgType.gettype(sargs[i])) + sargs[i], + i, self.opcode) + rargs.append(t) return rargs instructions = { diff --git a/asm/ffi.py b/asm/ffi.py index 87f6275..7468996 100644 --- a/asm/ffi.py +++ b/asm/ffi.py @@ -1,6 +1,18 @@ from ctypes import * +from enum import Enum dll = CDLL("./libcreole.so") +class CompileRet(Enum): + COMPILE_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 + class CIns(Structure): _fields_ = [("opcode", c_int), ("w_flags", c_ubyte * 3), diff --git a/asm/test.py b/asm/test.py index dc07655..b9d6520 100644 --- a/asm/test.py +++ b/asm/test.py @@ -2,17 +2,57 @@ from creole import * import unittest import ffi -class ProgramTest(unittest.TestCase): - def test_oneline(self): +class PushTest(unittest.TestCase): + def test_parse_push_reg(self): p = Program() - p.parse_asm_line("PUSH r0") + p.parse_asm_line("push r5") b = p() self.assertEqual(b, b'\x01\xC2\x80\x00') + ins = ffi.parse_line(b) + self.assertEqual(ins[0], instructions["push"].opcode) + self.assertEqual(ins[1][0], (1,5)) + + def test_parse_push_catch_typecheck_push_imm(self): + p = Program() + with self.assertRaises(TypecheckException) as cm: + p.parse_asm_line("push 0") + self.assertEqual(cm.exception.argtype, ArgType.REG) + self.assertEqual(cm.exception.sarg, '0') + self.assertEqual(cm.exception.i, 0) + self.assertEqual(cm.exception.opcode, 1) + + def test_parse_push_catch_typecheck_push_lab(self): + p = Program() + with self.assertRaises(TypecheckException) as cm: + p.parse_asm_line("push l0") + self.assertEqual(cm.exception.argtype, ArgType.REG) + self.assertEqual(cm.exception.sarg, 'l0') + self.assertEqual(cm.exception.i, 0) + self.assertEqual(cm.exception.opcode, 1) + + def test_parse_push_catch_typecheck_argument_overflow(self): + p = Program() + with self.assertRaises(TypecheckLenException) as cm: + p.parse_asm_line("push r1 r2") + self.assertEqual(cm.exception.opcode, 1) + self.assertEqual(cm.exception.insargs, ["r1", "r2"]) + self.assertEqual(cm.exception.argtypelen, 1) + + def test_parse_push_catch_typecheck_argument_underflow(self): + p = Program() + with self.assertRaises(TypecheckLenException) as cm: + p.parse_asm_line("push") + self.assertEqual(cm.exception.opcode, 1) + self.assertEqual(cm.exception.insargs, []) + self.assertEqual(cm.exception.argtypelen, 1) + def test_large_reg(self): p = Program(regnum=0x8000000) p.parse_asm_line("PUSH r134217727") b = p() self.assertEqual(b, b'\x01\xFC\x87\xbf\xbf\xbf\xbf\x00') + +class ProgramTest(unittest.TestCase): def test_two(self): p = Program() p.parse_asm_line("PUSH r1") @@ -39,12 +79,14 @@ class ProgramTest(unittest.TestCase): b = p() self.assertEqual(b, b_ex) - def test_parse_reg(self): + def test_parse_imm(self): p = Program() - p.parse_asm_line("push r5") + p.parse_asm_line("add r1 23 3648") ins = ffi.parse_line(p()) - self.assertEqual(ins[0], instructions["push"].opcode) - self.assertEqual(ins[1][0], (1,5)) + self.assertEqual(ins[0], instructions["add"].opcode) + self.assertEqual(ins[1][0], (1,1)) + self.assertEqual(ins[1][1], (0,23)) + self.assertEqual(ins[1][2], (0,3648)) if __name__ == "__main__": unittest.main()