use std::sync::Arc;
use cljrs_ir::IrFunction;
use cljrs_reader::Form;
use cljrs_value::Value;
use cljrs_env::env::Env;
#[derive(Debug)]
pub enum LowerError {
NotReady,
LowerFailed(String),
ConvertFailed(String),
}
impl std::fmt::Display for LowerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
LowerError::NotReady => write!(f, "compiler not ready"),
LowerError::LowerFailed(msg) => write!(f, "lowering failed: {msg}"),
LowerError::ConvertFailed(msg) => write!(f, "IR conversion failed: {msg}"),
}
}
}
pub fn lower_arity(
name: Option<&str>,
params: &[Arc<str>],
rest_param: Option<&Arc<str>>,
body: &[Form],
ns: &Arc<str>,
env: &mut Env,
) -> Result<IrFunction, LowerError> {
if !env
.globals
.compiler_ready
.load(std::sync::atomic::Ordering::Acquire)
{
return Err(LowerError::NotReady);
}
lower_arity_inner(name, params, rest_param, body, ns, env, false)
}
pub fn lower_and_optimize_arity(
name: Option<&str>,
params: &[Arc<str>],
rest_param: Option<&Arc<str>>,
body: &[Form],
ns: &Arc<str>,
env: &mut Env,
) -> Result<IrFunction, LowerError> {
if !env
.globals
.compiler_ready
.load(std::sync::atomic::Ordering::Acquire)
{
return Err(LowerError::NotReady);
}
lower_arity_inner(name, params, rest_param, body, ns, env, true)
}
fn lower_arity_inner(
name: Option<&str>,
params: &[Arc<str>],
rest_param: Option<&Arc<str>>,
body: &[Form],
ns: &Arc<str>,
env: &mut Env,
optimize: bool,
) -> Result<IrFunction, LowerError> {
use cljrs_gc::GcPtr;
use cljrs_value::collections::vector::PersistentVector;
let globals = &env.globals;
let lower_fn = globals
.lookup_var_in_ns("cljrs.compiler.anf", "lower-fn-body")
.ok_or_else(|| {
LowerError::LowerFailed("cljrs.compiler.anf/lower-fn-body not found".to_string())
})?;
let lower_fn_val = lower_fn.get().deref().unwrap_or(Value::Nil);
let expanded_body: Vec<Form> = body
.iter()
.map(|f| cljrs_interp::macros::macroexpand_all(f, env).unwrap_or_else(|_| f.clone()))
.collect();
let body = expanded_body.as_slice();
let fname_val = match name {
Some(n) => Value::string(n.to_string()),
None => Value::Nil,
};
let ns_val = Value::string(ns.to_string());
let mut param_strs: Vec<Value> = params
.iter()
.map(|p| Value::string(p.to_string()))
.collect();
if let Some(rest) = rest_param {
param_strs.push(Value::string(rest.to_string()));
}
let params_val = Value::Vector(GcPtr::new(PersistentVector::from_iter(param_strs)));
let body_forms_val = Value::Vector(GcPtr::new(PersistentVector::from_iter(
body.iter().map(cljrs_builtins::form::form_to_value),
)));
cljrs_env::callback::push_eval_context(env);
use crate::apply::IR_LOWERING_ACTIVE;
let was_active = IR_LOWERING_ACTIVE.get();
IR_LOWERING_ACTIVE.set(true);
let ir_data = cljrs_env::callback::invoke(
&lower_fn_val,
vec![fname_val, ns_val, params_val, body_forms_val],
);
IR_LOWERING_ACTIVE.with(|c| c.set(was_active));
cljrs_env::callback::pop_eval_context();
let ir_data = ir_data.map_err(|e| LowerError::LowerFailed(format!("{e:?}")))?;
let ir_data = if optimize {
let optimize_fn = env
.globals
.lookup_var_in_ns("cljrs.compiler.optimize", "optimize")
.ok_or_else(|| {
LowerError::LowerFailed(
"cljrs.compiler.optimize/optimize not found (require it first)".to_string(),
)
})?;
let optimize_fn_val = optimize_fn.get().deref().unwrap_or(Value::Nil);
cljrs_env::callback::push_eval_context(env);
let was_active = IR_LOWERING_ACTIVE.get();
IR_LOWERING_ACTIVE.set(true);
let result = cljrs_env::callback::invoke(&optimize_fn_val, vec![ir_data]);
IR_LOWERING_ACTIVE.with(|c| c.set(was_active));
cljrs_env::callback::pop_eval_context();
result.map_err(|e| LowerError::LowerFailed(format!("optimize pass failed: {e:?}")))?
} else {
ir_data
};
crate::ir_convert::value_to_ir_function(&ir_data)
.map_err(|e| LowerError::ConvertFailed(format!("{e}")))
}