diff options
Diffstat (limited to 'src/forth/interp.rs')
| -rw-r--r-- | src/forth/interp.rs | 93 |
1 files changed, 57 insertions, 36 deletions
diff --git a/src/forth/interp.rs b/src/forth/interp.rs index 2ade22d..406c424 100644 --- a/src/forth/interp.rs +++ b/src/forth/interp.rs @@ -5,8 +5,7 @@ use std::ops::Index; pub enum OpCode { Num(i32), Str(usize, usize), - WordI(usize), - Word(&'static str), + Call(usize), Add, Sub, Ret, @@ -29,8 +28,21 @@ impl Index<usize> for ByteCode { } } +// .0 is wordlist entry's bytecode, .1 is index into that bytecode #[derive(Debug, Copy, Clone, PartialEq)] -pub(super) struct InstructionPointer(pub(super) usize); +pub(super) struct InstructionPointer { + pub(super) word: usize, + pub(super) offset: usize, +} + +impl InstructionPointer { + pub fn new() -> Self { + Self { + word: 0, + offset: 0, + } + } +} #[derive(Debug)] pub(super) struct DataStack(pub(super) Vec<i32>); @@ -39,7 +51,7 @@ pub(super) struct DataStack(pub(super) Vec<i32>); pub(super) struct CallStack(pub(super) Vec<InstructionPointer>); #[derive(Debug)] -pub(super) struct WordList(pub(super) HashMap<&'static str, InstructionPointer>); +pub(super) struct WordList(pub(super) Vec<ByteCode>); #[derive(Debug)] pub(super) struct WordCatalog<'a>(pub(super) HashMap<&'a str, usize>); @@ -49,14 +61,12 @@ pub struct Interp { stack: DataStack, callstack: CallStack, wordlist: WordList, - bytecode: ByteCode, ip: InstructionPointer, } #[derive(Debug)] pub enum RuntimeError { StackUnderflow, - UndefinedWord, } impl Interp { @@ -64,14 +74,14 @@ impl Interp { Self { stack: DataStack(Vec::new()), callstack: CallStack(Vec::new()), - wordlist: WordList(HashMap::new()), - bytecode: ByteCode(Vec::new()), - ip: InstructionPointer(0), + wordlist: WordList(vec![]), + ip: InstructionPointer::new(), } } pub fn tick(&mut self) -> Result<(), RuntimeError> { - match self.bytecode[self.ip.0] { + let bc = &self.wordlist.0[self.ip.word]; + match bc[self.ip.offset] { OpCode::Num(n) => self.stack.0.push(n), OpCode::Str(start, end) => eprintln!("got str: {} to {}", start, end), OpCode::Add => { @@ -85,19 +95,18 @@ impl Interp { self.stack.0.push(n2 - n1); }, OpCode::Ret => { - let ip = self.callstack.0.pop().ok_or(RuntimeError::StackUnderflow)?; - self.ip = ip; - }, - OpCode::Word(w) => { - let ip = self.wordlist.0.get(w).ok_or(RuntimeError::UndefinedWord)?; - self.callstack.0.push(self.ip); - self.ip.0 = ip.0 - 1; // we auto-increment at the end + self.ip = self.callstack.0.pop().ok_or(RuntimeError::StackUnderflow)?; }, - OpCode::WordI(i) => { + OpCode::Call(i) => { eprintln!("should jump to word based on index {}", i); + self.callstack.0.push(self.ip); + self.ip.word = i; + self.ip.offset = 0; + // skip the offset increment + return Ok(()) } } - self.ip.0 += 1; + self.ip.offset += 1; Ok(()) } } @@ -111,17 +120,20 @@ mod tests { fn simple_ticks() -> Result<(), RuntimeError> { let mut interp = Interp::new(); - interp.bytecode = ByteCode(vec![OpCode::Num(2), OpCode::Num(3), OpCode::Add]); + interp.wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Num(3), OpCode::Add])); interp.tick()?; - assert_eq!(interp.ip.0, 1); + 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.ip.0, 2); + 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.ip.0, 3); + assert_eq!(interp.ip.word, 0); + assert_eq!(interp.ip.offset, 3); assert_eq!(interp.stack.0.len(), 1); assert_eq!(interp.stack.0[0], 5, "result of addition"); @@ -131,42 +143,51 @@ mod tests { #[test] fn custom_word() -> Result<(), RuntimeError> { let mut interp = Interp::new(); - interp.bytecode = ByteCode(vec![ - OpCode::Num(2), OpCode::Num(3), OpCode::Word("sub"), OpCode::Num(-2), OpCode::Add, - // "sub" definition + interp.wordlist.0.push(ByteCode(vec![ + OpCode::Num(2), OpCode::Num(3), OpCode::Call(1), OpCode::Num(-2), OpCode::Add, + OpCode::Sub, OpCode::Ret, + ])); + // "sub" definition + interp.wordlist.0.push(ByteCode(vec![ OpCode::Sub, OpCode::Ret, - ]); - // 5 is offset of w - interp.wordlist.0.insert("sub", InstructionPointer(5)); + ])); + interp.tick()?; - assert_eq!(interp.ip.0, 1); + 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.ip.0, 2); + 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()?; // call sub - assert_eq!(interp.ip.0, 5); + assert_eq!(interp.ip.word, 1); + assert_eq!(interp.ip.offset, 0); assert_eq!(interp.stack.0.len(), 2); interp.tick()?; // - - assert_eq!(interp.ip.0, 6); + assert_eq!(interp.ip.word, 1); + assert_eq!(interp.ip.offset, 1); assert_eq!(interp.stack.0.len(), 1); interp.tick()?; // ret - assert_eq!(interp.ip.0, 3); + assert_eq!(interp.ip.word, 0); + assert_eq!(interp.ip.offset, 3); assert_eq!(interp.stack.0.len(), 1); assert_eq!(interp.stack.0[0], -1, "result of sub word"); interp.tick()?; // 2 - assert_eq!(interp.ip.0, 4); + assert_eq!(interp.ip.word, 0); + assert_eq!(interp.ip.offset, 4); assert_eq!(interp.stack.0.len(), 2); assert_eq!(interp.stack.0[1], -2, "post sub arg"); interp.tick()?; // - - assert_eq!(interp.ip.0, 5); + assert_eq!(interp.ip.word, 0); + assert_eq!(interp.ip.offset, 5); assert_eq!(interp.stack.0.len(), 1); assert_eq!(interp.stack.0[0], -3, "add opcode result"); |
