make_instruction_new works, time to refactor
This commit is contained in:
parent
6744896494
commit
f13cd7ad20
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
});
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user