Last active
January 24, 2019 01:08
-
-
Save dginev/63447ca98d0974f5c67f5ac646965dd4 to your computer and use it in GitHub Desktop.
Contextual variable capture in Rust, via Custom Derive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static mut CONTEXT_DEPTH: u32 = 0; | |
#[proc_macro_derive(BoundState)] | |
pub fn bound_state(_input: TokenStream) -> TokenStream { | |
let state_declaration = if unsafe {CONTEXT_DEPTH == 0} { | |
quote!( | |
macro_rules! state { | |
() => { | |
outer_state!() | |
}; | |
} | |
) | |
} else { | |
quote!( | |
macro_rules! state { | |
() => { | |
inner_state!() | |
}; | |
} | |
) | |
}; | |
state_declaration.into() | |
} | |
#[proc_macro_derive(StartStateFrame)] | |
pub fn start_state_frame(_input: TokenStream) -> TokenStream { | |
unsafe { CONTEXT_DEPTH +=1 }; | |
TokenStream::new() | |
} | |
#[proc_macro_derive(EndStateFrame)] | |
pub fn end_state_frame(_input: TokenStream) -> TokenStream { | |
unsafe { CONTEXT_DEPTH -=1 }; | |
TokenStream::new() | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//intended use: | |
bind_state!(outer); | |
def_macro!("first", // installs first in "outer" | |
before_digest => sub[inner] { | |
// take more actions... | |
// then install another definition while executing the outer def_macro! call: | |
def_macro!("second"); // installs second in "inner", calling "with_inner_state" behind the scenes | |
} | |
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// bind a fallback state, accessible at the "outer" scope | |
macro_rules! bind_state { | |
($state_arg:ident) => { | |
macro_rules! outer_state { | |
() => { | |
$state_arg | |
}; | |
} | |
} | |
} | |
/// context frame control : start | |
macro_rules! start_state_frame { | |
() => {{ | |
#[derive(StartStateFrame)] | |
struct _SFrame; | |
}} | |
} | |
/// context frame control : emd | |
macro_rules! end_state_frame { | |
() => {{ | |
#[derive(EndStateFrame)] | |
struct _EFrame; | |
}} | |
} | |
/// bind a state in the inner frame | |
macro_rules! bind_inner_state { | |
($inner_state:ident) => { | |
// this macro definition is local and won't exist outside of the caller's scope, | |
// allowing us to redefine inner_state locally in each inner context | |
macro_rules! inner_state { | |
() => { | |
$inner_state | |
}; | |
} | |
// tell custom derive that we are now one frame deeper | |
start_state_frame!(); | |
}; | |
} | |
/// bind a variable as an inner_state and invoke a block in that context | |
macro_rules! with_inner_state { | |
($body: block, $inner_state:ident) => {{ | |
bind_inner_state!($inner_state); | |
let macro_out = $body; | |
// tell custom derive we are done with the current frame | |
end_state_frame!(); | |
return macro_out; | |
}} | |
} | |
/// obtain the current contextual state via custom derive | |
macro_rules! current_state { | |
($st:ident) => { | |
let $st: &mut State = { | |
#[derive(BoundState)] | |
struct _Bound; | |
state!() | |
}; | |
}; | |
} | |
// concrete macros using the current state: | |
macro_rules! def_macro { | |
($name:expr) => { | |
current_state!(st) | |
// magically have access to "st", which is bound to the nearest contextual state | |
st.install_definition($name); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment