enable compiling and add compile test

This commit is contained in:
Peter McGoron 2023-02-11 14:28:43 +00:00
parent 98bf49d931
commit 582a0a2c9a
4 changed files with 164 additions and 72 deletions

View File

@ -13,6 +13,20 @@ class CompileRet(Enum):
LABEL_OVERFLOW = 7 LABEL_OVERFLOW = 7
TYPE_ERROR = 8 TYPE_ERROR = 8
CLEARED_INSTRUCTION = 9 CLEARED_INSTRUCTION = 9
PROGRAM_OVERFLOW = 10
class RunRet(Enum):
CONTINUE = 0
SYSCALL = 1
STOP = 2
STACK_OVERFLOW = 3
STACK_UNDERFLOW = 4
RUN_LABEL_OVERFLOW = 5
REGISTER_OVERFLOW = 6
UNKNOWN_OPCODE = 7
def is_halt(self):
return not (self == RunRet.CONTINUE or self == RunRet.SYSCALL)
class CIns(Structure): class CIns(Structure):
_fields_ = [("opcode", c_int), _fields_ = [("opcode", c_int),
@ -76,6 +90,17 @@ class Environment:
ret = dll.creole_compile(byref(self.cenv), byref(rd)) ret = dll.creole_compile(byref(self.cenv), byref(rd))
return CompileRet(ret) return CompileRet(ret)
def syscall(self, sc):
pass
def __call__(self):
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)
return ret
class CParseLineException(Exception): class CParseLineException(Exception):
pass pass
def parse_line(line): def parse_line(line):

View File

@ -98,7 +98,6 @@ class PopTest(unittest.TestCase):
def test_compile_throw_pop_literal(self): def test_compile_throw_pop_literal(self):
p = Program() p = Program()
ex = ffi.Environment()
with self.assertRaises(TypecheckException) as cm: with self.assertRaises(TypecheckException) as cm:
p.parse_asm_line("pop 6") p.parse_asm_line("pop 6")
self.assertEqual(cm.exception.argtype, ArgType.REG) self.assertEqual(cm.exception.argtype, ArgType.REG)
@ -108,7 +107,6 @@ class PopTest(unittest.TestCase):
def test_compile_throw_pop_label(self): def test_compile_throw_pop_label(self):
p = Program() p = Program()
ex = ffi.Environment()
with self.assertRaises(TypecheckException) as cm: with self.assertRaises(TypecheckException) as cm:
p.parse_asm_line("pop l9") p.parse_asm_line("pop l9")
self.assertEqual(cm.exception.argtype, ArgType.REG) self.assertEqual(cm.exception.argtype, ArgType.REG)
@ -128,45 +126,22 @@ class PopTest(unittest.TestCase):
p = Program() p = Program()
with self.assertRaises(TypecheckLenException) as cm: with self.assertRaises(TypecheckLenException) as cm:
p.parse_asm_line("push") p.parse_asm_line("push")
self.assertEqual(cm.exception.opcode, 2) self.assertEqual(cm.exception.opcode, 1)
self.assertEqual(cm.exception.insargs, []) self.assertEqual(cm.exception.insargs, [])
self.assertEqual(cm.exception.argtypelen, 1) self.assertEqual(cm.exception.argtypelen, 1)
class ProgramTest(unittest.TestCase): class ProgramTest(unittest.TestCase):
def test_two(self): def test_exec_simple_reg(self):
p = Program() p = Program()
p.parse_asm_line("PUSH r1") p.parse_asm_line("push 5")
p.parse_asm_line("ADD r1 5 6") p.parse_asm_line("push 6")
b = p()
self.assertEqual(b, b'\x01\xC2\x81\x00\x03\xC2\x81\xC0\x85\xC0\x86\x00')
def test_label(self):
p = Program()
b_ex = bytes()
p.parse_asm_line("CLB l0")
b_ex = b_ex + b'\x07\xC0\x80\x00'
p.parse_asm_line("pop r0") p.parse_asm_line("pop r0")
b_ex = b_ex + b'\x02\xC2\x80\x00'
p.parse_asm_line("pop r1") p.parse_asm_line("pop r1")
b_ex = b_ex + b'\x02\xC2\x81\x00' ex = ffi.Environment()
p.parse_asm_line("mul r2 r0 r1") self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
b_ex = b_ex + b'\x04\xC2\x82\xC2\x80\xC2\x81\x00' self.assertEqual(ex(), ffi.RunRet.STOP)
p.parse_asm_line("push r2") self.assertEqual(ex.cenv.reg[0], 6)
b_ex = b_ex + b'\x01\xC2\x82\x00' self.assertEqual(ex.cenv.reg[1], 5)
p.parse_asm_line("push r2")
b_ex = b_ex + b'\x01\xC2\x82\x00'
p.parse_asm_line("jl l0 r2 10")
b_ex = b_ex + b'\x06\xC0\x80\xC2\x82\xC0\x8A\x00'
b = p()
self.assertEqual(b, b_ex)
def test_parse_imm(self):
p = Program()
p.parse_asm_line("add r1 23 3648")
ins = ffi.parse_line(p())
self.assertEqual(ins[0], Instruction.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__": if __name__ == "__main__":
unittest.main() unittest.main()

148
creole.c
View File

@ -346,14 +346,27 @@ creole_parse_line(struct creole_ins *ins, struct creole_reader *r)
* High level compiling interface * High level compiling interface
*************************************************************************/ *************************************************************************/
static int typecheck(enum creole_word_flag fl, enum creole_arg_type typ) static int valid_register(struct creole_env *env, int reg)
{
return reg < env->reglen;
}
static int valid_label(struct creole_env *env, int reg)
{
return reg < env->lablen;
}
static int typecheck(struct creole_env *env, int val,
enum creole_word_flag fl, enum creole_arg_type typ)
{ {
switch (typ) { switch (typ) {
case TYPE_IMM: return fl == CREOLE_IMMEDIATE; case TYPE_IMM: return fl == CREOLE_IMMEDIATE;
case TYPE_REG: return fl == CREOLE_REGISTER; case TYPE_REG: return fl == CREOLE_REGISTER
&& valid_register(env, val);
case TYPE_VAL: return fl == CREOLE_IMMEDIATE case TYPE_VAL: return fl == CREOLE_IMMEDIATE
|| fl == CREOLE_REGISTER; || fl == CREOLE_REGISTER;
case TYPE_LAB: return fl == CREOLE_IMMEDIATE; case TYPE_LAB: return fl == CREOLE_IMMEDIATE
&& valid_label(env, val);
default: return 0; default: return 0;
} }
} }
@ -364,7 +377,7 @@ static enum creole_compiler_ret typecheck_ins(struct creole_env *env,
unsigned i; unsigned i;
for (i = 0; i < opcode_info[ins->opcode].arglen; i++) { for (i = 0; i < opcode_info[ins->opcode].arglen; i++) {
if (!typecheck(ins->w_flags[i], if (!typecheck(env, ins->w[i], ins->w_flags[i],
opcode_info[ins->opcode].argtype[i])) opcode_info[ins->opcode].argtype[i]))
return CREOLE_TYPE_ERROR; return CREOLE_TYPE_ERROR;
} }
@ -373,11 +386,13 @@ static enum creole_compiler_ret typecheck_ins(struct creole_env *env,
static void clear_ins(struct creole_ins *i) static void clear_ins(struct creole_ins *i)
{ {
i->opcode = 0; const struct creole_ins blank = {0};
i->w[0] = i->w[1] = i->w[2] = i->w_flags[0] = *i = blank;
i->w_flags[1] = i->w_flags[2] = 0;
} }
/****
* Get rid of instructions that can be written out at compile time.
***/
static enum creole_compiler_ret static enum creole_compiler_ret
handle_compiletime_immediate(struct creole_env *env, handle_compiletime_immediate(struct creole_env *env,
struct creole_ins *cur_ins) struct creole_ins *cur_ins)
@ -433,61 +448,118 @@ creole_compile(struct creole_env *env, struct creole_reader *r)
return CREOLE_COMPILE_OK; return CREOLE_COMPILE_OK;
} }
#if 0 enum creole_run_ret creole_reg_write(struct creole_env *env,
creole_word w, unsigned reg)
static creole_word read_word(struct creole_ins *ins, int i)
{ {
if (env->w_flags[i] == CREOLE_REGISTER) if (!valid_register(env, reg))
return env->reg[env->w[i]]; return CREOLE_REGISTER_OVERFLOW;
else env->reg[reg] = w;
return env->w[i]; return CREOLE_STEP_CONTINUE;
} }
int creole_step(struct creole_env *env) enum creole_run_ret creole_reg_read(struct creole_env *env, creole_word *w,
unsigned reg)
{
if (!valid_register(env, reg))
return CREOLE_REGISTER_OVERFLOW;
*w = env->reg[reg];
return CREOLE_STEP_CONTINUE;
}
static enum creole_run_ret read_val(struct creole_env *env,
struct creole_ins *ins,
unsigned arg,
creole_word *w)
{
if (ins->w_flags[arg] == CREOLE_REGISTER) {
return creole_reg_read(env, w, ins->w[arg]);
} else {
*w = ins->w[arg];
}
return CREOLE_STEP_CONTINUE;
}
enum creole_run_ret creole_push(struct creole_env *env, creole_word w)
{
if (env->stkptr == env->stklen)
return CREOLE_STACK_OVERFLOW;
env->stk[env->stkptr++] = w;
return CREOLE_STEP_CONTINUE;
}
enum creole_run_ret creole_pop(struct creole_env *env, creole_word *w)
{
if (env->stkptr == 0)
return CREOLE_STACK_UNDERFLOW;
*w = env->stk[--env->stkptr];
return CREOLE_STEP_CONTINUE;
}
static enum creole_run_ret
check_label(struct creole_env *env, creole_word label)
{
return label < env->lablen
? CREOLE_STEP_CONTINUE
: CREOLE_RUN_LABEL_OVERFLOW;
}
#define check(fun) do { \
rcode = fun; \
if (rcode != CREOLE_STEP_CONTINUE) \
return rcode; \
} while(0)
enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc)
{ {
struct creole_ins *ins = env->prg + env->prgptr; struct creole_ins *ins = env->prg + env->prgptr;
creole_word a1, a2; creole_word a1, a2;
int rcode = CREOLE_STEP_CONTINUE;
if (env->prgptr == env->prgend) if (env->prgptr == env->prgend)
return CREOLE_STEP_STOP; return CREOLE_STEP_STOP;
env->prgptr++;
switch (ins->opcode) { switch (ins->opcode) {
case CREOLE_PUSH: case CREOLE_PUSH:
if (env->stkptr == env->stklen) check(read_val(env, ins, 0, &a1));
return CREOLE_STACK_OVERFLOW; check(creole_push(env, a1));
env->stk[env->stkptr++] = env->reg[env->w[0]];
break; break;
case CREOLE_POP: case CREOLE_POP:
if (env->stkptr == 0) check(creole_pop(env, &a1));
return CREOLE_STACK_OVERFLOW; check(creole_reg_write(env, a1, ins->w[0]));
env->reg[env->w[0]] = env->stk[--env->stkptr];
break; break;
case CREOLE_ADD: case CREOLE_ADD:
a1 = read_word(ins, 1); check(read_val(env, ins, 1, &a1));
a2 = read_word(ins, 2); check(read_val(env, ins, 2, &a2));
env->reg[env->w[0]] = a1 + a2; check(creole_reg_write(env, ins->w[0], a1 + a2));
break; break;
case CREOLE_MUL: case CREOLE_MUL:
a1 = read_word(ins, 1); check(read_val(env, ins, 1, &a1));
a2 = read_word(ins, 2); check(read_val(env, ins, 2, &a2));
env->reg[env->w[0]] = a1 * a2; check(creole_reg_write(env, ins->w[0], a1 * a2));
break; break;
case CREOLE_DIV: case CREOLE_DIV:
a1 = read_word(ins, 1); check(read_val(env, ins, 1, &a1));
a2 = read_word(ins, 2); check(read_val(env, ins, 2, &a2));
env->reg[env->w[0]] = a1 / a2; check(creole_reg_write(env, ins->w[0], a1 / a2));
break; break;
case CREOLE_JL: case CREOLE_JL:
a1 = read_word(ins, 1); check(read_val(env, ins, 1, &a1));
a2 = read_word(ins, 2); check(read_val(env, ins, 2, &a2));
check(check_label(env, ins->w[0]));
if (a1 < a2) if (a1 < a2)
env->prgptr = env->lab[env->w[0]]; env->prgptr = env->lab[ins->w[0]];
break; break;
case SYS: case CREOLE_SYS:
a1 = read_word(ins, 1); check(read_val(env, ins, 0, sc));
/* do syscall */ rcode = CREOLE_STEP_SYSCALL;
break; break;
default:
rcode = CREOLE_STEP_UNKNOWN_OPCODE;
} }
env->prgptr++;
return rcode;
} }
#endif
#undef check

View File

@ -47,6 +47,18 @@ enum creole_compiler_ret {
CREOLE_COMPILE_RET_LEN CREOLE_COMPILE_RET_LEN
}; };
enum creole_run_ret {
CREOLE_STEP_CONTINUE,
CREOLE_STEP_SYSCALL,
CREOLE_STEP_STOP,
CREOLE_STACK_OVERFLOW,
CREOLE_STACK_UNDERFLOW,
CREOLE_RUN_LABEL_OVERFLOW,
CREOLE_REGISTER_OVERFLOW,
CREOLE_STEP_UNKNOWN_OPCODE,
CREOLE_RUN_RET_LEN
};
struct creole_ins { struct creole_ins {
enum creole_opcode opcode; enum creole_opcode opcode;
unsigned char w_flags[3]; unsigned char w_flags[3];
@ -79,4 +91,12 @@ creole_parse_line(struct creole_ins *ins, struct creole_reader *r);
enum creole_compiler_ret enum creole_compiler_ret
creole_compile(struct creole_env *env, struct creole_reader *r); creole_compile(struct creole_env *env, struct creole_reader *r);
enum creole_run_ret creole_reg_write(struct creole_env *env, creole_word w,
unsigned reg);
enum creole_run_ret creole_reg_read(struct creole_env *env, creole_word *w,
unsigned reg);
enum creole_run_ret creole_push(struct creole_env *env, creole_word w);
enum creole_run_ret creole_pop(struct creole_env *env, creole_word *w);
enum creole_run_ret creole_step(struct creole_env *env, creole_word *sc);
#endif /* CREOLE_H */ #endif /* CREOLE_H */