preprocessor_derive/lib.rs
1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Expr, File, ItemFn, parse_macro_input};
4
5mod evaluator;
6mod evcxr_engine;
7mod prelude;
8
9/// `preprocessor::op!(...)` — Expression-level macro for compile-time evaluation.
10///
11/// Parses the input expression, evaluates it at compile time,
12/// and replaces it with the literal result.
13///
14/// **Important**: If the expression cannot be fully evaluated at compile time
15/// (e.g., contains free variables, unknown functions, etc.), the macro will
16/// generate a compile-time error instead of passing through unchanged.
17///
18/// # Example
19/// ```ignore
20/// let result = preprocessor::op!(1 + 2 * 3); // → let result = 7;
21/// let x = preprocessor::op!(a + 1); // → COMPILE ERROR: cannot evaluate at compile time
22/// ```
23#[proc_macro]
24pub fn op(input: TokenStream) -> TokenStream {
25 let expr = parse_macro_input!(input as Expr);
26
27 let (transformed, changed) = evaluator::transform_expr(&expr);
28
29 if changed {
30 quote!(#transformed).into()
31 } else {
32 quote!(#expr).into()
33 }
34}
35
36/// `#[preprocessor::optimize]` — Function-level attribute macro for compile-time optimization.
37///
38/// Recursively traverses all statements and expressions in the function body,
39/// identifies sub-expressions that can be evaluated at compile time,
40/// and rewrites them as pre-computed literals.
41///
42/// # Example
43/// ```ignore
44/// #[preprocessor::optimize]
45/// fn compute() -> i32 {
46/// let x = 1 + 2; // → let x = 3;
47/// let y = x * 10; // kept as-is (depends on local variable)
48/// y
49/// }
50/// ```
51#[proc_macro_attribute]
52pub fn optimize(_attr: TokenStream, item: TokenStream) -> TokenStream {
53 let input_fn = parse_macro_input!(item as ItemFn);
54
55 // Transform the function body
56 let transformed_block = evaluator::transform_block(&input_fn.block);
57
58 let attrs = &input_fn.attrs;
59 let vis = &input_fn.vis;
60 let sig = &input_fn.sig;
61
62 let output = quote! {
63 #(#attrs)*
64 #vis #sig {
65 #transformed_block
66 }
67 };
68
69 output.into()
70}
71
72/// `#[preprocessor::prelude]` — Crate-level attribute macro for automatic dependency resolution.
73///
74/// Scans the crate for `op!` macro usages, identifies unqualified paths (like `Local`),
75/// resolves them to their full crate paths (like `chrono::Local`) based on `use` statements,
76/// and rewrites the `op!` calls to use these full paths. This allows `op!` to work
77/// without requiring fully qualified paths inside the macro invocation.
78///
79/// # Example
80/// ```ignore
81/// #![preprocessor::prelude]
82///
83/// fn main() {
84/// // Works even without `use chrono::Local;` if `Local` is imported elsewhere
85/// let time = preprocessor::op!(Local::now().to_string());
86/// }
87/// ```
88#[proc_macro_attribute]
89pub fn prelude(_attr: TokenStream, item: TokenStream) -> TokenStream {
90 let file = parse_macro_input!(item as File);
91 let output = prelude::process_file(file);
92
93 quote!(#output).into()
94}