summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/forth/compiler.rs2
-rw-r--r--src/forth/vm.rs97
2 files changed, 98 insertions, 1 deletions
diff --git a/src/forth/compiler.rs b/src/forth/compiler.rs
index b518b7e..5a6d502 100644
--- a/src/forth/compiler.rs
+++ b/src/forth/compiler.rs
@@ -242,7 +242,7 @@ mod tests {
use super::super::vm::OpCode;
use super::*;
- fn compiler_for(text: &str) -> Compiler {
+ fn compiler_for(text: &str) -> Compiler<'_> {
let mut p = Compiler::new(text);
p.compile().expect("badparse");
p
diff --git a/src/forth/vm.rs b/src/forth/vm.rs
index 4930514..ad4b78c 100644
--- a/src/forth/vm.rs
+++ b/src/forth/vm.rs
@@ -25,6 +25,10 @@ pub enum OpCode {
GTE,
LT,
LTE,
+ Not,
+ And,
+ Or,
+ Xor,
// 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’.
@@ -224,6 +228,29 @@ impl VM {
let v = if n2 <= n1 { -1 } else { 0 };
self.stack.0.push(v);
}
+ OpCode::Not => {
+ let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
+ let v = if n1 == 0 { -1 } else { 0 };
+ self.stack.0.push(v);
+ }
+ OpCode::And => {
+ let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
+ let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
+ let v = if n1 == 0 || n2 == 0 { 0 } else { -1 };
+ self.stack.0.push(v);
+ }
+ OpCode::Or => {
+ let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
+ let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
+ let v = if n1 == -1 || n2 == -1 { -1 } else { 0 };
+ self.stack.0.push(v);
+ }
+ OpCode::Xor => {
+ let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
+ let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
+ let v = if (n1 == 0 && n2 != 0) || (n1 != 0 && n2 == 0) { -1 } else { 0 };
+ self.stack.0.push(v);
+ }
OpCode::Ret => {
self.ip = self.callstack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
}
@@ -282,6 +309,14 @@ mod tests {
WordList(v.into_iter().map(|x| ByteCode(x)).collect())
}
+ fn op_from(v: bool) -> OpCode {
+ if v {
+ OpCode::Num(-1)
+ } else {
+ OpCode::Num(0)
+ }
+ }
+
#[test]
fn simple_ticks() {
let mut wordlist = WordList(vec![]);
@@ -670,4 +705,66 @@ mod tests {
vm.run().expect("should run to completion");
assert_eq!(vm.stack.0, vec![]);
}
+
+ #[test]
+ fn opcode_not() {
+ let wordlist = wl_for(vec![vec![OpCode::Num(0), OpCode::Not]]);
+ let mut vm = VM::new(wordlist);
+ vm.run().expect("should run to completion");
+ assert_eq!(vm.stack.0, vec![-1]);
+ }
+
+ #[test]
+ fn opcode_and() {
+ let tests = [
+ (true, true, -1),
+ (false, false, 0),
+ (true, false, 0),
+ (false, true, 0),
+ ];
+ for (a, b, res) in tests {
+ let wordlist = wl_for(vec![vec![
+ op_from(a), op_from(b), OpCode::And
+ ]]);
+ let mut vm = VM::new(wordlist);
+ vm.run().expect("should run to completion");
+ assert_eq!(vm.stack.0, vec![res]);
+ }
+ }
+
+ #[test]
+ fn opcode_or() {
+ let tests = [
+ (true, true, -1),
+ (false, false, 0),
+ (true, false, -1),
+ (false, true, -1),
+ ];
+ for (a, b, res) in tests {
+ let wordlist = wl_for(vec![vec![
+ op_from(a), op_from(b), OpCode::Or
+ ]]);
+ let mut vm = VM::new(wordlist);
+ vm.run().expect("should run to completion");
+ assert_eq!(vm.stack.0, vec![res]);
+ }
+ }
+
+ #[test]
+ fn opcode_xor() {
+ let tests = [
+ (true, true, 0),
+ (false, false, 0),
+ (true, false, -1),
+ (false, true, -1),
+ ];
+ for (a, b, res) in tests {
+ let wordlist = wl_for(vec![vec![
+ op_from(a), op_from(b), OpCode::Xor
+ ]]);
+ let mut vm = VM::new(wordlist);
+ vm.run().expect("should run to completion");
+ assert_eq!(vm.stack.0, vec![res]);
+ }
+ }
}