code done
Some checks failed
/ Test-Suite (push) Failing after 20s

This commit is contained in:
Rowan Torbitzky-Lane 2025-04-18 23:23:29 -05:00
parent 9abb9d1feb
commit 0a1f0fa601
4 changed files with 89 additions and 58 deletions

View File

@ -37,6 +37,8 @@ mod utils;
/// ///
/// Suggestion: If you need to pull an index from the int stack, make it the first argument /// Suggestion: If you need to pull an index from the int stack, make it the first argument
/// to your function. /// to your function.
///
/// If there is an instruction with no stacks as input, must put a comma at the end.
#[proc_macro] #[proc_macro]
pub fn run_instruction(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn run_instruction(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let f = parse_macro_input!(input as Extract); let f = parse_macro_input!(input as Extract);

View File

@ -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. //! This Stack that isn't repeated is the desired output stack.
//! Extract: Function, Stack, State, (`,` Stack)* ;? //! Extract: Function, Stack, State (`,` Stack)* ;?
//! //!
//! Function: identifier //! Function: identifier
//! //!
@ -56,6 +59,12 @@ impl ToTokens for Extract {
let stacks = &self.stacks; let stacks = &self.stacks;
let aux = &self.aux; 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(); let mut counts = Vec::new();
for stack in stacks { for stack in stacks {
match counts.iter_mut().find(|(x, _)| x == stack) { 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 conditions = counts.iter().map(|(stack, count)| {
let inner_stack = &stack.0; let inner_stack = &stack.0;
quote! { #inner_state.#inner_stack.len() >= #count } 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 store_values = stacks.iter().enumerate().map(|(i, stack)| {
let inner_stack = &&stack.0; let inner_stack = &&stack.0;
let var_name = quote::format_ident!("val_{}", i); let var_name = quote::format_ident!("val_{}", i);
quote! { let #var_name = #inner_state.#inner_stack.pop().unwrap(); } 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()) let value_vars = (0..stacks.len())
.map(|i| quote::format_ident!("val_{}", i)) .map(|i| quote::format_ident!("val_{}", i))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// Create restore operations for each stack // Create restore code in case None is returned from the function.
let restore_values = stacks let restore_values =
.iter() stacks
.zip(value_vars.iter().rev()) .iter()
.map(|(stack, var)| { .rev()
let inner_stack = &&stack.0; .zip(value_vars.iter().rev())
quote! { #inner_state.#inner_stack.push(#var.clone()); } .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 { let aux_run = match aux {
true => quote! { true => quote! {
let result = #inner_func(#(#value_vars.clone()),*); 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! { tokens.extend(quote! {
if true #(&& (#conditions))* { if true #(&& (#conditions))* {
#(#store_values)* #(#store_values)*

View File

@ -349,20 +349,19 @@ pub fn exec_when(state: &mut PushState) {
/// Pushes true if the second code item is found within the first item. /// Pushes true if the second code item is found within the first item.
/// If the first item isn't a block, coerced into one. /// If the first item isn't a block, coerced into one.
pub fn _member(vals: Vec<Gene>) -> Option<bool> { pub fn _member(a: Gene, b: Gene) -> Option<bool> {
let block = match vals[0].clone() { let block = match b {
Gene::Block(val) => val, Gene::Block(val) => val,
val => vec![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. /// Pushes the nth item of the top element of the code stack.
/// If top code item isn't a block, wrap one around it. /// If top code item isn't a block, wrap one around it.
pub fn _nth(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> { pub fn _nth(a: Gene, idx: i128) -> Option<Gene> {
let gene_vec = match vals[0].clone() { let gene_vec = match a {
Gene::Block(val) => val, Gene::Block(val) => val,
val => vec![val], val => vec![val],
}; };
@ -370,22 +369,19 @@ pub fn _nth(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> {
if gene_vec_len == 0 { if gene_vec_len == 0 {
return None; 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()) 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. /// 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![])) 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. /// Checks to see if the top item on the code/exec stack is an empty block.
/// True if is, False if not. /// True if is, False if not.
pub fn _is_empty_block(vals: Vec<Gene>) -> Option<bool> { pub fn _is_empty_block(a: Gene) -> Option<bool> {
Some(match vals[0].clone() { Some(match a {
Gene::Block(val) => { Gene::Block(val) => {
if val.is_empty() { if val.is_empty() {
true true
@ -396,70 +392,64 @@ pub fn _is_empty_block(vals: Vec<Gene>) -> Option<bool> {
_ => false, _ => 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. /// Returns the size of the top item on the code/exec stack.
pub fn _size(vals: Vec<Gene>) -> Option<i128> { pub fn _size(a: Gene) -> Option<i128> {
Some(match vals[0].clone() { Some(match a.clone() {
Gene::Block(val) => val.len() as i128, Gene::Block(val) => val.len() as i128,
_ => 1, _ => 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. /// Returns a nested element inside a block based on an int.
pub fn _extract(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> { pub fn _extract(a: Gene, idx: i128) -> Option<Gene> {
match vals[0].clone() { match &a {
block @ Gene::Block(_) => { block @ Gene::Block(_) => {
let block_len = block.rec_len(); let block_len = block.rec_len();
if block_len == 0 { if block_len == 0 {
None None
} else { } else {
let ndx = (auxs[0] % block_len as i128).abs() as usize; let ndx = (idx % block_len as i128).abs() as usize;
Some(vals[0].clone().code_at_point(ndx)?) 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 /// 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 /// int from the top of the int stack. The top code item is coerced into a block
/// if needed. /// if needed.
pub fn _insert(vals: Vec<Gene>, auxs: Vec<i128>) -> Option<Gene> { pub fn _insert(a: Gene, b: Gene, idx: i128) -> Option<Gene> {
let mut block = match vals[0].clone() { let mut block = match a.clone() {
iblock @ Gene::Block(_) => iblock, iblock @ Gene::Block(_) => iblock,
val => Gene::Block(vec![val]), val => Gene::Block(vec![val]),
}; };
if block.rec_len() == 0 { 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(); let ndx = idx.abs() as usize % block.rec_len();
block.with_code_inserted_at_point(vals[1].clone(), ndx); block.with_code_inserted_at_point(b, ndx);
Some(block) 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. /// 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 /// If not found, pushes -1. If top code item isn't a block, returns 0 if top
/// two code items equal, -1 otherwise. /// two code items equal, -1 otherwise.
pub fn _first_position(vals: Vec<Gene>) -> Option<i128> { pub fn _first_position(a: Gene, b: Gene) -> Option<i128> {
let bad_cond: bool = match &vals[0] { let bad_cond: bool = match &a {
Gene::Block(val) => val.len() == 0, Gene::Block(val) => val.len() == 0,
_ => true, _ => true,
}; };
if bad_cond { if bad_cond {
if vals[0] == vals[1] { if a == b {
return Some(0); return Some(0);
} }
} else { } else {
match &vals[0] { match &a {
Gene::Block(val) => { Gene::Block(val) => {
for (idx, el) in val.iter().enumerate() { for (idx, el) in val.iter().enumerate() {
if el == &vals[1] { if el == &b {
return Some(idx as i128); return Some(idx as i128);
} }
} }
@ -469,11 +459,10 @@ pub fn _first_position(vals: Vec<Gene>) -> Option<i128> {
} }
Some(-1) Some(-1)
} }
make_instruction_clone!(code, int, _first_position, Gene, 2);
/// Reverses the top block. Does nothing if not a block. /// Reverses the top block. Does nothing if not a block.
pub fn _reverse(vals: Vec<Gene>) -> Option<Gene> { pub fn _reverse(a: Gene) -> Option<Gene> {
Some(match vals[0].clone() { Some(match a {
Gene::Block(mut val) => { Gene::Block(mut val) => {
val.reverse(); val.reverse();
Gene::Block(val) Gene::Block(val)
@ -481,7 +470,6 @@ pub fn _reverse(vals: Vec<Gene>) -> Option<Gene> {
val => val, val => val,
}) })
} }
make_instruction_clone!(code, code, _reverse, Gene, 1);
macro_rules! make_code_instructions { macro_rules! make_code_instructions {
($stack:ident) => { ($stack:ident) => {
@ -494,7 +482,16 @@ macro_rules! make_code_instructions {
make_instruction_new!(_but_last, $stack, $stack, $stack); make_instruction_new!(_but_last, $stack, $stack, $stack);
make_instruction_new!(_wrap_block, $stack, $stack, $stack); make_instruction_new!(_wrap_block, $stack, $stack, $stack);
make_instruction_new!(_combine, $stack, $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!(); all_code_instructions!();
make_instruction_new!(_if, code, code, code, code, boolean);
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -892,7 +888,7 @@ mod tests {
let mut test_state = EMPTY_STATE; let mut test_state = EMPTY_STATE;
// Code tests // 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]; test_state.boolean = vec![true];
code_if(&mut test_state); code_if(&mut test_state);
assert_eq!(vec![Gene::GeneInt(1)], test_state.exec); assert_eq!(vec![Gene::GeneInt(1)], test_state.exec);
@ -915,7 +911,7 @@ mod tests {
test_state.boolean = vec![false]; test_state.boolean = vec![false];
exec_if(&mut test_state); exec_if(&mut test_state);
assert_eq!(vec![Gene::GeneInt(0)], test_state.exec); assert_eq!(vec![Gene::GeneInt(0)], test_state.exec);
test_state.exec.clear();*/ test_state.exec.clear();
} }
#[test] #[test]
@ -959,24 +955,24 @@ mod tests {
let mut test_state = EMPTY_STATE; let mut test_state = EMPTY_STATE;
test_state.code = vec![ test_state.code = vec![
Gene::GeneInt(0),
Gene::Block(vec![ Gene::Block(vec![
Gene::GeneInt(0), Gene::GeneInt(0),
Gene::GeneInt(4), Gene::GeneInt(4),
Gene::StateFunc(exec_do_range), Gene::StateFunc(exec_do_range),
]), ]),
Gene::GeneInt(0),
]; ];
code_member(&mut test_state); code_member(&mut test_state);
assert_eq!(vec![true], test_state.boolean); assert_eq!(vec![true], test_state.boolean);
test_state.boolean.clear(); test_state.boolean.clear();
test_state.code = vec![ test_state.code = vec![
Gene::GeneInt(0),
Gene::Block(vec![ Gene::Block(vec![
Gene::GeneInt(5), Gene::GeneInt(5),
Gene::GeneInt(4), Gene::GeneInt(4),
Gene::StateFunc(exec_do_range), Gene::StateFunc(exec_do_range),
]), ]),
Gene::GeneInt(0),
]; ];
code_member(&mut test_state); code_member(&mut test_state);
assert_eq!(vec![false], test_state.boolean); assert_eq!(vec![false], test_state.boolean);

View File

@ -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 /// returning multiple variables from the function
macro_rules! make_instruction_new_aux { macro_rules! make_instruction_new_aux {
($func:ident, $prefix:ident, $out_stack:ident, $($stacks:ident), *) => { ($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; pub mod code;