diff --git a/schala-lang/language/src/lib.rs b/schala-lang/language/src/lib.rs index 12632f0..ae71054 100644 --- a/schala-lang/language/src/lib.rs +++ b/schala-lang/language/src/lib.rs @@ -7,7 +7,6 @@ extern crate itertools; extern crate lazy_static; #[macro_use] extern crate maplit; -#[macro_use] extern crate schala_repl; #[macro_use] extern crate schala_repl_codegen; diff --git a/schala-repl-codegen/src/lib.rs b/schala-repl-codegen/src/lib.rs index ce2cad3..6016783 100644 --- a/schala-repl-codegen/src/lib.rs +++ b/schala-repl-codegen/src/lib.rs @@ -1,4 +1,5 @@ #![feature(trace_macros)] +#![recursion_limit="128"] extern crate proc_macro; extern crate proc_macro2; #[macro_use] @@ -66,10 +67,62 @@ fn get_attribute_identifier(attr_name: &str, attrs: &Vec) -> Option

) -> proc_macro2::TokenStream { +/* a pass_chain function signature looks like: + * fn(&mut ProgrammingLanguageInterface, A, Option<&mut DebugHandler>) -> Result + * + * TODO use some kind of failure-handling library to make this better + */ +fn generate_pass_chain(idents: Vec) -> 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! { + { + 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! { - let mut chain = pass_chain![self, options; #(#idents),* ]; - chain(input) + { + 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! { use schala_repl::PassDescriptor; @@ -131,7 +184,6 @@ pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream #file_ext.to_string() } fn execute_pipeline(&mut self, input: &str, options: &EvalOptions) -> FinishedComputation { - #pass_chain } fn get_passes(&self) -> Vec { diff --git a/schala-repl/src/language.rs b/schala-repl/src/language.rs index 29c04ff..6c389e8 100644 --- a/schala-repl/src/language.rs +++ b/schala-repl/src/language.rs @@ -173,61 +173,3 @@ pub trait ProgrammingLanguageInterface { None } } - -/* a pass_chain function signature looks like: - * fn(&mut ProgrammingLanguageInterface, A, Option<&mut DebugHandler>) -> Result - * - * 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 - } - }; -}