diff options
Diffstat (limited to 'src/forth/vm.rs')
| -rw-r--r-- | src/forth/vm.rs | 215 |
1 files changed, 127 insertions, 88 deletions
diff --git a/src/forth/vm.rs b/src/forth/vm.rs index 5bc1835..4930514 100644 --- a/src/forth/vm.rs +++ b/src/forth/vm.rs @@ -54,8 +54,7 @@ impl ByteCode { // it there. impl PartialEq<Vec<OpCode>> for &ByteCode { fn eq(&self, other: &Vec<OpCode>) -> bool { - self.0.len() == other.len() && - std::iter::zip(&self.0, other).all(|(a, b)| a == b) + self.0.len() == other.len() && std::iter::zip(&self.0, other).all(|(a, b)| a == b) } } @@ -75,10 +74,7 @@ pub struct InstructionPointer { impl InstructionPointer { pub fn new() -> Self { - Self { - word: 0, - offset: 0, - } + Self { word: 0, offset: 0 } } } @@ -155,116 +151,114 @@ impl VM { let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; self.stack.0.push(n2 + n1); - }, + } OpCode::Sub => { let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; self.stack.0.push(n2 - n1); - }, + } OpCode::Mul => { let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; self.stack.0.push(n2 * n1); - }, + } OpCode::Div => { let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; self.stack.0.push(n2 / n1); - }, + } OpCode::Dup => { let len = self.stack.0.len(); if len < 1 { - return Err(RuntimeError::StackUnderflow) + return Err(RuntimeError::StackUnderflow); } - self.stack.0.push(self.stack.0[len-1]); - }, + self.stack.0.push(self.stack.0[len - 1]); + } OpCode::Drop => { self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; - }, + } OpCode::Swap => { let top = self.stack.0.len() - 1; if top < 1 { - return Err(RuntimeError::StackUnderflow) + return Err(RuntimeError::StackUnderflow); } - let (a, b) = - self.stack.0.split_at_mut(top); - std::mem::swap(&mut a[top-1], &mut b[0]); - }, + let (a, b) = self.stack.0.split_at_mut(top); + std::mem::swap(&mut a[top - 1], &mut b[0]); + } OpCode::Rot => { let top = self.stack.0.len() - 1; if top < 2 { - return Err(RuntimeError::StackUnderflow) + return Err(RuntimeError::StackUnderflow); } - let (a, b) = - self.stack.0.split_at_mut(top); - std::mem::swap(&mut a[top-1], &mut b[0]); - std::mem::swap(&mut a[top-2], &mut b[0]); - }, + let (a, b) = self.stack.0.split_at_mut(top); + std::mem::swap(&mut a[top - 1], &mut b[0]); + std::mem::swap(&mut a[top - 2], &mut b[0]); + } OpCode::EQ => { let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let v = if n2 == n1 { -1 } else { 0 }; self.stack.0.push(v); - }, + } OpCode::GT => { let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let v = if n2 > n1 { -1 } else { 0 }; self.stack.0.push(v); - }, + } OpCode::GTE => { let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let v = if n2 >= n1 { -1 } else { 0 }; self.stack.0.push(v); - }, + } OpCode::LT => { let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let v = if n2 < n1 { -1 } else { 0 }; self.stack.0.push(v); - }, + } OpCode::LTE => { let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; let v = if n2 <= n1 { -1 } else { 0 }; self.stack.0.push(v); - }, + } OpCode::Ret => { self.ip = self.callstack.0.pop().ok_or(RuntimeError::StackUnderflow)?; - }, + } OpCode::Call(i) => { self.call(i); // skip the offset increment - return Ok(true) - }, + return Ok(true); + } OpCode::TCall(i) => { self.jmp(i); // skip the offset increment - return Ok(true) - }, + return Ok(true); + } OpCode::If(true_clause, false_clause) => { let flag = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; if flag != 0 { self.call(true_clause); - return Ok(true) + return Ok(true); } else if let Some(false_clause) = false_clause { self.call(false_clause); - return Ok(true) + return Ok(true); } - }, + } OpCode::TIf(true_clause, false_clause) => { let flag = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; if flag != 0 { self.jmp(true_clause); - return Ok(true) + return Ok(true); } else if let Some(false_clause) = false_clause { self.jmp(false_clause); - return Ok(true) + return Ok(true); } else { self.ip = self.callstack.0.pop().ok_or(RuntimeError::StackUnderflow)?; } - }, + } } self.ip.offset += 1; Ok(self.ip.offset < self.wordlist.0[self.ip.word].len()) @@ -272,38 +266,52 @@ impl VM { pub fn run(&mut self) -> Result<usize, RuntimeError> { let mut count = 0; - while self.tick()? { count += 1 } + while self.tick()? { + count += 1 + } Ok(count) } } #[cfg(test)] mod tests { - use super::*; use super::OpCode; + use super::*; fn wl_for(v: Vec<Vec<OpCode>>) -> WordList { - WordList(v.into_iter() - .map(|x| ByteCode(x)) - .collect()) + WordList(v.into_iter().map(|x| ByteCode(x)).collect()) } #[test] fn simple_ticks() { let mut wordlist = WordList(vec![]); - wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Num(3), OpCode::Add])); + wordlist + .0 + .push(ByteCode(vec![OpCode::Num(2), OpCode::Num(3), OpCode::Add])); let mut vm = VM::new(wordlist); - assert_eq!(vm.tick().expect("should execute instruction"), true, "first arg running"); + assert_eq!( + vm.tick().expect("should execute instruction"), + true, + "first arg running" + ); assert_eq!(vm.ip.word, 0); assert_eq!(vm.ip.offset, 1); assert_eq!(vm.stack.0.len(), 1); assert_eq!(vm.stack.0[0], 2, "first argument"); - assert_eq!(vm.tick().expect("should execute instruction"), true, "second arg running"); + assert_eq!( + vm.tick().expect("should execute instruction"), + true, + "second arg running" + ); assert_eq!(vm.ip.word, 0); assert_eq!(vm.ip.offset, 2); assert_eq!(vm.stack.0.len(), 2); assert_eq!(vm.stack.0[1], 3, "second argument"); - assert_eq!(vm.tick().expect("should execute instruction"), false, "stopped after addition"); + assert_eq!( + vm.tick().expect("should execute instruction"), + false, + "stopped after addition" + ); assert_eq!(vm.ip.word, 0); assert_eq!(vm.ip.offset, 3); assert_eq!(vm.stack.0.len(), 1); @@ -314,14 +322,17 @@ mod tests { fn custom_word() { let mut wordlist = WordList(vec![]); wordlist.0.push(ByteCode(vec![ - OpCode::Num(2), OpCode::Num(3), OpCode::Call(1), OpCode::Num(-2), OpCode::Add, - OpCode::Sub, OpCode::Ret, + OpCode::Num(2), + OpCode::Num(3), + OpCode::Call(1), + OpCode::Num(-2), + OpCode::Add, + OpCode::Sub, + OpCode::Ret, ])); let mut vm = VM::new(wordlist); // "sub" definition - vm.wordlist.0.push(ByteCode(vec![ - OpCode::Sub, OpCode::Ret, - ])); + vm.wordlist.0.push(ByteCode(vec![OpCode::Sub, OpCode::Ret])); vm.tick().expect("should execute instruction"); assert_eq!(vm.ip.word, 0); @@ -367,7 +378,9 @@ mod tests { fn tail_call() { let mut wordlist = WordList(vec![]); wordlist.0.push(ByteCode(vec![OpCode::Call(1)])); - wordlist.0.push(ByteCode(vec![OpCode::TCall(2), OpCode::Ret])); + wordlist + .0 + .push(ByteCode(vec![OpCode::TCall(2), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Ret])); let mut vm = VM::new(wordlist); @@ -382,7 +395,11 @@ mod tests { #[test] fn if_then_true() { let mut wordlist = WordList(vec![]); - wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::If(1, None), OpCode::Num(0)])); + wordlist.0.push(ByteCode(vec![ + OpCode::Num(-1), + OpCode::If(1, None), + OpCode::Num(0), + ])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); let mut vm = VM::new(wordlist); @@ -405,7 +422,11 @@ mod tests { #[test] fn if_then_false() { let mut wordlist = WordList(vec![]); - wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::If(1, None), OpCode::Num(-1)])); + wordlist.0.push(ByteCode(vec![ + OpCode::Num(0), + OpCode::If(1, None), + OpCode::Num(-1), + ])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); let mut vm = VM::new(wordlist); @@ -422,7 +443,11 @@ mod tests { #[test] fn if_else_then_true() { let mut wordlist = WordList(vec![]); - wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::If(1, Some(2)), OpCode::Num(-2)])); + wordlist.0.push(ByteCode(vec![ + OpCode::Num(-1), + OpCode::If(1, Some(2)), + OpCode::Num(-2), + ])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); let mut vm = VM::new(wordlist); @@ -446,7 +471,11 @@ mod tests { #[test] fn if_else_then_false() { let mut wordlist = WordList(vec![]); - wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::If(1, Some(2)), OpCode::Num(-2)])); + wordlist.0.push(ByteCode(vec![ + OpCode::Num(0), + OpCode::If(1, Some(2)), + OpCode::Num(-2), + ])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); let mut vm = VM::new(wordlist); @@ -470,8 +499,14 @@ mod tests { #[test] fn tail_if_then_true() { let mut wordlist = WordList(vec![]); - wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(0)])); - wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::TIf(2, None), OpCode::Ret])); + wordlist + .0 + .push(ByteCode(vec![OpCode::Call(1), OpCode::Num(0)])); + wordlist.0.push(ByteCode(vec![ + OpCode::Num(-1), + OpCode::TIf(2, None), + OpCode::Ret, + ])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); let mut vm = VM::new(wordlist); @@ -515,8 +550,14 @@ mod tests { #[test] fn tail_if_else_then_true() { let mut wordlist = WordList(vec![]); - wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(-2)])); - wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::TIf(2, Some(3)), OpCode::Ret])); + wordlist + .0 + .push(ByteCode(vec![OpCode::Call(1), OpCode::Num(-2)])); + wordlist.0.push(ByteCode(vec![ + OpCode::Num(-1), + OpCode::TIf(2, Some(3)), + OpCode::Ret, + ])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); let mut vm = VM::new(wordlist); @@ -541,12 +582,17 @@ mod tests { #[test] fn tail_if_else_then_false() { - let wordlist = WordList(vec![ - vec![OpCode::Call(1), OpCode::Num(-2)], - vec![OpCode::Num(0), OpCode::TIf(2, Some(3)), OpCode::Ret], - vec![OpCode::Num(1), OpCode::Ret], - vec![OpCode::Num(2), OpCode::Ret], - ].into_iter().map(|x| ByteCode(x)).collect()); + let wordlist = WordList( + vec![ + vec![OpCode::Call(1), OpCode::Num(-2)], + vec![OpCode::Num(0), OpCode::TIf(2, Some(3)), OpCode::Ret], + vec![OpCode::Num(1), OpCode::Ret], + vec![OpCode::Num(2), OpCode::Ret], + ] + .into_iter() + .map(|x| ByteCode(x)) + .collect(), + ); let mut vm = VM::new(wordlist); vm.tick().expect("should execute instruction"); @@ -565,9 +611,7 @@ mod tests { #[test] fn opcode_dup() { - let wordlist = wl_for(vec![ - vec![OpCode::Num(1), OpCode::Dup], - ]); + let wordlist = wl_for(vec![vec![OpCode::Num(1), OpCode::Dup]]); let mut vm = VM::new(wordlist); vm.run().expect("should run to completion"); assert_eq!(vm.stack.0, vec![1, 1]); @@ -575,18 +619,14 @@ mod tests { #[test] fn opcode_dup_underflow() { - let wordlist = wl_for(vec![ - vec![OpCode::Dup], - ]); + let wordlist = wl_for(vec![vec![OpCode::Dup]]); let mut vm = VM::new(wordlist); assert!(vm.run().is_err_and(|e| e == RuntimeError::StackUnderflow)); } #[test] fn opcode_swap() { - let wordlist = wl_for(vec![ - vec![OpCode::Num(1), OpCode::Num(2), OpCode::Swap], - ]); + let wordlist = wl_for(vec![vec![OpCode::Num(1), OpCode::Num(2), OpCode::Swap]]); let mut vm = VM::new(wordlist); vm.run().expect("should run to completion"); assert_eq!(vm.stack.0, vec![2, 1]); @@ -594,18 +634,19 @@ mod tests { #[test] fn opcode_swap_underflow() { - let wordlist = wl_for(vec![ - vec![OpCode::Num(1), OpCode::Swap], - ]); + let wordlist = wl_for(vec![vec![OpCode::Num(1), OpCode::Swap]]); let mut vm = VM::new(wordlist); assert!(vm.run().is_err_and(|e| e == RuntimeError::StackUnderflow)); } #[test] fn opcode_rot() { - let wordlist = wl_for(vec![ - vec![OpCode::Num(1), OpCode::Num(2), OpCode::Num(3), OpCode::Rot], - ]); + let wordlist = wl_for(vec![vec![ + OpCode::Num(1), + OpCode::Num(2), + OpCode::Num(3), + OpCode::Rot, + ]]); let mut vm = VM::new(wordlist); vm.run().expect("should run to completion"); assert_eq!(vm.stack.0, vec![2, 3, 1]); @@ -613,9 +654,7 @@ mod tests { #[test] fn opcode_rot_underflow() { - let wordlist = wl_for(vec![ - vec![OpCode::Num(1), OpCode::Num(2), OpCode::Rot], - ]); + let wordlist = wl_for(vec![vec![OpCode::Num(1), OpCode::Num(2), OpCode::Rot]]); let mut vm = VM::new(wordlist); assert!(vm.run().is_err_and(|e| e == RuntimeError::StackUnderflow)); } |
