-1

I'm building my first interpreter to learn Rust better, and I'm sort of stuck on this problem, because me tape/memory is a vector of unsigned 8-bit integers, but loops sometimes result on negatives.

I'm trying to execute this code in BF (which should result in "hello"):

+[----->+++<]>+.---.+++++++..+++.

I keep getting "attempted to subtract with overflow" because the first loop turns the bit into a negative, when it should stop when it notices that it's a 0.

Here's my function:

fn compile(code: Vec<Operations>) {
    // Memory vector is of arbitrary length for now //
    let mut memory: Vec<u8> = vec![0; 1000];
    let mut mem_ptr = 0;
    let mut code_ptr = 0;
    let mut bracket_idx: Vec<usize> = Vec::new();
    println!("{:?}", code);

    while code_ptr < code.len() { 
        let command = code[code_ptr]; 
        match command { 
            Operations::IncrementByte => memory[mem_ptr] += 1,
            Operations::DecrementByte => memory[mem_ptr] -= 1,
            Operations::IncrementPtr => mem_ptr += 1,
            Operations::DecrementPtr => mem_ptr -= 1, 
            Operations::Read => log_ptr(&[memory[mem_ptr] as u8]), 
            Operations::StartLoop => bracket_idx.push(code_ptr), 
            Operations::EndLoop => { 
                if memory[mem_ptr] != 0 {
                    code_ptr = *bracket_idx.last().unwrap()
                }
                else {
                    bracket_idx.pop();
                }
            }, 
            _ => println!("ERROR") 
        };
        code_ptr += 1;
    }
    println!("{:?}", memory);
}
ggorlen
  • 44,755
  • 7
  • 76
  • 106
Caio Ishikawa
  • 179
  • 11
  • 1
    Don't paste the answer in the question, see [this question](https://meta.stackoverflow.com/questions/387912/can-should-i-edit-my-question-to-an-add-answer) on meta about this. – Filipe Rodrigues Dec 15 '21 at 17:26
  • @FilipeRodrigues Yeah I get that, but the answer from sepp2k is correct, I just posted the changes I made to the code because there's no markdown editor for the below comments. – Caio Ishikawa Dec 16 '21 at 09:35
  • @CaioIshikawa Please post the answer [as an answer](https://stackoverflow.com/help/self-answer). "Correct" is something that can change over time, in many cases, and should be subject to votes so the community can decide what's best practice. Even if you have something that's objectively perfect for all time and definitively answers your question, it's still site policy to keep answers out of the question because it harms readability and context for future visitors. – ggorlen Jan 13 '22 at 17:44

2 Answers2

2

The first time you enter the loop, the current value will be 1 (because there's one + before the loop). Inside the loop, you then subtract 5 from that value (because there are 5 -s). So this program will simply not work if subtracting from 0 causes an overflow error.

So to make this program work, you'll need to use the wrapping_* family of methods to get wrap around instead of panics on overflow.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • After reading the docs for the wrapping_* methods, I'm still not sure what they do. This is what the docs say about the wrapping_sub method: "Wrapping (modular) subtraction. Computes self - rhs, wrapping around at the boundary of the type." But I don't understand what "wrapping around the boundary of the type" implies. Does it just mean ignoring the overflow? – Caio Ishikawa Dec 15 '21 at 15:31
  • @CaioIshikawa Wrapping around means that subtracting one from the minimum value gives you the maximum value and adding one to the maximum value gives you the minimum value. So yes, it means ignoring the overflow in the sense that instead of panicking on overflow, it behaves in the way that two's complement integers naturally behave, so no special code needs to be generated to handle overflows. – sepp2k Dec 15 '21 at 15:36
  • Solved! Thank you! – Caio Ishikawa Dec 15 '21 at 16:12
1

Most integer operations exist in different forms:

Each of these allow different level of control about what would happen if under- or overflow should happen. It's up to you to decide what you want your application to do in that case, and chose the relevant method.

There are also wrapper types, such as std::num::Wrapping and std::num::Saturating which give a more terse syntax if you always want the given behavior for all operations.

mcarton
  • 27,633
  • 5
  • 85
  • 95