Move pass chain generation from macro to codegen

This commit is contained in:
greg 2018-10-20 18:00:05 -07:00
parent ffe7deb00a
commit 37070a6b3e
3 changed files with 57 additions and 64 deletions

View File

@ -7,7 +7,6 @@ extern crate itertools;
extern crate lazy_static; extern crate lazy_static;
#[macro_use] #[macro_use]
extern crate maplit; extern crate maplit;
#[macro_use]
extern crate schala_repl; extern crate schala_repl;
#[macro_use] #[macro_use]
extern crate schala_repl_codegen; extern crate schala_repl_codegen;

View File

@ -1,4 +1,5 @@
#![feature(trace_macros)] #![feature(trace_macros)]
#![recursion_limit="128"]
extern crate proc_macro; extern crate proc_macro;
extern crate proc_macro2; extern crate proc_macro2;
#[macro_use] #[macro_use]
@ -66,10 +67,62 @@ fn get_attribute_identifier(attr_name: &str, attrs: &Vec<Attribute>) -> Option<p
}) })
} }
fn pass_chain_new(idents: Vec<Ident>) -> proc_macro2::TokenStream { /* a pass_chain function signature looks like:
* fn(&mut ProgrammingLanguageInterface, A, Option<&mut DebugHandler>) -> Result<B, String>
*
* TODO use some kind of failure-handling library to make this better
*/
fn generate_pass_chain(idents: Vec<Ident>) -> proc_macro2::TokenStream {
let final_return = quote! {
{
let final_output: FinishedComputation = unfinished_computation.finish(Ok(input_to_next_stage));
final_output
}
};
let nested_passes = idents.iter()
.rev()
.fold(final_return, |later_fragment, pass_name| {
quote! { quote! {
let mut chain = pass_chain![self, options; #(#idents),* ]; {
chain(input) let pass_name = stringify!(#pass_name);
let (output, duration) = {
let ref debug_map = eval_options.debug_passes;
let debug_handle = match debug_map.get(pass_name) {
Some(PassDebugOptionsDescriptor { opts }) => {
let ptr = &mut unfinished_computation;
ptr.cur_debug_options = opts.clone();
Some(ptr)
}
_ => None
};
let start = time::Instant::now();
let pass_output = #pass_name(self, input_to_next_stage, debug_handle);
let elapsed = start.elapsed();
(pass_output, elapsed)
};
if eval_options.debug_timing {
unfinished_computation.durations.push(duration);
}
match output {
Ok(input_to_next_stage) => #later_fragment,
//TODO this error type needs to be guaranteed to provide a useable string
Err(err) => return unfinished_computation.output(Err(format!("Pass {} failed:\n{}", pass_name, err))),
}
}
}
});
quote! {
{
use std::time;
use schala_repl::PassDebugOptionsDescriptor;
let eval_options = options;
let input_to_next_stage = input;
let mut unfinished_computation = UnfinishedComputation::default();
#nested_passes
}
} }
} }
@ -119,7 +172,7 @@ pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream
} }
}); });
let pass_chain = pass_chain_new(pass_idents.collect()); let pass_chain = generate_pass_chain(pass_idents.collect());
let tokens = quote! { let tokens = quote! {
use schala_repl::PassDescriptor; use schala_repl::PassDescriptor;
@ -131,7 +184,6 @@ pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream
#file_ext.to_string() #file_ext.to_string()
} }
fn execute_pipeline(&mut self, input: &str, options: &EvalOptions) -> FinishedComputation { fn execute_pipeline(&mut self, input: &str, options: &EvalOptions) -> FinishedComputation {
#pass_chain #pass_chain
} }
fn get_passes(&self) -> Vec<PassDescriptor> { fn get_passes(&self) -> Vec<PassDescriptor> {

View File

@ -173,61 +173,3 @@ pub trait ProgrammingLanguageInterface {
None None
} }
} }
/* a pass_chain function signature looks like:
* fn(&mut ProgrammingLanguageInterface, A, Option<&mut DebugHandler>) -> Result<B, String>
*
* TODO use some kind of failure-handling library to make this better
*/
#[macro_export]
macro_rules! pass_chain {
($state:expr, $eval_options:expr; $($pass:path), *) => {
|text_input| {
let mut comp = UnfinishedComputation::default();
pass_chain_helper! { ($state, comp, $eval_options); text_input $(, $pass)* }
}
};
}
#[macro_export]
macro_rules! pass_chain_helper {
(($state:expr, $comp:expr, $eval_options:expr); $input:expr, $pass:path $(, $rest:path)*) => {
{
use std::time;
use schala_repl::PassDebugOptionsDescriptor;
let pass_name = stringify!($pass);
let (output, duration) = {
let ref debug_map = $eval_options.debug_passes;
let debug_handle = match debug_map.get(pass_name) {
Some(PassDebugOptionsDescriptor { opts }) => {
let ptr = &mut $comp;
ptr.cur_debug_options = opts.clone();
Some(ptr)
}
_ => None
};
let start = time::Instant::now();
let pass_output = $pass($state, $input, debug_handle);
let elapsed = start.elapsed();
(pass_output, elapsed)
};
if $eval_options.debug_timing {
$comp.durations.push(duration);
}
match output {
Ok(result) => pass_chain_helper! { ($state, $comp, $eval_options); result $(, $rest)* },
Err(err) => { //TODO this error type needs to be guaranteed to provide a useable string
$comp.output(Err(format!("Pass {} failed:\n{}", pass_name, err)))
}
}
}
};
// Done
(($state:expr, $comp:expr, $eval_options:expr); $final_output:expr) => {
{
let final_output: FinishedComputation = $comp.finish(Ok($final_output));
final_output
}
};
}