1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use crate::Chunk;

#[derive(Clone)]
pub struct InstructionPointer {
    pub chunk_id: usize,
    pub instruction_pointer: usize,
}

impl InstructionPointer {
    pub fn new(chunk_id: usize) -> InstructionPointer {
        InstructionPointer {
            instruction_pointer: 0,
            chunk_id,
        }
    }

    pub fn read_and_advance<Value>(&mut self, chunk: &Chunk<Value>) -> Option<u8> {
        if self.instruction_pointer < chunk.code.len() {
            let instruction = chunk.code[self.instruction_pointer];
            self.instruction_pointer += 1;
            Some(instruction)
        } else {
            None
        }
    }

    pub fn jump_forward(&mut self, offset: usize) {
        self.instruction_pointer += offset
    }

    pub fn jump_backward(&mut self, offset: usize) {
        if self.instruction_pointer < offset {
            panic!("Jumped too far backward: ip={}", self.instruction_pointer);
        } else {
            self.instruction_pointer -= offset;
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::{Chunk, InstructionPointer};

    #[test]
    fn should_iterate_over_all_instructions() {
        let mut expected_code: Vec<u8> = vec![];
        for i in 1..100 {
            expected_code.push(i);
        }

        let chunk = Chunk {
            constants: vec![0],
            code: expected_code.clone(),
        };

        let mut pointer = InstructionPointer::new(0);
        let mut actual_code: Vec<u8> = vec![];

        while let Some(byte) = pointer.read_and_advance(&chunk) {
            actual_code.push(byte)
        }
        assert_eq!(expected_code, actual_code)
    }

    #[test]
    fn should_jump_backward() {
        let code: Vec<u8> = vec![0, 1, 2, 3, 4, 5];

        let chunk = Chunk {
            constants: vec![0],
            code,
        };

        let expected_code: Vec<u8> = vec![0, 1, 2, 3, 4, 5, 2, 3, 4, 5];

        let mut pointer = InstructionPointer::new(0);
        let mut actual_code: Vec<u8> = vec![];

        while let Some(byte) = pointer.read_and_advance(&chunk) {
            actual_code.push(byte)
        }
        pointer.jump_backward(4);
        while let Some(byte) = pointer.read_and_advance(&chunk) {
            actual_code.push(byte)
        }

        assert_eq!(expected_code, actual_code)
    }

    #[test]
    fn should_jump_forward() {
        let chunk = Chunk {
            constants: vec![0],
            code: vec![0, 1, 2, 3, 4, 5],
        };

        let mut pointer = InstructionPointer::new(0);
        pointer.jump_forward(5);

        assert_eq!(5, pointer.read_and_advance(&chunk).unwrap());
    }

    #[test]
    #[should_panic]
    fn should_panic_if_jumps_too_far_backward() {
        let mut pointer = InstructionPointer::new(0);
        pointer.jump_backward(10);
    }
}