From 5fe9ca40c640b10a5ce8c62d579595ecd6150eaa Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Sun, 6 Apr 2025 17:06:33 -0500 Subject: [PATCH] various progress around the block --- src/instructions/logical.rs | 150 ++++++++++++++++++++++++++++++++++++ src/instructions/mod.rs | 26 +------ src/instructions/numeric.rs | 46 +++++------ src/instructions/utils.rs | 38 ++++++++- src/main.rs | 2 +- src/push/interpreter.rs | 97 ++++++++++++++++++++++- src/push/state.rs | 8 +- 7 files changed, 309 insertions(+), 58 deletions(-) create mode 100644 src/instructions/logical.rs diff --git a/src/instructions/logical.rs b/src/instructions/logical.rs new file mode 100644 index 0000000..58cf011 --- /dev/null +++ b/src/instructions/logical.rs @@ -0,0 +1,150 @@ +//! # Logical Instructions +//! +//! This file holds instructions for the boolean stack. + +use super::utils::{LogicalTrait, NumericTrait}; +use crate::push::state::PushState; + +/// Runs logical and on two values +fn _and(vals: Vec) -> Option +where + T: Copy + LogicalTrait, +{ + Some(vals[0].logical_and(vals[1])) +} +make_instruction!(boolean, boolean, _and, bool, 2); + +/// Runs logical or on two values +fn _or(vals: Vec) -> Option +where + T: Copy + LogicalTrait, +{ + Some(vals[0].logical_or(vals[1])) +} +make_instruction!(boolean, boolean, _or, bool, 2); + +/// Runs logical not on two values +fn _not(vals: Vec) -> Option +where + T: Copy + LogicalTrait, +{ + Some(vals[0].logical_not()) +} +make_instruction!(boolean, boolean, _not, bool, 1); + +/// Runs logical xor on two values +fn _xor(vals: Vec) -> Option +where + T: Copy + LogicalTrait, +{ + Some(vals[0].logical_xor(vals[1])) +} +make_instruction!(boolean, boolean, _xor, bool, 2); + +/// Inverts the first value and runs logical and on two values +fn _invert_first_then_and(vals: Vec) -> Option +where + T: Copy + LogicalTrait, +{ + Some(vals[0].logical_not().logical_and(vals[1])) +} +make_instruction!(boolean, boolean, _invert_first_then_and, bool, 2); + +/// Inverts the second value and runs logical and on two values +fn _invert_second_then_and(vals: Vec) -> Option +where + T: Copy + LogicalTrait, +{ + Some(vals[0].logical_and(vals[1].logical_not())) +} +make_instruction!(boolean, boolean, _invert_second_then_and, bool, 2); + +// fn _to_int(vals: Vec) -> Option +// where +// T: Copy + NumericTrait, +// { +// Some(T::from_bool(vals)) +// } + +#[cfg(test)] +mod tests { + use super::*; + use crate::push::state::EMPTY_STATE; + + #[test] + fn and_test() { + let mut test_state = EMPTY_STATE; + + test_state.boolean = vec![true, false, true]; + boolean_and(&mut test_state); + assert_eq!(vec![true, false], test_state.boolean); + + test_state.boolean = vec![true, true]; + boolean_and(&mut test_state); + assert_eq!(vec![true], test_state.boolean); + } + + #[test] + fn or_test() { + let mut test_state = EMPTY_STATE; + + test_state.boolean = vec![true, false, true]; + boolean_or(&mut test_state); + assert_eq!(vec![true, true], test_state.boolean); + + test_state.boolean = vec![false, false]; + boolean_or(&mut test_state); + assert_eq!(vec![false], test_state.boolean); + } + + #[test] + fn not_test() { + let mut test_state = EMPTY_STATE; + + test_state.boolean = vec![true, false, true]; + boolean_not(&mut test_state); + assert_eq!(vec![true, false, false], test_state.boolean); + + test_state.boolean = vec![false, false]; + boolean_not(&mut test_state); + assert_eq!(vec![false, true], test_state.boolean); + } + + #[test] + fn xor_test() { + let mut test_state = EMPTY_STATE; + + test_state.boolean = vec![true, false, true]; + boolean_xor(&mut test_state); + assert_eq!(vec![true, true], test_state.boolean); + + test_state.boolean = vec![false, false]; + boolean_xor(&mut test_state); + assert_eq!(vec![false], test_state.boolean); + + test_state.boolean = vec![true, true]; + boolean_xor(&mut test_state); + assert_eq!(vec![false], test_state.boolean); + } + + #[test] + fn invert_test() { + let mut test_state = EMPTY_STATE; + + test_state.boolean = vec![true, false]; + boolean_invert_first_then_and(&mut test_state); + assert_eq!(vec![true], test_state.boolean); + + test_state.boolean = vec![false, false]; + boolean_invert_first_then_and(&mut test_state); + assert_eq!(vec![false], test_state.boolean); + + test_state.boolean = vec![true, false]; + boolean_invert_second_then_and(&mut test_state); + assert_eq!(vec![false], test_state.boolean); + + test_state.boolean = vec![false, true]; + boolean_invert_second_then_and(&mut test_state); + assert_eq!(vec![true], test_state.boolean); + } +} diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index f3360f8..81bdaa5 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -45,30 +45,6 @@ pub mod macros { } } +pub mod logical; pub mod numeric; pub mod utils; - -// /// A macro that makes a push instruction given: the name of the stack to use, -// /// an internal function to call, and the type of a function. -// #[macro_export] -// macro_rules! make_instruction { -// ($stack_name:ident, $fn_name:ident, $fn_type:ty) => { -// paste::item! { -// fn [< $stack_name $fn_name >] (state: &mut PushState, num_inputs: usize) { -// if state.$stack_name.len() < num_inputs { -// return; -// } -// let mut inputs: Vec<$fn_type> = Vec::with_capacity(num_inputs); -// for n in 0..num_inputs { -// inputs.push(state.$stack_name[n]); -// } -// if let Some(result) = $fn_name(inputs) { -// for _ in 0..num_inputs { -// state.$stack_name.pop(); -// } -// state.$stack_name.push(result); -// } -// } -// } -// }; -// } diff --git a/src/instructions/numeric.rs b/src/instructions/numeric.rs index df6897b..39fc2bd 100644 --- a/src/instructions/numeric.rs +++ b/src/instructions/numeric.rs @@ -11,7 +11,7 @@ use rust_decimal::prelude::{FromPrimitive, ToPrimitive}; use std::cmp::{max, min}; use std::ops::{Add, Div, Mul, Sub}; -use super::utils::InstructionTrait; +use super::utils::NumericTrait; /// Adds two addable values together. fn _add(vals: Vec) -> Option @@ -46,7 +46,7 @@ make_instruction!(float, float, _mult, Decimal, 2); /// Divides two values from each other. fn _div(vals: Vec) -> Option where - T: Div + Copy + InstructionTrait, + T: Div + Copy + NumericTrait, { vals[1].checked_div(vals[0]) } @@ -56,7 +56,7 @@ make_instruction!(float, float, _div, Decimal, 2); /// Takes the remainder of two values fn _rem(vals: Vec) -> Option where - T: Div + Copy + InstructionTrait, + T: Div + Copy + NumericTrait, { vals[1].checked_mod(vals[0]) } @@ -86,7 +86,7 @@ make_instruction!(float, float, _min, Decimal, 2); /// Increments a single value by 1 fn _inc(vals: Vec) -> Option where - T: InstructionTrait + Copy, + T: NumericTrait + Copy, { Some(vals[0].increment()) } @@ -96,7 +96,7 @@ make_instruction!(float, float, _inc, Decimal, 1); /// Decrements a single value by 1 fn _dec(vals: Vec) -> Option where - T: InstructionTrait + Copy, + T: NumericTrait + Copy, { Some(vals[0].decrement()) } @@ -146,7 +146,7 @@ make_instruction!(float, boolean, _gte, Decimal, 2); /// Runs sin on a single item. fn _sin(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].safe_sin() } @@ -156,7 +156,7 @@ make_instruction!(float, float, _sin, Decimal, 1); /// Runs arcsin on a single item. fn _arcsin(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].safe_sin()?.inverse() } @@ -166,7 +166,7 @@ make_instruction!(float, float, _arcsin, Decimal, 1); /// Runs cos on a single item. fn _cos(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].safe_cos() } @@ -176,7 +176,7 @@ make_instruction!(float, float, _cos, Decimal, 1); /// Runs arcsin on a single item. fn _arccos(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].safe_cos()?.inverse() } @@ -186,7 +186,7 @@ make_instruction!(float, float, _arccos, Decimal, 1); /// Runs tan on a single item. fn _tan(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].safe_tan() } @@ -196,7 +196,7 @@ make_instruction!(float, float, _tan, Decimal, 1); /// Runs arctan on a single item. fn _arctan(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].safe_tan()?.inverse() } @@ -216,21 +216,21 @@ fn _to_float(vals: Vec) -> Option { make_instruction!(int, float, _to_float, i128, 1); /// Converts a single number to a bool. -fn _to_bool(vals: Vec) -> Option +fn _from_bool(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { - Some(vals[0].to_bool()) + Some(T::from_bool(vals[0])) } -make_instruction!(int, boolean, _to_bool, i128, 1); -make_instruction!(float, boolean, _to_bool, Decimal, 1); +make_instruction!(int, boolean, _from_bool, bool, 1); +make_instruction!(float, boolean, _from_bool, bool, 1); /// Takes the log base 10 of a single Decimal. Acts as a /// NoOp if the value is 0. If the value is negative, takes /// the absolute value of the number. fn _log(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].absolute().safe_log10() } @@ -240,7 +240,7 @@ make_instruction!(float, float, _log, Decimal, 1); /// Takes the exp of a single value. Ints get truncated. fn _exp(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].safe_exp() } @@ -250,7 +250,7 @@ make_instruction!(float, float, _exp, Decimal, 1); /// Takes the square root of the absolute value of a single value. fn _sqrt(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].safe_sqrt() } @@ -261,7 +261,7 @@ make_instruction!(float, float, _sqrt, Decimal, 1); /// does nothing (returns None). Truncates an int to 0. fn _inv(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { vals[0].inverse() } @@ -271,7 +271,7 @@ make_instruction!(float, float, _inv, Decimal, 1); /// Takes the absolute value of the top number fn _abs(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { Some(vals[0].absolute()) } @@ -281,7 +281,7 @@ make_instruction!(float, float, _abs, Decimal, 1); /// Reverses the sign of the top number fn _sign_reverse(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { Some(vals[0].sign_reverse()) } @@ -291,7 +291,7 @@ make_instruction!(float, float, _sign_reverse, Decimal, 1); /// Squares the top number fn _square(vals: Vec) -> Option where - T: Copy + InstructionTrait, + T: Copy + NumericTrait, { Some(vals[0].square()) } diff --git a/src/instructions/utils.rs b/src/instructions/utils.rs index 9b9dd45..59e7972 100644 --- a/src/instructions/utils.rs +++ b/src/instructions/utils.rs @@ -8,7 +8,7 @@ use std::ops::Div; /// /// Trig functions named safe rather than checked to not overlap /// with Decimal library's checked function names. -pub trait InstructionTrait: Sized + Div { +pub trait NumericTrait: Sized + Div { fn checked_div(self, v: Self) -> Option; fn checked_mod(self, v: Self) -> Option; fn increment(self) -> Self; @@ -24,9 +24,10 @@ pub trait InstructionTrait: Sized + Div { fn to_bool(self) -> bool; fn sign_reverse(self) -> Self; fn square(self) -> Self; + fn from_bool(v: bool) -> Self; } -impl InstructionTrait for Decimal { +impl NumericTrait for Decimal { fn checked_div(self, v: Self) -> Option { if v == dec!(0.0) { None } else { Some(self / v) } } @@ -72,9 +73,12 @@ impl InstructionTrait for Decimal { fn square(self) -> Self { self * self } + fn from_bool(v: bool) -> Self { + if v { dec!(1.0) } else { dec!(0.0) } + } } -impl InstructionTrait for i128 { +impl NumericTrait for i128 { fn checked_div(self, v: Self) -> Option { if v == 0 { None } else { Some(self / v) } } @@ -125,4 +129,32 @@ impl InstructionTrait for i128 { fn square(self) -> Self { self * self } + fn from_bool(v: bool) -> Self { + if v { 1 } else { 0 } + } +} + +pub trait LogicalTrait { + fn logical_and(self, v: Self) -> Self; + fn logical_or(self, v: Self) -> Self; + fn logical_not(self) -> Self; + fn logical_xor(self, v: Self) -> Self; +} + +impl LogicalTrait for bool { + fn logical_and(self, v: Self) -> Self { + self && v + } + fn logical_or(self, v: Self) -> Self { + self || v + } + fn logical_not(self) -> Self { + !self + } + fn logical_xor(self, v: Self) -> Self { + match (self, v) { + (true, true) | (false, false) => false, + _ => true, + } + } } diff --git a/src/main.rs b/src/main.rs index 80e509f..91c96f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use instructions::utils::InstructionTrait; +use instructions::utils::NumericTrait; use rust_decimal::MathematicalOps; use rust_decimal::prelude::*; diff --git a/src/push/interpreter.rs b/src/push/interpreter.rs index 21b450d..43d4058 100644 --- a/src/push/interpreter.rs +++ b/src/push/interpreter.rs @@ -1,5 +1,8 @@ use crate::push::state::*; +/// The main function that disperses the exec stack Genes into +/// the respective stacks. Also is where the individual instructions +/// (such as int_add) is ran. pub fn gene_to_stack(state: &mut PushState, gene: Gene) { match gene { Gene::GeneInt(x) => state.int.push(x), @@ -23,15 +26,22 @@ pub fn gene_to_stack(state: &mut PushState, gene: Gene) { } } +/// Where a push program's exec stack is interpreted to completion. +/// TODO: Decide where to place loading in a push program. pub fn interpret_program(state: &mut PushState, step_limit: usize, max_stack_size: isize) { let mut steps: usize = 0; while state.exec.len() > 0 && steps < step_limit { - if let Some(val) = state.exec.pop() {} + if let Some(gene) = state.exec.pop() { + gene_to_stack(state, gene); + steps += 1; + } } } #[cfg(test)] mod tests { + use crate::instructions::numeric::int_add; + use super::*; use rust_decimal::dec; @@ -52,6 +62,89 @@ mod tests { assert_eq!(vec![true], test_state.boolean); test_state.boolean.clear(); - // Need to finish these tests later. + gene_to_stack( + &mut test_state, + Gene::GeneString("test".as_bytes().to_vec()), + ); + assert_eq!(vec!["test".as_bytes().to_vec()], test_state.string); + test_state.string.clear(); + + gene_to_stack(&mut test_state, Gene::GeneChar('a')); + gene_to_stack(&mut test_state, Gene::GeneChar('b')); + gene_to_stack(&mut test_state, Gene::GeneChar('c')); + assert_eq!(vec!['a', 'b', 'c'], test_state.char); + test_state.char.clear(); + + gene_to_stack(&mut test_state, Gene::GeneVectorInt(vec![1, 2, 3])); + gene_to_stack(&mut test_state, Gene::GeneVectorInt(vec![4, 5, 6])); + assert_eq!(vec![vec![1, 2, 3], vec![4, 5, 6]], test_state.vector_int); + test_state.vector_int.clear(); + + gene_to_stack( + &mut test_state, + Gene::GeneVectorFloat(vec![dec!(1.7), dec!(2.4), dec!(3.9)]), + ); + gene_to_stack( + &mut test_state, + Gene::GeneVectorFloat(vec![dec!(4.7), dec!(5.4), dec!(6.9)]), + ); + assert_eq!( + vec![ + vec![dec!(1.7), dec!(2.4), dec!(3.9)], + vec![dec!(4.7), dec!(5.4), dec!(6.9)] + ], + test_state.vector_float + ); + test_state.vector_float.clear(); + + gene_to_stack(&mut test_state, Gene::GeneVectorBoolean(vec![true, false])); + assert_eq!(vec![vec![true, false]], test_state.vector_boolean); + test_state.vector_boolean.clear(); + + gene_to_stack( + &mut test_state, + Gene::GeneVectorString(vec!["test0".as_bytes().to_vec()]), + ); + gene_to_stack( + &mut test_state, + Gene::GeneVectorString(vec![ + "test1".as_bytes().to_vec(), + "test2".as_bytes().to_vec(), + ]), + ); + assert_eq!( + vec![ + vec!["test0".as_bytes().to_vec()], + vec!["test1".as_bytes().to_vec(), "test2".as_bytes().to_vec()] + ], + test_state.vector_string + ); + test_state.vector_string.clear(); + + gene_to_stack(&mut test_state, Gene::GeneVectorChar(vec!['a', 'b'])); + gene_to_stack(&mut test_state, Gene::GeneVectorChar(vec!['b', 'c', 'd'])); + assert_eq!( + vec![vec!['a', 'b'], vec!['b', 'c', 'd']], + test_state.vector_char + ); + test_state.vector_char.clear(); + + let test_block: Gene = Gene::Block(Box::new(vec![ + Gene::GeneInt(1), + Gene::GeneFloat(dec!(2.3)), + Gene::StateFunc(int_add), + ])); + test_state.exec.push(Gene::GeneInt(2)); + gene_to_stack(&mut test_state, test_block); + assert_eq!( + vec![ + Gene::GeneInt(2), + Gene::GeneInt(1), + Gene::GeneFloat(dec!(2.3)), + Gene::StateFunc(int_add) + ], + test_state.exec + ); + // println!("{:?}", test_state.exec); } } diff --git a/src/push/state.rs b/src/push/state.rs index 6e0c226..8c8d65e 100644 --- a/src/push/state.rs +++ b/src/push/state.rs @@ -10,12 +10,12 @@ pub struct PushState { pub float: Vec, pub string: Vec>, pub boolean: Vec, - pub char: Vec, + pub char: Vec, pub vector_int: Vec>, pub vector_float: Vec>, pub vector_string: Vec>>, pub vector_boolean: Vec>, - pub vector_char: Vec>, + pub vector_char: Vec>, pub exec: Vec, pub code: Vec, } @@ -41,12 +41,12 @@ pub enum Gene { GeneFloat(Decimal), GeneBoolean(bool), GeneString(Vec), - GeneChar(u8), + GeneChar(char), GeneVectorInt(Vec), GeneVectorFloat(Vec), GeneVectorBoolean(Vec), GeneVectorString(Vec>), - GeneVectorChar(Vec), + GeneVectorChar(Vec), StateFunc(fn(&mut PushState)), Close, Open(u8),