make_instruction_new works, time to refactor

This commit is contained in:
Rowan Torbitzky-Lane 2025-04-18 01:13:09 -05:00
parent 6744896494
commit f13cd7ad20
5 changed files with 96 additions and 34 deletions

View File

@ -1,10 +1,39 @@
use crate::utils::canextractstate::Extract;
use crate::utils::instruction::Extract;
use quote::quote;
use syn::parse_macro_input;
mod utils;
/// Checks to see if extracting values from a state is possible.
/// 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.
///
/// An function with multiple outputs, for example this one:
/// ```
/// fn aux_iadd(x: i128, y: i128) -> Vec<i128> {
/// 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.
#[proc_macro]
pub fn run_instruction(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let f = parse_macro_input!(input as Extract);

View File

@ -1,11 +1,13 @@
// This Stack that isn't repeated is the desired output stack.
// Extract: Function, Stack, State, (`,` Stack)*
//
// Function: identifier
//
// State: identifier
//
// Stack: identifier
//! 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;
@ -13,11 +15,20 @@ 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 {
@ -26,11 +37,13 @@ impl Parse for Extract {
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,
})
}
}
@ -41,6 +54,7 @@ impl ToTokens for Extract {
let inner_out_stack = &self.out_stack.0;
let inner_state = &self.state.0;
let stacks = &self.stacks;
let aux = &self.aux;
let mut counts = Vec::new();
for stack in stacks {
@ -60,10 +74,20 @@ impl ToTokens for Extract {
quote! { #inner_state.#inner_stack.pop().unwrap() }
});
let aux_run = match aux {
true => quote! {
let result = #inner_func(#(#values),*);
#inner_state.#inner_out_stack.extend(result.iter());
},
false => quote! {
let result = #inner_func(#(#values),*);
#inner_state.#inner_out_stack.push(result);
},
};
tokens.extend(quote! {
if true #(&& (#conditions))* {
let result = vec![#inner_func(#(#values, )*)];
#inner_state.#inner_out_stack.extend(result.iter());
#aux_run
}
});
}

View File

@ -1,6 +1,6 @@
use syn::parse::{Parse, ParseStream};
pub mod canextractstate;
pub mod instruction;
fn parse_zero_or_more<T: Parse>(input: ParseStream) -> Vec<T> {
let mut result = Vec::new();

View File

@ -316,14 +316,15 @@ pub mod macros {
};
}
/// Extracts values from passed state based on an amount of stacks passed.
macro_rules! extract_state_vals {
($state:ident) => {};
($state:ident, $stack:ident) => {
$state.$stack.pop().unwrap()
};
($state:ident, $stack:ident, $($rest:ident), *) => {
($state.$stack.pop().unwrap(), $(extract_state_vals!($state, $rest)), *)
/// Runs a function and ensures needed 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! {
pub fn [< $prefix $func >] (state: &mut PushState) {
run_instruction!($func, $out_stack, state, $($stacks), *, ;);
}
}
};
}
}
@ -778,28 +779,26 @@ mod tests {
use super::*;
use crate::push::state::EMPTY_STATE;
#[test]
fn extract_state_vals_test() {
let mut test_state = EMPTY_STATE;
test_state.int = vec![0, 1];
test_state.boolean = vec![true];
let res = extract_state_vals!(test_state, int, int, boolean);
assert_eq!((1, 0, true), res);
}
#[test]
fn make_instruction_new_test() {
fn _test_func(x: i128, y: i128) -> i128 {
x + y
}
fn _aux_test_func(x: i128, y: i128) -> Vec<i128> {
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);
}
}

View File

@ -5,6 +5,10 @@ fn iadd(x: i128, y: i128) -> i128 {
x + y
}
fn aux_iadd(x: i128, y: i128) -> Vec<i128> {
vec![x + y, x - y]
}
#[test]
fn run_extract_test() {
let mut test_state = EMPTY_STATE;
@ -13,7 +17,13 @@ fn run_extract_test() {
run_instruction!(iadd, int, test_state, int, int);
assert_eq!(vec![3], test_state.int);
/*test_state.int = vec![1];
test_state.int = vec![1];
run_instruction!(iadd, int, test_state, int, int);
assert_eq!(vec![1], test_state.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);
}