Skip to content

Instantly share code, notes, and snippets.

@dginev
Last active January 24, 2019 01:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dginev/63447ca98d0974f5c67f5ac646965dd4 to your computer and use it in GitHub Desktop.
Save dginev/63447ca98d0974f5c67f5ac646965dd4 to your computer and use it in GitHub Desktop.
Contextual variable capture in Rust, via Custom Derive
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()
}
//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
}
);
/// 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