Mapping enum <=> u8


#1
  1. Context: we are writing a forth interpreter. We have something like:

#[repr(u8)]
pub enum OpCode {

}

#[derive(Debug)]
pub enum Data {
    I32(i32),
    F32(f32),
    Str(String),
}


pub fn exec(code_stack: &mut Vec<u8>, data_stack: &mut Vec<Data>) {
    code_stack.reverse();
    while let Some(op_code) = code_stack.pop() {
        println!("data_stack: {:?}", data_stack);
        match op_code {
            1u8 => { // print
                let x = data_stack.pop().unwrap();
                println!("{:?}", x); },
            2u8 => { // +
                let rhs = data_stack.pop().unwrap();
                let lhs = data_stack.pop().unwrap();
                match (lhs, rhs) {
                    (Data::I32(lhs), Data::I32(rhs)) => data_stack.push(Data::I32(lhs + rhs)),
                    (Data::F32(lhs), Data::F32(rhs)) => data_stack.push(Data::F32(lhs + rhs)),
                    _ => panic!("+ fail"),
                } },
            3u8 => { // -
                let rhs = data_stack.pop().unwrap();
                let lhs = data_stack.pop().unwrap();
                match (lhs, rhs) {
                    (Data::I32(lhs), Data::I32(rhs)) => data_stack.push(Data::I32(lhs - rhs)),
                    (Data::F32(lhs), Data::F32(rhs)) => data_stack.push(Data::F32(lhs - rhs)),
                    _ => panic!("- fail"),
                } },
            4u8 => { // *
                let rhs = data_stack.pop().unwrap();
                let lhs = data_stack.pop().unwrap();
                match (lhs, rhs) {
                    (Data::I32(lhs), Data::I32(rhs)) => data_stack.push(Data::I32(lhs * rhs)),
                    (Data::F32(lhs), Data::F32(rhs)) => data_stack.push(Data::F32(lhs * rhs)),
                    _ => panic!("* fail"),
                } },
            5u8 => { // /
                let rhs = data_stack.pop().unwrap();
                let lhs = data_stack.pop().unwrap();
                match (lhs, rhs) {
                    (Data::I32(lhs), Data::I32(rhs)) => data_stack.push(Data::I32(lhs / rhs)),
                    (Data::F32(lhs), Data::F32(rhs)) => data_stack.push(Data::F32(lhs / rhs)),
                    _ => panic!("/ fail"),
                } },
            _ => panic!(format!("unrecognized instr: {:?}", op_code))
        }
    }
}




#[test]
fn test_00() {
    let mut code_stack = Vec::<u8>::new();
    let mut data_stack = Vec::<Data>::new();

    data_stack.push(Data::I32(1));
    data_stack.push(Data::I32(2));
    data_stack.push(Data::I32(3));
    code_stack.push(2);
    code_stack.push(3);
    code_stack.push(1);

    exec(&mut code_stack, &mut data_stack);

}
  1. Question: How do we create Print, Plus, Minus, Mul, Div in enum OpCode so that:

Print == 1
Plus == 2
Minus == 3

It’s not enough to just have the enum, I want the actual u8 mapping (so storing Forth bitcode in file is “stable”)


#2

This should work:

#[repr(u8)]
pub enum OpCode {
    Print = 1,
    Plus = 2,
    Minus = 3,
}

#3

That worked. Thanks!


#4

BTW, Rust’s optimizations depend on enum always having one of valid values. Never let Rust enum have an invalid value. If your OpCode has 1,2,3 and you read 4 from the file and cast it to OpCode, you will get undefined behavior (it does corrupt memory and crash programs).