From f13cd7ad20dfb17440d5da9eeef580c2674fcbd9 Mon Sep 17 00:00:00 2001 From: Rowan Torbitzky-Lane Date: Fri, 18 Apr 2025 01:13:09 -0500 Subject: [PATCH] make_instruction_new works, time to refactor --- rush_macro/src/lib.rs | 33 +++++++++++++- .../{canextractstate.rs => instruction.rs} | 44 ++++++++++++++----- rush_macro/src/utils/mod.rs | 2 +- src/instructions/mod.rs | 37 ++++++++-------- ...tractstate_test.rs => instruction_test.rs} | 14 +++++- 5 files changed, 96 insertions(+), 34 deletions(-) rename rush_macro/src/utils/{canextractstate.rs => instruction.rs} (75%) rename tests/{extractstate_test.rs => instruction_test.rs} (51%) diff --git a/rush_macro/src/lib.rs b/rush_macro/src/lib.rs index 84e49a3..45b963f 100644 --- a/rush_macro/src/lib.rs +++ b/rush_macro/src/lib.rs @@ -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 { +/// 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); diff --git a/rush_macro/src/utils/canextractstate.rs b/rush_macro/src/utils/instruction.rs similarity index 75% rename from rush_macro/src/utils/canextractstate.rs rename to rush_macro/src/utils/instruction.rs index 8a4ac62..1ffe818 100644 --- a/rush_macro/src/utils/canextractstate.rs +++ b/rush_macro/src/utils/instruction.rs @@ -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(input: ParseStream) -> bool { + if let Ok(_) = input.parse::() { + return true; + } + false +} + pub struct Extract { func: Function, out_stack: Stack, state: State, stacks: Vec, + 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::(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 } }); } diff --git a/rush_macro/src/utils/mod.rs b/rush_macro/src/utils/mod.rs index c963bb0..28504b1 100644 --- a/rush_macro/src/utils/mod.rs +++ b/rush_macro/src/utils/mod.rs @@ -1,6 +1,6 @@ use syn::parse::{Parse, ParseStream}; -pub mod canextractstate; +pub mod instruction; fn parse_zero_or_more(input: ParseStream) -> Vec { let mut result = Vec::new(); diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index 93f6e10..67604bc 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -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 { + 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); } } diff --git a/tests/extractstate_test.rs b/tests/instruction_test.rs similarity index 51% rename from tests/extractstate_test.rs rename to tests/instruction_test.rs index 35226c5..1eefdb4 100644 --- a/tests/extractstate_test.rs +++ b/tests/instruction_test.rs @@ -5,6 +5,10 @@ fn iadd(x: i128, y: i128) -> i128 { x + y } +fn aux_iadd(x: i128, y: i128) -> Vec { + 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); }