I am writing the following Forth VM code. There's lots of obvious repetition but not lots of obvious refactoring. Suggestions ?
use std::string::String;
use std::collections::HashMap;
#[derive(Debug)]
#[repr(u8)]
pub enum Instr {
Print = 1,
Add = 2,
Sub = 3,
Mul = 4,
Div = 5,
Mod = 6,
VarStore = 7,
VarGet = 8,
And = 9,
Or = 10,
Not = 11,
AltPush = 12,
AltPop = 13,
AltCopy = 14,
Lt = 15,
Le = 16,
Eq = 17,
Ge = 18,
Gt = 19,
Drop = 20,
}
#[derive(Debug, Clone)]
pub enum Data {
Bool(bool),
I32(i32),
F32(f32),
Str(String),
}
pub struct State {
code: Vec<Instr>,
data: Vec<Data>,
alt: Vec<Data>,
variables: HashMap<String, Data>,
}
pub fn exec(state: &mut State) {
let State { code, data, alt, variables } = state;
while let Some(op_code) = code.pop() {
// println!("data: {:?}", data);
match op_code {
Instr::Print => {
let x = data.pop().unwrap();
println!("{:?}", x); },
Instr::Add => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::I32(lhs + rhs)),
(Data::F32(lhs), Data::F32(rhs)) => data.push(Data::F32(lhs + rhs)),
_ => panic!("+ fail"), } },
Instr::Sub => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::I32(lhs - rhs)),
(Data::F32(lhs), Data::F32(rhs)) => data.push(Data::F32(lhs - rhs)),
_ => panic!("- fail"), } },
Instr::Mul => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::I32(lhs * rhs)),
(Data::F32(lhs), Data::F32(rhs)) => data.push(Data::F32(lhs * rhs)),
_ => panic!("* fail"), } },
Instr::Div => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::I32(lhs / rhs)),
(Data::F32(lhs), Data::F32(rhs)) => data.push(Data::F32(lhs / rhs)),
_ => panic!("/ fail"), } },
Instr::Mod => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::I32(lhs % rhs)),
_ => panic!("mod fail"), } },
Instr::VarStore => {
match data.pop().unwrap() {
Data::Str(name) => {
let value = data.pop().unwrap();
variables.insert(name, value); } ,
_ => panic!(), } },
Instr::VarGet => {
match data.pop().unwrap() {
Data::Str(name) => {
let value = variables.get(&name).unwrap();
data.push(value.clone()); },
_ => panic!(), } }
Instr::And => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::Bool(lhs), Data::Bool(rhs)) => data.push(Data::Bool(lhs && rhs)),
_ => panic!("and fail"), } }
Instr::Or => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::Bool(lhs), Data::Bool(rhs)) => data.push(Data::Bool(lhs || rhs)),
_ => panic!("or fail"), } }
Instr::Not => {
let lhs = data.pop().unwrap();
match lhs {
Data::Bool(lhs) => data.push(Data::Bool(! lhs)),
_ => panic!("not fail"), } }
Instr::AltPush => {
let lhs = data.pop().unwrap();
alt.push(lhs); },
Instr::AltPop => {
let lhs = alt.pop().unwrap();
data.push(lhs); },
Instr::AltCopy => {
let lhs = alt.pop().unwrap();
data.push(lhs); },
Instr::Drop => { data.pop(); },
Instr::Lt => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::Bool(lhs < rhs)),
(Data::F32(lhs), Data::F32(rhs)) => data.push(Data::Bool(lhs < rhs)),
_ => panic!("lt fail") } },
Instr::Le => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::Bool(lhs <= rhs)),
(Data::F32(lhs), Data::F32(rhs)) => data.push(Data::Bool(lhs <= rhs)),
_ => panic!("le fail") } },
Instr::Eq => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::Bool(lhs == rhs)),
(Data::F32(lhs), Data::F32(rhs)) => data.push(Data::Bool(lhs == rhs)),
_ => panic!("eq fail"), } },
Instr::Ge => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::Bool(lhs >= rhs)),
(Data::F32(lhs), Data::F32(rhs)) => data.push(Data::Bool(lhs >= rhs)),
_ => panic!("ge fail"), } },
Instr::Gt => {
let rhs = data.pop().unwrap();
let lhs = data.pop().unwrap();
match (lhs, rhs) {
(Data::I32(lhs), Data::I32(rhs)) => data.push(Data::Bool(lhs > rhs)),
(Data::F32(lhs), Data::F32(rhs)) => data.push(Data::Bool(lhs > rhs)),
_ => panic!("gt fail"), } },
_ => panic!(format!("unrecognized instr: {:?}", op_code))
}
}
}