Merge new instruction definitions #2
@ -7,3 +7,4 @@ edition = "2024"
|
||||
rand = "0.9.0"
|
||||
paste = "1.0.15"
|
||||
rust_decimal = { version = "1.37", features = ["macros", "maths"] }
|
||||
rush_macro = { path = "rush_macro" }
|
12
rush_macro/Cargo.toml
Normal file
12
rush_macro/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "rush_macro"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = { version = "2.0.100", features = ["full"] }
|
||||
quote = { version = "1.0.40" }
|
||||
proc-macro2 = { version = "1.0.95" }
|
54
rush_macro/src/lib.rs
Normal file
54
rush_macro/src/lib.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use crate::utils::instruction::Extract;
|
||||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
mod utils;
|
||||
|
||||
/// This macro kinda goes super crazy mode
|
||||
/// Here's how to use the macro:
|
||||
///
|
||||
/// `run_instruction!(function_name, output_stack, push state, any amount of
|
||||
/// comma separated stacks by name ; (the semicolon instructs use whether the instruction
|
||||
/// has multiple outputs. If ; passed, assumes multiple, without assumes just one output))`
|
||||
///
|
||||
/// An instruction for int add would be:
|
||||
/// `run_instruction!(_add, int, state, int, int)`
|
||||
/// assuming the _add function takes two integers and returns a single value (Hint: It does).
|
||||
///
|
||||
/// Important notice: the order in which the last amount
|
||||
/// of stacks are passed in matters. The first `int` identifier will tell
|
||||
/// the macro to pop the top int first, the second `int` will pop the next int
|
||||
/// and so on. This even works with multiple different types of stacks.
|
||||
///
|
||||
/// Another important notice: This macro generates boundary checking as well.
|
||||
/// If there are not enough items in the stack to run the function, it
|
||||
/// will not be called.
|
||||
///
|
||||
/// A function with multiple outputs, for example this one:
|
||||
/// ```
|
||||
/// fn aux_iadd(x: i128, y: i128) -> Option<Vec<i128>> {
|
||||
/// Some(vec![x + y, x - y])
|
||||
/// }
|
||||
///
|
||||
/// run_instruction!(aux_iadd, int, state, int, int;);
|
||||
/// ```
|
||||
/// would have the ; placed at the end of the instruction. Check rush's `tests/instruction_test.rs`
|
||||
/// file for an example using this code.
|
||||
///
|
||||
/// Suggestion: If you need to pull an index from the int stack, make it the first argument
|
||||
/// to your function.
|
||||
///
|
||||
/// If there is an instruction with no stacks as input, must put a comma at the end.
|
||||
#[proc_macro]
|
||||
pub fn run_instruction(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let f = parse_macro_input!(input as Extract);
|
||||
quote! { #f }.into()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
assert!(true);
|
||||
}
|
||||
}
|
193
rush_macro/src/utils/instruction.rs
Normal file
193
rush_macro/src/utils/instruction.rs
Normal file
@ -0,0 +1,193 @@
|
||||
//! Where the hard work for automatically generating instructions is done.
|
||||
//! Check `run_instruction!` for more details.
|
||||
//!
|
||||
//! This Stack that isn't repeated is the desired output stack.
|
||||
//! Extract: Function, Stack, State (`,` Stack)* ;?
|
||||
//!
|
||||
//! Function: identifier
|
||||
//!
|
||||
//! State: identifier
|
||||
//!
|
||||
//! Stack: identifier
|
||||
//!
|
||||
//! Aux: expression
|
||||
|
||||
use crate::utils::parse_zero_or_more;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{ToTokens, quote};
|
||||
use std::cmp::PartialEq;
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
|
||||
/// Checks if there is a semicolon at the end
|
||||
fn parse_aux<T: Parse>(input: ParseStream) -> bool {
|
||||
if let Ok(_) = input.parse::<syn::Token![;]>() {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub struct Extract {
|
||||
func: Function,
|
||||
out_stack: Stack,
|
||||
state: State,
|
||||
stacks: Vec<Stack>,
|
||||
aux: bool,
|
||||
}
|
||||
|
||||
impl Parse for Extract {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let func = input.parse()?;
|
||||
let out_stack = input.parse()?;
|
||||
let state = input.parse()?;
|
||||
let stacks = parse_zero_or_more(input);
|
||||
let aux = parse_aux::<syn::Token![,]>(input);
|
||||
Ok(Extract {
|
||||
func,
|
||||
out_stack,
|
||||
state,
|
||||
stacks,
|
||||
aux,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Extract {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let inner_func = &self.func;
|
||||
let inner_out_stack = &self.out_stack.0;
|
||||
let inner_state = &self.state.0;
|
||||
let stacks = &self.stacks;
|
||||
let aux = &self.aux;
|
||||
|
||||
// Gets the counts of each stack passed to the macro held
|
||||
// similarly to a map as: (stack, count).
|
||||
//
|
||||
// Chosen over a HashMap bc these types don't implement Hash
|
||||
// HashMaps are O(nlogn) at worst this is O(n^2). Largest instruction
|
||||
// passes 4 stacks so this shouldn't matter in the long run.
|
||||
let mut counts = Vec::new();
|
||||
for stack in stacks {
|
||||
match counts.iter_mut().find(|(x, _)| x == stack) {
|
||||
Some((_, count)) => *count += 1,
|
||||
None => counts.push((stack, 1usize)),
|
||||
}
|
||||
}
|
||||
|
||||
// Writes the piece of the code that ensures the stacks have enough values
|
||||
// to function without error.
|
||||
let conditions = counts.iter().map(|(stack, count)| {
|
||||
let inner_stack = &stack.0;
|
||||
quote! { #inner_state.#inner_stack.len() >= #count }
|
||||
});
|
||||
|
||||
// In case the instruction returns None (meaning revert the state),
|
||||
// need to store the values to return them
|
||||
let store_values = stacks.iter().enumerate().map(|(i, stack)| {
|
||||
let inner_stack = &&stack.0;
|
||||
let var_name = quote::format_ident!("val_{}", i);
|
||||
quote! { let #var_name = #inner_state.#inner_stack.pop().unwrap(); }
|
||||
});
|
||||
|
||||
// Create the variable names themselves to store the
|
||||
// popped values.
|
||||
let value_vars = (0..stacks.len())
|
||||
.map(|i| quote::format_ident!("val_{}", i))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Create restore code in case None is returned from the function.
|
||||
let restore_values =
|
||||
stacks
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(value_vars.iter().rev())
|
||||
.map(|(stack, var)| {
|
||||
let inner_stack = &&stack.0;
|
||||
quote! { #inner_state.#inner_stack.push(#var.clone()); }
|
||||
});
|
||||
|
||||
// The logic for running the function and returning values
|
||||
// if bad.
|
||||
let aux_run = match aux {
|
||||
true => quote! {
|
||||
let result = #inner_func(#(#value_vars.clone()),*);
|
||||
if let Some(result) = result {
|
||||
// Transforming the result vector into an iterator with .iter() was
|
||||
// causing problems with the vector_string stack. Iterating this way
|
||||
// fixes the problem.
|
||||
for n in 0..result.len() {
|
||||
#inner_state.#inner_out_stack.push(result[n].clone())
|
||||
}
|
||||
} else {
|
||||
#(#restore_values)*
|
||||
}
|
||||
},
|
||||
false => quote! { // This arm is used most of the time
|
||||
let result = #inner_func(#(#value_vars.clone()),*);
|
||||
if let Some(result) = result {
|
||||
#inner_state.#inner_out_stack.push(result);
|
||||
} else {
|
||||
#(#restore_values)*
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Where the pieces of the puzzle are put together.
|
||||
// tokens then used to create the function.
|
||||
tokens.extend(quote! {
|
||||
if true #(&& (#conditions))* {
|
||||
#(#store_values)*
|
||||
#aux_run
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct State(syn::Ident);
|
||||
|
||||
impl Parse for State {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
_ = input.parse::<syn::Token![,]>();
|
||||
syn::Ident::parse(input).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for State {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
self.0.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
||||
struct Stack(syn::Ident);
|
||||
|
||||
impl Parse for Stack {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
_ = input.parse::<syn::Token![,]>();
|
||||
syn::Ident::parse(input).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Stack {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
self.0.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Stack> for &Stack {
|
||||
fn eq(&self, other: &Stack) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
struct Function(syn::Ident);
|
||||
|
||||
impl Parse for Function {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
syn::Ident::parse(input).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Function {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
self.0.to_tokens(tokens)
|
||||
}
|
||||
}
|
11
rush_macro/src/utils/mod.rs
Normal file
11
rush_macro/src/utils/mod.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
|
||||
pub mod instruction;
|
||||
|
||||
fn parse_zero_or_more<T: Parse>(input: ParseStream) -> Vec<T> {
|
||||
let mut result = Vec::new();
|
||||
while let Ok(item) = input.parse() {
|
||||
result.push(item);
|
||||
}
|
||||
result
|
||||
}
|
@ -5,32 +5,29 @@ use crate::push::state::{Gene, PushState};
|
||||
use super::common::{code_from_exec, code_pop, int_pop};
|
||||
|
||||
/// Checks to see if a single gene is a block.
|
||||
fn _is_block(vals: Vec<Gene>) -> Option<bool> {
|
||||
Some(match vals[0] {
|
||||
fn _is_block(a: Gene) -> Option<bool> {
|
||||
Some(match a {
|
||||
Gene::Block(_) => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
make_instruction_clone!(code, boolean, _is_block, Gene, 1);
|
||||
|
||||
/// Checks to see if a single gene is not a block.
|
||||
fn _is_singular(vals: Vec<Gene>) -> Option<bool> {
|
||||
Some(_is_block(vals)?.not())
|
||||
fn _is_singular(a: Gene) -> Option<bool> {
|
||||
Some(_is_block(a)?.not())
|
||||
}
|
||||
make_instruction_clone!(code, boolean, _is_singular, Gene, 1);
|
||||
|
||||
/// Returns the length of a block, else 1 if not a block
|
||||
fn _length(vals: Vec<Gene>) -> Option<i128> {
|
||||
Some(match &vals[0] {
|
||||
fn _length(a: Gene) -> Option<i128> {
|
||||
Some(match &a {
|
||||
Gene::Block(x) => x.len() as i128,
|
||||
_ => 1,
|
||||
})
|
||||
}
|
||||
make_instruction_clone!(code, int, _length, Gene, 1);
|
||||
|
||||
/// Returns the first item in a block if doable, else None
|
||||
fn _first(vals: Vec<Gene>) -> Option<Gene> {
|
||||
match &vals[0] {
|
||||
fn _first(a: Gene) -> Option<Gene> {
|
||||
match &a {
|
||||
Gene::Block(x) => {
|
||||
if x.len() > 1 {
|
||||
Some(x[0].clone())
|
||||
@ -41,11 +38,10 @@ fn _first(vals: Vec<Gene>) -> Option<Gene> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
make_instruction_clone!(code, code, _first, Gene, 1);
|
||||
|
||||
/// Returns the first item in a block if applicable, else None
|
||||
fn _last(vals: Vec<Gene>) -> Option<Gene> {
|
||||
match &vals[0] {
|
||||
fn _last(a: Gene) -> Option<Gene> {
|
||||
match &a {
|
||||
Gene::Block(x) => {
|
||||
if x.len() > 1 {
|
||||
Some(x.last()?.clone())
|
||||
@ -56,11 +52,10 @@ fn _last(vals: Vec<Gene>) -> Option<Gene> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
make_instruction_clone!(code, code, _last, Gene, 1);
|
||||
|
||||
/// Returns all but the first code item in a block if applicable, else None
|
||||
fn _rest(vals: Vec<Gene>) -> Option<Gene> {
|
||||
match &vals[0] {
|
||||
fn _rest(a: Gene) -> Option<Gene> {
|
||||
match &a {
|
||||
Gene::Block(x) => {
|
||||
if x.len() > 1 {
|
||||
Some(Gene::Block(x[1..].to_vec()))
|
||||
@ -71,11 +66,10 @@ fn _rest(vals: Vec<Gene>) -> Option<Gene> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
make_instruction_clone!(code, code, _rest, Gene, 1);
|
||||
|
||||
/// Returns all but the first code item in a block if applicable, else None
|
||||
fn _but_last(vals: Vec<Gene>) -> Option<Gene> {
|
||||
match &vals[0] {
|
||||
fn _but_last(a: Gene) -> Option<Gene> {
|
||||
match &a {
|
||||
Gene::Block(x) => {
|
||||
let x_len = x.len();
|
||||
if x_len > 1 {
|
||||
@ -87,19 +81,17 @@ fn _but_last(vals: Vec<Gene>) -> Option<Gene> {
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
make_instruction_clone!(code, code, _but_last, Gene, 1);
|
||||
|
||||
/// Returns all the vals wrapped in a code block
|
||||
fn _wrap_block(vals: Vec<Gene>) -> Option<Gene> {
|
||||
Some(Gene::Block(vals))
|
||||
/// Returns a gene wrapped in a block
|
||||
fn _wrap_block(a: Gene) -> Option<Gene> {
|
||||
Some(Gene::Block(vec![a]))
|
||||
}
|
||||
make_instruction_clone!(code, code, _wrap_block, Gene, 1);
|
||||
|
||||
/// Combines two genes into one. Accounts for blocks.
|
||||
/// If the second gene is a block and the first one isn't,
|
||||
/// appends the first gene to the second gene.
|
||||
fn _combine(vals: Vec<Gene>) -> Option<Gene> {
|
||||
match (&vals[0], &vals[1]) {
|
||||
fn _combine(a: Gene, b: Gene) -> Option<Gene> {
|
||||
match (&a, &b) {
|
||||
(Gene::Block(x), Gene::Block(y)) => {
|
||||
let x_clone = x.clone();
|
||||
let mut y_clone = y.clone();
|
||||
@ -119,7 +111,6 @@ fn _combine(vals: Vec<Gene>) -> Option<Gene> {
|
||||
(x, y) => Some(Gene::Block(vec![x.clone(), y.clone()])),
|
||||
}
|
||||
}
|
||||
make_instruction_clone!(code, code, _combine, Gene, 2);
|
||||
|
||||
/// Pushes `code_pop` and the top item of the code stack to the exec stack.
|
||||
/// Top code item gets executed before being removed from code stack.
|
||||
@ -330,15 +321,9 @@ pub fn code_map(state: &mut PushState) {
|
||||
|
||||
/// If top bool is true, execute top element of code/exec stack and skip the second.
|
||||
/// If false, execute second element and skip the top.
|
||||
pub fn _if(vals: Vec<Gene>, auxs: Vec<bool>) -> Option<Gene> {
|
||||
Some(if auxs[0] {
|
||||
vals[0].clone()
|
||||
} else {
|
||||
vals[1].clone()
|
||||
})
|
||||
pub fn _if(a: Gene, b: Gene, cond: bool) -> Option<Gene> {
|
||||
Some(if cond { a } else { b })
|
||||
}
|
||||
make_instruction_aux!(code, exec, _if, Gene, 2, boolean, 1, bool);
|
||||
make_instruction_aux!(exec, exec, _if, Gene, 2, boolean, 1, bool);
|
||||
|
||||
/// Evaluates the top code item if the top code is true, else pops it.
|
||||
pub fn code_when(state: &mut PushState) {
|
||||
@ -364,20 +349,19 @@ pub fn exec_when(state: &mut PushState) {
|
||||
|
||||
/// Pushes true if the second code item is found within the first item.
|
||||
/// If the first item isn't a block, coerced into one.
|
||||
pub fn _member(vals: Vec<Gene>) -> Option<bool> {
|
||||
let block = match vals[0].clone() {
|
||||
pub fn _member(a: Gene, b: Gene) -> Option<bool> {
|
||||
let block = match b {
|
||||
Gene::Block(val) => val,
|
||||
val => vec![val],
|
||||
};
|
||||
|
||||
Some(block.contains(&vals[1]))
|
||||
Some(block.contains(&a))
|
||||
}
|
||||
make_instruction_clone!(code, boolean, _member, Gene, 2);
|
||||
|
||||
/// Pushes the nth item of the top element of the code stack.
|
||||
/// If top code item isn't a block, wrap one around it.
|
||||
pub fn _nth(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> {
|
||||
let gene_vec = match vals[0].clone() {
|
||||
pub fn _nth(a: Gene, idx: i128) -> Option<Gene> {
|
||||
let gene_vec = match a {
|
||||
Gene::Block(val) => val,
|
||||
val => vec![val],
|
||||
};
|
||||
@ -385,22 +369,19 @@ pub fn _nth(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> {
|
||||
if gene_vec_len == 0 {
|
||||
return None;
|
||||
}
|
||||
let ndx = auxs[0].abs() as usize % gene_vec_len;
|
||||
let ndx = idx.abs() as usize % gene_vec_len;
|
||||
Some(gene_vec[ndx].clone())
|
||||
}
|
||||
make_instruction_aux!(code, code, _nth, Gene, 1, int, 1, i128);
|
||||
|
||||
/// Pushes an empty block to the top of a stack.
|
||||
pub fn _make_empty_block<T>(_: Vec<T>) -> Option<Gene> {
|
||||
pub fn _make_empty_block<T>() -> Option<Gene> {
|
||||
Some(Gene::Block(vec![]))
|
||||
}
|
||||
make_instruction_clone!(code, code, _make_empty_block, Gene, 0);
|
||||
make_instruction_clone!(exec, exec, _make_empty_block, Gene, 0);
|
||||
|
||||
/// Checks to see if the top item on the code/exec stack is an empty block.
|
||||
/// True if is, False if not.
|
||||
pub fn _is_empty_block(vals: Vec<Gene>) -> Option<bool> {
|
||||
Some(match vals[0].clone() {
|
||||
pub fn _is_empty_block(a: Gene) -> Option<bool> {
|
||||
Some(match a {
|
||||
Gene::Block(val) => {
|
||||
if val.is_empty() {
|
||||
true
|
||||
@ -411,70 +392,64 @@ pub fn _is_empty_block(vals: Vec<Gene>) -> Option<bool> {
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
make_instruction_clone!(code, boolean, _is_empty_block, Gene, 1);
|
||||
make_instruction_clone!(exec, boolean, _is_empty_block, Gene, 1);
|
||||
|
||||
/// Returns the size of the top item on the code/exec stack.
|
||||
pub fn _size(vals: Vec<Gene>) -> Option<i128> {
|
||||
Some(match vals[0].clone() {
|
||||
pub fn _size(a: Gene) -> Option<i128> {
|
||||
Some(match a.clone() {
|
||||
Gene::Block(val) => val.len() as i128,
|
||||
_ => 1,
|
||||
})
|
||||
}
|
||||
make_instruction_clone!(code, int, _size, Gene, 1);
|
||||
make_instruction_clone!(exec, int, _size, Gene, 1);
|
||||
|
||||
/// Returns a nested element inside a block based on an int.
|
||||
pub fn _extract(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> {
|
||||
match vals[0].clone() {
|
||||
pub fn _extract(a: Gene, idx: i128) -> Option<Gene> {
|
||||
match &a {
|
||||
block @ Gene::Block(_) => {
|
||||
let block_len = block.rec_len();
|
||||
if block_len == 0 {
|
||||
None
|
||||
} else {
|
||||
let ndx = (auxs[0] % block_len as i128).abs() as usize;
|
||||
Some(vals[0].clone().code_at_point(ndx)?)
|
||||
let ndx = (idx % block_len as i128).abs() as usize;
|
||||
Some(a.code_at_point(ndx)?)
|
||||
}
|
||||
}
|
||||
val => Some(val),
|
||||
_ => Some(a),
|
||||
}
|
||||
}
|
||||
make_instruction_aux!(code, code, _extract, Gene, 1, int, 1, i128);
|
||||
|
||||
/// Inserts a gene at a given position in into the top block based off an
|
||||
/// int from the top of the int stack. The top code item is coerced into a block
|
||||
/// if needed.
|
||||
pub fn _insert(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> {
|
||||
let mut block = match vals[0].clone() {
|
||||
pub fn _insert(a: Gene, b: Gene, idx: i128) -> Option<Gene> {
|
||||
let mut block = match a.clone() {
|
||||
iblock @ Gene::Block(_) => iblock,
|
||||
val => Gene::Block(vec![val]),
|
||||
};
|
||||
if block.rec_len() == 0 {
|
||||
return _combine(vec![block, vals[1].clone()]);
|
||||
return _combine(block, b);
|
||||
}
|
||||
let ndx = auxs[0].abs() as usize % block.rec_len();
|
||||
block.with_code_inserted_at_point(vals[1].clone(), ndx);
|
||||
let ndx = idx.abs() as usize % block.rec_len();
|
||||
block.with_code_inserted_at_point(b, ndx);
|
||||
Some(block)
|
||||
}
|
||||
make_instruction_aux!(code, code, _insert, Gene, 2, int, 1, i128);
|
||||
|
||||
/// Pushes the first position of the 2nd code item within the top code item.
|
||||
/// If not found, pushes -1. If top code item isn't a block, returns 0 if top
|
||||
/// two code items equal, -1 otherwise.
|
||||
pub fn _first_position(vals: Vec<Gene>) -> Option<i128> {
|
||||
let bad_cond: bool = match &vals[0] {
|
||||
pub fn _first_position(a: Gene, b: Gene) -> Option<i128> {
|
||||
let bad_cond: bool = match &a {
|
||||
Gene::Block(val) => val.len() == 0,
|
||||
_ => true,
|
||||
};
|
||||
if bad_cond {
|
||||
if vals[0] == vals[1] {
|
||||
if a == b {
|
||||
return Some(0);
|
||||
}
|
||||
} else {
|
||||
match &vals[0] {
|
||||
match &a {
|
||||
Gene::Block(val) => {
|
||||
for (idx, el) in val.iter().enumerate() {
|
||||
if el == &vals[1] {
|
||||
if el == &b {
|
||||
return Some(idx as i128);
|
||||
}
|
||||
}
|
||||
@ -484,11 +459,10 @@ pub fn _first_position(vals: Vec<Gene>) -> Option<i128> {
|
||||
}
|
||||
Some(-1)
|
||||
}
|
||||
make_instruction_clone!(code, int, _first_position, Gene, 2);
|
||||
|
||||
/// Reverses the top block. Does nothing if not a block.
|
||||
pub fn _reverse(vals: Vec<Gene>) -> Option<Gene> {
|
||||
Some(match vals[0].clone() {
|
||||
pub fn _reverse(a: Gene) -> Option<Gene> {
|
||||
Some(match a {
|
||||
Gene::Block(mut val) => {
|
||||
val.reverse();
|
||||
Gene::Block(val)
|
||||
@ -496,7 +470,55 @@ pub fn _reverse(vals: Vec<Gene>) -> Option<Gene> {
|
||||
val => val,
|
||||
})
|
||||
}
|
||||
make_instruction_clone!(code, code, _reverse, Gene, 1);
|
||||
|
||||
/// Acts as a noop, does nothing to the state internally.
|
||||
macro_rules! noop {
|
||||
($stack:ident, $name:ident) => {
|
||||
paste::item! {
|
||||
pub fn [< $stack $name >] (_: &mut PushState) {
|
||||
()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! make_code_instructions {
|
||||
($stack:ident) => {
|
||||
make_instruction_new!(_is_block, $stack, boolean, $stack);
|
||||
make_instruction_new!(_is_singular, $stack, boolean, $stack);
|
||||
make_instruction_new!(_length, $stack, int, $stack);
|
||||
make_instruction_new!(_first, $stack, $stack, $stack);
|
||||
make_instruction_new!(_last, $stack, $stack, $stack);
|
||||
make_instruction_new!(_rest, $stack, $stack, $stack);
|
||||
make_instruction_new!(_but_last, $stack, $stack, $stack);
|
||||
make_instruction_new!(_wrap_block, $stack, $stack, $stack);
|
||||
make_instruction_new!(_combine, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_if, $stack, exec, $stack, $stack, boolean);
|
||||
make_instruction_new!(_member, $stack, boolean, $stack, $stack);
|
||||
make_instruction_new!(_nth, $stack, $stack, $stack, int);
|
||||
make_instruction_empty!(_make_empty_block, $stack, $stack, Gene);
|
||||
make_instruction_new!(_is_empty_block, $stack, boolean, $stack);
|
||||
make_instruction_new!(_size, $stack, int, $stack);
|
||||
make_instruction_new!(_extract, $stack, $stack, $stack, int);
|
||||
make_instruction_new!(_insert, $stack, $stack, $stack, $stack, int);
|
||||
make_instruction_new!(_first_position, $stack, int, $stack, $stack);
|
||||
make_instruction_new!(_reverse, $stack, $stack, $stack);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! all_code_instructions {
|
||||
() => {
|
||||
make_code_instructions!(code);
|
||||
make_code_instructions!(exec);
|
||||
|
||||
// Misc instructions
|
||||
noop!(code, _noop);
|
||||
noop!(exec, _noop);
|
||||
noop!(code, _noop_block);
|
||||
noop!(exec, _noop_block);
|
||||
};
|
||||
}
|
||||
all_code_instructions!();
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -950,24 +972,24 @@ mod tests {
|
||||
let mut test_state = EMPTY_STATE;
|
||||
|
||||
test_state.code = vec![
|
||||
Gene::GeneInt(0),
|
||||
Gene::Block(vec![
|
||||
Gene::GeneInt(0),
|
||||
Gene::GeneInt(4),
|
||||
Gene::StateFunc(exec_do_range),
|
||||
]),
|
||||
Gene::GeneInt(0),
|
||||
];
|
||||
code_member(&mut test_state);
|
||||
assert_eq!(vec![true], test_state.boolean);
|
||||
test_state.boolean.clear();
|
||||
|
||||
test_state.code = vec![
|
||||
Gene::GeneInt(0),
|
||||
Gene::Block(vec![
|
||||
Gene::GeneInt(5),
|
||||
Gene::GeneInt(4),
|
||||
Gene::StateFunc(exec_do_range),
|
||||
]),
|
||||
Gene::GeneInt(0),
|
||||
];
|
||||
code_member(&mut test_state);
|
||||
assert_eq!(vec![false], test_state.boolean);
|
||||
@ -1233,4 +1255,19 @@ mod tests {
|
||||
test_state.code
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn noop_test() {
|
||||
let mut test_state = EMPTY_STATE;
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
let test_state_copy = test_state.clone();
|
||||
code_noop(&mut test_state);
|
||||
assert_eq!(test_state, test_state_copy);
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
let test_state_copy = test_state.clone();
|
||||
exec_noop(&mut test_state);
|
||||
assert_eq!(test_state, test_state_copy);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
use rust_decimal::Decimal;
|
||||
use std::cmp::{max, min};
|
||||
|
||||
use crate::push::state::{Gene, PushState};
|
||||
@ -7,216 +6,100 @@ 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<T>(_: Vec<T>) -> Option<T> {
|
||||
None
|
||||
}
|
||||
make_instruction_clone!(code, code, _noop, Gene, 0);
|
||||
make_instruction_clone!(exec, exec, _noop, Gene, 0);
|
||||
|
||||
fn _noop_block<T>(_: Vec<T>) -> Option<T> {
|
||||
None
|
||||
}
|
||||
make_instruction_clone!(code, code, _noop_block, Gene, 0);
|
||||
make_instruction_clone!(exec, exec, _noop_block, Gene, 0);
|
||||
|
||||
/// Pops the top value from the stack
|
||||
fn _pop<T>(vals: Vec<T>) -> Option<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
// This is suboptimal, how to re-write?
|
||||
// Calls for a complete overhaul later down the line.
|
||||
Some(vals[0].clone())
|
||||
macro_rules! pop {
|
||||
($in_stack:ident) => {
|
||||
paste::item! {
|
||||
pub fn [< $in_stack _pop >] (state: &mut PushState) {
|
||||
state.$in_stack.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! gene_map {
|
||||
(int, $val:ident) => {
|
||||
Gene::GeneInt($val)
|
||||
};
|
||||
(float, $val:ident) => {
|
||||
Gene::GeneFloat($val)
|
||||
};
|
||||
(string, $val:ident) => {
|
||||
Gene::GeneString($val)
|
||||
};
|
||||
(boolean, $val:ident) => {
|
||||
Gene::GeneBoolean($val)
|
||||
};
|
||||
(char, $val:ident) => {
|
||||
Gene::GeneChar($val)
|
||||
};
|
||||
(vector_int, $val:ident) => {
|
||||
Gene::GeneVectorInt($val)
|
||||
};
|
||||
(vector_float, $val:ident) => {
|
||||
Gene::GeneVectorFloat($val)
|
||||
};
|
||||
(vector_string, $val:ident) => {
|
||||
Gene::GeneVectorString($val)
|
||||
};
|
||||
(vector_boolean, $val:ident) => {
|
||||
Gene::GeneVectorBoolean($val)
|
||||
};
|
||||
(vector_char, $val:ident) => {
|
||||
Gene::GeneVectorChar($val)
|
||||
};
|
||||
(code, $val:ident) => {
|
||||
$val
|
||||
};
|
||||
(exec, $val:ident) => {
|
||||
$val
|
||||
};
|
||||
}
|
||||
make_instruction_no_out!(int, _pop, i128, 1);
|
||||
make_instruction_no_out!(float, _pop, Decimal, 1);
|
||||
make_instruction_no_out!(string, _pop, Vec<char>, 1);
|
||||
make_instruction_no_out!(boolean, _pop, bool, 1);
|
||||
make_instruction_no_out!(char, _pop, char, 1);
|
||||
make_instruction_no_out!(vector_int, _pop, Vec<i128>, 1);
|
||||
make_instruction_no_out!(vector_float, _pop, Vec<Decimal>, 1);
|
||||
make_instruction_no_out!(vector_string, _pop, Vec<Vec<char>>, 1);
|
||||
make_instruction_no_out!(vector_boolean, _pop, Vec<bool>, 1);
|
||||
make_instruction_no_out!(vector_char, _pop, Vec<char>, 1);
|
||||
make_instruction_no_out!(code, _pop, Gene, 1);
|
||||
make_instruction_no_out!(exec, _pop, Gene, 1);
|
||||
|
||||
/// Wraps a type in its respective Gene
|
||||
macro_rules! make_code {
|
||||
($in_stack:ident, $gene:ident) => {
|
||||
($stack:ident) => {
|
||||
paste::item! {
|
||||
pub fn [< code_from_ $in_stack >] (state: &mut PushState) {
|
||||
if let Some(val) = state.$in_stack.pop() {
|
||||
state.code.push(Gene::$gene(val));
|
||||
pub fn [< code_from_ $stack >] (state: &mut PushState) {
|
||||
if let Some(val) = state.$stack.pop() {
|
||||
let push_val = gene_map!($stack, val);
|
||||
state.code.push(push_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
make_code!(int, GeneInt);
|
||||
make_code!(float, GeneFloat);
|
||||
make_code!(string, GeneString);
|
||||
make_code!(boolean, GeneBoolean);
|
||||
make_code!(char, GeneChar);
|
||||
make_code!(vector_int, GeneVectorInt);
|
||||
make_code!(vector_float, GeneVectorFloat);
|
||||
make_code!(vector_string, GeneVectorString);
|
||||
make_code!(vector_boolean, GeneVectorBoolean);
|
||||
make_code!(vector_char, GeneVectorChar);
|
||||
|
||||
pub fn code_from_exec(state: &mut PushState) {
|
||||
if let Some(gene) = state.exec.pop() {
|
||||
state.code.push(gene);
|
||||
}
|
||||
}
|
||||
|
||||
/// Duplicates an item
|
||||
pub fn _dup<T: Clone>(vals: Vec<T>) -> Option<Vec<T>> {
|
||||
Some(vec![vals[0].clone(), vals[0].clone()])
|
||||
pub fn _dup<T: Clone>(val: T) -> Option<Vec<T>> {
|
||||
Some(vec![val.clone(), val])
|
||||
}
|
||||
make_instruction_mult!(int, int, _dup, i128, 1);
|
||||
make_instruction_mult!(float, float, _dup, Decimal, 1);
|
||||
make_instruction_mult!(string, string, _dup, Vec<char>, 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<i128>, 1);
|
||||
make_instruction_mult!(vector_float, vector_float, _dup, Vec<Decimal>, 1);
|
||||
make_instruction_mult!(vector_string, vector_string, _dup, Vec<Vec<char>>, 1);
|
||||
make_instruction_mult!(vector_boolean, vector_boolean, _dup, Vec<bool>, 1);
|
||||
make_instruction_mult!(vector_char, vector_char, _dup, Vec<char>, 1);
|
||||
make_instruction_mult!(code, code, _dup, Gene, 1);
|
||||
make_instruction_mult!(exec, exec, _dup, Gene, 1);
|
||||
|
||||
pub fn _dup_times<T: Clone>(vals: Vec<T>, auxs: Vec<i128>) -> Option<Vec<T>> {
|
||||
Some(vec![vals[0].clone(); auxs[0] as usize])
|
||||
pub fn _dup_times<T: Clone>(amt: i128, val: T) -> Option<Vec<T>> {
|
||||
Some(vec![val; amt 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<char>, 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<i128>,
|
||||
1,
|
||||
int,
|
||||
1,
|
||||
i128
|
||||
);
|
||||
make_instruction_mult_aux!(
|
||||
vector_float,
|
||||
vector_float,
|
||||
_dup_times,
|
||||
Vec<Decimal>,
|
||||
1,
|
||||
int,
|
||||
1,
|
||||
i128
|
||||
);
|
||||
make_instruction_mult_aux!(
|
||||
vector_string,
|
||||
vector_string,
|
||||
_dup_times,
|
||||
Vec<Vec<char>>,
|
||||
1,
|
||||
int,
|
||||
1,
|
||||
i128
|
||||
);
|
||||
make_instruction_mult_aux!(
|
||||
vector_boolean,
|
||||
vector_boolean,
|
||||
_dup_times,
|
||||
Vec<bool>,
|
||||
1,
|
||||
int,
|
||||
1,
|
||||
i128
|
||||
);
|
||||
make_instruction_mult_aux!(
|
||||
vector_char,
|
||||
vector_char,
|
||||
_dup_times,
|
||||
Vec<char>,
|
||||
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<T: Clone>(vals: Vec<T>) -> Option<Vec<T>> {
|
||||
Some(vec![vals[0].clone(), vals[1].clone()])
|
||||
pub fn _swap<T: Clone>(a: T, b: T) -> Option<Vec<T>> {
|
||||
Some(vec![a, b])
|
||||
}
|
||||
make_instruction_mult!(int, int, _swap, i128, 2);
|
||||
make_instruction_mult!(float, float, _swap, Decimal, 2);
|
||||
make_instruction_mult!(string, string, _swap, Vec<char>, 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<i128>, 2);
|
||||
make_instruction_mult!(vector_float, vector_float, _swap, Vec<Decimal>, 2);
|
||||
make_instruction_mult!(vector_string, vector_string, _swap, Vec<Vec<char>>, 2);
|
||||
make_instruction_mult!(vector_boolean, vector_boolean, _swap, Vec<bool>, 2);
|
||||
make_instruction_mult!(vector_char, vector_char, _swap, Vec<char>, 2);
|
||||
make_instruction_mult!(code, code, _swap, Gene, 2);
|
||||
make_instruction_mult!(exec, exec, _swap, Gene, 2);
|
||||
|
||||
/// Rotates three values
|
||||
pub fn _rotate<T: Clone>(vals: Vec<T>) -> Option<Vec<T>> {
|
||||
Some(vec![vals[2].clone(), vals[0].clone(), vals[1].clone()])
|
||||
pub fn _rotate<T>(a: T, b: T, c: T) -> Option<Vec<T>> {
|
||||
Some(vec![c, a, b])
|
||||
}
|
||||
make_instruction_mult!(int, int, _rotate, i128, 3);
|
||||
make_instruction_mult!(float, float, _rotate, Decimal, 3);
|
||||
make_instruction_mult!(string, string, _rotate, Vec<char>, 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<i128>, 3);
|
||||
make_instruction_mult!(vector_float, vector_float, _rotate, Vec<Decimal>, 3);
|
||||
make_instruction_mult!(vector_string, vector_string, _rotate, Vec<Vec<char>>, 3);
|
||||
make_instruction_mult!(vector_boolean, vector_boolean, _rotate, Vec<bool>, 3);
|
||||
make_instruction_mult!(vector_char, vector_char, _rotate, Vec<char>, 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<T: Clone + Eq>(vals: Vec<T>) -> Option<bool> {
|
||||
Some(vals[1] == vals[0])
|
||||
pub fn _equal<T: Eq>(a: T, b: T) -> Option<bool> {
|
||||
Some(b == a)
|
||||
}
|
||||
make_instruction!(int, boolean, _equal, i128, 2);
|
||||
make_instruction!(float, boolean, _equal, Decimal, 2);
|
||||
make_instruction_clone!(string, boolean, _equal, Vec<char>, 2);
|
||||
make_instruction!(boolean, boolean, _equal, bool, 2);
|
||||
make_instruction!(char, boolean, _equal, char, 2);
|
||||
make_instruction_clone!(vector_int, boolean, _equal, Vec<i128>, 2);
|
||||
make_instruction_clone!(vector_float, boolean, _equal, Vec<Decimal>, 2);
|
||||
make_instruction_clone!(vector_string, boolean, _equal, Vec<Vec<char>>, 2);
|
||||
make_instruction_clone!(vector_boolean, boolean, _equal, Vec<bool>, 2);
|
||||
make_instruction_clone!(vector_char, boolean, _equal, Vec<char>, 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<T: Clone + Eq>(vals: Vec<T>) -> Option<bool> {
|
||||
Some(vals[1] != vals[0])
|
||||
pub fn _not_equal<T: Clone + Eq>(a: T, b: T) -> Option<bool> {
|
||||
Some(b != a)
|
||||
}
|
||||
make_instruction!(int, boolean, _not_equal, i128, 2);
|
||||
make_instruction!(float, boolean, _not_equal, Decimal, 2);
|
||||
make_instruction_clone!(string, boolean, _not_equal, Vec<char>, 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<i128>, 2);
|
||||
make_instruction_clone!(vector_float, boolean, _not_equal, Vec<Decimal>, 2);
|
||||
make_instruction_clone!(vector_string, boolean, _not_equal, Vec<Vec<char>>, 2);
|
||||
make_instruction_clone!(vector_boolean, boolean, _not_equal, Vec<bool>, 2);
|
||||
make_instruction_clone!(vector_char, boolean, _not_equal, Vec<char>, 2);
|
||||
make_instruction_clone!(code, boolean, _not_equal, Gene, 2);
|
||||
make_instruction_clone!(exec, boolean, _not_equal, Gene, 2);
|
||||
|
||||
/// Removes all values from a stack
|
||||
macro_rules! flush_state {
|
||||
($in_stack:ident) => {
|
||||
paste::item! {
|
||||
@ -226,19 +109,8 @@ macro_rules! flush_state {
|
||||
}
|
||||
};
|
||||
}
|
||||
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);
|
||||
|
||||
/// Returns the depth of a stack
|
||||
macro_rules! stack_depth {
|
||||
($in_stack:ident) => {
|
||||
paste::item! {
|
||||
@ -248,71 +120,136 @@ macro_rules! stack_depth {
|
||||
}
|
||||
};
|
||||
}
|
||||
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) => {
|
||||
($in_stack:ident) => {
|
||||
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 {
|
||||
let in_stack_name = stringify!($in_stack);
|
||||
if in_stack_name == "int" && state.$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);
|
||||
// no -1 from in_stack_len, 1 subtracted within the min_max_bounds function
|
||||
let pre_idx = state.int.pop().unwrap();
|
||||
let idx = min_max_bounds(pre_idx, state.$in_stack.len());
|
||||
let item = state.$in_stack.remove(state.$in_stack.len() - 1 - 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, "");
|
||||
|
||||
macro_rules! yank_dup {
|
||||
($in_stack:ident) => {
|
||||
paste::item! {
|
||||
pub fn [< $in_stack _yank_dup >] (state: &mut PushState) {
|
||||
if state.int.is_empty() || state.$in_stack.is_empty() {
|
||||
return;
|
||||
}
|
||||
let in_stack_name = stringify!($in_stack);
|
||||
if in_stack_name == "int" && state.$in_stack.len() < 2 {
|
||||
return;
|
||||
}
|
||||
// no -1 from in_stack_len, 1 subtracted within the min_max_bounds function
|
||||
let pre_idx = state.int.pop().unwrap();
|
||||
let idx = min_max_bounds(pre_idx, state.$in_stack.len());
|
||||
let item = state.$in_stack[state.$in_stack.len() - 1 - idx].clone();
|
||||
state.$in_stack.push(item);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! shove {
|
||||
($in_stack:ident) => {
|
||||
paste::item! {
|
||||
pub fn [< $in_stack _shove >] (state: &mut PushState) {
|
||||
if state.int.is_empty() || state.$in_stack.is_empty() {
|
||||
return;
|
||||
}
|
||||
let in_stack_name = stringify!($in_stack);
|
||||
if in_stack_name == "int" && state.$in_stack.len() < 2 {
|
||||
return;
|
||||
}
|
||||
let pre_idx = state.int.pop().unwrap();
|
||||
let idx = min_max_bounds(pre_idx, state.$in_stack.len());
|
||||
let item = state.$in_stack.pop().unwrap();
|
||||
state.$in_stack.insert(state.$in_stack.len() - idx, item);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! shove_dup {
|
||||
($in_stack:ident) => {
|
||||
paste::item! {
|
||||
pub fn [< $in_stack _shove_dup >] (state: &mut PushState) {
|
||||
if state.int.is_empty() || state.$in_stack.is_empty() {
|
||||
return;
|
||||
}
|
||||
let in_stack_name = stringify!($in_stack);
|
||||
if in_stack_name == "int" && state.$in_stack.len() < 2 {
|
||||
return;
|
||||
}
|
||||
let pre_idx = state.int.pop().unwrap();
|
||||
let idx = min_max_bounds(pre_idx, state.$in_stack.len());
|
||||
let item = state.$in_stack[state.$in_stack.len() - 1].clone();
|
||||
state.$in_stack.insert(state.$in_stack.len() - idx, item);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! is_state_empty {
|
||||
($in_stack:ident) => {
|
||||
paste::item! {
|
||||
pub fn [< $in_stack _is_empty >] (state: &mut PushState) {
|
||||
state.boolean.push(state.$in_stack.is_empty());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! make_common_instructions {
|
||||
($stack:ident) => {
|
||||
pop!($stack);
|
||||
make_code!($stack);
|
||||
make_instruction_new_aux!(_dup, $stack, $stack, $stack);
|
||||
make_instruction_new_aux!(_dup_times, $stack, $stack, int, $stack);
|
||||
make_instruction_new_aux!(_swap, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new_aux!(_rotate, $stack, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_equal, $stack, boolean, $stack, $stack);
|
||||
flush_state!($stack);
|
||||
stack_depth!($stack);
|
||||
yank!($stack);
|
||||
yank_dup!($stack);
|
||||
shove!($stack);
|
||||
shove_dup!($stack);
|
||||
is_state_empty!($stack);
|
||||
};
|
||||
}
|
||||
|
||||
make_common_instructions!(int);
|
||||
make_common_instructions!(float);
|
||||
make_common_instructions!(string);
|
||||
make_common_instructions!(boolean);
|
||||
make_common_instructions!(char);
|
||||
make_common_instructions!(vector_int);
|
||||
make_common_instructions!(vector_float);
|
||||
make_common_instructions!(vector_string);
|
||||
make_common_instructions!(vector_boolean);
|
||||
make_common_instructions!(vector_char);
|
||||
make_common_instructions!(code);
|
||||
make_common_instructions!(exec);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::push::state::EMPTY_STATE;
|
||||
|
||||
#[test]
|
||||
fn noop_test() {
|
||||
let mut test_state = EMPTY_STATE;
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
let test_state_copy = test_state.clone();
|
||||
code_noop(&mut test_state);
|
||||
assert_eq!(test_state, test_state_copy);
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
let test_state_copy = test_state.clone();
|
||||
exec_noop(&mut test_state);
|
||||
assert_eq!(test_state, test_state_copy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pop_test() {
|
||||
let mut test_state = EMPTY_STATE;
|
||||
@ -424,5 +361,90 @@ mod tests {
|
||||
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);
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
int_yank(&mut test_state);
|
||||
assert_eq!(vec![1], test_state.int);
|
||||
|
||||
test_state.int = vec![0];
|
||||
test_state.boolean = vec![true, true, true, false];
|
||||
boolean_yank(&mut test_state);
|
||||
assert_eq!(vec![true, true, true, false], test_state.boolean);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn yank_dup_test() {
|
||||
let mut test_state = EMPTY_STATE;
|
||||
|
||||
test_state.int = vec![1, 2, 3, 4, 5, 6, 7, 8, 2];
|
||||
int_yank_dup(&mut test_state);
|
||||
assert_eq!(vec![1, 2, 3, 4, 5, 6, 7, 8, 6], test_state.int);
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
int_yank_dup(&mut test_state);
|
||||
assert_eq!(vec![1, 1], test_state.int);
|
||||
|
||||
test_state.int = vec![0];
|
||||
test_state.boolean = vec![true, true, true, false];
|
||||
boolean_yank_dup(&mut test_state);
|
||||
assert_eq!(vec![true, true, true, false, false], test_state.boolean);
|
||||
|
||||
test_state.int = vec![0];
|
||||
int_yank_dup(&mut test_state);
|
||||
assert_eq!(vec![0], test_state.int);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shove_test() {
|
||||
let mut test_state = EMPTY_STATE;
|
||||
|
||||
test_state.int = vec![1, 2, 3, 4, 5, 1];
|
||||
int_shove(&mut test_state);
|
||||
assert_eq!(vec![1, 2, 3, 5, 4], test_state.int);
|
||||
|
||||
test_state.int = vec![1, 2, 3, 4, 5, 0];
|
||||
int_shove(&mut test_state);
|
||||
assert_eq!(vec![1, 2, 3, 4, 5], test_state.int);
|
||||
|
||||
test_state.int = vec![-1];
|
||||
test_state.boolean = vec![true, true, true, false];
|
||||
boolean_shove(&mut test_state);
|
||||
assert_eq!(vec![true, true, false, true], test_state.boolean);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shove_dup_test() {
|
||||
let mut test_state = EMPTY_STATE;
|
||||
|
||||
test_state.int = vec![1, 1, 2, 3, 2];
|
||||
int_shove_dup(&mut test_state);
|
||||
assert_eq!(vec![1, 1, 3, 2, 3], test_state.int);
|
||||
|
||||
test_state.int = vec![1, 2, 3, 4, 5, 1];
|
||||
int_shove_dup(&mut test_state);
|
||||
assert_eq!(vec![1, 2, 3, 4, 5, 5], test_state.int);
|
||||
|
||||
test_state.int = vec![1, 2, 3, 4, 5, 0];
|
||||
int_shove_dup(&mut test_state);
|
||||
assert_eq!(vec![1, 2, 3, 4, 5, 5], test_state.int);
|
||||
|
||||
test_state.int = vec![-1];
|
||||
test_state.boolean = vec![true, true, true, false];
|
||||
boolean_shove_dup(&mut test_state);
|
||||
assert_eq!(vec![true, true, true, false, false], test_state.boolean);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_state_empty_test() {
|
||||
let mut test_state = EMPTY_STATE;
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
int_is_empty(&mut test_state);
|
||||
assert_eq!(vec![false], test_state.boolean);
|
||||
test_state.boolean.clear();
|
||||
|
||||
test_state.int = vec![];
|
||||
int_is_empty(&mut test_state);
|
||||
assert_eq!(vec![true], test_state.boolean);
|
||||
}
|
||||
}
|
||||
|
@ -7,74 +7,86 @@ use crate::push::state::PushState;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
/// Runs logical and on two values
|
||||
fn _and<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _and<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Copy + LogicalTrait,
|
||||
T: LogicalTrait,
|
||||
{
|
||||
Some(vals[0].logical_and(vals[1]))
|
||||
Some(b.logical_and(a))
|
||||
}
|
||||
make_instruction!(boolean, boolean, _and, bool, 2);
|
||||
|
||||
/// Runs logical or on two values
|
||||
fn _or<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _or<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Copy + LogicalTrait,
|
||||
T: LogicalTrait,
|
||||
{
|
||||
Some(vals[0].logical_or(vals[1]))
|
||||
Some(b.logical_or(a))
|
||||
}
|
||||
make_instruction!(boolean, boolean, _or, bool, 2);
|
||||
|
||||
/// Runs logical not on two values
|
||||
fn _not<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _not<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + LogicalTrait,
|
||||
T: LogicalTrait,
|
||||
{
|
||||
Some(vals[0].logical_not())
|
||||
Some(a.logical_not())
|
||||
}
|
||||
make_instruction!(boolean, boolean, _not, bool, 1);
|
||||
|
||||
/// Runs logical xor on two values
|
||||
fn _xor<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _xor<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Copy + LogicalTrait,
|
||||
T: LogicalTrait,
|
||||
{
|
||||
Some(vals[0].logical_xor(vals[1]))
|
||||
Some(b.logical_xor(a))
|
||||
}
|
||||
make_instruction!(boolean, boolean, _xor, bool, 2);
|
||||
|
||||
/// Inverts the first value and runs logical and on two values
|
||||
fn _invert_first_then_and<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _invert_first_then_and<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Copy + LogicalTrait,
|
||||
T: LogicalTrait,
|
||||
{
|
||||
Some(vals[0].logical_not().logical_and(vals[1]))
|
||||
Some(a.logical_not().logical_and(b))
|
||||
}
|
||||
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<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _invert_second_then_and<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Copy + LogicalTrait,
|
||||
T: LogicalTrait,
|
||||
{
|
||||
Some(vals[0].logical_and(vals[1].logical_not()))
|
||||
Some(a.logical_and(b.logical_not()))
|
||||
}
|
||||
make_instruction!(boolean, boolean, _invert_second_then_and, bool, 2);
|
||||
|
||||
fn _from_int<T>(vals: Vec<i128>) -> Option<T>
|
||||
fn _from_int<T>(a: i128) -> Option<T>
|
||||
where
|
||||
T: Copy + CastingTrait,
|
||||
T: CastingTrait,
|
||||
{
|
||||
T::from_int(vals[0])
|
||||
T::from_int(a)
|
||||
}
|
||||
make_instruction_out!(int, boolean, _from_int, i128, 1);
|
||||
|
||||
fn _from_float<T>(vals: Vec<Decimal>) -> Option<T>
|
||||
fn _from_float<T>(a: Decimal) -> Option<T>
|
||||
where
|
||||
T: Copy + CastingTrait,
|
||||
T: CastingTrait,
|
||||
{
|
||||
T::from_float(vals[0])
|
||||
T::from_float(a)
|
||||
}
|
||||
make_instruction_out!(float, boolean, _from_float, Decimal, 1);
|
||||
|
||||
macro_rules! make_logical_instructions {
|
||||
($stack:ident) => {
|
||||
make_instruction_new!(_and, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_or, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_not, $stack, $stack, $stack);
|
||||
make_instruction_new!(_xor, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_invert_first_then_and, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_invert_second_then_and, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_from_int, $stack, $stack, int);
|
||||
make_instruction_new!(_from_float, $stack, $stack, float);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! all_logical_instructions {
|
||||
() => {
|
||||
make_logical_instructions!(boolean);
|
||||
};
|
||||
}
|
||||
all_logical_instructions!();
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -1,304 +1,35 @@
|
||||
use crate::instructions::code::*;
|
||||
use crate::instructions::common::*;
|
||||
use crate::instructions::logical::*;
|
||||
use crate::instructions::numeric::*;
|
||||
use crate::instructions::vector::*;
|
||||
use crate::push::state::PushState;
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros {
|
||||
/// A macro that makes a push instruction given: the name of the input stack to use,
|
||||
/// the name of the output stack, an internal function to call, the type of a function,
|
||||
/// and the arity of the internal function call.
|
||||
///
|
||||
/// The `in_stack` argument refers to which push stack should this operate on.
|
||||
/// The `out_stack` argument refers to which push stack should the result be pushed to.
|
||||
/// The `fn_name` argument refers to the name of the function that is to operate
|
||||
/// on the values popped from `in_stack`.
|
||||
/// The `fn_type` argument refers to the type of `in_stack`. For example, the
|
||||
/// int stack is type: *Vec<i128>*. `fn_type` is *i128* in this case.
|
||||
/// The `fn_arity` argument refers to how many popped stack items are needed to
|
||||
/// execute the instruction. If the amount of items in the stack is less than
|
||||
/// this value, the instruction does nothing. How many items exactly should be passed
|
||||
/// as a list to the functions used for calculations.
|
||||
///
|
||||
/// What causes an instruction to NoOp:
|
||||
/// 1) There aren't enough values on a stack to execute an instruction.
|
||||
/// 2) The internal operation the instruction executes is unable to be ran without
|
||||
/// erroring such as division by 0.
|
||||
#[macro_export]
|
||||
macro_rules! make_instruction {
|
||||
($in_stack:ident, $out_stack:ident, $fn_name:ident, $fn_type:ty, $fn_arity:stmt) => {
|
||||
/// Runs a function and ensures the necessary variables are extracted from a state without error
|
||||
macro_rules! make_instruction_new {
|
||||
($func:ident, $prefix:ident, $out_stack:ident, $($stacks:ident), *) => {
|
||||
paste::item! {
|
||||
/// Runs the $fn_name function on the top $fn_arity items from
|
||||
/// the $in_stack and places the calculated value on the $out_stack.
|
||||
pub fn [< $in_stack $fn_name >] (state: &mut PushState) {
|
||||
let in_stack_len = state.$in_stack.len();
|
||||
if in_stack_len < $fn_arity {
|
||||
return;
|
||||
}
|
||||
let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity);
|
||||
for n in 1..=$fn_arity {
|
||||
inputs.push(state.$in_stack[in_stack_len - n]);
|
||||
}
|
||||
if let Some(result) = $fn_name(inputs) {
|
||||
for _ in 0..$fn_arity {
|
||||
state.$in_stack.pop();
|
||||
}
|
||||
state.$out_stack.push(result);
|
||||
}
|
||||
pub fn [< $prefix $func >] (state: &mut PushState) {
|
||||
rush_macro::run_instruction!($func, $out_stack, state, $($stacks), *);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The same as make_instruction above but prepends the output
|
||||
/// stack to the function name rather than the input stack.
|
||||
#[macro_export]
|
||||
macro_rules! make_instruction_out {
|
||||
($in_stack:ident, $out_stack:ident, $fn_name:ident, $fn_type:ty, $fn_arity:stmt) => {
|
||||
/// Runs a function and ensures the necessary variables are extracted from a state without error while
|
||||
/// returning multiple variables from the function
|
||||
macro_rules! make_instruction_new_aux {
|
||||
($func:ident, $prefix:ident, $out_stack:ident, $($stacks:ident), *) => {
|
||||
paste::item! {
|
||||
/// Runs the $fn_name function on the top $fn_arity items from
|
||||
/// the $in_stack and places the calculated value on the $out_stack.
|
||||
pub fn [< $out_stack $fn_name >] (state: &mut PushState) {
|
||||
let in_stack_len = state.$in_stack.len();
|
||||
if in_stack_len < $fn_arity {
|
||||
return;
|
||||
}
|
||||
let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity);
|
||||
for n in 1..=$fn_arity {
|
||||
inputs.push(state.$in_stack[in_stack_len - n].clone());
|
||||
}
|
||||
if let Some(result) = $fn_name(inputs) {
|
||||
for _ in 0..$fn_arity {
|
||||
state.$in_stack.pop();
|
||||
}
|
||||
state.$out_stack.push(result);
|
||||
}
|
||||
pub fn [< $prefix $func >] (state: &mut PushState) {
|
||||
rush_macro::run_instruction!($func, $out_stack, state, $($stacks), *, ;);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The same as make_instruction but uses clone() to fill the arguments
|
||||
/// to each function rather than a reference. Is slower, but will be okay
|
||||
/// for the time being.
|
||||
#[macro_export]
|
||||
macro_rules! make_instruction_clone {
|
||||
($in_stack:ident, $out_stack:ident, $fn_name:ident, $fn_type:ty, $fn_arity:stmt) => {
|
||||
/// Makes an instruction that takes no input stacks. Must specify a type for this
|
||||
/// one so because the result needs a type, and the compiler can't infer it here :(
|
||||
macro_rules! make_instruction_empty {
|
||||
($func:ident, $prefix:ident, $out_stack:ident, $out_type:ty) => {
|
||||
paste::item! {
|
||||
/// Runs the $fn_name function on the top $fn_arity items from
|
||||
/// the $in_stack and places the calculated value on the $out_stack.
|
||||
#[allow(clippy::reversed_empty_ranges, unused_comparisons)]
|
||||
pub fn [< $in_stack $fn_name >] (state: &mut PushState) {
|
||||
let in_stack_len = state.$in_stack.len();
|
||||
if in_stack_len < $fn_arity {
|
||||
return;
|
||||
}
|
||||
let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity);
|
||||
for n in 1..=$fn_arity {
|
||||
inputs.push(state.$in_stack[in_stack_len - n].clone());
|
||||
}
|
||||
if let Some(result) = $fn_name(inputs) {
|
||||
for _ in 0..$fn_arity {
|
||||
state.$in_stack.pop();
|
||||
}
|
||||
state.$out_stack.push(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! make_instruction_mult {
|
||||
($in_stack:ident, $out_stack:ident, $fn_name:ident, $fn_type:ty, $fn_arity:stmt) => {
|
||||
paste::item! {
|
||||
/// Runs the $fn_name function on the top $fn_arity items from
|
||||
/// the $in_stack and places the calculated value on the $out_stack.
|
||||
pub fn [< $in_stack $fn_name >] (state: &mut PushState) {
|
||||
let in_stack_len = state.$in_stack.len();
|
||||
if in_stack_len < $fn_arity {
|
||||
return;
|
||||
}
|
||||
let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity);
|
||||
for n in 1..=$fn_arity {
|
||||
inputs.push(state.$in_stack[in_stack_len - n].clone());
|
||||
}
|
||||
if let Some(result) = $fn_name(inputs) {
|
||||
for _ in 0..$fn_arity {
|
||||
state.$in_stack.pop();
|
||||
}
|
||||
state.$out_stack.extend(result.into_iter());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Same as the make_instruction macro except it pushes nothing to the
|
||||
/// output stack.
|
||||
#[macro_export]
|
||||
macro_rules! make_instruction_no_out {
|
||||
($in_stack:ident, $fn_name:ident, $fn_type:ty, $fn_arity:stmt) => {
|
||||
paste::item! {
|
||||
/// Runs the $fn_name function on the top $fn_arity items from
|
||||
/// the $in_stack and places the calculated value on the $out_stack.
|
||||
#[allow(unused_comparisons)]
|
||||
pub fn [< $in_stack $fn_name >] (state: &mut PushState) {
|
||||
let in_stack_len = state.$in_stack.len();
|
||||
if in_stack_len < $fn_arity {
|
||||
return;
|
||||
}
|
||||
let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity);
|
||||
for n in 1..=$fn_arity {
|
||||
inputs.push(state.$in_stack[in_stack_len - n].clone());
|
||||
}
|
||||
if let Some(_) = $fn_name(inputs) {
|
||||
for _ in 0..$fn_arity {
|
||||
state.$in_stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Same as `make_instruction!` but can work on two stacks.
|
||||
///
|
||||
/// `aux_stack` is an auxiliary stack to be used as input to internal function.
|
||||
/// `aux_arity` is the amount of the auxiliary stack to use.
|
||||
/// `aux_type` is the type of the auxiliary stack
|
||||
#[macro_export]
|
||||
macro_rules! make_instruction_aux {
|
||||
($in_stack:ident, $out_stack:ident, $fn_name:ident, $fn_type:ty, $fn_arity:stmt, $aux_stack:ident, $aux_arity:stmt, $aux_type:ty) => {
|
||||
paste::item! {
|
||||
/// Runs the $fn_name function on the top $fn_arity items from
|
||||
/// the $in_stack and places the calculated value on the $out_stack.
|
||||
/// $aux_stack is also used and popped $aux_arity time(s).
|
||||
pub fn [< $in_stack $fn_name >] (state: &mut PushState) {
|
||||
let in_stack_len = state.$in_stack.len();
|
||||
let aux_stack_len = state.$aux_stack.len();
|
||||
if in_stack_len < $fn_arity || aux_stack_len < $aux_arity {
|
||||
return;
|
||||
}
|
||||
let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity);
|
||||
let mut aux_inputs: Vec<$aux_type> = Vec::with_capacity($aux_arity);
|
||||
for n in 1..=$aux_arity {
|
||||
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 let Some(result) = $fn_name(inputs, aux_inputs) {
|
||||
for _ in 0..$aux_arity {
|
||||
state.$aux_stack.pop();
|
||||
}
|
||||
for _ in 0..$fn_arity {
|
||||
state.$in_stack.pop();
|
||||
}
|
||||
state.$out_stack.push(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Same as make_instruction_mult but can handle one auxiliary variable.
|
||||
#[macro_export]
|
||||
macro_rules! make_instruction_mult_aux {
|
||||
($in_stack:ident, $out_stack:ident, $fn_name:ident, $fn_type:ty, $fn_arity:stmt, $aux_stack:ident, $aux_arity:stmt, $aux_type:ty) => {
|
||||
paste::item! {
|
||||
/// Runs the $fn_name function on the top $fn_arity items from
|
||||
/// the $in_stack and places the calculated value on the $out_stack.
|
||||
/// $aux_stack is also used and popped $aux_arity time(s).
|
||||
pub fn [< $in_stack $fn_name >] (state: &mut PushState) {
|
||||
let in_stack_len = state.$in_stack.len();
|
||||
let aux_stack_len = state.$aux_stack.len();
|
||||
if in_stack_len < $fn_arity || aux_stack_len < $aux_arity {
|
||||
return;
|
||||
}
|
||||
let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity);
|
||||
let mut aux_inputs: Vec<$aux_type> = Vec::with_capacity($aux_arity);
|
||||
for n in 1..=$aux_arity {
|
||||
aux_inputs.push(state.$aux_stack[aux_stack_len - n].clone());
|
||||
}
|
||||
for n in 1..=$fn_arity {
|
||||
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 {
|
||||
state.$aux_stack.pop();
|
||||
}
|
||||
for _ in 0..$fn_arity {
|
||||
state.$in_stack.pop();
|
||||
}
|
||||
state.$out_stack.extend(result.into_iter());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Same as `make_instruction!` but can work on three stacks. Is there a way
|
||||
/// to generalize even this?
|
||||
///
|
||||
/// `aux_stack` is an auxiliary stack to be used as input to internal function.
|
||||
/// `aux_arity` is the amount of the auxiliary stack to use.
|
||||
/// `aux_type` is the type of the auxiliary stack
|
||||
#[macro_export]
|
||||
macro_rules! make_instruction_aux2 {
|
||||
($in_stack:ident, $out_stack:ident, $fn_name:ident, $fn_type:ty, $fn_arity:stmt, $aux0_stack:ident, $aux0_arity:stmt, $aux0_type:ty, $aux1_stack:ident, $aux1_arity:stmt, $aux1_type:ty) => {
|
||||
paste::item! {
|
||||
/// Runs the $fn_name function on the top $fn_arity items from
|
||||
/// the $in_stack and places the calculated value on the $out_stack.
|
||||
/// $aux_stack is also used and popped $aux_arity time(s).
|
||||
pub fn [< $in_stack $fn_name >] (state: &mut PushState) {
|
||||
let in_stack_len = state.$in_stack.len();
|
||||
let aux0_stack_len = state.$aux0_stack.len();
|
||||
let aux1_stack_len = state.$aux1_stack.len();
|
||||
if in_stack_len < $fn_arity || aux0_stack_len < $aux0_arity || aux1_stack_len < $aux1_arity {
|
||||
return;
|
||||
}
|
||||
// This is crazy jank, not meant for use in actual code :)
|
||||
// https://doc.rust-lang.org/std/any/fn.type_name.html
|
||||
if std::any::type_name::<$aux0_type>() == std::any::type_name::<$aux1_type>() {
|
||||
if aux0_stack_len + aux1_stack_len < $aux0_arity + $aux1_arity {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut inputs: Vec<$fn_type> = Vec::with_capacity($fn_arity);
|
||||
let mut aux0_inputs: Vec<$aux0_type> = Vec::with_capacity($aux0_arity);
|
||||
let mut aux1_inputs: Vec<$aux1_type> = Vec::with_capacity($aux1_arity);
|
||||
for n in 1..=$aux1_arity {
|
||||
aux1_inputs.push(state.$aux1_stack[aux1_stack_len - n].clone());
|
||||
}
|
||||
for n in 1..=$aux0_arity {
|
||||
if std::any::type_name::<$aux0_type>() == std::any::type_name::<$aux1_type>() {
|
||||
aux0_inputs.push(state.$aux0_stack[aux0_stack_len - $aux1_arity - n].clone());
|
||||
} else {
|
||||
aux0_inputs.push(state.$aux0_stack[aux0_stack_len - n].clone());
|
||||
}
|
||||
}
|
||||
// Stack shouldn't be the same for all three
|
||||
for n in 1..=$fn_arity {
|
||||
inputs.push(state.$in_stack[in_stack_len - n].clone());
|
||||
}
|
||||
if let Some(result) = $fn_name(inputs, aux0_inputs, aux1_inputs) {
|
||||
for _ in 0..$aux1_arity {
|
||||
state.$aux1_stack.pop();
|
||||
}
|
||||
for _ in 0..$aux0_arity {
|
||||
state.$aux0_stack.pop();
|
||||
}
|
||||
for _ in 0..$fn_arity {
|
||||
state.$in_stack.pop();
|
||||
}
|
||||
pub fn [< $prefix $func >] (state: &mut PushState) {
|
||||
if let Some(result) = $func::<$out_type>() {
|
||||
state.$out_stack.push(result);
|
||||
}
|
||||
}
|
||||
@ -314,440 +45,31 @@ pub mod numeric;
|
||||
pub mod utils;
|
||||
pub mod vector;
|
||||
|
||||
// unsure how to procedurally read a file and put all functions
|
||||
// into a vector. Probably need to use procedural macros, but I'm not there yet.
|
||||
pub fn int_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// numeric.rs
|
||||
int_add,
|
||||
int_sub,
|
||||
int_mult,
|
||||
int_div,
|
||||
int_rem,
|
||||
int_max,
|
||||
int_min,
|
||||
int_inc,
|
||||
int_dec,
|
||||
int_lt,
|
||||
int_gt,
|
||||
int_lte,
|
||||
int_gte,
|
||||
int_sin,
|
||||
int_arcsin,
|
||||
int_cos,
|
||||
int_arccos,
|
||||
int_tan,
|
||||
int_arctan,
|
||||
int_from_float,
|
||||
int_from_boolean,
|
||||
int_log,
|
||||
int_exp,
|
||||
int_sqrt,
|
||||
int_inv,
|
||||
int_abs,
|
||||
int_sign_reverse,
|
||||
int_square,
|
||||
// common.rs
|
||||
int_pop,
|
||||
]
|
||||
}
|
||||
pub fn float_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// numeric
|
||||
float_add,
|
||||
float_sub,
|
||||
float_mult,
|
||||
float_div,
|
||||
float_rem,
|
||||
float_max,
|
||||
float_min,
|
||||
float_inc,
|
||||
float_dec,
|
||||
float_lt,
|
||||
float_gt,
|
||||
float_lte,
|
||||
float_gte,
|
||||
float_sin,
|
||||
float_arcsin,
|
||||
float_cos,
|
||||
float_arccos,
|
||||
float_tan,
|
||||
float_arctan,
|
||||
float_from_int,
|
||||
float_from_boolean,
|
||||
float_log,
|
||||
float_exp,
|
||||
float_sqrt,
|
||||
float_inv,
|
||||
float_abs,
|
||||
float_sign_reverse,
|
||||
float_square,
|
||||
// common.rs
|
||||
float_pop,
|
||||
]
|
||||
}
|
||||
pub fn string_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// numeric.rs
|
||||
string_concat,
|
||||
string_conj,
|
||||
string_conj_end,
|
||||
string_take_n,
|
||||
string_take_last_n,
|
||||
string_sub,
|
||||
string_first,
|
||||
string_from_first_prim,
|
||||
string_from_prim,
|
||||
string_last,
|
||||
string_from_last_prim,
|
||||
string_nth,
|
||||
string_from_nth_prim,
|
||||
string_rest,
|
||||
string_but_last,
|
||||
string_drop,
|
||||
string_drop_last,
|
||||
string_length,
|
||||
string_reverse,
|
||||
string_push_all,
|
||||
string_make_empty,
|
||||
string_is_empty,
|
||||
string_contains,
|
||||
string_contains_vector_non_contiguous,
|
||||
string_contains_vector_contiguous,
|
||||
string_index_of,
|
||||
string_index_of_vector,
|
||||
string_occurrences_of,
|
||||
string_occurrences_of_vector,
|
||||
string_parse_to_prim,
|
||||
string_set_nth,
|
||||
string_split_on,
|
||||
string_replace,
|
||||
string_remove,
|
||||
string_insert,
|
||||
string_insert_vector,
|
||||
// common.rs
|
||||
string_pop,
|
||||
]
|
||||
}
|
||||
pub fn boolean_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// logical.rs
|
||||
boolean_and,
|
||||
boolean_or,
|
||||
boolean_not,
|
||||
boolean_xor,
|
||||
boolean_invert_first_then_and,
|
||||
boolean_invert_second_then_and,
|
||||
boolean_from_int,
|
||||
boolean_from_float,
|
||||
// common.rs
|
||||
boolean_pop,
|
||||
]
|
||||
}
|
||||
pub fn char_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// common.rs
|
||||
char_pop,
|
||||
]
|
||||
}
|
||||
pub fn vector_int_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// vector.rs
|
||||
vector_int_concat,
|
||||
vector_int_conj,
|
||||
vector_int_conj_end,
|
||||
vector_int_take_n,
|
||||
vector_int_take_last_n,
|
||||
vector_int_sub,
|
||||
vector_int_first,
|
||||
vector_int_from_first_prim,
|
||||
vector_int_from_prim,
|
||||
vector_int_last,
|
||||
vector_int_from_last_prim,
|
||||
vector_int_nth,
|
||||
vector_int_from_nth_prim,
|
||||
vector_int_rest,
|
||||
vector_int_but_last,
|
||||
vector_int_drop,
|
||||
vector_int_drop_last,
|
||||
vector_int_length,
|
||||
vector_int_reverse,
|
||||
vector_int_push_all,
|
||||
vector_int_make_empty,
|
||||
vector_int_is_empty,
|
||||
vector_int_contains,
|
||||
vector_int_contains_vector_non_contiguous,
|
||||
vector_int_contains_vector_contiguous,
|
||||
vector_int_index_of,
|
||||
vector_int_index_of_vector,
|
||||
vector_int_occurrences_of,
|
||||
vector_int_occurrences_of_vector,
|
||||
vector_int_parse_to_prim,
|
||||
vector_int_set_nth,
|
||||
vector_int_split_on,
|
||||
vector_int_replace,
|
||||
vector_int_remove,
|
||||
vector_int_iterate,
|
||||
vector_int_sort,
|
||||
vector_int_sort_reverse,
|
||||
vector_int_insert,
|
||||
vector_int_insert_vector,
|
||||
vector_int_mean,
|
||||
vector_int_maximum,
|
||||
vector_int_minimum,
|
||||
vector_int_sum,
|
||||
vector_int_mode,
|
||||
vector_int_two_norm,
|
||||
vector_int_cumulative_sum,
|
||||
// common.rs
|
||||
vector_int_pop,
|
||||
]
|
||||
}
|
||||
pub fn vector_float_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// vector.rs
|
||||
vector_float_concat,
|
||||
vector_float_conj,
|
||||
vector_float_conj_end,
|
||||
vector_float_take_n,
|
||||
vector_float_take_last_n,
|
||||
vector_float_sub,
|
||||
vector_float_first,
|
||||
vector_float_from_first_prim,
|
||||
vector_float_from_prim,
|
||||
vector_float_last,
|
||||
vector_float_from_last_prim,
|
||||
vector_float_nth,
|
||||
vector_float_from_nth_prim,
|
||||
vector_float_rest,
|
||||
vector_float_but_last,
|
||||
vector_float_drop,
|
||||
vector_float_drop_last,
|
||||
vector_float_length,
|
||||
vector_float_reverse,
|
||||
vector_float_push_all,
|
||||
vector_float_make_empty,
|
||||
vector_float_is_empty,
|
||||
vector_float_contains,
|
||||
vector_float_contains_vector_non_contiguous,
|
||||
vector_float_contains_vector_contiguous,
|
||||
vector_float_index_of,
|
||||
vector_float_index_of_vector,
|
||||
vector_float_occurrences_of,
|
||||
vector_float_occurrences_of_vector,
|
||||
vector_float_parse_to_prim,
|
||||
vector_float_set_nth,
|
||||
vector_float_split_on,
|
||||
vector_float_replace,
|
||||
vector_float_remove,
|
||||
vector_float_iterate,
|
||||
vector_float_sort,
|
||||
vector_float_sort_reverse,
|
||||
vector_float_insert,
|
||||
vector_float_insert_vector,
|
||||
vector_float_mean,
|
||||
vector_float_maximum,
|
||||
vector_float_minimum,
|
||||
vector_float_sum,
|
||||
vector_float_mode,
|
||||
vector_float_two_norm,
|
||||
vector_float_cumulative_sum,
|
||||
// common.rs
|
||||
vector_float_pop,
|
||||
]
|
||||
}
|
||||
pub fn vector_string_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// vector.rs
|
||||
vector_string_concat,
|
||||
vector_string_conj,
|
||||
vector_string_conj_end,
|
||||
vector_string_take_n,
|
||||
vector_string_take_last_n,
|
||||
vector_string_sub,
|
||||
vector_string_first,
|
||||
vector_string_from_first_prim,
|
||||
vector_string_from_prim,
|
||||
vector_string_last,
|
||||
vector_string_from_last_prim,
|
||||
vector_string_nth,
|
||||
vector_string_from_nth_prim,
|
||||
vector_string_rest,
|
||||
vector_string_but_last,
|
||||
vector_string_drop,
|
||||
vector_string_drop_last,
|
||||
vector_string_length,
|
||||
vector_string_reverse,
|
||||
vector_string_make_empty,
|
||||
vector_string_is_empty,
|
||||
vector_string_contains,
|
||||
vector_string_contains_vector_non_contiguous,
|
||||
vector_string_contains_vector_contiguous,
|
||||
vector_string_index_of,
|
||||
vector_string_index_of_vector,
|
||||
vector_string_occurrences_of,
|
||||
vector_string_occurrences_of_vector,
|
||||
vector_string_parse_to_prim,
|
||||
vector_string_set_nth,
|
||||
vector_string_split_on,
|
||||
vector_string_replace,
|
||||
vector_string_remove,
|
||||
vector_string_insert,
|
||||
vector_string_insert_vector,
|
||||
// common.rs
|
||||
vector_string_pop,
|
||||
]
|
||||
}
|
||||
pub fn vector_boolean_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// vector.rs
|
||||
vector_boolean_concat,
|
||||
vector_boolean_conj,
|
||||
vector_boolean_conj_end,
|
||||
vector_boolean_take_n,
|
||||
vector_boolean_take_last_n,
|
||||
vector_boolean_sub,
|
||||
vector_boolean_first,
|
||||
vector_boolean_from_first_prim,
|
||||
vector_boolean_from_prim,
|
||||
vector_boolean_last,
|
||||
vector_boolean_from_last_prim,
|
||||
vector_boolean_nth,
|
||||
vector_boolean_from_nth_prim,
|
||||
vector_boolean_rest,
|
||||
vector_boolean_but_last,
|
||||
vector_boolean_drop,
|
||||
vector_boolean_drop_last,
|
||||
vector_boolean_length,
|
||||
vector_boolean_reverse,
|
||||
vector_boolean_push_all,
|
||||
vector_boolean_make_empty,
|
||||
vector_boolean_is_empty,
|
||||
vector_boolean_contains,
|
||||
vector_boolean_contains_vector_non_contiguous,
|
||||
vector_boolean_contains_vector_contiguous,
|
||||
vector_boolean_index_of,
|
||||
vector_boolean_index_of_vector,
|
||||
vector_boolean_occurrences_of,
|
||||
vector_boolean_occurrences_of_vector,
|
||||
vector_boolean_parse_to_prim,
|
||||
vector_boolean_set_nth,
|
||||
vector_boolean_split_on,
|
||||
vector_boolean_replace,
|
||||
vector_boolean_remove,
|
||||
vector_boolean_iterate,
|
||||
vector_boolean_insert,
|
||||
vector_boolean_insert_vector,
|
||||
// common.rs
|
||||
vector_boolean_pop,
|
||||
]
|
||||
}
|
||||
pub fn vector_char_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// vector.rs
|
||||
vector_char_concat,
|
||||
vector_char_conj,
|
||||
vector_char_conj_end,
|
||||
vector_char_take_n,
|
||||
vector_char_take_last_n,
|
||||
vector_char_sub,
|
||||
vector_char_first,
|
||||
vector_char_from_first_prim,
|
||||
vector_char_from_prim,
|
||||
vector_char_last,
|
||||
vector_char_from_last_prim,
|
||||
vector_char_nth,
|
||||
vector_char_from_nth_prim,
|
||||
vector_char_rest,
|
||||
vector_char_but_last,
|
||||
vector_char_drop,
|
||||
vector_char_drop_last,
|
||||
vector_char_length,
|
||||
vector_char_reverse,
|
||||
vector_char_push_all,
|
||||
vector_char_make_empty,
|
||||
vector_char_is_empty,
|
||||
vector_char_contains,
|
||||
vector_char_contains_vector_non_contiguous,
|
||||
vector_char_contains_vector_contiguous,
|
||||
vector_char_index_of,
|
||||
vector_char_index_of_vector,
|
||||
vector_char_occurrences_of,
|
||||
vector_char_occurrences_of_vector,
|
||||
vector_char_parse_to_prim,
|
||||
vector_char_set_nth,
|
||||
vector_char_split_on,
|
||||
vector_char_replace,
|
||||
vector_char_remove,
|
||||
vector_char_iterate,
|
||||
vector_char_insert,
|
||||
vector_char_insert_vector,
|
||||
// common.rs
|
||||
vector_char_pop,
|
||||
]
|
||||
}
|
||||
pub fn exec_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// code.rs
|
||||
exec_do_range,
|
||||
exec_do_count,
|
||||
exec_do_times,
|
||||
exec_while,
|
||||
exec_do_while,
|
||||
exec_if,
|
||||
exec_when,
|
||||
exec_make_empty_block,
|
||||
exec_is_empty_block,
|
||||
exec_size,
|
||||
// common.rs
|
||||
exec_noop,
|
||||
exec_noop_block,
|
||||
exec_pop,
|
||||
]
|
||||
}
|
||||
pub fn code_instructions() -> Vec<fn(&mut PushState)> {
|
||||
vec![
|
||||
// code.rs
|
||||
code_is_block,
|
||||
code_is_singular,
|
||||
code_length,
|
||||
code_first,
|
||||
code_last,
|
||||
code_rest,
|
||||
code_but_last,
|
||||
code_wrap_block,
|
||||
code_combine,
|
||||
code_do_then_pop,
|
||||
code_do_range,
|
||||
code_do_count,
|
||||
code_do_times,
|
||||
code_map,
|
||||
code_if,
|
||||
code_when,
|
||||
code_member,
|
||||
code_nth,
|
||||
code_make_empty_block,
|
||||
code_is_empty_block,
|
||||
code_size,
|
||||
code_extract,
|
||||
code_insert,
|
||||
code_insert,
|
||||
code_first_position,
|
||||
code_reverse,
|
||||
// common.rs
|
||||
code_noop,
|
||||
code_noop_block,
|
||||
code_pop,
|
||||
code_from_int,
|
||||
code_from_float,
|
||||
code_from_string,
|
||||
code_from_boolean,
|
||||
code_from_char,
|
||||
code_from_vector_int,
|
||||
code_from_vector_float,
|
||||
code_from_vector_string,
|
||||
code_from_vector_boolean,
|
||||
code_from_vector_char,
|
||||
code_from_exec,
|
||||
]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//use super::*;
|
||||
use crate::push::state::{EMPTY_STATE, PushState};
|
||||
|
||||
#[test]
|
||||
fn make_instruction_new_test() {
|
||||
fn _test_func(x: i128, y: i128) -> Option<i128> {
|
||||
Some(x + y)
|
||||
}
|
||||
|
||||
fn _aux_test_func(x: i128, y: i128) -> Option<Vec<i128>> {
|
||||
Some(vec![x + y, x - y])
|
||||
}
|
||||
|
||||
let mut test_state = EMPTY_STATE;
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
make_instruction_new!(_test_func, int, int, int, int);
|
||||
int_test_func(&mut test_state);
|
||||
assert_eq!(vec![3], test_state.int);
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
make_instruction_new_aux!(_aux_test_func, int, int, int, int);
|
||||
int_aux_test_func(&mut test_state);
|
||||
assert_eq!(vec![3, 1], test_state.int);
|
||||
}
|
||||
}
|
||||
|
@ -12,296 +12,282 @@ use std::ops::{Add, Div, Mul, Sub};
|
||||
|
||||
use super::utils::{CastingTrait, NumericTrait};
|
||||
|
||||
/// Adds two addable values together.
|
||||
fn _add<T>(vals: Vec<T>) -> Option<T>
|
||||
/// Adds two values together.
|
||||
fn _add<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Add<Output = T> + Copy,
|
||||
T: Add<Output = T>,
|
||||
{
|
||||
Some(vals[1] + vals[0])
|
||||
Some(b + a)
|
||||
}
|
||||
make_instruction!(int, int, _add, i128, 2);
|
||||
make_instruction!(float, float, _add, Decimal, 2);
|
||||
|
||||
/// Subtracts two subtractable values from each other.
|
||||
fn _sub<T>(vals: Vec<T>) -> Option<T>
|
||||
/// Subtracts two values from each other.
|
||||
fn _sub<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Sub<Output = T> + Copy,
|
||||
T: Sub<Output = T>,
|
||||
{
|
||||
Some(vals[1] - vals[0])
|
||||
Some(b - a)
|
||||
}
|
||||
make_instruction!(int, int, _sub, i128, 2);
|
||||
make_instruction!(float, float, _sub, Decimal, 2);
|
||||
|
||||
/// Multiplies two multipliable values together.
|
||||
fn _mult<T>(vals: Vec<T>) -> Option<T>
|
||||
/// Multiplies two values with each other.
|
||||
fn _mult<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Mul<Output = T> + Copy,
|
||||
{
|
||||
Some(vals[1] * vals[0])
|
||||
Some(b * a)
|
||||
}
|
||||
make_instruction!(int, int, _mult, i128, 2);
|
||||
make_instruction!(float, float, _mult, Decimal, 2);
|
||||
|
||||
/// Divides two values from each other.
|
||||
fn _div<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _div<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Div<Output = T> + Copy + NumericTrait,
|
||||
T: Div<Output = T> + NumericTrait,
|
||||
{
|
||||
vals[1].checked_div(vals[0])
|
||||
b.checked_div(a)
|
||||
}
|
||||
make_instruction!(int, int, _div, i128, 2);
|
||||
make_instruction!(float, float, _div, Decimal, 2);
|
||||
|
||||
/// Takes the remainder of two values
|
||||
fn _rem<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _rem<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Div<Output = T> + Copy + NumericTrait,
|
||||
{
|
||||
vals[1].checked_mod(vals[0])
|
||||
b.checked_mod(a)
|
||||
}
|
||||
make_instruction!(int, int, _rem, i128, 2);
|
||||
make_instruction!(float, float, _rem, Decimal, 2);
|
||||
|
||||
/// Takes the max of two values
|
||||
fn _max<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _max<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Ord + Copy,
|
||||
T: Ord,
|
||||
{
|
||||
Some(max(vals[1], vals[0]))
|
||||
Some(max(a, b))
|
||||
}
|
||||
make_instruction!(int, int, _max, i128, 2);
|
||||
make_instruction!(float, float, _max, Decimal, 2);
|
||||
|
||||
/// Takes the min of two values
|
||||
fn _min<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _min<T>(a: T, b: T) -> Option<T>
|
||||
where
|
||||
T: Ord + Copy,
|
||||
T: Ord,
|
||||
{
|
||||
Some(min(vals[1], vals[0]))
|
||||
Some(min(a, b))
|
||||
}
|
||||
make_instruction!(int, int, _min, i128, 2);
|
||||
make_instruction!(float, float, _min, Decimal, 2);
|
||||
|
||||
/// Increments a single value by 1
|
||||
fn _inc<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _inc<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: NumericTrait + Copy,
|
||||
{
|
||||
Some(vals[0].increment())
|
||||
Some(a.increment())
|
||||
}
|
||||
make_instruction!(int, int, _inc, i128, 1);
|
||||
make_instruction!(float, float, _inc, Decimal, 1);
|
||||
|
||||
/// Decrements a single value by 1
|
||||
fn _dec<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _dec<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: NumericTrait + Copy,
|
||||
T: NumericTrait,
|
||||
{
|
||||
Some(vals[0].decrement())
|
||||
Some(a.decrement())
|
||||
}
|
||||
make_instruction!(int, int, _dec, i128, 1);
|
||||
make_instruction!(float, float, _dec, Decimal, 1);
|
||||
|
||||
/// Checks if the 2nd to top value is less than the top value
|
||||
fn _lt<T>(vals: Vec<T>) -> Option<bool>
|
||||
fn _lt<T>(a: T, b: T) -> Option<bool>
|
||||
where
|
||||
T: Ord + Copy,
|
||||
T: Ord,
|
||||
{
|
||||
Some(vals[1] < vals[0])
|
||||
Some(b < a)
|
||||
}
|
||||
make_instruction!(int, boolean, _lt, i128, 2);
|
||||
make_instruction!(float, boolean, _lt, Decimal, 2);
|
||||
|
||||
/// Checks if the 2nd to top value is greater than the top value
|
||||
fn _gt<T>(vals: Vec<T>) -> Option<bool>
|
||||
fn _gt<T>(a: T, b: T) -> Option<bool>
|
||||
where
|
||||
T: Ord + Copy,
|
||||
T: Ord,
|
||||
{
|
||||
Some(vals[1] > vals[0])
|
||||
Some(b > a)
|
||||
}
|
||||
make_instruction!(int, boolean, _gt, i128, 2);
|
||||
make_instruction!(float, boolean, _gt, Decimal, 2);
|
||||
|
||||
/// Checks if the 2nd to top value is less than or equal to the top value
|
||||
fn _lte<T>(vals: Vec<T>) -> Option<bool>
|
||||
fn _lte<T>(a: T, b: T) -> Option<bool>
|
||||
where
|
||||
T: Ord + Copy,
|
||||
{
|
||||
Some(vals[1] <= vals[0])
|
||||
Some(b <= a)
|
||||
}
|
||||
make_instruction!(int, boolean, _lte, i128, 2);
|
||||
make_instruction!(float, boolean, _lte, Decimal, 2);
|
||||
|
||||
/// Checks if the 2nd to top value is greater than or equal to the top value
|
||||
fn _gte<T>(vals: Vec<T>) -> Option<bool>
|
||||
fn _gte<T>(a: T, b: T) -> Option<bool>
|
||||
where
|
||||
T: Ord + Copy,
|
||||
T: Ord,
|
||||
{
|
||||
Some(vals[1] >= vals[0])
|
||||
Some(b >= a)
|
||||
}
|
||||
make_instruction!(int, boolean, _gte, i128, 2);
|
||||
make_instruction!(float, boolean, _gte, Decimal, 2);
|
||||
|
||||
/// Runs sin on a single item.
|
||||
fn _sin<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _sin<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].safe_sin()
|
||||
a.safe_sin()
|
||||
}
|
||||
make_instruction!(int, int, _sin, i128, 1);
|
||||
make_instruction!(float, float, _sin, Decimal, 1);
|
||||
|
||||
/// Runs arcsin on a single item.
|
||||
fn _arcsin<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _arcsin<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].safe_sin()?.inverse()
|
||||
a.safe_sin()?.inverse()
|
||||
}
|
||||
make_instruction!(int, int, _arcsin, i128, 1);
|
||||
make_instruction!(float, float, _arcsin, Decimal, 1);
|
||||
|
||||
/// Runs cos on a single item.
|
||||
fn _cos<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _cos<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].safe_cos()
|
||||
a.safe_cos()
|
||||
}
|
||||
make_instruction!(int, int, _cos, i128, 1);
|
||||
make_instruction!(float, float, _cos, Decimal, 1);
|
||||
|
||||
/// Runs arcsin on a single item.
|
||||
fn _arccos<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _arccos<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].safe_cos()?.inverse()
|
||||
a.safe_cos()?.inverse()
|
||||
}
|
||||
make_instruction!(int, int, _arccos, i128, 1);
|
||||
make_instruction!(float, float, _arccos, Decimal, 1);
|
||||
|
||||
/// Runs tan on a single item.
|
||||
fn _tan<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _tan<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].safe_tan()
|
||||
a.safe_tan()
|
||||
}
|
||||
make_instruction!(int, int, _tan, i128, 1);
|
||||
make_instruction!(float, float, _tan, Decimal, 1);
|
||||
|
||||
/// Runs arctan on a single item.
|
||||
fn _arctan<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _arctan<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].safe_tan()?.inverse()
|
||||
a.safe_tan()?.inverse()
|
||||
}
|
||||
|
||||
/// Converts a single value from an int to an arbitrary type.
|
||||
fn _from_int<T>(a: i128) -> Option<T>
|
||||
where
|
||||
T: CastingTrait,
|
||||
{
|
||||
T::from_int(a)
|
||||
}
|
||||
make_instruction!(int, int, _arctan, i128, 1);
|
||||
make_instruction!(float, float, _arctan, Decimal, 1);
|
||||
|
||||
/// Converts a single value from a float to an arbitrary type.
|
||||
fn _from_int<T>(vals: Vec<i128>) -> Option<T>
|
||||
fn _from_float<T>(a: Decimal) -> Option<T>
|
||||
where
|
||||
T: Copy + CastingTrait,
|
||||
T: CastingTrait,
|
||||
{
|
||||
T::from_int(vals[0])
|
||||
T::from_float(a)
|
||||
}
|
||||
make_instruction_out!(int, float, _from_int, i128, 1);
|
||||
|
||||
/// Converts a single value from a float to an arbitrary type.
|
||||
fn _from_float<T>(vals: Vec<Decimal>) -> Option<T>
|
||||
/// Converts a bool to a new type.
|
||||
fn _from_boolean<T>(a: bool) -> Option<T>
|
||||
where
|
||||
T: Copy + CastingTrait,
|
||||
T: CastingTrait,
|
||||
{
|
||||
T::from_float(vals[0])
|
||||
T::from_bool(a)
|
||||
}
|
||||
make_instruction_out!(float, int, _from_float, Decimal, 1);
|
||||
|
||||
/// Converts a bool to a to a new type.
|
||||
fn _from_boolean<T>(vals: Vec<bool>) -> Option<T>
|
||||
where
|
||||
T: Copy + CastingTrait,
|
||||
{
|
||||
T::from_bool(vals[0])
|
||||
}
|
||||
make_instruction_out!(boolean, int, _from_boolean, bool, 1);
|
||||
make_instruction_out!(boolean, float, _from_boolean, bool, 1);
|
||||
|
||||
/// Takes the log base 10 of a single Decimal. Acts as a
|
||||
/// Takes 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<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _log<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].absolute().safe_log10()
|
||||
a.absolute().safe_log10()
|
||||
}
|
||||
make_instruction!(int, int, _log, i128, 1);
|
||||
make_instruction!(float, float, _log, Decimal, 1);
|
||||
|
||||
/// Takes the exp of a single value. Ints get truncated.
|
||||
fn _exp<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _exp<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].safe_exp()
|
||||
a.safe_exp()
|
||||
}
|
||||
make_instruction!(int, int, _exp, i128, 1);
|
||||
make_instruction!(float, float, _exp, Decimal, 1);
|
||||
|
||||
/// Takes the square root of the absolute value of a single value.
|
||||
fn _sqrt<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _sqrt<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].safe_sqrt()
|
||||
a.safe_sqrt()
|
||||
}
|
||||
make_instruction!(int, int, _sqrt, i128, 1);
|
||||
make_instruction!(float, float, _sqrt, Decimal, 1);
|
||||
|
||||
/// Takes the inverse of a single value. If the number is 0,
|
||||
/// does nothing (returns None). Truncates an int to 0.
|
||||
fn _inv<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _inv<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
vals[0].inverse()
|
||||
a.inverse()
|
||||
}
|
||||
make_instruction!(int, int, _inv, i128, 1);
|
||||
make_instruction!(float, float, _inv, Decimal, 1);
|
||||
|
||||
/// Takes the absolute value of the top number
|
||||
fn _abs<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _abs<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
Some(vals[0].absolute())
|
||||
Some(a.absolute())
|
||||
}
|
||||
make_instruction!(int, int, _abs, i128, 1);
|
||||
make_instruction!(float, float, _abs, Decimal, 1);
|
||||
|
||||
/// Reverses the sign of the top number
|
||||
fn _sign_reverse<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _sign_reverse<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
Some(vals[0].sign_reverse())
|
||||
Some(a.sign_reverse())
|
||||
}
|
||||
make_instruction!(int, int, _sign_reverse, i128, 1);
|
||||
make_instruction!(float, float, _sign_reverse, Decimal, 1);
|
||||
|
||||
/// Squares the top number
|
||||
fn _square<T>(vals: Vec<T>) -> Option<T>
|
||||
fn _square<T>(a: T) -> Option<T>
|
||||
where
|
||||
T: Copy + NumericTrait,
|
||||
T: NumericTrait,
|
||||
{
|
||||
Some(vals[0].square())
|
||||
Some(a.square())
|
||||
}
|
||||
make_instruction!(int, int, _square, i128, 1);
|
||||
make_instruction!(float, float, _square, Decimal, 1);
|
||||
|
||||
macro_rules! make_numeric_instructions {
|
||||
($stack:ident) => {
|
||||
make_instruction_new!(_add, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_sub, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_mult, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_div, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_rem, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_max, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_min, $stack, $stack, $stack, $stack);
|
||||
make_instruction_new!(_inc, $stack, $stack, $stack);
|
||||
make_instruction_new!(_dec, $stack, $stack, $stack);
|
||||
make_instruction_new!(_lt, $stack, boolean, $stack, $stack);
|
||||
make_instruction_new!(_gt, $stack, boolean, $stack, $stack);
|
||||
make_instruction_new!(_lte, $stack, boolean, $stack, $stack);
|
||||
make_instruction_new!(_gte, $stack, boolean, $stack, $stack);
|
||||
make_instruction_new!(_sin, $stack, $stack, $stack);
|
||||
make_instruction_new!(_arcsin, $stack, $stack, $stack);
|
||||
make_instruction_new!(_cos, $stack, $stack, $stack);
|
||||
make_instruction_new!(_arccos, $stack, $stack, $stack);
|
||||
make_instruction_new!(_tan, $stack, $stack, $stack);
|
||||
make_instruction_new!(_arctan, $stack, $stack, $stack);
|
||||
make_instruction_new!(_from_boolean, $stack, $stack, boolean);
|
||||
make_instruction_new!(_log, $stack, $stack, $stack);
|
||||
make_instruction_new!(_exp, $stack, $stack, $stack);
|
||||
make_instruction_new!(_sqrt, $stack, $stack, $stack);
|
||||
make_instruction_new!(_inv, $stack, $stack, $stack);
|
||||
make_instruction_new!(_abs, $stack, $stack, $stack);
|
||||
make_instruction_new!(_sign_reverse, $stack, $stack, $stack);
|
||||
make_instruction_new!(_square, $stack, $stack, $stack);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! all_numeric_instructions {
|
||||
() => {
|
||||
make_numeric_instructions!(int);
|
||||
make_numeric_instructions!(float);
|
||||
make_instruction_new!(_from_int, float, float, int);
|
||||
make_instruction_new!(_from_float, int, int, float);
|
||||
};
|
||||
}
|
||||
all_numeric_instructions!();
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -312,170 +298,100 @@ mod tests {
|
||||
/// Tests the _add function.
|
||||
#[test]
|
||||
fn add_test() {
|
||||
let vals: Vec<i64> = vec![1, 2];
|
||||
assert_eq!(Some(3), _add(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(1.1), dec!(2.2)];
|
||||
assert_eq!(Some(dec!(3.3)), _add(vals));
|
||||
assert_eq!(Some(3), _add(1, 2));
|
||||
assert_eq!(Some(dec!(3.3)), _add(dec!(1.1), dec!(2.2)));
|
||||
}
|
||||
|
||||
/// Tests the _sub function.
|
||||
#[test]
|
||||
fn sub_test() {
|
||||
let vals: Vec<i64> = vec![1, 2];
|
||||
assert_eq!(Some(1), _sub(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(1.1), dec!(2.2)];
|
||||
assert_eq!(Some(dec!(1.1)), _sub(vals));
|
||||
assert_eq!(Some(1), _sub(1, 2));
|
||||
assert_eq!(Some(dec!(1.1)), _sub(dec!(1.1), dec!(2.2)));
|
||||
}
|
||||
|
||||
/// Tests the _mult function.
|
||||
#[test]
|
||||
fn mult_test() {
|
||||
let vals: Vec<i128> = vec![4, 5];
|
||||
assert_eq!(Some(20), _mult(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(1.1), dec!(2.2)];
|
||||
assert_eq!(Some(dec!(2.42)), _mult(vals));
|
||||
assert_eq!(Some(20), _mult(5, 4));
|
||||
assert_eq!(Some(dec!(2.42)), _mult(dec!(2.2), dec!(1.1)));
|
||||
}
|
||||
|
||||
/// Tests the _div function
|
||||
#[test]
|
||||
fn div_test() {
|
||||
let vals: Vec<i128> = vec![4, 20];
|
||||
assert_eq!(Some(5), _div(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![3, 20];
|
||||
assert_eq!(Some(6), _div(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(1.6), dec!(2.2)];
|
||||
assert_eq!(Some(dec!(1.375)), _div(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![0, 1];
|
||||
assert_eq!(None, _div(vals));
|
||||
assert_eq!(Some(5), _div(4, 20));
|
||||
assert_eq!(Some(6), _div(3, 20));
|
||||
assert_eq!(Some(dec!(1.375)), _div(dec!(1.6), dec!(2.2)));
|
||||
assert_eq!(None, _div(0, 1));
|
||||
}
|
||||
|
||||
/// Tests the _rem function
|
||||
#[test]
|
||||
fn rem_test() {
|
||||
let vals: Vec<i128> = vec![3, 20];
|
||||
assert_eq!(Some(2), _rem(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![20, 20];
|
||||
assert_eq!(Some(0), _rem(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![0, 9];
|
||||
assert_eq!(None, _rem(vals));
|
||||
assert_eq!(Some(2), _rem(3, 20));
|
||||
assert_eq!(Some(0), _rem(20, 20));
|
||||
assert_eq!(None, _rem(0, 9));
|
||||
}
|
||||
|
||||
/// Tests the _max function
|
||||
#[test]
|
||||
fn max_test() {
|
||||
let vals: Vec<i128> = vec![1, 2];
|
||||
assert_eq!(Some(2), _max(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![3, 0];
|
||||
assert_eq!(Some(3), _max(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(2.2), dec!(1.1)];
|
||||
assert_eq!(Some(dec!(2.2)), _max(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(3.3), dec!(-1.1)];
|
||||
assert_eq!(Some(dec!(3.3)), _max(vals));
|
||||
assert_eq!(Some(2), _max(1, 2));
|
||||
assert_eq!(Some(3), _max(3, 0));
|
||||
assert_eq!(Some(dec!(2.2)), _max(dec!(2.2), dec!(1.1)));
|
||||
assert_eq!(Some(dec!(3.3)), _max(dec!(3.3), dec!(-1.1)));
|
||||
}
|
||||
|
||||
/// Tests the _min function
|
||||
#[test]
|
||||
fn min_test() {
|
||||
let vals: Vec<i128> = vec![1, 2];
|
||||
assert_eq!(Some(1), _min(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![3, 0];
|
||||
assert_eq!(Some(0), _min(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(2.2), dec!(1.1)];
|
||||
assert_eq!(Some(dec!(1.1)), _min(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(3.3), dec!(-1.1)];
|
||||
assert_eq!(Some(dec!(-1.1)), _min(vals));
|
||||
assert_eq!(Some(1), _min(1, 2));
|
||||
assert_eq!(Some(0), _min(3, 0));
|
||||
assert_eq!(Some(dec!(1.1)), _min(dec!(2.2), dec!(1.1)));
|
||||
assert_eq!(Some(dec!(-1.1)), _min(dec!(3.3), dec!(-1.1)));
|
||||
}
|
||||
|
||||
/// Tests the _inc and _dec functions
|
||||
#[test]
|
||||
fn inc_dec_test() {
|
||||
let vals: Vec<i128> = vec![2];
|
||||
assert_eq!(Some(3), _inc(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![10];
|
||||
assert_eq!(Some(9), _dec(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(2.2)];
|
||||
assert_eq!(Some(dec!(3.2)), _inc(vals));
|
||||
|
||||
let vals: Vec<Decimal> = vec![dec!(5.6)];
|
||||
assert_eq!(Some(dec!(4.6)), _dec(vals));
|
||||
assert_eq!(Some(3), _inc(2));
|
||||
assert_eq!(Some(9), _dec(10));
|
||||
assert_eq!(Some(dec!(3.2)), _inc(dec!(2.2)));
|
||||
assert_eq!(Some(dec!(4.6)), _dec(dec!(5.6)));
|
||||
}
|
||||
|
||||
/// Tests the _lt, _gt, _lte, and _gte functions
|
||||
#[test]
|
||||
fn lt_gt_lte_gte_test() {
|
||||
let vals: Vec<i128> = vec![3, 2];
|
||||
assert_eq!(Some(true), _lt(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![1, 4];
|
||||
assert_eq!(Some(false), _lt(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![3, 3];
|
||||
assert_eq!(Some(false), _lt(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![2, 3];
|
||||
assert_eq!(Some(true), _gt(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![4, 1];
|
||||
assert_eq!(Some(false), _gt(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![3, 3];
|
||||
assert_eq!(Some(false), _gt(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![3, 2];
|
||||
assert_eq!(Some(true), _lte(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![1, 4];
|
||||
assert_eq!(Some(false), _lte(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![3, 3];
|
||||
assert_eq!(Some(true), _lte(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![2, 3];
|
||||
assert_eq!(Some(true), _gte(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![4, 1];
|
||||
assert_eq!(Some(false), _gte(vals));
|
||||
|
||||
let vals: Vec<i128> = vec![3, 3];
|
||||
assert_eq!(Some(true), _gte(vals));
|
||||
assert_eq!(Some(true), _lt(3, 2));
|
||||
assert_eq!(Some(false), _lt(1, 4));
|
||||
assert_eq!(Some(false), _lt(3, 3));
|
||||
assert_eq!(Some(true), _gt(2, 3));
|
||||
assert_eq!(Some(false), _gt(4, 1));
|
||||
assert_eq!(Some(false), _gt(3, 3));
|
||||
assert_eq!(Some(true), _lte(3, 2));
|
||||
assert_eq!(Some(false), _lte(1, 4));
|
||||
assert_eq!(Some(true), _lte(3, 3));
|
||||
assert_eq!(Some(true), _gte(2, 3));
|
||||
assert_eq!(Some(false), _gte(4, 1));
|
||||
assert_eq!(Some(true), _gte(3, 3));
|
||||
}
|
||||
|
||||
/// Tests the various trig functions.
|
||||
#[test]
|
||||
fn trig_tests() {
|
||||
let vals = vec![Decimal::PI];
|
||||
assert_eq!(Some(dec!(0.0)), _sin(vals));
|
||||
|
||||
let vals = vec![Decimal::QUARTER_PI];
|
||||
assert_eq!(Some(dec!(1.4142135623869512272301701717)), _arcsin(vals));
|
||||
|
||||
let vals = vec![Decimal::PI];
|
||||
assert_eq!(Some(dec!(-1.0)), _cos(vals));
|
||||
|
||||
let vals = vec![Decimal::QUARTER_PI];
|
||||
assert_eq!(Some(dec!(1.4142135626023406165042434783)), _arccos(vals));
|
||||
|
||||
let vals = vec![Decimal::PI];
|
||||
assert_eq!(Some(dec!(0.0)), _tan(vals));
|
||||
|
||||
let vals = vec![Decimal::QUARTER_PI];
|
||||
assert_eq!(Some(dec!(1.0000000043184676055890307049)), _arctan(vals));
|
||||
assert_eq!(Some(dec!(0.0)), _sin(Decimal::PI));
|
||||
assert_eq!(
|
||||
Some(dec!(1.4142135623869512272301701717)),
|
||||
_arcsin(Decimal::QUARTER_PI)
|
||||
);
|
||||
assert_eq!(Some(dec!(-1.0)), _cos(Decimal::PI));
|
||||
assert_eq!(Some(dec!(-1.0)), _arccos(Decimal::PI));
|
||||
assert_eq!(Some(dec!(0.0)), _tan(Decimal::PI));
|
||||
assert_eq!(
|
||||
Some(dec!(1.0000000043184676055890307049)),
|
||||
_arctan(Decimal::QUARTER_PI)
|
||||
);
|
||||
}
|
||||
|
||||
/// Tests that the various addition functions.
|
||||
@ -539,7 +455,6 @@ mod tests {
|
||||
assert_eq!(vec![2, 0], test_state.int);
|
||||
|
||||
test_state.int = vec![6, 3];
|
||||
|
||||
int_div(&mut test_state);
|
||||
assert_eq!(vec![2], test_state.int);
|
||||
|
||||
@ -725,6 +640,13 @@ mod tests {
|
||||
test_state.float = vec![dec!(2.1)];
|
||||
int_from_float(&mut test_state);
|
||||
assert_eq!(vec![2], test_state.int);
|
||||
test_state.float.clear();
|
||||
test_state.int.clear();
|
||||
|
||||
test_state.boolean = vec![true];
|
||||
int_from_boolean(&mut test_state);
|
||||
assert_eq!(vec![1], test_state.int);
|
||||
test_state.boolean.clear();
|
||||
}
|
||||
|
||||
/// Tests the log function
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1 +1,2 @@
|
||||
|
||||
pub mod instructions;
|
||||
pub mod push;
|
||||
|
18
src/main.rs
18
src/main.rs
@ -7,18 +7,12 @@ mod push;
|
||||
|
||||
fn main() {
|
||||
// These need to stay so linter doesn't go crazy.
|
||||
int_instructions();
|
||||
float_instructions();
|
||||
string_instructions();
|
||||
boolean_instructions();
|
||||
char_instructions();
|
||||
vector_int_instructions();
|
||||
vector_float_instructions();
|
||||
vector_string_instructions();
|
||||
vector_boolean_instructions();
|
||||
vector_char_instructions();
|
||||
exec_instructions();
|
||||
code_instructions();
|
||||
let mut empty_state = EMPTY_STATE;
|
||||
interpret_program(&mut empty_state, 1000, 1000);
|
||||
|
||||
let mut counts: Vec<(&str, usize)> = vec![];
|
||||
counts.push(("int", 2));
|
||||
counts.push(("float", 1));
|
||||
|
||||
// counts.iter().map()
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ impl Gene {
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
return None;
|
||||
None
|
||||
}
|
||||
val => Some(val),
|
||||
}
|
||||
|
45
tests/instruction_test.rs
Normal file
45
tests/instruction_test.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use rush::push::state::EMPTY_STATE;
|
||||
use rush_macro::run_instruction;
|
||||
|
||||
fn iadd(x: i128, y: i128) -> Option<i128> {
|
||||
Some(x + y)
|
||||
}
|
||||
|
||||
fn aux_iadd(x: i128, y: i128) -> Option<Vec<i128>> {
|
||||
Some(vec![x + y, x - y])
|
||||
}
|
||||
|
||||
fn two_stacks(x: i128, y: i128, cond: bool) -> Option<i128> {
|
||||
if cond { Some(x + y) } else { Some(x - y) }
|
||||
}
|
||||
|
||||
fn aux_char(ch: Vec<char>) -> Option<Vec<char>> {
|
||||
Some(ch)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_extract_test() {
|
||||
let mut test_state = EMPTY_STATE;
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
run_instruction!(iadd, int, test_state, int, int);
|
||||
assert_eq!(vec![3], test_state.int);
|
||||
|
||||
test_state.int = vec![1];
|
||||
run_instruction!(iadd, int, test_state, int, int);
|
||||
assert_eq!(vec![1], test_state.int);
|
||||
|
||||
// If you're coming from the run_instruction docs, this is
|
||||
// the one.
|
||||
test_state.int = vec![1, 2];
|
||||
run_instruction!(aux_iadd, int, test_state, int, int;);
|
||||
assert_eq!(vec![3, 1], test_state.int);
|
||||
|
||||
test_state.int = vec![1, 2];
|
||||
test_state.boolean = vec![true];
|
||||
run_instruction!(two_stacks, int, test_state, int, int, boolean);
|
||||
|
||||
test_state.vector_char = vec![vec!['a', 'b']];
|
||||
run_instruction!(aux_char, char, test_state, vector_char;);
|
||||
assert_eq!(vec!['a', 'b'], test_state.char);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user