use std::collections::HashMap;
use std::sync::Arc;
use crate::destructure::bind_pattern;
use crate::env::{Env, RequireRefer, RequireSpec};
use crate::error::{EvalError, EvalResult};
use crate::eval::{eval, eval_body, form_to_value, is_special_form};
use crate::loader::load_ns;
use cljrs_gc::GcPtr;
use cljrs_reader::Form;
use cljrs_reader::form::FormKind;
use cljrs_value::error::ExceptionInfo;
use cljrs_value::{
CljxFn, CljxFnArity, Keyword, MapValue, MultiFn, Protocol, ProtocolFn, ProtocolMethod,
TypeInstance, Value, ValueError,
};
pub const SPECIAL_FORMS: &[&str] = &[
"def",
"fn*",
"fn",
"if",
"do",
"let*",
"let",
"loop*",
"loop",
"recur",
"quote",
"var",
"set!",
"throw",
"try",
"defn",
"defn-",
"defmacro",
"defonce",
"and",
"or",
".",
"ns",
"require",
"letfn",
"in-ns",
"alias",
"defprotocol",
"extend-type",
"extend-protocol",
"defmulti",
"defmethod",
"defrecord",
"reify",
"load-file",
"binding",
"with-out-str",
];
pub fn eval_special(head: &str, args: &[Form], env: &mut Env) -> EvalResult {
match head {
"def" => eval_def(args, env),
"fn*" | "fn" => eval_fn(args, env),
"if" => eval_if(args, env),
"do" => eval_body(args, env),
"let*" | "let" => eval_let(args, env),
"loop*" | "loop" => eval_loop(args, env),
"recur" => eval_recur(args, env),
"quote" => eval_quote(args),
"var" => eval_var(args, env),
"set!" => eval_set_bang(args, env),
"throw" => eval_throw(args, env),
"try" => eval_try(args, env),
"defn" | "defn-" => eval_defn(args, env),
"defmacro" => eval_defmacro(args, env),
"defonce" => eval_defonce(args, env),
"and" => eval_and(args, env),
"or" => eval_or(args, env),
"." => Err(EvalError::Runtime("interop not yet implemented".into())),
"ns" => eval_ns(args, env),
"require" => eval_require(args, env),
"letfn" => eval_letfn(args, env),
"in-ns" => eval_in_ns(args, env),
"alias" => eval_alias(args, env),
"defprotocol" => eval_defprotocol(args, env),
"extend-type" => eval_extend_type(args, env),
"extend-protocol" => eval_extend_protocol(args, env),
"defmulti" => eval_defmulti(args, env),
"defmethod" => eval_defmethod(args, env),
"defrecord" => eval_defrecord(args, env),
"reify" => eval_reify(args, env),
"load-file" => eval_load_file(args, env),
"binding" => eval_binding(args, env),
"with-out-str" => eval_with_out_str(args, env),
_ => unreachable!("unknown special form: {head}"),
}
}
fn eval_def(args: &[Form], env: &mut Env) -> EvalResult {
if args.is_empty() {
return Err(EvalError::Runtime("def requires a name".into()));
}
let (name, meta_opt) = extract_def_name(&args[0], env)?;
let val_idx = if args.len() > 2 && matches!(args[1].kind, FormKind::Str(_)) {
2
} else {
1
};
let val = if args.len() > val_idx {
eval(&args[val_idx], env)?
} else {
Value::Nil
};
let var = env
.globals
.intern(&env.current_ns, Arc::from(name.as_str()), val.clone());
if let Some(meta_val) = meta_opt {
var.get().set_meta(meta_val);
}
Ok(Value::Var(var))
}
fn extract_def_name(form: &Form, env: &mut Env) -> EvalResult<(String, Option<Value>)> {
match &form.kind {
FormKind::Symbol(s) => Ok((s.clone(), None)),
FormKind::Meta(meta_form, inner) => {
let meta_val = compile_meta_form(meta_form, env)?;
match &inner.kind {
FormKind::Symbol(s) => Ok((s.clone(), Some(meta_val))),
_ => Err(EvalError::Runtime("def name must be a symbol".into())),
}
}
_ => Err(EvalError::Runtime("def name must be a symbol".into())),
}
}
fn compile_meta_form(meta: &Form, env: &mut Env) -> EvalResult<Value> {
match &meta.kind {
FormKind::Keyword(k) => {
let m = MapValue::empty().assoc(Value::keyword(Keyword::parse(k)), Value::Bool(true));
Ok(Value::Map(m))
}
FormKind::Symbol(s) => {
let m = MapValue::empty().assoc(
Value::keyword(Keyword::parse("tag")),
Value::string(s.clone()),
);
Ok(Value::Map(m))
}
_ => eval(meta, env), }
}
fn eval_fn(args: &[Form], env: &mut Env) -> EvalResult {
let mut idx = 0;
let mut name: Option<Arc<str>> = None;
if let Some(FormKind::Symbol(s)) = args.first().map(|f| &f.kind)
&& !is_special_form(s)
{
name = Some(Arc::from(s.as_str()));
idx = 1;
}
let rest = &args[idx..];
if rest.is_empty() {
return Err(EvalError::Runtime("fn* requires params and body".into()));
}
let arities = match &rest[0].kind {
FormKind::Vector(_) => {
vec![parse_arity(&rest[0], &rest[1..])?]
}
FormKind::List(_) => {
rest.iter()
.map(|arity_form| {
if let FormKind::List(forms) = &arity_form.kind {
if forms.is_empty() {
return Err(EvalError::Runtime("arity clause requires params".into()));
}
parse_arity(&forms[0], &forms[1..])
} else {
Err(EvalError::Runtime("expected arity clause (list)".into()))
}
})
.collect::<EvalResult<Vec<_>>>()?
}
_ => {
return Err(EvalError::Runtime(
"fn* expects vector or arity clauses".into(),
));
}
};
let (closed_over_names, closed_over_vals) = env.all_local_bindings();
let cljrs_fn = CljxFn::new(
name,
arities,
closed_over_names,
closed_over_vals,
false,
Arc::clone(&env.current_ns),
);
Ok(Value::Fn(GcPtr::new(cljrs_fn)))
}
pub fn parse_arity(params_form: &Form, body: &[Form]) -> EvalResult<CljxFnArity> {
let param_forms = match ¶ms_form.kind {
FormKind::Vector(v) => v,
_ => {
return Err(EvalError::Runtime(
"fn arity params must be a vector".into(),
));
}
};
let mut params: Vec<Arc<str>> = Vec::new();
let mut rest_param: Option<Arc<str>> = None;
let mut destructure_params: Vec<(usize, Form)> = Vec::new();
let mut destructure_rest: Option<Form> = None;
let mut saw_amp = false;
for p in param_forms {
match &p.kind {
FormKind::Symbol(s) if s == "&" => {
saw_amp = true;
}
FormKind::Symbol(s) => {
if saw_amp {
rest_param = Some(Arc::from(s.as_str()));
break;
} else {
params.push(Arc::from(s.as_str()));
}
}
FormKind::Vector(_) | FormKind::Map(_) => {
if saw_amp {
let gensym = format!("__destructure_rest_{}", params.len());
rest_param = Some(Arc::from(gensym.as_str()));
destructure_rest = Some(p.clone());
break;
} else {
let idx = params.len();
let gensym = format!("__destructure_{idx}");
params.push(Arc::from(gensym.as_str()));
destructure_params.push((idx, p.clone()));
}
}
_ => {
return Err(EvalError::Runtime(
"fn params must be symbols, vectors, or maps".into(),
));
}
}
}
Ok(CljxFnArity {
params,
rest_param,
body: body.to_vec(),
destructure_params,
destructure_rest,
})
}
fn eval_if(args: &[Form], env: &mut Env) -> EvalResult {
if args.is_empty() {
return Err(EvalError::Runtime("if requires a test".into()));
}
let test = eval(&args[0], env)?;
let truthy = !matches!(test, Value::Nil | Value::Bool(false));
if truthy {
if args.len() > 1 {
eval(&args[1], env)
} else {
Ok(Value::Nil)
}
} else if args.len() > 2 {
eval(&args[2], env)
} else {
Ok(Value::Nil)
}
}
fn eval_let(args: &[Form], env: &mut Env) -> EvalResult {
let bindings = match args.first().map(|f| &f.kind) {
Some(FormKind::Vector(v)) => v.clone(),
_ => return Err(EvalError::Runtime("let* requires a binding vector".into())),
};
if bindings.len() % 2 != 0 {
return Err(EvalError::Runtime(
"let* binding vector must have even length".into(),
));
}
let body = &args[1..];
let chains = crate::virtualize::detect_let_chains(&bindings);
let virtualizable_chains = find_virtualizable_chains(&chains, &bindings, body);
env.push_frame();
let pairs: Vec<_> = bindings.chunks(2).collect();
let mut i = 0;
while i < pairs.len() {
if let Some(chain) = virtualizable_chains.iter().find(|c| c.start == i) {
match eval_virtualized_chain(chain, &pairs, env) {
Ok(()) => {
i += chain.len;
continue;
}
Err(e) => {
env.pop_frame();
return Err(e);
}
}
}
let pair = pairs[i];
let val = match eval(&pair[1], env) {
Ok(v) => v,
Err(e) => {
env.pop_frame();
return Err(e);
}
};
if let Err(e) = bind_pattern(&pair[0], val, env) {
env.pop_frame();
return Err(e);
}
i += 1;
}
let result = eval_body(body, env);
env.pop_frame();
result
}
fn find_virtualizable_chains<'a>(
chains: &'a [crate::virtualize::LetChain],
bindings: &[Form],
body: &[Form],
) -> Vec<&'a crate::virtualize::LetChain> {
chains
.iter()
.filter(|chain| {
for j in chain.start..(chain.start + chain.len - 1) {
let name = match &bindings[j * 2].kind {
FormKind::Symbol(s) => s.as_str(),
_ => return false,
};
if crate::virtualize::binding_used_in_body(name, body) {
return false;
}
if crate::virtualize::binding_used_in_other_bindings(
name,
bindings,
chain.start,
chain.len,
) {
return false;
}
}
true
})
.collect()
}
fn eval_virtualized_chain(
chain: &crate::virtualize::LetChain,
pairs: &[&[Form]],
env: &mut Env,
) -> Result<(), EvalError> {
use crate::transients::{
builtin_assoc_bang, builtin_conj_bang, builtin_persistent_bang, builtin_transient,
};
let first_pair = pairs[chain.start];
let first_expr_forms = match &first_pair[1].kind {
FormKind::List(forms) => forms,
_ => unreachable!("chain detection ensures this is a list"),
};
let root_collection = eval(&first_expr_forms[1], env)?;
let mut transient = match builtin_transient(std::slice::from_ref(&root_collection)) {
Ok(t) => t,
Err(_) => {
return eval_chain_normally(chain, pairs, env);
}
};
for j in 0..chain.len {
let pair_idx = chain.start + j;
let pair = pairs[pair_idx];
let expr_forms = match &pair[1].kind {
FormKind::List(forms) => forms,
_ => unreachable!(),
};
let mut args = vec![transient.clone()];
for arg_form in expr_forms.iter().skip(2) {
args.push(eval(arg_form, env)?);
}
transient = match chain.ops[j] {
crate::virtualize::ChainOpKind::Assoc => {
builtin_assoc_bang(&args).map_err(|e| EvalError::Runtime(e.to_string()))?
}
crate::virtualize::ChainOpKind::Conj => {
builtin_conj_bang(&args).map_err(|e| EvalError::Runtime(e.to_string()))?
}
};
if j < chain.len - 1 {
let name = match &pair[0].kind {
FormKind::Symbol(s) => s.clone(),
_ => unreachable!(),
};
env.bind(Arc::from(name.as_str()), Value::Nil);
}
}
let persistent =
builtin_persistent_bang(&[transient]).map_err(|e| EvalError::Runtime(e.to_string()))?;
let last_pair = pairs[chain.start + chain.len - 1];
bind_pattern(&last_pair[0], persistent, env)?;
Ok(())
}
fn eval_chain_normally(
chain: &crate::virtualize::LetChain,
pairs: &[&[Form]],
env: &mut Env,
) -> Result<(), EvalError> {
for j in 0..chain.len {
let pair_idx = chain.start + j;
let pair = pairs[pair_idx];
let val = eval(&pair[1], env)?;
bind_pattern(&pair[0], val, env)?;
}
Ok(())
}
pub fn eval_loop(args: &[Form], env: &mut Env) -> EvalResult {
let bindings = match args.first().map(|f| &f.kind) {
Some(FormKind::Vector(v)) => v.clone(),
_ => return Err(EvalError::Runtime("loop* requires a binding vector".into())),
};
if bindings.len() % 2 != 0 {
return Err(EvalError::Runtime(
"loop* binding vector must have even length".into(),
));
}
let body = &args[1..];
let patterns: Vec<Form> = bindings.iter().step_by(2).cloned().collect();
let mut current_vals: Vec<Value> = Vec::new();
for i in (1..bindings.len()).step_by(2) {
current_vals.push(eval(&bindings[i], env)?);
}
loop {
let _vals_root = crate::root_values(¤t_vals);
crate::gc_safepoint(env);
env.push_frame();
for (pat, val) in patterns.iter().zip(current_vals.iter()) {
if let Err(e) = bind_pattern(pat, val.clone(), env) {
env.pop_frame();
return Err(e);
}
}
let result = eval_body_recur(body, env);
env.pop_frame();
match result {
Ok(v) => return Ok(v),
Err(EvalError::Recur(new_vals)) => {
if new_vals.len() != patterns.len() {
return Err(EvalError::Arity {
name: "recur".into(),
expected: patterns.len().to_string(),
got: new_vals.len(),
});
}
current_vals = new_vals;
}
Err(e) => return Err(e),
}
}
}
fn eval_recur(args: &[Form], env: &mut Env) -> EvalResult {
let vals: Vec<Value> = args
.iter()
.map(|f| eval(f, env))
.collect::<EvalResult<_>>()?;
Err(EvalError::Recur(vals))
}
pub fn eval_body_recur(body: &[Form], env: &mut Env) -> EvalResult {
let mut result = Value::Nil;
for form in body {
result = eval(form, env)?;
}
Ok(result)
}
fn eval_quote(args: &[Form]) -> EvalResult {
match args.first() {
Some(f) => Ok(form_to_value(f)),
None => Err(EvalError::Runtime("quote requires an argument".into())),
}
}
fn eval_var(args: &[Form], env: &mut Env) -> EvalResult {
let sym = match args.first().map(|f| &f.kind) {
Some(FormKind::Symbol(s)) => s.clone(),
_ => return Err(EvalError::Runtime("var requires a symbol".into())),
};
let parsed = cljrs_value::Symbol::parse(&sym);
let ns = parsed.namespace.as_deref().unwrap_or(&env.current_ns);
let name = parsed.name.as_ref();
env.globals
.lookup_var_in_ns(ns, name)
.map(Value::Var)
.ok_or_else(|| EvalError::UnboundSymbol(sym))
}
fn eval_set_bang(args: &[Form], env: &mut Env) -> EvalResult {
let sym = match args.first().map(|f| &f.kind) {
Some(FormKind::Symbol(s)) => s.clone(),
_ => return Err(EvalError::Runtime("set! requires a symbol".into())),
};
let val = if args.len() > 1 {
eval(&args[1], env)?
} else {
Value::Nil
};
let parsed = cljrs_value::Symbol::parse(&sym);
let ns = parsed.namespace.as_deref().unwrap_or(&env.current_ns);
let var = env
.globals
.lookup_var_in_ns(ns, &parsed.name)
.ok_or_else(|| EvalError::UnboundSymbol(sym))?;
if !crate::dynamics::set_thread_local(&var, val.clone()) {
var.get().bind(val.clone());
}
Ok(val)
}
fn eval_throw(args: &[Form], env: &mut Env) -> EvalResult {
let val = match args.first() {
Some(f) => eval(f, env)?,
None => Value::Nil,
};
let val = match val {
Value::Error(_) => val,
other => {
let msg = format!("{}", other);
Value::Error(GcPtr::new(ExceptionInfo::new(
ValueError::Other(msg.clone()),
msg,
None,
None,
)))
}
};
Err(EvalError::Thrown(val))
}
struct CatchClause<'a> {
type_sym: &'a str,
binding: &'a str,
body: &'a [Form],
}
fn eval_error_to_value(err: &EvalError) -> Value {
let msg = err.to_string();
Value::Error(GcPtr::new(ExceptionInfo::new(
ValueError::Other(msg.clone()),
msg,
None,
None,
)))
}
fn catch_type_matches(type_name: &str, val: &Value) -> bool {
let short = type_name.rsplit('.').next().unwrap_or(type_name);
match short {
"Object" | "Exception" | "Throwable" | "Error" => true,
"ExceptionInfo" => matches!(val, Value::Error(_)),
_ => false,
}
}
fn eval_try(args: &[Form], env: &mut Env) -> EvalResult {
let (body, catches, fin_body) = parse_try_args(args);
let mut result = eval_body(body, env);
let err_opt = match std::mem::replace(&mut result, Ok(Value::Nil)) {
Ok(v) => {
result = Ok(v);
None
}
Err(EvalError::Recur(args)) => {
result = Err(EvalError::Recur(args));
None
}
Err(other) => Some(other),
};
if let Some(err) = err_opt {
let thrown_val = match err {
EvalError::Thrown(v) => v,
ref other => eval_error_to_value(other),
};
let mut handled = false;
for c in &catches {
if catch_type_matches(c.type_sym, &thrown_val) {
env.push_frame();
env.bind(Arc::from(c.binding), thrown_val.clone());
result = eval_body(c.body, env);
env.pop_frame();
handled = true;
break;
}
}
if !handled {
result = Err(EvalError::Thrown(thrown_val));
}
}
if !fin_body.is_empty() {
let _ = eval_body(fin_body, env);
}
result
}
fn parse_try_args(args: &[Form]) -> (&[Form], Vec<CatchClause<'_>>, &[Form]) {
let mut body_end = args.len();
let mut catches: Vec<CatchClause<'_>> = Vec::new();
let mut fin_body: &[Form] = &[];
for (i, form) in args.iter().enumerate() {
if let FormKind::List(parts) = &form.kind
&& let Some(FormKind::Symbol(s)) = parts.first().map(|f| &f.kind)
{
if s == "catch" {
if i < body_end {
body_end = i;
}
let type_sym = match parts.get(1).map(|f| &f.kind) {
Some(FormKind::Symbol(s)) => s.as_str(),
_ => continue,
};
let binding = match parts.get(2).map(|f| &f.kind) {
Some(FormKind::Symbol(s)) => s.as_str(),
_ => continue,
};
catches.push(CatchClause {
type_sym,
binding,
body: &parts[3..],
});
continue;
}
if s == "finally" {
if i < body_end {
body_end = i;
}
fin_body = &parts[1..];
continue;
}
}
}
(&args[..body_end], catches, fin_body)
}
pub fn eval_defn(args: &[Form], env: &mut Env) -> EvalResult {
let name = require_sym(args, 0, "defn")?;
let mut rest_start = 1;
if rest_start < args.len() && matches!(args[rest_start].kind, FormKind::Str(_)) {
rest_start += 1;
}
if rest_start < args.len() && matches!(args[rest_start].kind, FormKind::Map(_)) {
rest_start += 1;
}
let mut fn_args = vec![Form::new(
FormKind::Symbol(name.to_string()),
args[0].span.clone(),
)];
fn_args.extend_from_slice(&args[rest_start..]);
let fn_val = eval_fn(&fn_args, env)?;
let var = env
.globals
.intern(&env.current_ns, Arc::from(name), fn_val.clone());
Ok(Value::Var(var))
}
fn prepend_macro_params(form: &Form) -> Form {
let span = form.span.clone();
match &form.kind {
FormKind::Vector(params) => {
let mut new_params = vec![
Form::new(FormKind::Symbol("&form".to_string()), span.clone()),
Form::new(FormKind::Symbol("&env".to_string()), span.clone()),
];
new_params.extend_from_slice(params);
Form::new(FormKind::Vector(new_params), span)
}
FormKind::List(forms) => {
if let Some(first) = forms.first() {
let new_params_form = prepend_macro_params(first);
let mut new_forms = vec![new_params_form];
new_forms.extend_from_slice(&forms[1..]);
Form::new(FormKind::List(new_forms), span)
} else {
form.clone()
}
}
_ => form.clone(),
}
}
fn eval_defmacro(args: &[Form], env: &mut Env) -> EvalResult {
let name = require_sym(args, 0, "defmacro")?;
let mut rest_start = 1;
if rest_start < args.len() && matches!(args[rest_start].kind, FormKind::Str(_)) {
rest_start += 1;
}
if rest_start < args.len() && matches!(args[rest_start].kind, FormKind::Map(_)) {
rest_start += 1;
}
let mut fn_args = vec![Form::new(
FormKind::Symbol(name.to_string()),
args[0].span.clone(),
)];
for form in &args[rest_start..] {
fn_args.push(prepend_macro_params(form));
}
let fn_val = eval_fn(&fn_args, env)?;
let macro_val = match fn_val {
Value::Fn(f) => {
let mut mfn = f.get().clone();
mfn.is_macro = true;
Value::Macro(GcPtr::new(mfn))
}
other => other,
};
let var = env
.globals
.intern(&env.current_ns, Arc::from(name), macro_val.clone());
Ok(Value::Var(var))
}
fn eval_defonce(args: &[Form], env: &mut Env) -> EvalResult {
let name = require_sym(args, 0, "defonce")?;
if let Some(var) = env.globals.lookup_var(&env.current_ns, name)
&& var.get().is_bound()
{
return Ok(Value::Var(var));
}
eval_def(args, env)
}
fn eval_and(args: &[Form], env: &mut Env) -> EvalResult {
let mut result = Value::Bool(true);
for form in args {
result = eval(form, env)?;
if matches!(result, Value::Nil | Value::Bool(false)) {
return Ok(result);
}
}
Ok(result)
}
fn eval_or(args: &[Form], env: &mut Env) -> EvalResult {
let mut last = Value::Nil;
for form in args {
last = eval(form, env)?;
if !matches!(last, Value::Nil | Value::Bool(false)) {
return Ok(last);
}
}
Ok(last)
}
fn eval_require(args: &[Form], env: &mut Env) -> EvalResult {
for arg in args {
let val = eval(arg, env)?;
let spec = parse_require_spec_val(val).map_err(EvalError::Runtime)?;
load_ns(env.globals.clone(), &spec, &env.current_ns)?;
}
Ok(Value::Nil)
}
fn parse_require_spec_val(val: Value) -> Result<RequireSpec, String> {
match val {
Value::Symbol(s) => Ok(RequireSpec {
ns: s.get().name.clone(),
alias: None,
refer: RequireRefer::None,
}),
Value::Vector(v) => {
let items: Vec<Value> = v.get().iter().cloned().collect();
if items.is_empty() {
return Err("require spec vector must not be empty".into());
}
let ns = match &items[0] {
Value::Symbol(s) => s.get().name.clone(),
other => {
return Err(format!(
"require spec: first element must be a symbol, got {}",
other.type_name()
));
}
};
let mut alias = None;
let mut refer = RequireRefer::None;
let mut i = 1;
while i < items.len() {
match &items[i] {
Value::Keyword(k) if k.get().name.as_ref() == "as" => {
i += 1;
alias = Some(match items.get(i) {
Some(Value::Symbol(s)) => s.get().name.clone(),
_ => return Err("require :as expects a symbol".into()),
});
}
Value::Keyword(k) if k.get().name.as_ref() == "refer" => {
i += 1;
refer = match items.get(i) {
Some(Value::Keyword(k2)) if k2.get().name.as_ref() == "all" => {
RequireRefer::All
}
Some(Value::Vector(rv)) => {
let names: Vec<Arc<str>> = rv
.get()
.iter()
.map(|v| match v {
Value::Symbol(s) => Ok(s.get().name.clone()),
other => Err(format!(
"require :refer expects symbols, got {}",
other.type_name()
)),
})
.collect::<Result<_, _>>()?;
RequireRefer::Named(names)
}
_ => {
return Err(
"require :refer expects :all or a vector of symbols".into()
);
}
};
}
other => {
return Err(format!(
"require spec: unexpected option {}",
other.type_name()
));
}
}
i += 1;
}
Ok(RequireSpec { ns, alias, refer })
}
other => Err(format!(
"require expects a symbol or vector, got {}",
other.type_name()
)),
}
}
pub fn select_reader_cond(clauses: &[Form]) -> Option<&Form> {
let mut default: Option<&Form> = None;
let mut i = 0;
while i + 1 < clauses.len() {
match &clauses[i].kind {
FormKind::Keyword(k) if k == "rust" => return Some(&clauses[i + 1]),
FormKind::Keyword(k) if k == "default" => default = Some(&clauses[i + 1]),
_ => {}
}
i += 2;
}
default
}
fn parse_require_spec_form(form: &Form) -> Result<RequireSpec, String> {
match &form.kind {
FormKind::Symbol(s) => Ok(RequireSpec {
ns: Arc::from(s.as_str()),
alias: None,
refer: RequireRefer::None,
}),
FormKind::Vector(items) => {
if items.is_empty() {
return Err("require spec vector must not be empty".into());
}
let ns = match &items[0].kind {
FormKind::Symbol(s) => Arc::from(s.as_str()),
_ => return Err("require spec: first element must be a symbol".into()),
};
let mut alias = None;
let mut refer = RequireRefer::None;
let mut i = 1;
while i < items.len() {
let item = match &items[i].kind {
FormKind::ReaderCond { clauses, .. } => {
match select_reader_cond(clauses) {
Some(f) => f,
None => {
i += 1;
continue;
} }
}
_ => &items[i],
};
match &item.kind {
FormKind::Keyword(k) if k == "as" => {
i += 1;
alias = Some(match items.get(i).map(|f| &f.kind) {
Some(FormKind::Symbol(s)) => Arc::from(s.as_str()),
_ => return Err("require :as expects a symbol".into()),
});
}
FormKind::Keyword(k) if k == "refer" => {
i += 1;
refer = match items.get(i).map(|f| &f.kind) {
Some(FormKind::Keyword(k2)) if k2 == "all" => RequireRefer::All,
Some(FormKind::Vector(rv)) => {
let names: Vec<Arc<str>> = rv
.iter()
.map(|f| match &f.kind {
FormKind::Symbol(s) => Ok(Arc::from(s.as_str())),
_ => Err("require :refer expects symbols".to_string()),
})
.collect::<Result<_, _>>()?;
RequireRefer::Named(names)
}
_ => return Err("require :refer expects :all or a vector".into()),
};
}
_ => return Err(format!("require spec: unexpected form at position {i}")),
}
i += 1;
}
Ok(RequireSpec { ns, alias, refer })
}
_ => Err("require spec must be a symbol or vector".into()),
}
}
fn eval_ns(args: &[Form], env: &mut Env) -> EvalResult {
let name = require_sym(args, 0, "ns")?;
env.globals.get_or_create_ns(name);
env.current_ns = Arc::from(name);
if name != "clojure.core" {
env.globals.refer_all(name, "clojure.core");
}
sync_star_ns(env);
for clause in &args[1..] {
if let FormKind::List(items) = &clause.kind {
match items.first().map(|f| &f.kind) {
Some(FormKind::Keyword(k)) if k == "require" => {
let expanded = crate::eval::expand_reader_conds(&items[1..]);
for spec_form in &expanded {
let spec =
parse_require_spec_form(spec_form).map_err(EvalError::Runtime)?;
load_ns(env.globals.clone(), &spec, name)?;
}
}
_ => {}
}
}
}
let ns_ptr = env.globals.get_or_create_ns(&env.current_ns);
Ok(Value::Namespace(ns_ptr))
}
fn eval_load_file(args: &[Form], env: &mut Env) -> EvalResult {
if args.is_empty() {
return Err(EvalError::Runtime(
"load-file requires a path argument".into(),
));
}
let path_val = eval(&args[0], env)?;
let path = match &path_val {
Value::Str(s) => s.get().clone(),
v => {
return Err(EvalError::Runtime(format!(
"load-file: expected string, got {}",
v.type_name()
)));
}
};
let src = std::fs::read_to_string(&path)
.map_err(|e| EvalError::Runtime(format!("load-file: {e}")))?;
let mut parser = cljrs_reader::Parser::new(src, path.clone());
let forms = parser
.parse_all()
.map_err(|e| EvalError::Runtime(format!("load-file parse error: {e}")))?;
let mut result = Value::Nil;
for form in forms {
let _alloc_frame = cljrs_gc::push_alloc_frame();
result = eval(&form, env)?;
}
Ok(result)
}
fn eval_letfn(args: &[Form], env: &mut Env) -> EvalResult {
let bindings = match args.first().map(|f| &f.kind) {
Some(FormKind::Vector(v)) => v.clone(),
_ => return Err(EvalError::Runtime("letfn requires a binding vector".into())),
};
env.push_frame();
for binding in &bindings {
if let FormKind::List(parts) = &binding.kind {
if parts.is_empty() {
continue;
}
let fn_val = match eval_fn(parts, env) {
Ok(v) => v,
Err(e) => {
env.pop_frame();
return Err(e);
}
};
let name = match &parts[0].kind {
FormKind::Symbol(s) => s.clone(),
_ => {
env.pop_frame();
return Err(EvalError::Runtime(
"letfn binding name must be a symbol".into(),
));
}
};
env.bind(Arc::from(name.as_str()), fn_val);
}
}
let body = &args[1..];
let result = eval_body(body, env);
env.pop_frame();
result
}
fn eval_in_ns(args: &[Form], env: &mut Env) -> EvalResult {
if args.is_empty() {
return Err(EvalError::Runtime("in-ns requires a namespace name".into()));
}
let ns_val = eval(&args[0], env)?;
let ns_name = extract_ns_name(&ns_val)?;
env.globals.get_or_create_ns(&ns_name);
env.globals.refer_all(&ns_name, "clojure.core");
env.current_ns = Arc::from(ns_name.as_str());
sync_star_ns(env);
let ns_ptr = env.globals.get_or_create_ns(&env.current_ns);
Ok(Value::Namespace(ns_ptr))
}
fn eval_alias(args: &[Form], env: &mut Env) -> EvalResult {
if args.len() < 2 {
return Err(EvalError::Runtime(
"alias requires alias-sym and namespace-sym".into(),
));
}
let alias_val = eval(&args[0], env)?;
let ns_val = eval(&args[1], env)?;
let alias_name = extract_ns_name(&alias_val)?;
let full_ns = extract_ns_name(&ns_val)?;
let ns_ptr = env.globals.get_or_create_ns(&env.current_ns);
let mut aliases = ns_ptr.get().aliases.lock().unwrap();
aliases.insert(Arc::from(alias_name.as_str()), Arc::from(full_ns.as_str()));
Ok(Value::Nil)
}
fn extract_ns_name(v: &Value) -> EvalResult<String> {
match v {
Value::Symbol(s) => {
Ok(s.get().name.as_ref().to_string())
}
Value::Str(s) => Ok(s.get().clone()),
Value::Keyword(k) => Ok(k.get().name.as_ref().to_string()),
other => Err(EvalError::Runtime(format!(
"expected a symbol or string for namespace name, got {}",
other.type_name()
))),
}
}
fn eval_defprotocol(args: &[Form], env: &mut Env) -> EvalResult {
let name = require_sym(args, 0, "defprotocol")?;
let proto_name: Arc<str> = Arc::from(name);
let methods_start = if args.len() > 1 && matches!(args[1].kind, FormKind::Str(_)) {
2
} else {
1
};
let mut methods: Vec<ProtocolMethod> = Vec::new();
for form in &args[methods_start..] {
let parts = match &form.kind {
FormKind::List(parts) => parts,
_ => continue, };
if parts.is_empty() {
continue;
}
let method_name = match &parts[0].kind {
FormKind::Symbol(s) => Arc::from(s.as_str()),
_ => continue,
};
let (min_arity, variadic) = if let Some(params_form) =
parts.iter().find(|f| matches!(f.kind, FormKind::Vector(_)))
{
if let FormKind::Vector(param_forms) = ¶ms_form.kind {
let variadic = param_forms
.iter()
.any(|p| matches!(&p.kind, FormKind::Symbol(s) if s == "&"));
let fixed: usize = param_forms
.iter()
.filter(|p| !matches!(&p.kind, FormKind::Symbol(s) if s == "&"))
.count();
(fixed, variadic)
} else {
(1, false)
}
} else {
(1, false)
};
methods.push(ProtocolMethod {
name: method_name,
min_arity,
variadic,
});
}
let ns: Arc<str> = env.current_ns.clone();
let proto = Protocol::new(proto_name.clone(), ns, methods.clone());
let proto_ptr = GcPtr::new(proto);
let proto_var = env.globals.intern(
&env.current_ns,
proto_name.clone(),
Value::Protocol(proto_ptr.clone()),
);
for method in &methods {
let pf = ProtocolFn {
protocol: proto_ptr.clone(),
method_name: method.name.clone(),
min_arity: method.min_arity,
variadic: method.variadic,
};
env.globals.intern(
&env.current_ns,
method.name.clone(),
Value::ProtocolFn(GcPtr::new(pf)),
);
}
Ok(Value::Var(proto_var))
}
fn eval_extend_type(args: &[Form], env: &mut Env) -> EvalResult {
if args.is_empty() {
return Err(EvalError::Runtime(
"extend-type requires a type symbol".into(),
));
}
let type_sym = match &args[0].kind {
FormKind::Symbol(s) => s.clone(),
_ => {
return Err(EvalError::Runtime(
"extend-type: first arg must be a type symbol".into(),
));
}
};
let type_tag = crate::apply::resolve_type_tag(&type_sym);
let mut current_proto: Option<GcPtr<cljrs_value::Protocol>> = None;
for form in &args[1..] {
match &form.kind {
FormKind::Symbol(s) => {
let val = env.globals.lookup_in_ns(&env.current_ns, s);
match val {
Some(Value::Protocol(p)) => {
current_proto = Some(p);
}
_ => {
return Err(EvalError::Runtime(format!(
"extend-type: {} is not a protocol",
s
)));
}
}
}
FormKind::List(parts) => {
let proto = current_proto.as_ref().ok_or_else(|| {
EvalError::Runtime("extend-type: method before protocol name".into())
})?;
if parts.is_empty() {
continue;
}
let method_name = match &parts[0].kind {
FormKind::Symbol(s) => Arc::from(s.as_str()),
_ => continue,
};
let fn_val = build_impl_fn(parts, env)?;
let mut impls = proto.get().impls.lock().unwrap();
impls
.entry(type_tag.clone())
.or_default()
.insert(method_name, fn_val);
}
_ => {}
}
}
Ok(Value::Nil)
}
fn eval_extend_protocol(args: &[Form], env: &mut Env) -> EvalResult {
if args.is_empty() {
return Err(EvalError::Runtime(
"extend-protocol requires a protocol".into(),
));
}
let proto_sym = match &args[0].kind {
FormKind::Symbol(s) => s.clone(),
_ => {
return Err(EvalError::Runtime(
"extend-protocol: first arg must be a protocol symbol".into(),
));
}
};
let proto_val = env.globals.lookup_in_ns(&env.current_ns, &proto_sym);
let proto_ptr = match proto_val {
Some(Value::Protocol(p)) => p,
_ => {
return Err(EvalError::Runtime(format!(
"extend-protocol: {} is not a protocol",
proto_sym
)));
}
};
let mut current_type: Option<Arc<str>> = None;
for form in &args[1..] {
match &form.kind {
FormKind::Symbol(s) => {
current_type = Some(crate::apply::resolve_type_tag(s));
}
FormKind::List(parts) => {
let type_tag = current_type.as_ref().ok_or_else(|| {
EvalError::Runtime("extend-protocol: method before type name".into())
})?;
if parts.is_empty() {
continue;
}
let method_name = match &parts[0].kind {
FormKind::Symbol(s) => Arc::from(s.as_str()),
_ => continue,
};
let fn_val = build_impl_fn(parts, env)?;
let mut impls = proto_ptr.get().impls.lock().unwrap();
impls
.entry(type_tag.clone())
.or_default()
.insert(method_name, fn_val);
}
_ => {}
}
}
Ok(Value::Nil)
}
fn build_impl_fn(parts: &[Form], env: &mut Env) -> EvalResult<Value> {
if parts.len() < 2 {
return Err(EvalError::Runtime(
"protocol method impl requires params and body".into(),
));
}
let params_form = &parts[1];
let body = &parts[2..];
let arity = parse_arity(params_form, body)?;
let (closed_over_names, closed_over_vals) = env.all_local_bindings();
let fn_name = match &parts[0].kind {
FormKind::Symbol(s) => Some(Arc::from(s.as_str())),
_ => None,
};
let cljrs_fn = CljxFn::new(
fn_name,
vec![arity],
closed_over_names,
closed_over_vals,
false,
Arc::clone(&env.current_ns),
);
Ok(Value::Fn(GcPtr::new(cljrs_fn)))
}
fn eval_defmulti(args: &[Form], env: &mut Env) -> EvalResult {
let name = require_sym(args, 0, "defmulti")?;
let name_arc: Arc<str> = Arc::from(name);
let rest_start = if args.len() > 2 && matches!(args[1].kind, FormKind::Str(_)) {
2
} else {
1
};
if args.len() <= rest_start {
return Err(EvalError::Runtime(
"defmulti requires a dispatch function".into(),
));
}
let dispatch_fn = eval(&args[rest_start], env)?;
let mut default_dispatch = ":default".to_string();
let mut i = rest_start + 1;
while i + 1 < args.len() {
if let FormKind::Keyword(k) = &args[i].kind
&& k == "default"
{
let dv = eval(&args[i + 1], env)?;
default_dispatch = format!("{}", dv);
}
i += 2;
}
let mfn = MultiFn::new(name_arc.clone(), dispatch_fn, default_dispatch);
let var = env
.globals
.intern(&env.current_ns, name_arc, Value::MultiFn(GcPtr::new(mfn)));
Ok(Value::Var(var))
}
fn eval_defmethod(args: &[Form], env: &mut Env) -> EvalResult {
if args.len() < 3 {
return Err(EvalError::Runtime(
"defmethod requires name, dispatch-val, params, and body".into(),
));
}
let multi_name = require_sym(args, 0, "defmethod")?;
let mf_ptr = match env.globals.lookup_in_ns(&env.current_ns, multi_name) {
Some(Value::MultiFn(mf)) => mf,
_ => {
return Err(EvalError::Runtime(format!(
"defmethod: {} is not a multimethod",
multi_name
)));
}
};
let dispatch_val = eval(&args[1], env)?;
let key = format!("{}", dispatch_val);
let params_form = &args[2];
let body = &args[3..];
let arity = parse_arity(params_form, body)?;
let (closed_over_names, closed_over_vals) = env.all_local_bindings();
let fn_name = Some(Arc::from(multi_name));
let cljrs_fn = CljxFn::new(
fn_name,
vec![arity],
closed_over_names,
closed_over_vals,
false,
Arc::clone(&env.current_ns),
);
let fn_val = Value::Fn(GcPtr::new(cljrs_fn));
mf_ptr.get().methods.lock().unwrap().insert(key, fn_val);
Ok(Value::MultiFn(mf_ptr))
}
fn eval_binding(args: &[Form], env: &mut Env) -> EvalResult {
let pairs = match args.first().map(|f| &f.kind) {
Some(FormKind::Vector(v)) => v.clone(),
_ => return Err(EvalError::Runtime("binding requires a vector".into())),
};
if pairs.len() % 2 != 0 {
return Err(EvalError::Runtime(
"binding vector must have even count".into(),
));
}
let mut frame: HashMap<usize, Value> = HashMap::new();
for pair in pairs.chunks(2) {
let sym_str = match &pair[0].kind {
FormKind::Symbol(s) => s.clone(),
_ => return Err(EvalError::Runtime("binding targets must be symbols".into())),
};
let parsed = cljrs_value::Symbol::parse(&sym_str);
let ns_part = parsed
.namespace
.as_deref()
.unwrap_or(env.current_ns.as_ref());
let var_ptr = env
.globals
.lookup_var_in_ns(ns_part, &parsed.name)
.ok_or_else(|| EvalError::UnboundSymbol(sym_str.clone()))?;
let val = eval(&pair[1], env)?;
frame.insert(crate::dynamics::var_key_of(&var_ptr), val);
}
let _guard = crate::dynamics::push_frame(frame);
eval_body(&args[1..], env)
}
fn eval_defrecord(args: &[Form], env: &mut Env) -> EvalResult {
if args.len() < 2 {
return Err(EvalError::Runtime(
"defrecord requires a name and field vector".into(),
));
}
let type_name = require_sym(args, 0, "defrecord")?;
let type_tag: Arc<str> = Arc::from(type_name);
let field_names: Vec<Arc<str>> = match &args[1].kind {
FormKind::Vector(fields) => fields
.iter()
.map(|f| match &f.kind {
FormKind::Symbol(s) => Ok(Arc::from(s.as_str())),
_ => Err(EvalError::Runtime(
"defrecord field names must be symbols".into(),
)),
})
.collect::<EvalResult<_>>()?,
_ => {
return Err(EvalError::Runtime(
"defrecord requires a field vector as second arg".into(),
));
}
};
register_impls_for_tag(&type_tag, &args[2..], env)?;
let ns = env.current_ns.clone();
let globals = env.globals.clone();
let field_names_clone = field_names.clone();
let type_tag2 = type_tag.clone();
{
let params: Vec<Arc<str>> = field_names.clone();
let rest_param = None;
use cljrs_reader::form::FormKind as FK;
let dummy_span =
cljrs_types::span::Span::new(std::sync::Arc::new("<defrecord>".into()), 0, 0, 1, 1);
let make_form = |kind: FK| Form {
kind,
span: dummy_span.clone(),
};
let mut kv_forms: Vec<Form> = Vec::new();
for f in &field_names {
kv_forms.push(make_form(FK::Keyword(f.as_ref().to_string())));
kv_forms.push(make_form(FK::Symbol(f.as_ref().to_string())));
}
let map_form = make_form(FK::Map(kv_forms));
let body = vec![make_form(FK::List(vec![
make_form(FK::Symbol("make-type-instance".into())),
make_form(FK::Str(type_tag.as_ref().to_string())),
map_form,
]))];
let arity = CljxFnArity {
params,
rest_param,
body,
destructure_params: vec![],
destructure_rest: None,
};
let fn_name: Arc<str> = Arc::from(format!("->{}", type_name));
let ctor = CljxFn::new(
Some(fn_name.clone()),
vec![arity],
vec![],
vec![],
false,
Arc::clone(&ns),
);
globals.intern(&ns, fn_name, Value::Fn(GcPtr::new(ctor)));
}
{
use cljrs_reader::form::FormKind as FK;
let dummy_span =
cljrs_types::span::Span::new(std::sync::Arc::new("<defrecord>".into()), 0, 0, 1, 1);
let make_form = |kind: FK| Form {
kind,
span: dummy_span.clone(),
};
let m_sym: Arc<str> = Arc::from("m__");
let body = vec![make_form(FK::List(vec![
make_form(FK::Symbol("make-type-instance".into())),
make_form(FK::Str(type_tag2.as_ref().to_string())),
make_form(FK::Symbol(m_sym.as_ref().to_string())),
]))];
let arity = CljxFnArity {
params: vec![m_sym],
rest_param: None,
body,
destructure_params: vec![],
destructure_rest: None,
};
let fn_name: Arc<str> = Arc::from(format!("map->{}", type_name));
let ctor = CljxFn::new(
Some(fn_name.clone()),
vec![arity],
vec![],
vec![],
false,
Arc::clone(&ns),
);
globals.intern(&ns, fn_name, Value::Fn(GcPtr::new(ctor)));
}
let _ = field_names_clone;
let type_sym = cljrs_value::Symbol::simple(type_name);
globals.intern(
&ns,
Arc::from(type_name),
Value::Symbol(GcPtr::new(type_sym)),
);
Ok(Value::Nil)
}
fn eval_reify(args: &[Form], env: &mut Env) -> EvalResult {
let n = crate::builtins::GENSYM_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let type_tag: Arc<str> = Arc::from(format!("reify__{}", n));
register_impls_for_tag(&type_tag, args, env)?;
Ok(Value::TypeInstance(GcPtr::new(TypeInstance {
type_tag,
fields: MapValue::empty(),
})))
}
fn register_impls_for_tag(type_tag: &Arc<str>, forms: &[Form], env: &mut Env) -> EvalResult<()> {
let mut current_proto: Option<GcPtr<cljrs_value::Protocol>> = None;
for form in forms {
match &form.kind {
FormKind::Symbol(s) => {
let val = env.globals.lookup_in_ns(&env.current_ns, s);
match val {
Some(Value::Protocol(p)) => {
current_proto = Some(p);
}
_ => {
return Err(EvalError::Runtime(format!(
"reify/defrecord: {} is not a protocol",
s
)));
}
}
}
FormKind::List(parts) => {
let proto = current_proto.as_ref().ok_or_else(|| {
EvalError::Runtime("reify/defrecord: method impl before protocol name".into())
})?;
if parts.is_empty() {
continue;
}
let method_name = match &parts[0].kind {
FormKind::Symbol(s) => Arc::from(s.as_str()),
_ => continue,
};
let fn_val = build_impl_fn(parts, env)?;
let mut impls = proto.get().impls.lock().unwrap();
impls
.entry(type_tag.clone())
.or_default()
.insert(method_name, fn_val);
}
_ => {}
}
}
Ok(())
}
pub fn sync_star_ns(env: &mut Env) {
if let Some(star_ns_var) = env.globals.lookup_var("clojure.core", "*ns*") {
let ns_ptr = env.globals.get_or_create_ns(&env.current_ns);
star_ns_var.get().bind(Value::Namespace(ns_ptr));
}
}
fn require_sym<'a>(args: &'a [Form], idx: usize, form_name: &str) -> EvalResult<&'a str> {
match args.get(idx).map(|f| &f.kind) {
Some(FormKind::Symbol(s)) => Ok(s.as_str()),
_ => Err(EvalError::Runtime(format!(
"{form_name} requires a symbol at position {idx}"
))),
}
}
fn eval_with_out_str(body: &[Form], env: &mut Env) -> EvalResult {
crate::builtins::push_output_capture();
let result = eval_body(body, env);
let captured = crate::builtins::pop_output_capture().unwrap_or_default();
result?;
Ok(Value::string(captured))
}