Skip to main content

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}