schala/schala-repl-codegen/src/lib.rs

200 lines
6.7 KiB
Rust
Raw Normal View History

2018-07-16 03:40:35 -07:00
#![feature(trace_macros)]
#![recursion_limit="128"]
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;
2018-04-30 02:24:21 -07:00
use proc_macro::TokenStream;
2018-05-03 00:20:55 -07:00
use syn::{Ident, Attribute, DeriveInput};
2018-09-29 03:23:48 -07:00
fn find_attr_by_name<'a>(name: &str, attrs: &'a Vec<Attribute>) -> Option<&'a Attribute> {
attrs.iter().find(|attr| {
let first = attr.path.segments.first();
let seg: Option<&&syn::PathSegment> = first.as_ref().map(|x| x.value());
seg.map(|seg| seg.ident.to_string() == name).unwrap_or(false)
})
}
fn extract_attribute_arg_by_name(name: &str, attrs: &Vec<Attribute>) -> Option<String> {
use syn::{Meta, Lit, MetaNameValue};
2018-09-29 03:23:48 -07:00
find_attr_by_name(name, attrs)
.and_then(|attr| {
match attr.interpret_meta() {
Some(Meta::NameValue(MetaNameValue { lit: Lit::Str(litstr), .. })) => Some(litstr.value()),
2018-05-02 04:50:26 -07:00
_ => None,
}
})
}
2018-07-16 03:40:35 -07:00
fn extract_attribute_list(name: &str, attrs: &Vec<Attribute>) -> Option<Vec<(Ident, Option<Vec<Ident>>)>> {
2018-05-03 00:20:55 -07:00
use syn::{Meta, MetaList, NestedMeta};
2018-09-29 03:23:48 -07:00
find_attr_by_name(name, attrs)
.and_then(|attr| {
2018-05-03 00:20:55 -07:00
match attr.interpret_meta() {
Some(Meta::List(MetaList { nested, .. })) => {
Some(nested.iter().map(|nested_meta| match nested_meta {
2018-09-26 01:41:58 -07:00
&NestedMeta::Meta(Meta::Word(ref ident)) => (ident.clone(), None),
&NestedMeta::Meta(Meta::List(MetaList { ref ident, nested: ref nested2, .. })) => {
2018-07-16 03:40:35 -07:00
let own_args = nested2.iter().map(|nested_meta2| match nested_meta2 {
2018-09-26 01:41:58 -07:00
&NestedMeta::Meta(Meta::Word(ref ident)) => ident.clone(),
2018-07-16 03:40:35 -07:00
_ => panic!("Bad format for doubly-nested attribute list")
}).collect();
2018-09-26 01:41:58 -07:00
(ident.clone(), Some(own_args))
2018-07-16 03:40:35 -07:00
},
2018-05-03 00:20:55 -07:00
_ => panic!("Bad format for nested list")
}).collect())
},
_ => panic!("{} must be a comma-delimited list surrounded by parens", name)
}
})
}
2018-10-15 20:12:00 -07:00
fn get_attribute_identifier(attr_name: &str, attrs: &Vec<Attribute>) -> Option<proc_macro2::Ident> {
find_attr_by_name(attr_name, attrs).and_then(|attr| {
let tts = attr.tts.clone().into_iter().collect::<Vec<_>>();
if tts.len() == 2 {
let ref after_equals: proc_macro2::TokenTree = tts[1];
match after_equals {
proc_macro2::TokenTree::Ident(ident) => Some(ident.clone()),
_ => None
}
} else {
None
}
2018-10-15 20:12:00 -07:00
})
}
2019-01-07 02:43:31 -08:00
/* a pass_chain function signature with input A and output B looks like:
* fn(A, &mut ProgrammingLanguageInterface, 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! {
{
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();
2019-01-07 02:43:31 -08:00
let pass_output = #pass_name(input_to_next_stage, self, 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
}
}
}
2018-10-15 20:12:00 -07:00
#[proc_macro_derive(ProgrammingLanguageInterface,
attributes(LanguageName, SourceFileExtension, PipelineSteps, DocMethod, HandleCustomInterpreterDirectives))]
2018-10-15 20:12:00 -07:00
pub fn derive_programming_language_interface(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let attrs = &ast.attrs;
let language_name: String = extract_attribute_arg_by_name("LanguageName", attrs).expect("LanguageName is required");
let file_ext = extract_attribute_arg_by_name("SourceFileExtension", attrs).expect("SourceFileExtension is required");
let passes = extract_attribute_list("PipelineSteps", attrs).expect("PipelineSteps are required");
let pass_idents = passes.iter().map(|x| x.0.clone());
2018-10-15 20:12:00 -07:00
let get_doc_impl = match get_attribute_identifier("DocMethod", attrs) {
None => quote! { },
Some(method_name) => quote! {
fn get_doc(&self, commands: &Vec<&str>) -> Option<String> {
self.#method_name(commands)
}
}
};
2018-07-16 03:40:35 -07:00
let handle_custom_interpreter_directives_impl = match get_attribute_identifier("HandleCustomInterpreterDirectives", attrs) {
None => quote! { },
Some(method_name) => quote! {
fn handle_custom_interpreter_directives(&mut self, commands: &Vec<&str>) -> Option<String> {
//println!("If #method_name is &self not &mut self, this runs forever");
self.#method_name(commands)
}
}
};
2018-07-16 03:40:35 -07:00
let pass_descriptors = passes.iter().map(|pass| {
2018-07-16 19:36:55 -07:00
let name = pass.0.to_string();
2018-07-16 03:40:35 -07:00
let opts: Vec<String> = match &pass.1 {
None => vec![],
Some(opts) => opts.iter().map(|o| o.to_string()).collect(),
};
quote! {
PassDescriptor {
2018-07-16 19:36:55 -07:00
name: #name.to_string(),
2018-07-16 03:40:35 -07:00
debug_options: vec![#(format!(#opts)),*]
}
}
});
2018-05-02 02:14:36 -07:00
let pass_chain = generate_pass_chain(pass_idents.collect());
2018-09-26 01:41:58 -07:00
let tokens = quote! {
2018-07-07 18:08:21 -07:00
use schala_repl::PassDescriptor;
2018-04-30 02:24:21 -07:00
impl ProgrammingLanguageInterface for #name {
2018-05-02 02:14:36 -07:00
fn get_language_name(&self) -> String {
2018-05-02 16:06:28 -07:00
#language_name.to_string()
2018-05-02 02:14:36 -07:00
}
fn get_source_file_suffix(&self) -> String {
2018-05-02 20:43:05 -07:00
#file_ext.to_string()
2018-05-02 02:14:36 -07:00
}
fn execute_pipeline(&mut self, input: &str, options: &EvalOptions) -> FinishedComputation {
#pass_chain
2018-05-02 02:14:36 -07:00
}
2018-07-06 03:17:37 -07:00
fn get_passes(&self) -> Vec<PassDescriptor> {
2018-07-16 03:40:35 -07:00
vec![ #(#pass_descriptors),* ]
2018-05-02 02:14:36 -07:00
}
2018-10-15 20:12:00 -07:00
#get_doc_impl
#handle_custom_interpreter_directives_impl
}
2018-04-30 02:24:21 -07:00
};
2018-09-22 00:24:27 -07:00
let output: TokenStream = tokens.into();
output
}