From 0a1f0fa601b664011d11cd15ab608c7b8dc87164 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Fri, 18 Apr 2025 23:23:29 -0500 Subject: [PATCH] code done --- rush_macro/src/lib.rs | 2 + rush_macro/src/utils/instruction.rs | 41 ++++++++++---- src/instructions/code.rs | 88 ++++++++++++++--------------- src/instructions/mod.rs | 16 +++++- 4 files changed, 89 insertions(+), 58 deletions(-) diff --git a/rush_macro/src/lib.rs b/rush_macro/src/lib.rs index dffb7c6..fbe64cd 100644 --- a/rush_macro/src/lib.rs +++ b/rush_macro/src/lib.rs @@ -37,6 +37,8 @@ mod utils; /// /// 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); diff --git a/rush_macro/src/utils/instruction.rs b/rush_macro/src/utils/instruction.rs index 4bae797..fbf5b3a 100644 --- a/rush_macro/src/utils/instruction.rs +++ b/rush_macro/src/utils/instruction.rs @@ -1,5 +1,8 @@ +//! 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)* ;? +//! Extract: Function, Stack, State (`,` Stack)* ;? //! //! Function: identifier //! @@ -56,6 +59,12 @@ impl ToTokens for Extract { 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) { @@ -64,32 +73,40 @@ impl ToTokens for Extract { } } + // 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 } }); - // Create variables to store popped values + // 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 slices of variable names for restoration and function call + // Create the variable names themselves to store the + // popped values. let value_vars = (0..stacks.len()) .map(|i| quote::format_ident!("val_{}", i)) .collect::>(); - // Create restore operations for each stack - let restore_values = stacks - .iter() - .zip(value_vars.iter().rev()) - .map(|(stack, var)| { - let inner_stack = &&stack.0; - quote! { #inner_state.#inner_stack.push(#var.clone()); } - }); + // 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()),*); @@ -109,6 +126,8 @@ impl ToTokens for Extract { }, }; + // Where the pieces of the puzzle are put together. + // tokens then used to create the function. tokens.extend(quote! { if true #(&& (#conditions))* { #(#store_values)* diff --git a/src/instructions/code.rs b/src/instructions/code.rs index f5f6b31..c0a9f9c 100644 --- a/src/instructions/code.rs +++ b/src/instructions/code.rs @@ -349,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) -> Option { - let block = match vals[0].clone() { +pub fn _member(a: Gene, b: Gene) -> Option { + 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, auxs: Vec) -> Option { - let gene_vec = match vals[0].clone() { +pub fn _nth(a: Gene, idx: i128) -> Option { + let gene_vec = match a { Gene::Block(val) => val, val => vec![val], }; @@ -370,22 +369,19 @@ pub fn _nth(vals: Vec, auxs: Vec) -> Option { 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(_: Vec) -> Option { +pub fn _make_empty_block() -> Option { 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) -> Option { - Some(match vals[0].clone() { +pub fn _is_empty_block(a: Gene) -> Option { + Some(match a { Gene::Block(val) => { if val.is_empty() { true @@ -396,70 +392,64 @@ pub fn _is_empty_block(vals: Vec) -> Option { _ => 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) -> Option { - Some(match vals[0].clone() { +pub fn _size(a: Gene) -> Option { + 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, auxs: Vec) -> Option { - match vals[0].clone() { +pub fn _extract(a: Gene, idx: i128) -> Option { + 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, auxs: Vec) -> Option { - let mut block = match vals[0].clone() { +pub fn _insert(a: Gene, b: Gene, idx: i128) -> Option { + let mut block = match a.clone() { iblock @ Gene::Block(_) => iblock, val => Gene::Block(vec![val]), }; if block.rec_len() == 0 { - return _combine(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) -> Option { - let bad_cond: bool = match &vals[0] { +pub fn _first_position(a: Gene, b: Gene) -> Option { + 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); } } @@ -469,11 +459,10 @@ pub fn _first_position(vals: Vec) -> Option { } 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) -> Option { - Some(match vals[0].clone() { +pub fn _reverse(a: Gene) -> Option { + Some(match a { Gene::Block(mut val) => { val.reverse(); Gene::Block(val) @@ -481,7 +470,6 @@ pub fn _reverse(vals: Vec) -> Option { val => val, }) } -make_instruction_clone!(code, code, _reverse, Gene, 1); macro_rules! make_code_instructions { ($stack:ident) => { @@ -494,7 +482,16 @@ macro_rules! make_code_instructions { 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, $stack, $stack, $stack, boolean); + 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); }; } @@ -505,7 +502,6 @@ macro_rules! all_code_instructions { }; } all_code_instructions!(); -make_instruction_new!(_if, code, code, code, code, boolean); #[cfg(test)] mod tests { @@ -892,7 +888,7 @@ mod tests { let mut test_state = EMPTY_STATE; // Code tests - /*test_state.code = vec![Gene::GeneInt(0), Gene::GeneInt(1)]; + test_state.code = vec![Gene::GeneInt(0), Gene::GeneInt(1)]; test_state.boolean = vec![true]; code_if(&mut test_state); assert_eq!(vec![Gene::GeneInt(1)], test_state.exec); @@ -915,7 +911,7 @@ mod tests { test_state.boolean = vec![false]; exec_if(&mut test_state); assert_eq!(vec![Gene::GeneInt(0)], test_state.exec); - test_state.exec.clear();*/ + test_state.exec.clear(); } #[test] @@ -959,24 +955,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); diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index 327b4c3..fd2ce21 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -308,7 +308,7 @@ pub mod macros { }; } - /// Runs a function and ensures needed variables are extracted from a state without error while + /// 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), *) => { @@ -319,6 +319,20 @@ pub mod macros { } }; } + + /// 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! { + pub fn [< $prefix $func >] (state: &mut PushState) { + if let Some(result) = $func::<$out_type>() { + state.$out_stack.push(result); + } + } + } + }; + } } pub mod code;