summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/forth/vm.rs178
1 files changed, 75 insertions, 103 deletions
diff --git a/src/forth/vm.rs b/src/forth/vm.rs
index 2305393..5bc1835 100644
--- a/src/forth/vm.rs
+++ b/src/forth/vm.rs
@@ -289,31 +289,29 @@ mod tests {
}
#[test]
- fn simple_ticks() -> Result<(), RuntimeError> {
+ fn simple_ticks() {
let mut wordlist = WordList(vec![]);
wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Num(3), OpCode::Add]));
let mut vm = VM::new(wordlist);
- assert_eq!(vm.tick()?, 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()?, 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()?, 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);
assert_eq!(vm.stack.0[0], 5, "result of addition");
-
- Ok(())
}
#[test]
- fn custom_word() -> Result<(), RuntimeError> {
+ 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,
@@ -325,191 +323,178 @@ mod tests {
OpCode::Sub, OpCode::Ret,
]));
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
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");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
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");
- vm.tick()?; // call sub
+ vm.tick().expect("should execute instruction"); // call sub
assert_eq!(vm.ip.word, 1);
assert_eq!(vm.ip.offset, 0);
assert_eq!(vm.stack.0.len(), 2);
- vm.tick()?; // -
+ vm.tick().expect("should execute instruction"); // -
assert_eq!(vm.ip.word, 1);
assert_eq!(vm.ip.offset, 1);
assert_eq!(vm.stack.0.len(), 1);
- vm.tick()?; // ret
+ vm.tick().expect("should execute instruction"); // ret
assert_eq!(vm.ip.word, 0);
assert_eq!(vm.ip.offset, 3);
assert_eq!(vm.stack.0.len(), 1);
assert_eq!(vm.stack.0[0], -1, "result of sub word");
- vm.tick()?; // 2
+ vm.tick().expect("should execute instruction"); // 2
assert_eq!(vm.ip.word, 0);
assert_eq!(vm.ip.offset, 4);
assert_eq!(vm.stack.0.len(), 2);
assert_eq!(vm.stack.0[1], -2, "post sub arg");
- vm.tick()?; // -
+ vm.tick().expect("should execute instruction"); // -
assert_eq!(vm.ip.word, 0);
assert_eq!(vm.ip.offset, 5);
assert_eq!(vm.stack.0.len(), 1);
assert_eq!(vm.stack.0[0], -3, "add opcode result");
-
- Ok(())
}
#[test]
- fn tail_call() -> Result<(), RuntimeError> {
+ 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::Ret]));
let mut vm = VM::new(wordlist);
- vm.tick()?; // call
+ vm.tick().expect("should execute instruction"); // call
assert_eq!(vm.callstack.0.len(), 1, "callstack after call");
- vm.tick()?; // tcall
+ vm.tick().expect("should execute instruction"); // tcall
assert_eq!(vm.callstack.0.len(), 1, "callstack after tcall");
- vm.tick()?; // ret
+ vm.tick().expect("should execute instruction"); // ret
assert_eq!(vm.callstack.0.len(), 0, "callstack after ret");
- Ok(())
}
#[test]
- fn if_then_true() -> Result<(), RuntimeError> {
+ 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::Ret]));
let mut vm = VM::new(wordlist);
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push -1 stack len");
assert_eq!(vm.stack.0[0], -1, "push -1 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 0, "if stack ");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push 1 stack len");
assert_eq!(vm.stack.0[0], 1, "push 1 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "ret stack len");
assert_eq!(vm.stack.0[0], 1, "ret stack val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 2, "push 0 stack len");
assert_eq!(vm.stack.0[1], 0, "push 0 stack val");
-
- Ok(())
}
#[test]
- fn if_then_false() -> Result<(), RuntimeError> {
+ 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(1), OpCode::Ret]));
let mut vm = VM::new(wordlist);
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push 0 stack len");
assert_eq!(vm.stack.0[0], 0, "push 0 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 0, "if stack len");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push -1 stack len");
assert_eq!(vm.stack.0[0], -1, "push -1 val");
-
- Ok(())
}
#[test]
- fn if_else_then_true() -> Result<(), RuntimeError> {
+ 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::Ret]));
wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret]));
let mut vm = VM::new(wordlist);
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push -1 stack len");
assert_eq!(vm.stack.0[0], -1, "push -1 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 0, "if stack len");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push 1 stack len");
assert_eq!(vm.stack.0[0], 1, "push 1 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "ret stack len");
assert_eq!(vm.stack.0[0], 1, "ret val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 2, "push -2 stack len");
assert_eq!(vm.stack.0[1], -2, "push -2 val");
-
- Ok(())
}
#[test]
- fn if_else_then_false() -> Result<(), RuntimeError> {
+ 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(1), OpCode::Ret]));
wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret]));
let mut vm = VM::new(wordlist);
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push 0 stack len");
assert_eq!(vm.stack.0[0], 0, "push 0 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 0, "if");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push 2 stack len");
assert_eq!(vm.stack.0[0], 2, "push 2 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "ret");
assert_eq!(vm.stack.0[0], 2, "ret");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 2, "push -2 stack len");
assert_eq!(vm.stack.0[1], -2, "push -2 val");
-
- Ok(())
}
#[test]
- fn tail_if_then_true() -> Result<(), RuntimeError> {
+ 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::Num(1), OpCode::Ret]));
let mut vm = VM::new(wordlist);
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 0, "call(1) stack len");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push -1 stack len");
assert_eq!(vm.stack.0[0], -1, "push -1 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 0, "tif stack ");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push 1 stack len");
assert_eq!(vm.stack.0[0], 1, "push 1 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "ret stack len");
assert_eq!(vm.stack.0[0], 1, "ret stack val");
- vm.tick()?; // no ret on wordlist[1] since it's ‘tif’
+ vm.tick().expect("should execute instruction"); // no ret on wordlist[1] since it's ‘tif’
assert_eq!(vm.stack.0.len(), 2, "push 0 stack len");
assert_eq!(vm.stack.0[1], 0, "push 0 stack val");
-
- Ok(())
}
#[test]
- fn tail_if_then_false() -> Result<(), RuntimeError> {
+ fn tail_if_then_false() {
let wordlist = wl_for(vec![
vec![OpCode::Call(1), OpCode::Num(0)],
vec![OpCode::Num(0), OpCode::TIf(2, None)],
@@ -517,20 +502,18 @@ mod tests {
]);
let mut vm = VM::new(wordlist);
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 0, "call(1) stack len");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0, vec![0], "push 0 stack");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0, vec![], "tif stack");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0, vec![0], "push 0 stack");
-
- Ok(())
}
#[test]
- fn tail_if_else_then_true() -> Result<(), RuntimeError> {
+ 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]));
@@ -538,28 +521,26 @@ mod tests {
wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret]));
let mut vm = VM::new(wordlist);
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 0, "call(1) stack len");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push -1 stack len");
assert_eq!(vm.stack.0[0], -1, "push -1 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 0, "tif stack len");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "push 1 stack len");
assert_eq!(vm.stack.0[0], 1, "push 1 val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 1, "ret stack len");
assert_eq!(vm.stack.0[0], 1, "ret val");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0.len(), 2, "push -2 stack len");
assert_eq!(vm.stack.0[1], -2, "push -2 val");
-
- Ok(())
}
#[test]
- fn tail_if_else_then_false() -> Result<(), RuntimeError> {
+ 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],
@@ -568,95 +549,86 @@ mod tests {
].into_iter().map(|x| ByteCode(x)).collect());
let mut vm = VM::new(wordlist);
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0, vec![], "call(1) stack");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0, vec![0], "push 0 stack");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0, vec![], "tif stack");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0, vec![2], "push 2 stack");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0, vec![2], "ret stack");
- vm.tick()?;
+ vm.tick().expect("should execute instruction");
assert_eq!(vm.stack.0, vec![2, -2], "push -2 stack");
-
- Ok(())
}
#[test]
- fn opcode_dup() -> Result<(), RuntimeError> {
+ fn opcode_dup() {
let wordlist = wl_for(vec![
vec![OpCode::Num(1), OpCode::Dup],
]);
let mut vm = VM::new(wordlist);
- vm.run()?;
+ vm.run().expect("should run to completion");
assert_eq!(vm.stack.0, vec![1, 1]);
- Ok(())
}
#[test]
- fn opcode_dup_underflow() -> Result<(), RuntimeError> {
+ fn opcode_dup_underflow() {
let wordlist = wl_for(vec![
vec![OpCode::Dup],
]);
let mut vm = VM::new(wordlist);
assert!(vm.run().is_err_and(|e| e == RuntimeError::StackUnderflow));
- Ok(())
}
#[test]
- fn opcode_swap() -> Result<(), RuntimeError> {
+ fn opcode_swap() {
let wordlist = wl_for(vec![
vec![OpCode::Num(1), OpCode::Num(2), OpCode::Swap],
]);
let mut vm = VM::new(wordlist);
- vm.run()?;
+ vm.run().expect("should run to completion");
assert_eq!(vm.stack.0, vec![2, 1]);
- Ok(())
}
#[test]
- fn opcode_swap_underflow() -> Result<(), RuntimeError> {
+ fn opcode_swap_underflow() {
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));
- Ok(())
}
#[test]
- fn opcode_rot() -> Result<(), RuntimeError> {
+ fn 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()?;
+ vm.run().expect("should run to completion");
assert_eq!(vm.stack.0, vec![2, 3, 1]);
- Ok(())
}
#[test]
- fn opcode_rot_underflow() -> Result<(), RuntimeError> {
+ fn opcode_rot_underflow() {
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));
- Ok(())
}
#[test]
- fn tif_no_ret() -> Result<(), RuntimeError> {
+ fn tif_no_ret() {
let wordlist = wl_for(vec![
vec![OpCode::Call(1)],
vec![OpCode::Num(0), OpCode::TIf(2, None), OpCode::Num(3)],
vec![OpCode::Num(-2)],
]);
let mut vm = VM::new(wordlist);
- vm.run()?;
+ vm.run().expect("should run to completion");
assert_eq!(vm.stack.0, vec![]);
- Ok(())
}
}