From f0000e8b134a2e68b1ddbb554b958aef79fc31f4 Mon Sep 17 00:00:00 2001 From: Brian Cully Date: Mon, 15 Dec 2025 09:44:53 -0500 Subject: vm: Add boolean operators for not, and, or, and xor --- src/forth/compiler.rs | 2 +- src/forth/vm.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/forth.rs | 2 +- 3 files changed, 99 insertions(+), 2 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]); + } + } } diff --git a/tests/forth.rs b/tests/forth.rs index 2538419..cd743b9 100644 --- a/tests/forth.rs +++ b/tests/forth.rs @@ -3,7 +3,7 @@ use automathon::forth::{ vm::{VM, WordList}, }; -fn compiler_for(text: &str) -> Compiler { +fn compiler_for(text: &str) -> Compiler<'_> { let mut p = Compiler::new(text); p.compile().expect("badparse"); p -- cgit v1.3