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); } }