use crate::scheme::arena::ValueId;
#[derive(Debug, Clone)]
pub enum Instruction {
Constant { value_id: ValueId },
Variable { depth: usize, offset: usize },
GlobalVariable { name: String },
Apply { n_args: usize },
Test { else_ip: usize },
Jump { target_ip: usize },
MakeClosure {
params: Vec<String>,
required_count: usize,
body_ip: usize,
n_free: usize,
},
Return,
Cons,
Car,
Cdr,
IsNull,
Add,
Subtract,
Multiply,
Divide,
Equal,
NumLt,
NumGt,
MakeList { n: usize },
Pop,
Dup,
SetVariable { depth: usize, offset: usize },
SetGlobalVariable { name: String },
DefineGlobal { name: String },
}
pub struct Program {
pub instructions: Vec<Instruction>,
}
impl Program {
pub fn new() -> Self {
Program {
instructions: Vec::new(),
}
}
pub fn emit(&mut self, insn: Instruction) -> usize {
let ip = self.instructions.len();
self.instructions.push(insn);
ip
}
pub fn patch_jump(&mut self, jump_ip: usize, target_ip: usize) {
match &mut self.instructions[jump_ip] {
Instruction::Jump { target_ip: ref mut t } => *t = target_ip,
Instruction::Test { else_ip: ref mut e } => *e = target_ip,
_ => panic!("Expected jump instruction at {}", jump_ip),
}
}
}
impl Default for Program {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scheme::arena::NIL_ID;
#[test]
fn test_emit_instruction() {
let mut program = Program::new();
let ip = program.emit(Instruction::Constant {
value_id: NIL_ID,
});
assert_eq!(ip, 0);
assert_eq!(program.instructions.len(), 1);
}
#[test]
fn test_patch_jump() {
let mut program = Program::new();
let jump_ip = program.emit(Instruction::Jump { target_ip: 0 });
let target_ip = program.emit(Instruction::Return);
program.patch_jump(jump_ip, target_ip);
match program.instructions[jump_ip] {
Instruction::Jump { target_ip: t } => assert_eq!(t, target_ip),
_ => panic!("Expected jump"),
}
}
}