summaryrefslogtreecommitdiffstats
path: root/src/forth/vm.rs
diff options
context:
space:
mode:
authorBrian Cully <bjc@spork.org>2025-08-31 15:26:10 -0400
committerBrian Cully <bjc@spork.org>2025-08-31 15:26:10 -0400
commitf41f180655f35e1e3da7948d736894fcc4345d51 (patch)
treef43e4c428a0ae6480f929b2ddbcf9c4478f3a4f2 /src/forth/vm.rs
parent35c98bd145d42147520c61e8d5cf4985077daf29 (diff)
downloadautomathon-f41f180655f35e1e3da7948d736894fcc4345d51.tar.gz
automathon-f41f180655f35e1e3da7948d736894fcc4345d51.zip
rustfmt
Diffstat (limited to 'src/forth/vm.rs')
-rw-r--r--src/forth/vm.rs215
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));
}