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}