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
rush_macro/src
src/instructions

@ -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);

@ -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::<Vec<_>>();
// 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)*

@ -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<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],
};
@ -370,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
@ -396,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(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);
}
}
@ -469,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)
@ -481,7 +470,6 @@ pub fn _reverse(vals: Vec<Gene>) -> Option<Gene> {
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);

@ -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;