diff --git a/src/instructions/common.rs b/src/instructions/common.rs index cce1ed5..9fbbee6 100644 --- a/src/instructions/common.rs +++ b/src/instructions/common.rs @@ -1,7 +1,12 @@ use rust_decimal::Decimal; +use std::cmp::{max, min}; use crate::push::state::{Gene, PushState}; +fn min_max_bounds(ndx: i128, length: usize) -> usize { + max(0, min(ndx.unsigned_abs() as usize, length - 1)) +} + /// Acts as a NoOp, does nothing with the vals list. fn _noop(_: Vec) -> Option { None @@ -66,6 +71,228 @@ pub fn code_from_exec(state: &mut PushState) { } } +/// Duplicates an item +pub fn _dup(vals: Vec) -> Option> { + Some(vec![vals[0].clone(), vals[0].clone()]) +} +make_instruction_mult!(int, int, _dup, i128, 1); +make_instruction_mult!(float, float, _dup, Decimal, 1); +make_instruction_mult!(string, string, _dup, Vec, 1); +make_instruction_mult!(boolean, boolean, _dup, bool, 1); +make_instruction_mult!(char, char, _dup, char, 1); +make_instruction_mult!(vector_int, vector_int, _dup, Vec, 1); +make_instruction_mult!(vector_float, vector_float, _dup, Vec, 1); +make_instruction_mult!(vector_string, vector_string, _dup, Vec>, 1); +make_instruction_mult!(vector_boolean, vector_boolean, _dup, Vec, 1); +make_instruction_mult!(vector_char, vector_char, _dup, Vec, 1); +make_instruction_mult!(code, code, _dup, Gene, 1); +make_instruction_mult!(exec, exec, _dup, Gene, 1); + +pub fn _dup_times(vals: Vec, auxs: Vec) -> Option> { + Some(vec![vals[0].clone(); auxs[0] as usize]) +} +make_instruction_mult_aux!(int, int, _dup_times, i128, 1, int, 1, i128); +make_instruction_mult_aux!(float, float, _dup_times, Decimal, 1, int, 1, i128); +make_instruction_mult_aux!(string, string, _dup_times, Vec, 1, int, 1, i128); +make_instruction_mult_aux!(boolean, boolean, _dup_times, bool, 1, int, 1, i128); +make_instruction_mult_aux!(char, char, _dup_times, char, 1, int, 1, i128); +make_instruction_mult_aux!( + vector_int, + vector_int, + _dup_times, + Vec, + 1, + int, + 1, + i128 +); +make_instruction_mult_aux!( + vector_float, + vector_float, + _dup_times, + Vec, + 1, + int, + 1, + i128 +); +make_instruction_mult_aux!( + vector_string, + vector_string, + _dup_times, + Vec>, + 1, + int, + 1, + i128 +); +make_instruction_mult_aux!( + vector_boolean, + vector_boolean, + _dup_times, + Vec, + 1, + int, + 1, + i128 +); +make_instruction_mult_aux!( + vector_char, + vector_char, + _dup_times, + Vec, + 1, + int, + 1, + i128 +); +make_instruction_mult_aux!(code, code, _dup_times, Gene, 1, int, 1, i128); +make_instruction_mult_aux!(exec, exec, _dup_times, Gene, 1, int, 1, i128); + +/// Swaps two values +pub fn _swap(vals: Vec) -> Option> { + Some(vec![vals[0].clone(), vals[1].clone()]) +} +make_instruction_mult!(int, int, _swap, i128, 2); +make_instruction_mult!(float, float, _swap, Decimal, 2); +make_instruction_mult!(string, string, _swap, Vec, 2); +make_instruction_mult!(boolean, boolean, _swap, bool, 2); +make_instruction_mult!(char, char, _swap, char, 2); +make_instruction_mult!(vector_int, vector_int, _swap, Vec, 2); +make_instruction_mult!(vector_float, vector_float, _swap, Vec, 2); +make_instruction_mult!(vector_string, vector_string, _swap, Vec>, 2); +make_instruction_mult!(vector_boolean, vector_boolean, _swap, Vec, 2); +make_instruction_mult!(vector_char, vector_char, _swap, Vec, 2); +make_instruction_mult!(code, code, _swap, Gene, 2); +make_instruction_mult!(exec, exec, _swap, Gene, 2); + +/// Rotates three values +pub fn _rotate(vals: Vec) -> Option> { + Some(vec![vals[2].clone(), vals[0].clone(), vals[1].clone()]) +} +make_instruction_mult!(int, int, _rotate, i128, 3); +make_instruction_mult!(float, float, _rotate, Decimal, 3); +make_instruction_mult!(string, string, _rotate, Vec, 3); +make_instruction_mult!(boolean, boolean, _rotate, bool, 3); +make_instruction_mult!(char, char, _rotate, char, 3); +make_instruction_mult!(vector_int, vector_int, _rotate, Vec, 3); +make_instruction_mult!(vector_float, vector_float, _rotate, Vec, 3); +make_instruction_mult!(vector_string, vector_string, _rotate, Vec>, 3); +make_instruction_mult!(vector_boolean, vector_boolean, _rotate, Vec, 3); +make_instruction_mult!(vector_char, vector_char, _rotate, Vec, 3); +make_instruction_mult!(code, code, _rotate, Gene, 3); +make_instruction_mult!(exec, exec, _rotate, Gene, 3); + +/// Checks if two values are equal +pub fn _equal(vals: Vec) -> Option { + Some(vals[1] == vals[0]) +} +make_instruction!(int, boolean, _equal, i128, 2); +make_instruction!(float, boolean, _equal, Decimal, 2); +make_instruction_clone!(string, boolean, _equal, Vec, 2); +make_instruction!(boolean, boolean, _equal, bool, 2); +make_instruction!(char, boolean, _equal, char, 2); +make_instruction_clone!(vector_int, boolean, _equal, Vec, 2); +make_instruction_clone!(vector_float, boolean, _equal, Vec, 2); +make_instruction_clone!(vector_string, boolean, _equal, Vec>, 2); +make_instruction_clone!(vector_boolean, boolean, _equal, Vec, 2); +make_instruction_clone!(vector_char, boolean, _equal, Vec, 2); +make_instruction_clone!(code, boolean, _equal, Gene, 2); +make_instruction_clone!(exec, boolean, _equal, Gene, 2); + +/// Checks if two values are not equal +pub fn _not_equal(vals: Vec) -> Option { + Some(vals[1] != vals[0]) +} +make_instruction!(int, boolean, _not_equal, i128, 2); +make_instruction!(float, boolean, _not_equal, Decimal, 2); +make_instruction_clone!(string, boolean, _not_equal, Vec, 2); +make_instruction!(boolean, boolean, _not_equal, bool, 2); +make_instruction!(char, boolean, _not_equal, char, 2); +make_instruction_clone!(vector_int, boolean, _not_equal, Vec, 2); +make_instruction_clone!(vector_float, boolean, _not_equal, Vec, 2); +make_instruction_clone!(vector_string, boolean, _not_equal, Vec>, 2); +make_instruction_clone!(vector_boolean, boolean, _not_equal, Vec, 2); +make_instruction_clone!(vector_char, boolean, _not_equal, Vec, 2); +make_instruction_clone!(code, boolean, _not_equal, Gene, 2); +make_instruction_clone!(exec, boolean, _not_equal, Gene, 2); + +macro_rules! flush_state { + ($in_stack:ident) => { + paste::item! { + pub fn [< $in_stack _flush >] (state: &mut PushState) { + state.$in_stack.clear(); + } + } + }; +} +flush_state!(int); +flush_state!(float); +flush_state!(string); +flush_state!(boolean); +flush_state!(char); +flush_state!(vector_int); +flush_state!(vector_float); +flush_state!(vector_string); +flush_state!(vector_boolean); +flush_state!(vector_char); +flush_state!(code); +flush_state!(exec); + +macro_rules! stack_depth { + ($in_stack:ident) => { + paste::item! { + pub fn [< $in_stack _depth >] (state: &mut PushState) { + state.int.push(state.$in_stack.len() as i128) + } + } + }; +} +stack_depth!(int); +stack_depth!(float); +stack_depth!(string); +stack_depth!(boolean); +stack_depth!(char); +stack_depth!(vector_int); +stack_depth!(vector_float); +stack_depth!(vector_string); +stack_depth!(vector_boolean); +stack_depth!(vector_char); +stack_depth!(code); +stack_depth!(exec); + +macro_rules! yank { + ($in_stack:ident, $in_type:expr) => { + paste::item! { + pub fn [< $in_stack _yank >] (state: &mut PushState) { + if state.int.is_empty() || state.$in_stack.is_empty() { + return; + } + let in_stack_len = state.$in_stack.len(); + if $in_type == "i128" && in_stack_len < 2 { + return; + } + // no -1 at the end, handled in the min_max_bounds function + let idx = min_max_bounds(state.int.pop().unwrap(), in_stack_len); + let item = state.$in_stack.remove(in_stack_len - idx); + state.$in_stack.push(item); + } + } + }; +} +yank!(int, "i128"); +yank!(float, ""); +yank!(string, ""); +yank!(boolean, ""); +yank!(char, ""); +yank!(vector_int, ""); +yank!(vector_float, ""); +yank!(vector_string, ""); +yank!(vector_boolean, ""); +yank!(vector_char, ""); +yank!(code, ""); +yank!(exec, ""); + #[cfg(test)] mod tests { use super::*; @@ -116,4 +343,86 @@ mod tests { code_from_exec(&mut test_state); assert_eq!(vec![Gene::GeneInt(5)], test_state.code); } + + #[test] + fn dup_test() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![1, 2]; + int_dup(&mut test_state); + assert_eq!(vec![1, 2, 2], test_state.int); + } + + #[test] + fn dup_times_test() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![3, 2]; + int_dup_times(&mut test_state); + assert_eq!(vec![3, 3], test_state.int); + + test_state.char = vec!['a', 'b']; + test_state.int = vec![3]; + char_dup_times(&mut test_state); + assert_eq!(vec!['a', 'b', 'b', 'b'], test_state.char); + } + + #[test] + fn swap_test() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![1, 2]; + int_swap(&mut test_state); + assert_eq!(vec![2, 1], test_state.int); + } + + #[test] + fn rotate_test() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![1, 2, 3]; + int_rotate(&mut test_state); + assert_eq!(vec![1, 3, 2], test_state.int); + } + + #[test] + fn equal_not_equal_test() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![1, 2, 3]; + int_equal(&mut test_state); + assert_eq!(vec![false], test_state.boolean); + test_state.boolean.clear(); + + test_state.int = vec![1, 2, 2]; + int_equal(&mut test_state); + assert_eq!(vec![true], test_state.boolean); + } + + #[test] + fn flush_test() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![1, 2, 3]; + int_flush(&mut test_state); + assert_eq!(Vec::::new(), test_state.int); + } + + #[test] + fn stack_depth_test() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![1, 2, 3]; + int_depth(&mut test_state); + assert_eq!(vec![1, 2, 3, 3], test_state.int); + } + + #[test] + fn yank_test() { + let mut test_state = EMPTY_STATE; + + test_state.int = vec![1, 2, 3, 4, 5, 6, 7, 8, 2]; + int_yank(&mut test_state); + assert_eq!(vec![1, 2, 3, 4, 5, 7, 8, 6], test_state.int); + } } diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index 35b8398..1edf472 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -224,7 +224,12 @@ pub mod macros { aux_inputs.push(state.$aux_stack[aux_stack_len - n].clone()); } for n in 1..=$fn_arity { - inputs.push(state.$in_stack[in_stack_len - n].clone()); + if std::any::type_name::<$fn_type>() == std::any::type_name::<$aux_type>() { + inputs.push(state.$in_stack[in_stack_len - $aux_arity - n].clone()); + } else { + inputs.push(state.$in_stack[in_stack_len - n].clone()); + } + //inputs.push(state.$in_stack[in_stack_len - n].clone()); } if let Some(result) = $fn_name(inputs, aux_inputs) { for _ in 0..$aux_arity {