I've been playing around with the idea of writing an embedded extension language that doesn't ever call host functions directly. Instead, invoking a host function traps the virtual processor and returns control to the host, which is then free to directly alter the processor's internal state.
The basic structure would look something like this, but I'm not particularly happy with some of the ergonomics. In particular, return ControlFlow::Break(Trap::Err(anyhow!(...)))
feels too verbose for what should be a common operation; is there a way to use ?
for this?
Alternatively, would I be better off returning something like Option
or Result
instead of ControlFlow
?
impl Processor {
fn step(&mut self) -> ControlFlow<Trap> {
let instr = match self.code.get(self.pc) {
Some(i) => i,
None => return ControlFlow::Break(Trap::Err(anyhow!("PC out of range: {}", self.pc))),
};
self.pc += 1;
return match instr {
Instr::PushAtom(x) => {
self.stack.push(x.clone());
ControlFlow::Continue(())
}
Instr::Halt => ControlFlow::Break(Trap::Halt),
Instr::SysCall(which) => ControlFlow::Break(Trap::SysCall(*which)),
Instr::Pop => match self.stack.pop() {
Some(_) => ControlFlow::Continue(),
None => ControlFlow::Break(Trap::Err(anyhow!("Stack Underflow"))),
},
// etc. ...
};
}
fn run(&mut self) -> Trap {
loop {
match self.step() {
ControlFlow::Break(trap) => return trap,
ControlFlow::Continue(_) => (),
}
}
}
}