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