diff options
Diffstat (limited to 'src/forth/interp.rs')
| -rw-r--r-- | src/forth/interp.rs | 205 |
1 files changed, 132 insertions, 73 deletions
diff --git a/src/forth/interp.rs b/src/forth/interp.rs index c7e8588..f1af4ae 100644 --- a/src/forth/interp.rs +++ b/src/forth/interp.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::ops::Index; #[derive(Clone, Debug, PartialEq)] @@ -11,13 +10,22 @@ pub enum OpCode { TIf(usize, Option<usize>), // tail version Add, Sub, + Mul, + Div, + Dup, + Drop, + EQ, + GT, + GTE, + LT, + LTE, // todo: maybe get rid of ret? it's only used in word definitions, // which are bounded, so running of the end can be treated as an // implicit ‘ret’. Ret, } -#[derive(Debug)] +#[derive(Clone, Debug)] pub(super) struct ByteCode(pub(super) Vec<OpCode>); impl ByteCode { @@ -55,17 +63,16 @@ pub(super) struct DataStack(pub(super) Vec<i32>); #[derive(Debug)] pub(super) struct CallStack(pub(super) Vec<InstructionPointer>); -#[derive(Debug)] +#[derive(Clone, Debug)] pub(super) struct WordList(pub(super) Vec<ByteCode>); #[derive(Debug)] -pub(super) struct WordCatalog<'a>(pub(super) HashMap<&'a str, usize>); - -#[derive(Debug)] pub struct Interp { - stack: DataStack, + // todo: don't be pub, probably + pub stack: DataStack, callstack: CallStack, - wordlist: WordList, + // todo: don't be pub + pub wordlist: WordList, ip: InstructionPointer, } @@ -75,12 +82,12 @@ pub enum RuntimeError { } impl Interp { - pub fn new() -> Self { + pub fn new(wordlist: WordList) -> Self { Self { stack: DataStack(Vec::new()), callstack: CallStack(Vec::new()), - wordlist: WordList(vec![]), ip: InstructionPointer::new(), + wordlist, } } @@ -94,9 +101,9 @@ impl Interp { self.jmp(word); } - pub fn tick(&mut self) -> Result<(), RuntimeError> { + // bool is still_running + pub fn tick(&mut self) -> Result<bool, RuntimeError> { let bc = &self.wordlist.0[self.ip.word]; - eprintln!("debug: executing {:?}", bc[self.ip.offset]); match bc[self.ip.offset] { OpCode::Num(n) => self.stack.0.push(n), OpCode::Str(start, end) => eprintln!("got str: {} to {}", start, end), @@ -110,51 +117,94 @@ impl Interp { 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 n = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; + self.stack.0.push(n); + self.stack.0.push(n); + }, + OpCode::Drop => { + self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; + }, + 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) => { - eprintln!("should call word based on index {}", i); self.call(i); // skip the offset increment - return Ok(()) + return Ok(true) }, OpCode::TCall(i) => { - eprintln!("should jump to word based on index {}", i); self.jmp(i); // skip the offset increment - return Ok(()) + return Ok(true) }, OpCode::If(true_clause, false_clause) => { let flag = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; if flag != 0 { - eprintln!("should call true branch {}", true_clause); self.call(true_clause); - return Ok(()) + return Ok(true) } else if let Some(false_clause) = false_clause { - eprintln!("should call false branch {}", false_clause); self.call(false_clause); - return Ok(()) - } else { - eprintln!("should false fall through"); + return Ok(true) } }, OpCode::TIf(true_clause, false_clause) => { let flag = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?; if flag != 0 { - eprintln!("should call true branch {}", true_clause); self.jmp(true_clause); - return Ok(()) + return Ok(true) } else if let Some(false_clause) = false_clause { - eprintln!("should call false branch {}", false_clause); self.jmp(false_clause); - return Ok(()) - } else { - eprintln!("should false fall through"); + return Ok(true) } }, } self.ip.offset += 1; + Ok(self.ip.offset < self.wordlist.0[self.ip.word].len()) + } + + pub fn run(&mut self) -> Result<(), RuntimeError> { + while self.tick()? {} Ok(()) } } @@ -166,20 +216,20 @@ mod tests { #[test] fn simple_ticks() -> Result<(), RuntimeError> { - - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Num(3), OpCode::Add])); - interp.tick()?; + let mut wordlist = WordList(vec![]); + wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Num(3), OpCode::Add])); + let mut interp = Interp::new(wordlist); + assert_eq!(interp.tick()?, true, "first arg running"); assert_eq!(interp.ip.word, 0); assert_eq!(interp.ip.offset, 1); assert_eq!(interp.stack.0.len(), 1); assert_eq!(interp.stack.0[0], 2, "first argument"); - interp.tick()?; + assert_eq!(interp.tick()?, true, "second arg running"); assert_eq!(interp.ip.word, 0); assert_eq!(interp.ip.offset, 2); assert_eq!(interp.stack.0.len(), 2); assert_eq!(interp.stack.0[1], 3, "second argument"); - interp.tick()?; + assert_eq!(interp.tick()?, false, "stopped after addition"); assert_eq!(interp.ip.word, 0); assert_eq!(interp.ip.offset, 3); assert_eq!(interp.stack.0.len(), 1); @@ -190,11 +240,12 @@ mod tests { #[test] fn custom_word() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![ + 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, ])); + let mut interp = Interp::new(wordlist); // "sub" definition interp.wordlist.0.push(ByteCode(vec![ OpCode::Sub, OpCode::Ret, @@ -244,11 +295,11 @@ mod tests { #[test] fn tail_call() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Call(1)])); - interp.wordlist.0.push(ByteCode(vec![OpCode::TCall(2), OpCode::Ret])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Ret])); - + 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::Ret])); + let mut interp = Interp::new(wordlist); interp.tick()?; // call assert_eq!(interp.callstack.0.len(), 1, "callstack after call"); @@ -261,9 +312,10 @@ mod tests { #[test] fn if_then_true() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::If(1, None), OpCode::Num(0)])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); + 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::Ret])); + let mut interp = Interp::new(wordlist); interp.tick()?; assert_eq!(interp.stack.0.len(), 1, "push -1 stack len"); @@ -285,9 +337,10 @@ mod tests { #[test] fn if_then_false() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::If(1, None), OpCode::Num(-1)])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); + 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(1), OpCode::Ret])); + let mut interp = Interp::new(wordlist); interp.tick()?; assert_eq!(interp.stack.0.len(), 1, "push 0 stack len"); @@ -303,10 +356,11 @@ mod tests { #[test] fn if_else_then_true() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::If(1, Some(2)), OpCode::Num(-2)])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); + 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::Ret])); + wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); + let mut interp = Interp::new(wordlist); interp.tick()?; assert_eq!(interp.stack.0.len(), 1, "push -1 stack len"); @@ -328,10 +382,11 @@ mod tests { #[test] fn if_else_then_false() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::If(1, Some(2)), OpCode::Num(-2)])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); + 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(1), OpCode::Ret])); + wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); + let mut interp = Interp::new(wordlist); interp.tick()?; assert_eq!(interp.stack.0.len(), 1, "push 0 stack len"); @@ -353,10 +408,11 @@ mod tests { #[test] fn tail_if_then_true() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(0)])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::TIf(2, None), OpCode::Ret])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); + 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::Num(1), OpCode::Ret])); + let mut interp = Interp::new(wordlist); interp.tick()?; assert_eq!(interp.stack.0.len(), 0, "call(1) stack len"); @@ -380,10 +436,11 @@ mod tests { #[test] fn tail_if_then_false() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(0)])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::TIf(2, None), OpCode::Ret])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); + let mut wordlist = WordList(vec![]); + wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(0)])); + wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::TIf(2, None), OpCode::Ret])); + wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); + let mut interp = Interp::new(wordlist); interp.tick()?; assert_eq!(interp.stack.0.len(), 0, "call(1) stack len"); @@ -403,11 +460,12 @@ mod tests { #[test] fn tail_if_else_then_true() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(-2)])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::TIf(2, Some(3)), OpCode::Ret])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); + 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::Num(1), OpCode::Ret])); + wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); + let mut interp = Interp::new(wordlist); interp.tick()?; assert_eq!(interp.stack.0.len(), 0, "call(1) stack len"); @@ -431,11 +489,12 @@ mod tests { #[test] fn tail_if_else_then_false() -> Result<(), RuntimeError> { - let mut interp = Interp::new(); - interp.wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(-2)])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::TIf(2, Some(3)), OpCode::Ret])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); - interp.wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); + let mut wordlist = WordList(vec![]); + wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(-2)])); + wordlist.0.push(ByteCode(vec![OpCode::Num(0), 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 interp = Interp::new(wordlist); interp.tick()?; assert_eq!(interp.stack.0.len(), 0, "call(1) stack len"); |
