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
TYPE_ERROR = 8
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):
_fields_ = [("opcode", c_int),
@ -76,6 +90,17 @@ class Environment:
ret = dll.creole_compile(byref(self.cenv), byref(rd))
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):
pass
def parse_line(line):

View File

@ -98,7 +98,6 @@ class PopTest(unittest.TestCase):
def test_compile_throw_pop_literal(self):
p = Program()
ex = ffi.Environment()
with self.assertRaises(TypecheckException) as cm:
p.parse_asm_line("pop 6")
self.assertEqual(cm.exception.argtype, ArgType.REG)
@ -108,7 +107,6 @@ class PopTest(unittest.TestCase):
def test_compile_throw_pop_label(self):
p = Program()
ex = ffi.Environment()
with self.assertRaises(TypecheckException) as cm:
p.parse_asm_line("pop l9")
self.assertEqual(cm.exception.argtype, ArgType.REG)
@ -128,45 +126,22 @@ class PopTest(unittest.TestCase):
p = Program()
with self.assertRaises(TypecheckLenException) as cm:
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.argtypelen, 1)
class ProgramTest(unittest.TestCase):
def test_two(self):
def test_exec_simple_reg(self):
p = Program()
p.parse_asm_line("PUSH r1")
p.parse_asm_line("ADD r1 5 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("push 5")
p.parse_asm_line("push 6")
p.parse_asm_line("pop r0")
b_ex = b_ex + b'\x02\xC2\x80\x00'
p.parse_asm_line("pop r1")
b_ex = b_ex + b'\x02\xC2\x81\x00'
p.parse_asm_line("mul r2 r0 r1")
b_ex = b_ex + b'\x04\xC2\x82\xC2\x80\xC2\x81\x00'
p.parse_asm_line("push r2")
b_ex = b_ex + b'\x01\xC2\x82\x00'
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))
ex = ffi.Environment()
self.assertEqual(ex.load(p()), ffi.CompileRet.OK)
self.assertEqual(ex(), ffi.RunRet.STOP)
self.assertEqual(ex.cenv.reg[0], 6)
self.assertEqual(ex.cenv.reg[1], 5)
if __name__ == "__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
*************************************************************************/
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) {
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
|| fl == CREOLE_REGISTER;
case TYPE_LAB: return fl == CREOLE_IMMEDIATE;
case TYPE_LAB: return fl == CREOLE_IMMEDIATE
&& valid_label(env, val);
default: return 0;
}
}
@ -364,7 +377,7 @@ static enum creole_compiler_ret typecheck_ins(struct creole_env *env,
unsigned 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]))
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)
{
i->opcode = 0;
i->w[0] = i->w[1] = i->w[2] = i->w_flags[0] =
i->w_flags[1] = i->w_flags[2] = 0;
const struct creole_ins blank = {0};
*i = blank;
}
/****
* Get rid of instructions that can be written out at compile time.
***/
static enum creole_compiler_ret
handle_compiletime_immediate(struct creole_env *env,
struct creole_ins *cur_ins)
@ -433,61 +448,118 @@ creole_compile(struct creole_env *env, struct creole_reader *r)
return CREOLE_COMPILE_OK;
}
#if 0
static creole_word read_word(struct creole_ins *ins, int i)
enum creole_run_ret creole_reg_write(struct creole_env *env,
creole_word w, unsigned reg)
{
if (env->w_flags[i] == CREOLE_REGISTER)
return env->reg[env->w[i]];
else
return env->w[i];
if (!valid_register(env, reg))
return CREOLE_REGISTER_OVERFLOW;
env->reg[reg] = w;
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;
creole_word a1, a2;
int rcode = CREOLE_STEP_CONTINUE;
if (env->prgptr == env->prgend)
return CREOLE_STEP_STOP;
env->prgptr++;
switch (ins->opcode) {
case CREOLE_PUSH:
if (env->stkptr == env->stklen)
return CREOLE_STACK_OVERFLOW;
env->stk[env->stkptr++] = env->reg[env->w[0]];
check(read_val(env, ins, 0, &a1));
check(creole_push(env, a1));
break;
case CREOLE_POP:
if (env->stkptr == 0)
return CREOLE_STACK_OVERFLOW;
env->reg[env->w[0]] = env->stk[--env->stkptr];
check(creole_pop(env, &a1));
check(creole_reg_write(env, a1, ins->w[0]));
break;
case CREOLE_ADD:
a1 = read_word(ins, 1);
a2 = read_word(ins, 2);
env->reg[env->w[0]] = a1 + a2;
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(creole_reg_write(env, ins->w[0], a1 + a2));
break;
case CREOLE_MUL:
a1 = read_word(ins, 1);
a2 = read_word(ins, 2);
env->reg[env->w[0]] = a1 * a2;
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(creole_reg_write(env, ins->w[0], a1 * a2));
break;
case CREOLE_DIV:
a1 = read_word(ins, 1);
a2 = read_word(ins, 2);
env->reg[env->w[0]] = a1 / a2;
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(creole_reg_write(env, ins->w[0], a1 / a2));
break;
case CREOLE_JL:
a1 = read_word(ins, 1);
a2 = read_word(ins, 2);
check(read_val(env, ins, 1, &a1));
check(read_val(env, ins, 2, &a2));
check(check_label(env, ins->w[0]));
if (a1 < a2)
env->prgptr = env->lab[env->w[0]];
env->prgptr = env->lab[ins->w[0]];
break;
case SYS:
a1 = read_word(ins, 1);
/* do syscall */
case CREOLE_SYS:
check(read_val(env, ins, 0, sc));
rcode = CREOLE_STEP_SYSCALL;
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
};
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 {
enum creole_opcode opcode;
unsigned char w_flags[3];
@ -79,4 +91,12 @@ creole_parse_line(struct creole_ins *ins, struct creole_reader *r);
enum creole_compiler_ret
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 */