Skip to main content

cljrs_eval/
lower.rs

1//! Rust-native IR lowering orchestration.
2//!
3//! Calls the Rust `cljrs_ir::lower` pipeline directly (no Clojure interpreter
4//! round-trip).  Macro expansion still runs through the interpreter since
5//! macros are user-defined Clojure functions.
6
7use std::sync::Arc;
8
9use cljrs_ir::IrFunction;
10use cljrs_reader::Form;
11
12use cljrs_env::env::Env;
13
14// ── Error type ──────────────────────────────────────────────────────────────
15
16#[derive(Debug)]
17pub enum LowerError {
18    /// The Rust lowering function failed.
19    LowerFailed(String),
20    /// IR conversion failed (kept for compatibility; no longer used internally).
21    ConvertFailed(String),
22    /// The compiler namespaces are not loaded yet (no longer used with Rust lowering,
23    /// kept for callers that may still pattern-match on this variant).
24    NotReady,
25}
26
27impl std::fmt::Display for LowerError {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        match self {
30            LowerError::NotReady => write!(f, "compiler not ready"),
31            LowerError::LowerFailed(msg) => write!(f, "lowering failed: {msg}"),
32            LowerError::ConvertFailed(msg) => write!(f, "IR conversion failed: {msg}"),
33        }
34    }
35}
36
37// ── Public entry point ──────────────────────────────────────────────────────
38
39/// Lower a function arity's body to IR using the native Rust compiler pipeline.
40pub fn lower_arity(
41    name: Option<&str>,
42    params: &[Arc<str>],
43    rest_param: Option<&Arc<str>>,
44    body: &[Form],
45    ns: &Arc<str>,
46    env: &mut Env,
47) -> Result<IrFunction, LowerError> {
48    lower_arity_inner(name, params, rest_param, body, ns, env, false)
49}
50
51/// Like [`lower_arity`], but also runs the region-optimization pass.
52pub fn lower_and_optimize_arity(
53    name: Option<&str>,
54    params: &[Arc<str>],
55    rest_param: Option<&Arc<str>>,
56    body: &[Form],
57    ns: &Arc<str>,
58    env: &mut Env,
59) -> Result<IrFunction, LowerError> {
60    lower_arity_inner(name, params, rest_param, body, ns, env, true)
61}
62
63fn lower_arity_inner(
64    name: Option<&str>,
65    params: &[Arc<str>],
66    rest_param: Option<&Arc<str>>,
67    body: &[Form],
68    ns: &Arc<str>,
69    env: &mut Env,
70    do_optimize: bool,
71) -> Result<IrFunction, LowerError> {
72    cljrs_logging::feat_debug!(
73        "lower",
74        "lowering {:?}/{:?} optimize? {}",
75        ns,
76        name,
77        do_optimize
78    );
79
80    // Macro expansion still requires the interpreter.
81    // Guard against re-entrant lowering during macro expansion.
82    use crate::apply::IR_LOWERING_ACTIVE;
83    let was_active = IR_LOWERING_ACTIVE.get();
84    IR_LOWERING_ACTIVE.set(true);
85
86    let expanded_body: Vec<Form> = body
87        .iter()
88        .map(|f| cljrs_interp::macros::macroexpand_all(f, env).unwrap_or_else(|_| f.clone()))
89        .collect();
90
91    IR_LOWERING_ACTIVE.with(|c| c.set(was_active));
92
93    // Build the flat params list (includes rest param as last element if present).
94    let mut all_params: Vec<Arc<str>> = params.to_vec();
95    if let Some(rest) = rest_param {
96        all_params.push(rest.clone());
97    }
98
99    let ir = cljrs_ir::lower::lower_fn_body(name, ns, &all_params, &expanded_body)
100        .map_err(|e| LowerError::LowerFailed(format!("{e:?}")))?;
101
102    Ok(if do_optimize {
103        cljrs_ir::lower::optimize(ir)
104    } else {
105        ir
106    })
107}