Skip to main content

arcis_interpreter/
module.rs

1use crate::{
2    circuit_builder::CircuitBuilder,
3    error::{Error, SpanRange},
4    helper_cleaner::HelperCleaner,
5    interpreter_builder::InterpreterBuilder,
6    utils::catch_unwind,
7};
8use arcis_compiler::core::{
9    global_value::global_expr_store::with_local_expr_store_as_global,
10    ir_builder::IRBuilder,
11};
12use proc_macro2::TokenStream;
13use quote::quote;
14
15pub fn run_interpreter_on_module(module: &syn::ItemMod) -> (TokenStream, Vec<Error>) {
16    let mut expr_store = IRBuilder::new(true);
17    let res = with_local_expr_store_as_global(
18        || {
19            catch_unwind(|| {
20                let interpreter_builder = InterpreterBuilder::new(module);
21                let mut interpreter = interpreter_builder.run();
22
23                interpreter.handle_all_unhandled_items();
24                if !interpreter.errors().is_empty() {
25                    let errors = interpreter.errors();
26                    return (quote! {}, errors);
27                }
28                let mut circuit_launcher = CircuitBuilder::new(interpreter);
29                circuit_launcher.build_circuits();
30                circuit_launcher
31                    .write_post_build(module.ident.to_string())
32                    .expect("Failed to write post build (pre-profiles, types).");
33
34                let tests = circuit_launcher.tests;
35                let errors = circuit_launcher.errors;
36                (tests, errors)
37            })
38        },
39        &mut expr_store,
40    );
41
42    res.unwrap_or_else(|p| {
43        let error_msg = match &p {
44            None => "unknown error",
45            Some(s) => s.as_str(),
46        };
47
48        (
49            quote! {},
50            vec![Error::new(
51                SpanRange::call_site(),
52                format!("The `#[encrypted]` macro panicked: {:?}.", error_msg),
53            )],
54        )
55    })
56}
57
58pub fn module_macro(input: TokenStream) -> TokenStream {
59    let mut module = match syn::parse2(input.clone()) {
60        Ok(module) => module,
61        Err(e) => {
62            let mut output = input;
63            output.extend(e.to_compile_error());
64            return output;
65        }
66    };
67
68    let (tests, errors) = run_interpreter_on_module(&module);
69
70    HelperCleaner::clean(&mut module);
71    let mod_name = &module.ident;
72    let mod_visibility = &module.vis;
73    let content = module.content.unwrap();
74    let mod_content = content.1.iter();
75    //let mod_unsafety = &module.unsafety;
76    //let mod_attrs = &module.attrs;
77    quote! {
78        #[allow(clippy::new_without_default)]
79        #mod_visibility mod #mod_name {
80            #(#mod_content)*
81            #(#errors)*
82            #[cfg(test)]
83            mod tests {
84                use super::*;
85                #tests
86            }
87        }
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94    #[ignore = "used for debugging only"]
95    #[test]
96    fn it_works() {
97        let token_stream = quote! {
98            mod blabla {
99                #[instruction]
100                fn gen_integers(max: u128) -> (u128, bool) {
101                    ArcisRNG::gen_integer_in_range(0, max, 8)
102                }
103            }
104        };
105
106        let result = module_macro(token_stream);
107        let syntax_tree = syn::parse_file(&result.to_string()).unwrap();
108        let formatted = prettyplease::unparse(&syntax_tree);
109        print!("{}", formatted);
110    }
111}