Mapping enum <=> u8

  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")

3 Likes

This should work:

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

That worked. Thanks!

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).

7 Likes

I was also looking for this, thanks!