struct VM<'p> { program: &'p Program, block: u32, ip: u32, stack: Vec, status: Status } enum Status { Running, AwaitSyscall(Term), Crash(&'static str), Exit(Term), } #[derive(Clone, Debug)] struct Term { atom: u32, args: Vec, } struct Program { blocks: Vec } struct Block { instructions: Vec } enum Instruction { // push Atom to stack, optionally with the current `stack` appended to it Push { atom: u32, cc: bool }, // do a binary operation between the top two stack items BinOp { op: fn(Term, Term) -> Result }, // expand the top stack item to the stack, failing if its len is not `len` Expand { len: u32 }, // jump to the given block. its stack is the top stack item Call { block: u32 }, // system call. pass the top stack item. get a stack item back Syscall { syscall: u32 }, // kill the program Crash(&'static str), // end the program, popping the top stack item Exit } const SYSCALL_DBG: u32 = 0x1000; fn main() { host_program(&Program { blocks: vec![ Block { instructions: vec![ Instruction::Expand { len: 0 }, Instruction::Push { atom: SYSCALL_DBG, cc: false }, Instruction::Push { atom: 0xdead0000, cc: false }, Instruction::Push { atom: 0x0000ba75, cc: false }, Instruction::BinOp { op: binop_add }, Instruction::BinOp { op: binop_append }, Instruction::Syscall { syscall: SYSCALL_DBG }, Instruction::Push { atom: 0x0, cc: false }, Instruction::Exit, ]} ] }).unwrap(); } fn host_program(p: &Program) -> Result { let mut vm = VM::start(p); loop { let status = vm.step(); match status { Status::Running => {} Status::AwaitSyscall(sc) => { match sc.atom { SYSCALL_DBG => { println!("debug print: {:08x?}", sc.args[0].atom); vm.complete_syscall(None) } _ => { return Err(format!("unknown syscall: {}", sc.atom)) } } } Status::Crash(err) => { return Err(err.to_string()); } Status::Exit(t) => { return Ok(t.clone()); } } } } impl<'p> VM<'p> { pub fn start(program: &'p Program) -> VM<'p> { VM { program, block: 0, ip: 0, stack: vec![Term { atom: 0, args: vec![] }], status: Status::Running, } } pub fn step<'a>(&'a mut self) -> &'a Status { let error = match self.internal_step() { Ok(_) => { return &self.status; } Err(e) => e }; self.status = Status::Crash(error); return &self.status } fn internal_step(&mut self) -> Result<(), &'static str> { if let Status::Running = self.status { } else { return Ok(()); } let instruction = self.program .blocks.get(self.block as usize).ok_or("block must exist")? .instructions.get(self.ip as usize).ok_or("instruction must exist")?; self.ip += 1; match instruction { &Instruction::Push { atom, cc } => { let mut term = Term { atom, args: vec![] }; if cc { term.args.extend(self.stack.drain(..)) } self.stack.push(term) } Instruction::BinOp { op } => { let t1 = self.stack.pop().ok_or("t1 must be present")?; let t0 = self.stack.pop().ok_or("t0 must be present")?; self.stack.push(op(t0, t1)?); } &Instruction::Expand { len } => { let term = self.stack.pop().ok_or("term must be present")?; if term.args.len() != len as usize { panic!("term must have {} args", len) } } &Instruction::Call { block } => { let invocation = self.stack.pop().ok_or("invocation must be present")?; if invocation.atom != block { panic!("invocation doesn't match block"); } self.stack.clear(); self.stack.push(invocation); self.block = block; self.ip = 0; } &Instruction::Syscall { syscall } => { let invocation = self.stack.pop().ok_or("invocation must be present")?; if invocation.atom != syscall { panic!("invocation doesn't match syscall"); } self.status = Status::AwaitSyscall(invocation) } Instruction::Crash(c) => { panic!("crashed: {}", c) } Instruction::Exit => { let exit = self.stack.pop().ok_or("exit arg must be present")?; self.status = Status::Exit(exit) } } Ok(()) } pub fn complete_syscall(&mut self, result: Option) { match &self.status { Status::AwaitSyscall(_) => { if let Some(t) = result { self.stack.push(t); } self.status = Status::Running; }, // continue _ => { self.status = Status::Crash("tried to complete a syscall while not awaiting one") } } } } fn binop_append(mut t0: Term, mut t1: Term) -> Result { t0.args.push(t1); Ok(t0) } fn binop_add(mut t0: Term, mut t1: Term) -> Result { t0.atom += t1.atom; Ok(t0) }