summaryrefslogtreecommitdiffstats
path: root/src/forth/interp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/forth/interp.rs')
-rw-r--r--src/forth/interp.rs93
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");