1use crate::builtins::form::form_to_value;
4use cljrs_gc::GcPtr;
5use cljrs_reader::{Form, FormKind};
6use cljrs_value::{
7 Atom, CljxFn, CljxFnArity, Delay, LazySeq, MapValue, PersistentList, Symbol, Thunk, Value,
8 Volatile,
9};
10use std::collections::HashMap;
11use std::sync::Arc;
12
13use crate::env::env::Env;
14use crate::env::error::{EvalError, EvalResult, value_error_to_eval_error};
15use crate::interp::destructure::value_to_seq_vec;
16use crate::interp::eval::eval;
17
18#[allow(dead_code)]
21fn eval_error_to_value(e: EvalError) -> Value {
22 match e {
23 EvalError::Thrown(v) => v,
24 other => Value::string(format!("{other}")),
25 }
26}
27
28fn fire_watches(
34 watches: &cljrs_gc::mutator::TrackedMutex<Vec<(Value, Value)>>,
35 reference: &Value,
36 old: &Value,
37 new: &Value,
38 env: &mut Env,
39) {
40 let ws: Vec<(Value, Value)> = watches.lock().unwrap().clone();
41 for (key, f) in &ws {
42 let args = vec![key.clone(), reference.clone(), old.clone(), new.clone()];
43 if let Err(e) = crate::env::apply::apply_value(f, args, env) {
44 WATCH_ERROR.with(|cell| {
51 cell.borrow_mut().replace(e);
52 });
53 return;
54 }
55 }
56}
57
58thread_local! {
59 static WATCH_ERROR: std::cell::RefCell<Option<EvalError>> = const { std::cell::RefCell::new(None) };
60}
61cljrs_gc::mutator_slot!("cljrs-runtime/WATCH_ERROR", refcell WATCH_ERROR: Option<EvalError>);
62
63fn check_watch_error() -> EvalResult<()> {
65 WATCH_ERROR.with(|cell| {
66 if let Some(e) = cell.borrow_mut().take() {
67 Err(e)
68 } else {
69 Ok(())
70 }
71 })
72}
73
74#[derive(Debug)]
78pub struct ClosureThunk {
79 pub f: CljxFn,
80 pub globals: std::sync::Arc<crate::env::env::GlobalEnv>,
81 pub ns: std::sync::Arc<str>,
82}
83
84struct CallableValueThunk {
90 callee: Value,
91 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
92 ns: std::sync::Arc<str>,
93}
94
95impl std::fmt::Debug for CallableValueThunk {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 f.debug_struct("CallableValueThunk")
98 .field("callee", &self.callee.type_name())
99 .field("ns", &self.ns)
100 .finish()
101 }
102}
103
104impl cljrs_gc::Trace for CallableValueThunk {
105 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
106 self.callee.trace(visitor);
107 }
108}
109
110impl Thunk for CallableValueThunk {
111 fn force(&self) -> Result<Value, String> {
112 let mut env = Env::new(self.globals.clone(), &self.ns);
113 crate::env::apply::apply_value(&self.callee, Vec::new(), &mut env)
114 .map_err(|e| format!("{e}"))
115 }
116}
117
118pub fn make_lazy_seq_from_fn(
131 f_val: &Value,
132 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
133 ns: std::sync::Arc<str>,
134) -> EvalResult {
135 let unwrapped = f_val.unwrap_meta();
136 if let Value::Fn(g) = unwrapped {
137 let thunk = ClosureThunk {
138 f: g.get().clone(),
139 globals,
140 ns,
141 };
142 return Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))));
143 }
144 if unwrapped.type_name() != "fn" {
147 return Err(EvalError::Runtime(format!(
148 "make-lazy-seq requires a fn, got {}",
149 unwrapped.type_name(),
150 )));
151 }
152 let thunk = CallableValueThunk {
153 callee: unwrapped.clone(),
154 globals,
155 ns,
156 };
157 Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))))
158}
159
160impl cljrs_gc::Trace for ClosureThunk {
161 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
162 self.f.trace(visitor);
163 }
164}
165
166impl Thunk for ClosureThunk {
167 fn force(&self) -> Result<Value, String> {
168 let _closed_root = crate::env::gc_roots::root_values(&self.f.closed_over_vals);
172 let mut env = Env::with_closure(self.globals.clone(), &self.ns, &self.f);
173 call_cljrs_fn(&self.f, &[], &mut env).map_err(|e| format!("{e}"))
174 }
175}
176
177pub fn is_form_intercepted(name: &str) -> bool {
184 matches!(
185 name,
186 "apply"
187 | "atom"
188 | "reset!"
189 | "swap!"
190 | "volatile!"
191 | "vreset!"
192 | "agent"
193 | "make-lazy-seq"
194 | "make-delay"
195 | "vswap!"
196 | "send"
197 | "send-off"
198 | "with-bindings*"
199 | "alter-var-root"
200 | "vary-meta"
201 | "eval"
202 | "find-ns"
203 | "the-ns"
204 | "ns-interns"
205 | "ns-publics"
206 | "ns-refers"
207 | "ns-map"
208 | "all-ns"
209 | "create-ns"
210 | "ns-aliases"
211 | "remove-ns"
212 | "alter-meta!"
213 | "ns-resolve"
214 | "resolve"
215 | "intern"
216 | "bound-fn*"
217 )
218}
219
220pub fn eval_call(func_form: &Form, arg_forms: &[Form], env: &mut Env) -> EvalResult {
228 if let FormKind::Symbol(s) = &func_form.kind
230 && is_method_sugar(s)
231 {
232 return eval_method_call(&s[1..], arg_forms, env);
233 }
234
235 let callee = eval(func_form, env)?;
237
238 let _callee_root = crate::env::gc_roots::root_value(&callee);
240
241 if let Value::Macro(mfn) = &callee {
243 let expanded = macro_apply(mfn.get(), func_form, arg_forms, env)?;
244 return eval(&expanded, env);
245 }
246
247 if let Value::NativeFunction(nf) = &callee {
249 crate::env::policy::check_native(&nf.get().name)?;
250 match nf.get().name.as_ref() {
251 "apply" => return handle_apply_call(arg_forms, env),
252 "atom" => return handle_atom_call(arg_forms, env),
253 "reset!" => return handle_reset_bang(arg_forms, env),
254 "swap!" => return handle_swap_call(arg_forms, env),
255 "volatile!" => return handle_volatile(arg_forms, env),
256 "vreset!" => return handle_vreset(arg_forms, env),
257 "agent" => return handle_agent_call(arg_forms, env),
258 "make-lazy-seq" => return handle_make_lazy_seq(arg_forms, env),
259 "make-delay" => return handle_make_delay(arg_forms, env),
260 "vswap!" => return handle_vswap(arg_forms, env),
261 "send" | "send-off" => return handle_send(arg_forms, env),
262 "with-bindings*" => return handle_with_bindings(arg_forms, env),
263 "alter-var-root" => return handle_alter_var_root(arg_forms, env),
264 "vary-meta" => return handle_vary_meta(arg_forms, env),
265 "eval" => return handle_eval(arg_forms, env),
266 "find-ns" | "the-ns" => return handle_find_ns(arg_forms, env),
267 "ns-interns" | "ns-publics" => return handle_ns_interns(arg_forms, env),
268 "ns-refers" => return handle_ns_refers(arg_forms, env),
269 "ns-map" => return handle_ns_map(arg_forms, env),
270 "all-ns" => return handle_all_ns(arg_forms, env),
271 "create-ns" => return handle_create_ns(arg_forms, env),
272 "ns-aliases" => return handle_ns_aliases(arg_forms, env),
273 "remove-ns" => return handle_remove_ns(arg_forms, env),
274 "alter-meta!" => return handle_alter_meta(arg_forms, env),
275 "ns-resolve" => return handle_ns_resolve(arg_forms, env),
276 "resolve" => return handle_resolve(arg_forms, env),
277 "intern" => return handle_intern(arg_forms, env),
278 "bound-fn*" => return handle_bound_fn_star(arg_forms, env),
279 _ => {}
280 }
281 }
282
283 let mut args: Vec<Value> = Vec::with_capacity(arg_forms.len());
286 for f in arg_forms {
287 let _args_root = crate::env::gc_roots::root_values(&args);
289 args.push(eval(f, env)?);
290 }
291
292 if let Value::Fn(f) = &callee {
297 if let Some(fut) = crate::env::apply::dispatch_if_async(&callee, &args, env) {
300 return Ok(fut);
301 }
302 let _args_root = crate::env::gc_roots::root_values(&args);
303 crate::env::gc_roots::gc_safepoint(env)?;
304 return env.call_cljrs_fn(f.get(), &args);
305 }
306
307 crate::env::apply::apply_value(&callee, args, env)
308}
309
310fn eval_method_call(method: &str, arg_forms: &[Form], env: &mut Env) -> EvalResult {
320 if arg_forms.is_empty() {
321 return Err(EvalError::Runtime(format!(
322 ".{method} requires a target object"
323 )));
324 }
325 let target = eval(&arg_forms[0], env)?;
326 let args: Vec<Value> = arg_forms[1..]
327 .iter()
328 .map(|f| eval(f, env))
329 .collect::<EvalResult<_>>()?;
330
331 dispatch_method(method, &target, &args)
332}
333
334pub use cljrs_ir::lower::is_method_sugar;
339
340pub fn dispatch_method(method: &str, target: &Value, args: &[Value]) -> EvalResult {
346 match target {
347 Value::Str(s) => dispatch_string_method(method, s.get(), args),
348 Value::Vector(v) => dispatch_vector_method(method, v, args),
349 Value::List(_) | Value::Cons(_) | Value::LazySeq(_) => {
350 dispatch_seq_method(method, target, args)
351 }
352 Value::TypeInstance(ti) => {
353 if ti.get().type_tag.as_ref() == "java.util.Date"
354 && method == "getTime"
355 && args.is_empty()
356 {
357 let key = Value::keyword(cljrs_value::Keyword::simple("millis"));
358 return Ok(ti.get().fields.get(&key).unwrap_or(Value::Nil));
359 }
360 if let Some(field) = method.strip_prefix('-') {
364 let key = Value::keyword(cljrs_value::Keyword::simple(field));
365 let inst = ti.get();
366 if let Some(atom) = &inst.mutable
369 && let Value::Map(m) = atom.get().deref()
370 && let Some(v) = m.get(&key)
371 {
372 return Ok(v);
373 }
374 Ok(inst.fields.get(&key).unwrap_or(Value::Nil))
375 } else {
376 Err(EvalError::Runtime(format!(
377 ".{method} not supported on {} (only .-field access is)",
378 target.type_name()
379 )))
380 }
381 }
382 _ => Err(EvalError::Runtime(format!(
383 ".{method} not supported on type {}",
384 target.type_name()
385 ))),
386 }
387}
388
389fn dispatch_string_method(method: &str, s: &str, args: &[Value]) -> EvalResult {
390 match method {
391 "indexOf" => {
392 let needle = match args.first() {
393 Some(Value::Str(s)) => s.get().to_string(),
394 Some(Value::Char(c)) => c.to_string(),
395 Some(v) => {
396 return Err(EvalError::Runtime(format!(
397 ".indexOf expects string or char argument, got {}",
398 v.type_name()
399 )));
400 }
401 None => return Err(EvalError::Runtime(".indexOf requires an argument".into())),
402 };
403 match s.find(&needle) {
404 Some(pos) => Ok(Value::Long(pos as i64)),
405 None => Ok(Value::Long(-1)),
406 }
407 }
408 "lastIndexOf" => {
409 let needle = match args.first() {
410 Some(Value::Str(s)) => s.get().to_string(),
411 Some(Value::Char(c)) => c.to_string(),
412 _ => {
413 return Err(EvalError::Runtime(
414 ".lastIndexOf requires a string or char argument".into(),
415 ));
416 }
417 };
418 match s.rfind(&needle) {
419 Some(pos) => Ok(Value::Long(pos as i64)),
420 None => Ok(Value::Long(-1)),
421 }
422 }
423 "startsWith" => {
424 let prefix = require_str_arg(args, ".startsWith")?;
425 Ok(Value::Bool(s.starts_with(&prefix)))
426 }
427 "endsWith" => {
428 let suffix = require_str_arg(args, ".endsWith")?;
429 Ok(Value::Bool(s.ends_with(&suffix)))
430 }
431 "contains" => {
432 let sub = require_str_arg(args, ".contains")?;
433 Ok(Value::Bool(s.contains(&sub)))
434 }
435 "length" => Ok(Value::Long(s.len() as i64)),
436 "isEmpty" => Ok(Value::Bool(s.is_empty())),
437 "charAt" => {
438 let idx = require_long_arg(args, ".charAt")? as usize;
439 s.chars()
440 .nth(idx)
441 .map(Value::Char)
442 .ok_or_else(|| EvalError::Runtime(format!(".charAt index {idx} out of bounds")))
443 }
444 "substring" => {
445 let start = require_long_arg(args, ".substring")? as usize;
446 let end = args
447 .get(1)
448 .map(|v| match v {
449 Value::Long(n) => Ok(*n as usize),
450 _ => Err(EvalError::Runtime(
451 ".substring end must be an integer".into(),
452 )),
453 })
454 .transpose()?;
455 let result = match end {
456 Some(e) => &s[start..e.min(s.len())],
457 None => &s[start..],
458 };
459 Ok(Value::Str(GcPtr::new(result.to_string())))
460 }
461 "toUpperCase" => Ok(Value::Str(GcPtr::new(s.to_uppercase()))),
462 "toLowerCase" => Ok(Value::Str(GcPtr::new(s.to_lowercase()))),
463 "trim" => Ok(Value::Str(GcPtr::new(s.trim().to_string()))),
464 "replace" => {
465 let from = require_str_arg(args, ".replace")?;
466 let to = match args.get(1) {
467 Some(Value::Str(s)) => s.get().to_string(),
468 Some(Value::Char(c)) => c.to_string(),
469 _ => {
470 return Err(EvalError::Runtime(
471 ".replace requires two string arguments".into(),
472 ));
473 }
474 };
475 Ok(Value::Str(GcPtr::new(s.replace(&from, &to))))
476 }
477 "split" => {
478 let sep = require_str_arg(args, ".split")?;
479 let parts: Vec<Value> = s
480 .split(&sep)
481 .map(|p| Value::Str(GcPtr::new(p.to_string())))
482 .collect();
483 Ok(Value::Vector(GcPtr::new(
484 cljrs_value::PersistentVector::from_iter(parts),
485 )))
486 }
487 _ => Err(EvalError::Runtime(format!(
488 ".{method} not supported on String"
489 ))),
490 }
491}
492
493fn dispatch_vector_method(
494 method: &str,
495 v: &GcPtr<cljrs_value::PersistentVector>,
496 args: &[Value],
497) -> EvalResult {
498 match method {
499 "indexOf" => {
500 let needle = args
501 .first()
502 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
503 for (i, item) in v.get().iter().enumerate() {
504 if item == needle {
505 return Ok(Value::Long(i as i64));
506 }
507 }
508 Ok(Value::Long(-1))
509 }
510 "size" | "count" => Ok(Value::Long(v.get().count() as i64)),
511 _ => Err(EvalError::Runtime(format!(
512 ".{method} not supported on Vector"
513 ))),
514 }
515}
516
517fn dispatch_seq_method(method: &str, target: &Value, args: &[Value]) -> EvalResult {
518 match method {
519 "indexOf" => {
520 let needle = args
521 .first()
522 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
523 let items = crate::interp::destructure::value_to_seq_vec(target);
524 for (i, item) in items.iter().enumerate() {
525 if item == needle {
526 return Ok(Value::Long(i as i64));
527 }
528 }
529 Ok(Value::Long(-1))
530 }
531 _ => Err(EvalError::Runtime(format!(
532 ".{method} not supported on {}",
533 target.type_name()
534 ))),
535 }
536}
537
538fn require_str_arg(args: &[Value], method: &str) -> Result<String, EvalError> {
539 match args.first() {
540 Some(Value::Str(s)) => Ok(s.get().to_string()),
541 Some(Value::Char(c)) => Ok(c.to_string()),
542 _ => Err(EvalError::Runtime(format!(
543 "{method} requires a string argument"
544 ))),
545 }
546}
547
548fn require_long_arg(args: &[Value], method: &str) -> Result<i64, EvalError> {
549 match args.first() {
550 Some(Value::Long(n)) => Ok(*n),
551 _ => Err(EvalError::Runtime(format!(
552 "{method} requires an integer argument"
553 ))),
554 }
555}
556
557pub fn resolve_type_tag(sym: &str) -> Arc<str> {
560 Arc::from(sym)
561}
562
563pub fn call_cljrs_fn(f: &CljxFn, args: &[Value], caller_env: &mut Env) -> EvalResult {
565 let arity = select_arity(f, args.len())?;
566
567 let _caller_root = crate::env::gc_roots::push_env_root(caller_env);
570
571 let mut env = Env::with_closure(caller_env.globals.clone(), &f.defining_ns, f);
574
575 let mut current_args = Vec::from(args);
576 loop {
577 let _args_root = crate::env::gc_roots::root_values(¤t_args);
580
581 crate::env::gc_roots::gc_safepoint(&env)?;
583
584 env.push_frame();
585
586 #[cfg(not(feature = "no-gc"))]
596 let _call_frame = cljrs_gc::push_alloc_frame();
597
598 if let Some(ref name) = f.name {
607 let self_val = if let Some(ref p) = f.self_ptr {
608 Value::Fn(p.clone())
609 } else {
610 Value::Fn(GcPtr::new(f.clone()))
611 };
612 env.bind(name.clone(), self_val);
613 }
614
615 bind_fn_params(arity, ¤t_args, &mut env)?;
617
618 #[cfg(not(feature = "no-gc"))]
623 let result = eval_body_recur_fn(&arity.body, &mut env);
624 #[cfg(feature = "no-gc")]
625 let result = {
626 let mut scratch = cljrs_gc::alloc_ctx::ScratchGuard::new();
627 eval_body_with_scratch(&arity.body, &mut scratch, &mut env)
629 };
630 env.pop_frame();
631 match result {
635 Ok(v) => return Ok(v),
636 Err(EvalError::Recur(new_args)) => {
637 if arity.rest_param.is_some() {
642 let n = arity.params.len();
643 if new_args.len() == n + 1 {
644 let mut flat = new_args[..n].to_vec();
645 let rest_val = &new_args[n];
647 match rest_val {
648 Value::Nil => {} _ => {
650 let rest_items = value_to_seq_vec(rest_val);
651 flat.extend(rest_items);
652 }
653 }
654 current_args = flat;
655 } else {
656 current_args = new_args;
657 }
658 } else {
659 current_args = new_args;
660 }
661 }
662 Err(e) => return Err(e),
663 }
664 }
665}
666
667pub fn bind_fn_params(arity: &CljxFnArity, args: &[Value], env: &mut Env) -> EvalResult<()> {
670 bind_fn_params_impl(arity, args, env, true)
671}
672
673pub fn bind_fn_params_positional(
684 arity: &CljxFnArity,
685 args: &[Value],
686 env: &mut Env,
687) -> EvalResult<()> {
688 bind_fn_params_impl(arity, args, env, false)
689}
690
691fn bind_fn_params_impl(
692 arity: &CljxFnArity,
693 args: &[Value],
694 env: &mut Env,
695 destructure: bool,
696) -> EvalResult<()> {
697 let n = arity.params.len();
698 for (i, name) in arity.params.iter().enumerate() {
700 let val = args.get(i).cloned().unwrap_or(Value::Nil);
701 env.bind(name.clone(), val);
702 }
703 if let Some(ref rest) = arity.rest_param {
705 let rest_items = args[n..].to_vec();
706 let rest_val = if rest_items.is_empty() {
707 Value::Nil
708 } else {
709 Value::List(GcPtr::new(PersistentList::from_iter(rest_items)))
710 };
711 env.bind(rest.clone(), rest_val.clone());
712 if destructure && let Some(ref pattern) = arity.destructure_rest {
714 let destructure_val = if pattern.is_kwargs_rest_pattern() {
718 let items = value_to_seq_vec(&rest_val);
719 Value::from_kwargs_rest(items).map_err(value_error_to_eval_error)?
720 } else {
721 rest_val
722 };
723 crate::interp::destructure::bind_pattern(pattern, destructure_val, env)?;
724 }
725 }
726 if destructure {
728 for (idx, pattern) in &arity.destructure_params {
729 let val = args.get(*idx).cloned().unwrap_or(Value::Nil);
730 crate::interp::destructure::bind_pattern(pattern, val, env)?;
731 }
732 }
733 Ok(())
734}
735
736#[cfg(not(feature = "no-gc"))]
738fn eval_body_recur_fn(body: &[cljrs_reader::Form], env: &mut Env) -> EvalResult {
739 let mut result = Value::Nil;
740 for form in body {
741 result = eval(form, env)?;
742 }
743 Ok(result)
744}
745
746#[cfg(feature = "no-gc")]
750fn eval_body_with_scratch(
751 body: &[cljrs_reader::Form],
752 scratch: &mut cljrs_gc::alloc_ctx::ScratchGuard,
753 env: &mut Env,
754) -> EvalResult {
755 if body.is_empty() {
756 scratch.pop_for_return();
757 return Ok(Value::Nil);
758 }
759 for form in &body[..body.len() - 1] {
761 eval(form, env)?;
762 }
763 scratch.pop_for_return();
765 eval(&body[body.len() - 1], env)
766}
767
768pub fn select_arity(f: &CljxFn, argc: usize) -> EvalResult<&CljxFnArity> {
770 let name = f.name.as_deref().unwrap_or("fn");
771 for arity in &f.arities {
773 if arity.rest_param.is_none() && arity.params.len() == argc {
774 return Ok(arity);
775 }
776 }
777 for arity in &f.arities {
779 if arity.rest_param.is_some() && argc >= arity.params.len() {
780 return Ok(arity);
781 }
782 }
783 let implicit = if f.is_macro { IMPLICIT_MACRO_ARGS } else { 0 };
788 let expected: Vec<String> = f
789 .arities
790 .iter()
791 .map(|a| {
792 let fixed = a.params.len().saturating_sub(implicit);
793 if a.rest_param.is_some() {
794 format!("{fixed}+")
795 } else {
796 fixed.to_string()
797 }
798 })
799 .collect();
800 Err(EvalError::Arity {
801 name: name.to_string(),
802 expected: expected.join(" or "),
803 got: argc.saturating_sub(implicit),
804 })
805}
806
807const IMPLICIT_MACRO_ARGS: usize = 2;
809
810fn macro_apply(
817 mfn: &CljxFn,
818 func_form: &Form,
819 arg_forms: &[Form],
820 env: &mut Env,
821) -> EvalResult<Form> {
822 let resolved_args: Vec<Form> = arg_forms
827 .iter()
828 .map(|f| crate::builtins::form::resolve_auto_forms(f, env))
829 .collect::<EvalResult<Vec<Form>>>()?;
830
831 let form_val = {
833 let mut items = vec![form_to_value(func_form)?];
834 for f in &resolved_args {
835 items.push(form_to_value(f)?);
836 }
837 Value::List(GcPtr::new(PersistentList::from_iter(items)))
838 };
839
840 let env_val = {
842 let (names, vals) = env.all_local_bindings();
843 let mut m = MapValue::empty();
844 for (name, val) in names.iter().zip(vals.iter()) {
845 m = m.assoc(Value::symbol(Symbol::simple(name.as_ref())), val.clone());
846 }
847 Value::Map(m)
848 };
849
850 let mut args = vec![form_val, env_val];
852 for f in &resolved_args {
853 args.push(form_to_value(f)?);
854 }
855
856 let expanded_val = call_cljrs_fn(mfn, args.as_ref(), env)?;
857 let _expanded_root = crate::env::gc_roots::root_value(&expanded_val);
860 let dummy_span = cljrs_types::span::Span::new(Arc::new("<macro>".to_string()), 0, 0, 1, 1);
861 crate::interp::macros::value_to_form(&expanded_val, dummy_span)
862}
863
864fn handle_apply_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
866 let mut evaled: Vec<Value> = Vec::with_capacity(arg_forms.len());
867 for f in arg_forms {
868 let _root = crate::env::gc_roots::root_values(&evaled);
869 evaled.push(eval(f, env)?);
870 }
871
872 if evaled.len() < 2 {
873 return Err(EvalError::Arity {
874 name: "apply".into(),
875 expected: "2+".into(),
876 got: evaled.len(),
877 });
878 }
879
880 let f = evaled.remove(0);
881 let last = evaled.pop().unwrap();
882 let _f_root = crate::env::gc_roots::root_value(&f);
884 let _last_root = crate::env::gc_roots::root_value(&last);
885 let _evaled_root = crate::env::gc_roots::root_values(&evaled);
886 let spread =
888 crate::builtins::builtins::value_to_seq(&last).map_err(value_error_to_eval_error)?;
889 evaled.extend(spread);
890 crate::env::apply::apply_value(&f, evaled, env)
891}
892
893pub fn handle_make_lazy_seq(arg_forms: &[Form], env: &mut Env) -> EvalResult {
895 if arg_forms.len() != 1 {
896 return Err(EvalError::Arity {
897 name: "make-lazy-seq".into(),
898 expected: "1".into(),
899 got: arg_forms.len(),
900 });
901 }
902 let f_val = eval(&arg_forms[0], env)?;
903 let f = match f_val {
904 Value::Fn(f) => f.get().clone(),
905 other => {
906 return Err(EvalError::Runtime(format!(
907 "make-lazy-seq requires a fn, got {}",
908 other.type_name()
909 )));
910 }
911 };
912 let thunk = ClosureThunk {
913 f,
914 globals: env.globals.clone(),
915 ns: env.current_ns.clone(),
916 };
917 Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))))
918}
919
920fn handle_make_delay(arg_forms: &[Form], env: &mut Env) -> EvalResult {
922 if arg_forms.len() != 1 {
923 return Err(EvalError::Arity {
924 name: "make-delay".into(),
925 expected: "1".into(),
926 got: arg_forms.len(),
927 });
928 }
929 let f_val = eval(&arg_forms[0], env)?;
930 let f = match f_val {
931 Value::Fn(f) => f.get().clone(),
932 other => {
933 return Err(EvalError::Runtime(format!(
934 "make-delay requires a fn, got {}",
935 other.type_name()
936 )));
937 }
938 };
939 let thunk = ClosureThunk {
940 f,
941 globals: env.globals.clone(),
942 ns: env.current_ns.clone(),
943 };
944 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
945}
946
947fn handle_vswap(arg_forms: &[Form], env: &mut Env) -> EvalResult {
949 if arg_forms.len() < 2 {
950 return Err(EvalError::Arity {
951 name: "vswap!".into(),
952 expected: "2+".into(),
953 got: arg_forms.len(),
954 });
955 }
956 let vol_val = eval(&arg_forms[0], env)?;
957 let f = eval(&arg_forms[1], env)?;
958 let extra: Vec<Value> = arg_forms[2..]
959 .iter()
960 .map(|a| eval(a, env))
961 .collect::<EvalResult<_>>()?;
962
963 match vol_val {
964 Value::Volatile(v) => {
965 let cur = v.get().deref();
966 let mut call_args = vec![cur];
967 call_args.extend(extra);
968 #[cfg(feature = "no-gc")]
971 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
972 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
973 v.get().reset(new_val.clone());
974 Ok(new_val)
975 }
976 other => Err(EvalError::Runtime(format!(
977 "vswap!: expected volatile, got {}",
978 other.type_name()
979 ))),
980 }
981}
982
983fn handle_volatile(arg_forms: &[Form], env: &mut Env) -> EvalResult {
987 if arg_forms.is_empty() {
988 return Err(EvalError::Arity {
989 name: "volatile!".into(),
990 expected: "1".into(),
991 got: 0,
992 });
993 }
994 #[cfg(feature = "no-gc")]
997 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
998 let initial = eval(&arg_forms[0], env)?;
999 Ok(Value::Volatile(GcPtr::new(Volatile::new(initial))))
1000}
1001
1002fn handle_vreset(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1006 if arg_forms.len() < 2 {
1007 return Err(EvalError::Arity {
1008 name: "vreset!".into(),
1009 expected: "2".into(),
1010 got: arg_forms.len(),
1011 });
1012 }
1013 let vol_val = eval(&arg_forms[0], env)?;
1014 #[cfg(feature = "no-gc")]
1017 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1018 let new_val = eval(&arg_forms[1], env)?;
1019 match &vol_val {
1020 Value::Volatile(v) => {
1021 v.get().reset(new_val.clone());
1022 Ok(new_val)
1023 }
1024 other => Err(EvalError::Runtime(format!(
1025 "vreset!: expected volatile, got {}",
1026 other.type_name()
1027 ))),
1028 }
1029}
1030
1031fn handle_agent_call(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
1035 Err(EvalError::Runtime("agent is not yet implemented".into()))
1036}
1037
1038fn handle_send(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
1040 Err(EvalError::Runtime(
1041 "send/send-off: agents are not yet implemented".into(),
1042 ))
1043}
1044
1045fn handle_atom_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1049 if arg_forms.is_empty() {
1050 return Err(EvalError::Arity {
1051 name: "atom".into(),
1052 expected: "1+".into(),
1053 got: 0,
1054 });
1055 }
1056 #[cfg(feature = "no-gc")]
1059 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1060 let initial = eval(&arg_forms[0], env)?;
1061
1062 let options: Vec<Value> = arg_forms[1..]
1064 .iter()
1065 .map(|f| eval(f, env))
1066 .collect::<EvalResult<_>>()?;
1067
1068 let mut meta_opt: Option<Value> = None;
1069 let mut validator_opt: Option<Value> = None;
1070 let mut i = 0;
1071 while i + 1 < options.len() {
1072 match &options[i] {
1073 Value::Keyword(k) if k.get().name.as_ref() == "meta" => {
1074 meta_opt = Some(options[i + 1].clone());
1075 i += 2;
1076 }
1077 Value::Keyword(k) if k.get().name.as_ref() == "validator" => {
1078 let vf = options[i + 1].clone();
1079 validator_opt = if vf == Value::Nil { None } else { Some(vf) };
1080 i += 2;
1081 }
1082 _ => {
1083 i += 2;
1084 }
1085 }
1086 }
1087
1088 if let Some(ref m) = meta_opt
1090 && !matches!(m, Value::Nil | Value::Map(_))
1091 {
1092 return Err(EvalError::Thrown(Value::string(
1093 "Atom metadata must be a map or nil".to_string(),
1094 )));
1095 }
1096
1097 if let Some(ref vf) = validator_opt {
1099 let result = crate::env::apply::apply_value(vf, vec![initial.clone()], env)?;
1100 if result == Value::Nil || result == Value::Bool(false) {
1101 return Err(EvalError::Thrown(Value::string(
1102 "Invalid initial value for atom".to_string(),
1103 )));
1104 }
1105 }
1106
1107 let atom = GcPtr::new(Atom::new(initial));
1108 if let Some(m) = meta_opt {
1109 atom.get()
1110 .set_meta(if m == Value::Nil { None } else { Some(m) });
1111 }
1112 if let Some(vf) = validator_opt {
1113 atom.get().set_validator(Some(vf));
1114 }
1115 Ok(Value::Atom(atom))
1116}
1117
1118fn shared_atom_reset(sa: &Arc<cljrs_value::SharedAtom>, new_val: Value) -> EvalResult {
1130 let promoted = cljrs_value::promote(&new_val).map_err(promote_error)?;
1131 sa.try_reset(promoted)
1132 .map_err(EvalError::MemoryLimitExceeded)?;
1133 Ok(new_val)
1134}
1135
1136fn promote_error(e: cljrs_value::PromoteError) -> EvalError {
1138 match e.memory {
1139 Some(fault) => EvalError::MemoryLimitExceeded(fault),
1140 None => EvalError::Runtime(e.to_string()),
1141 }
1142}
1143
1144fn shared_atom_swap(
1149 sa: &Arc<cljrs_value::SharedAtom>,
1150 f: &Value,
1151 extra: Vec<Value>,
1152 env: &mut Env,
1153) -> EvalResult {
1154 loop {
1155 let cur = sa.deref_val();
1156 let old_val = cljrs_value::demote(&cur);
1157 let mut call_args = Vec::with_capacity(1 + extra.len());
1158 call_args.push(old_val);
1159 call_args.extend(extra.iter().cloned());
1160 let new_val = crate::env::apply::apply_value(f, call_args, env)?;
1161 let promoted = cljrs_value::promote(&new_val).map_err(promote_error)?;
1162 if sa
1163 .try_compare_and_set(&cur, promoted)
1164 .map_err(EvalError::MemoryLimitExceeded)?
1165 {
1166 return Ok(new_val);
1167 }
1168 }
1170}
1171
1172fn handle_reset_bang(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1175 if arg_forms.len() < 2 {
1176 return Err(EvalError::Arity {
1177 name: "reset!".into(),
1178 expected: "2".into(),
1179 got: arg_forms.len(),
1180 });
1181 }
1182 let atom_val = eval(&arg_forms[0], env)?;
1183 #[cfg(feature = "no-gc")]
1186 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1187 let new_val = eval(&arg_forms[1], env)?;
1188
1189 let atom = match &atom_val {
1190 Value::Atom(a) => a.clone(),
1191 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1192 v => {
1193 return Err(EvalError::Runtime(format!(
1194 "reset! requires an atom, got {}",
1195 v.type_name()
1196 )));
1197 }
1198 };
1199
1200 validate_atom_value(&atom, &new_val, env)?;
1201 let old_val = atom.get().deref();
1202 atom.get().reset(new_val.clone());
1203 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1204 check_watch_error()?;
1205 Ok(new_val)
1206}
1207
1208fn validate_atom_value(atom: &GcPtr<Atom>, new_val: &Value, env: &mut Env) -> EvalResult<()> {
1210 if let Some(vf) = atom.get().get_validator() {
1211 let result = crate::env::apply::apply_value(&vf, vec![new_val.clone()], env)?;
1212 if result == Value::Nil || result == Value::Bool(false) {
1213 return Err(EvalError::Thrown(Value::string(
1214 "Invalid value for atom".to_string(),
1215 )));
1216 }
1217 }
1218 Ok(())
1219}
1220
1221fn handle_swap_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1224 let mut evaled: Vec<Value> = arg_forms
1225 .iter()
1226 .map(|f| eval(f, env))
1227 .collect::<EvalResult<_>>()?;
1228
1229 if evaled.len() < 2 {
1230 return Err(EvalError::Arity {
1231 name: "swap!".into(),
1232 expected: "2+".into(),
1233 got: evaled.len(),
1234 });
1235 }
1236
1237 let atom_val = evaled.remove(0);
1238 let f = evaled.remove(0);
1239
1240 let atom = match &atom_val {
1241 Value::Atom(a) => a.clone(),
1242 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, evaled, env),
1243 v => {
1244 return Err(EvalError::Runtime(format!(
1245 "swap! requires an atom, got {}",
1246 v.type_name()
1247 )));
1248 }
1249 };
1250
1251 let old_val = atom.get().deref();
1252 let mut args = vec![old_val.clone()];
1253 args.extend(evaled);
1254 #[cfg(feature = "no-gc")]
1257 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1258 let new_val = crate::env::apply::apply_value(&f, args, env)?;
1259 validate_atom_value(&atom, &new_val, env)?;
1260 atom.get().reset(new_val.clone());
1261 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1262 check_watch_error()?;
1263 Ok(new_val)
1264}
1265
1266fn handle_with_bindings(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1271 if arg_forms.len() < 2 {
1272 return Err(EvalError::Arity {
1273 name: "with-bindings*".into(),
1274 expected: "2".into(),
1275 got: arg_forms.len(),
1276 });
1277 }
1278 let map_val = eval(&arg_forms[0], env)?;
1279 let func_val = eval(&arg_forms[1], env)?;
1280
1281 let mut frame: HashMap<usize, Value> = HashMap::new();
1282 if let Value::Map(m) = &map_val {
1283 m.for_each(|k, v| {
1284 if let Value::Var(vp) = k {
1285 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1286 }
1287 });
1289 } else {
1290 return Err(EvalError::Runtime(
1291 "with-bindings*: first arg must be a map".into(),
1292 ));
1293 }
1294
1295 let _guard = crate::env::dynamics::push_frame(frame);
1296 crate::env::apply::apply_value(&func_val, vec![], env)
1297}
1298
1299fn handle_alter_var_root(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1303 if arg_forms.len() < 2 {
1304 return Err(EvalError::Arity {
1305 name: "alter-var-root".into(),
1306 expected: "2+".into(),
1307 got: arg_forms.len(),
1308 });
1309 }
1310 let var_val = eval(&arg_forms[0], env)?;
1311 let f = eval(&arg_forms[1], env)?;
1312 let extra: Vec<Value> = arg_forms[2..]
1313 .iter()
1314 .map(|form| eval(form, env))
1315 .collect::<EvalResult<_>>()?;
1316
1317 let vp = match &var_val {
1318 Value::Var(vp) => vp.clone(),
1319 v => {
1320 return Err(EvalError::Runtime(format!(
1321 "alter-var-root: expected var, got {}",
1322 v.type_name()
1323 )));
1324 }
1325 };
1326 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1327 let mut call_args = vec![old_val.clone()];
1328 call_args.extend(extra);
1329 #[cfg(feature = "no-gc")]
1332 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1333 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1334 vp.get().bind(new_val.clone());
1335 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1336 check_watch_error()?;
1337 Ok(new_val)
1338}
1339
1340fn handle_vary_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1344 if arg_forms.len() < 2 {
1345 return Err(EvalError::Arity {
1346 name: "vary-meta".into(),
1347 expected: "2+".into(),
1348 got: arg_forms.len(),
1349 });
1350 }
1351 let obj = eval(&arg_forms[0], env)?;
1352 let f = eval(&arg_forms[1], env)?;
1353 let extra: Vec<Value> = arg_forms[2..]
1354 .iter()
1355 .map(|form| eval(form, env))
1356 .collect::<EvalResult<_>>()?;
1357
1358 let current_meta = match &obj {
1359 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1360 _ => Value::Nil,
1361 };
1362 let mut call_args = vec![current_meta];
1363 call_args.extend(extra);
1364 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1365 if let Value::Var(vp) = &obj {
1366 vp.get().set_meta(new_meta);
1367 }
1368 Ok(obj)
1369}
1370
1371fn handle_eval(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1375 let [arg] = arg_forms else {
1376 return Err(EvalError::Arity {
1377 name: "eval".into(),
1378 expected: "1".into(),
1379 got: arg_forms.len(),
1380 });
1381 };
1382 let value = eval(arg, env)?;
1383 eval_eval(vec![value], env)
1384}
1385
1386pub fn eval_eval(args: Vec<Value>, env: &mut Env) -> EvalResult {
1391 let [value] = args.as_slice() else {
1392 return Err(EvalError::Arity {
1393 name: "eval".into(),
1394 expected: "1".into(),
1395 got: args.len(),
1396 });
1397 };
1398 if matches!(
1401 value.unwrap_meta(),
1402 Value::NativeFunction(_) | Value::Fn(_) | Value::Macro(_) | Value::BoundFn(_)
1403 ) {
1404 return Ok(value.clone());
1405 }
1406 let span = cljrs_types::span::Span::new(Arc::new("<eval>".to_string()), 0, 0, 1, 1);
1407 let form = crate::interp::macros::value_to_form(value, span)?;
1408 let mut top = Env::new(env.globals.clone(), &env.current_ns);
1409 eval(&form, &mut top)
1410}
1411
1412pub fn eval_reset_bang(args: Vec<Value>, env: &mut Env) -> EvalResult {
1420 if args.len() < 2 {
1421 return Err(EvalError::Arity {
1422 name: "reset!".into(),
1423 expected: "2".into(),
1424 got: args.len(),
1425 });
1426 }
1427 let atom_val = args[0].clone();
1428 let new_val = args[1].clone();
1429 let atom = match &atom_val {
1430 Value::Atom(a) => a.clone(),
1431 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1432 v => {
1433 return Err(EvalError::Runtime(format!(
1434 "reset! requires an atom, got {}",
1435 v.type_name()
1436 )));
1437 }
1438 };
1439 #[cfg(feature = "no-gc")]
1440 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1441 validate_atom_value(&atom, &new_val, env)?;
1442 let old_val = atom.get().deref();
1443 atom.get().reset(new_val.clone());
1444 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1445 check_watch_error()?;
1446 Ok(new_val)
1447}
1448
1449pub fn eval_swap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1451 if args.len() < 2 {
1452 return Err(EvalError::Arity {
1453 name: "swap!".into(),
1454 expected: "2+".into(),
1455 got: args.len(),
1456 });
1457 }
1458 let atom_val = args.remove(0);
1459 let f = args.remove(0);
1460 let atom = match &atom_val {
1461 Value::Atom(a) => a.clone(),
1462 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, args, env),
1463 v => {
1464 return Err(EvalError::Runtime(format!(
1465 "swap! requires an atom, got {}",
1466 v.type_name()
1467 )));
1468 }
1469 };
1470 let old_val = atom.get().deref();
1471 let mut call_args = vec![old_val.clone()];
1472 call_args.extend(args);
1473 #[cfg(feature = "no-gc")]
1474 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1475 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1476 validate_atom_value(&atom, &new_val, env)?;
1477 atom.get().reset(new_val.clone());
1478 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1479 check_watch_error()?;
1480 Ok(new_val)
1481}
1482
1483pub fn eval_volatile(args: Vec<Value>) -> EvalResult {
1485 if args.is_empty() {
1486 return Err(EvalError::Arity {
1487 name: "volatile!".into(),
1488 expected: "1".into(),
1489 got: 0,
1490 });
1491 }
1492 #[cfg(feature = "no-gc")]
1493 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1494 Ok(Value::Volatile(GcPtr::new(Volatile::new(
1495 args.into_iter().next().unwrap(),
1496 ))))
1497}
1498
1499pub fn eval_vreset_bang(args: Vec<Value>) -> EvalResult {
1501 if args.len() < 2 {
1502 return Err(EvalError::Arity {
1503 name: "vreset!".into(),
1504 expected: "2".into(),
1505 got: args.len(),
1506 });
1507 }
1508 #[cfg(feature = "no-gc")]
1509 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1510 let new_val = args[1].clone();
1511 match &args[0] {
1512 Value::Volatile(v) => {
1513 v.get().reset(new_val.clone());
1514 Ok(new_val)
1515 }
1516 other => Err(EvalError::Runtime(format!(
1517 "vreset!: expected volatile, got {}",
1518 other.type_name()
1519 ))),
1520 }
1521}
1522
1523pub fn eval_vswap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1525 if args.len() < 2 {
1526 return Err(EvalError::Arity {
1527 name: "vswap!".into(),
1528 expected: "2+".into(),
1529 got: args.len(),
1530 });
1531 }
1532 let vol_val = args.remove(0);
1533 let f = args.remove(0);
1534 match vol_val {
1535 Value::Volatile(v) => {
1536 let cur = v.get().deref();
1537 let mut call_args = vec![cur];
1538 call_args.extend(args);
1539 #[cfg(feature = "no-gc")]
1540 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1541 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1542 v.get().reset(new_val.clone());
1543 Ok(new_val)
1544 }
1545 other => Err(EvalError::Runtime(format!(
1546 "vswap!: expected volatile, got {}",
1547 other.type_name()
1548 ))),
1549 }
1550}
1551
1552pub fn make_delay_from_fn(
1557 f_val: &Value,
1558 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
1559 ns: std::sync::Arc<str>,
1560) -> EvalResult {
1561 let f = match f_val {
1562 Value::Fn(f) => f.get().clone(),
1563 other => {
1564 return Err(EvalError::Runtime(format!(
1565 "make-delay requires a fn, got {}",
1566 other.type_name()
1567 )));
1568 }
1569 };
1570 let thunk = ClosureThunk { f, globals, ns };
1571 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
1572}
1573
1574pub fn eval_alter_var_root(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1576 if args.len() < 2 {
1577 return Err(EvalError::Arity {
1578 name: "alter-var-root".into(),
1579 expected: "2+".into(),
1580 got: args.len(),
1581 });
1582 }
1583 let var_val = args.remove(0);
1584 let f = args.remove(0);
1585 let vp = match &var_val {
1586 Value::Var(vp) => vp.clone(),
1587 v => {
1588 return Err(EvalError::Runtime(format!(
1589 "alter-var-root: expected var, got {}",
1590 v.type_name()
1591 )));
1592 }
1593 };
1594 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1595 let mut call_args = vec![old_val.clone()];
1596 call_args.extend(args);
1597 #[cfg(feature = "no-gc")]
1598 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1599 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1600 vp.get().bind(new_val.clone());
1601 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1602 check_watch_error()?;
1603 Ok(new_val)
1604}
1605
1606pub fn eval_vary_meta(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1608 if args.len() < 2 {
1609 return Err(EvalError::Arity {
1610 name: "vary-meta".into(),
1611 expected: "2+".into(),
1612 got: args.len(),
1613 });
1614 }
1615 let obj = args.remove(0);
1616 let f = args.remove(0);
1617 let current_meta = match &obj {
1618 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1619 _ => Value::Nil,
1620 };
1621 let mut call_args = vec![current_meta];
1622 call_args.extend(args);
1623 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1624 if let Value::Var(vp) = &obj {
1625 vp.get().set_meta(new_meta);
1626 }
1627 Ok(obj)
1628}
1629
1630pub fn eval_with_bindings_star(args: Vec<Value>, env: &mut Env) -> EvalResult {
1632 if args.len() < 2 {
1633 return Err(EvalError::Arity {
1634 name: "with-bindings*".into(),
1635 expected: "2".into(),
1636 got: args.len(),
1637 });
1638 }
1639 let mut frame: HashMap<usize, Value> = HashMap::new();
1640 if let Value::Map(m) = &args[0] {
1641 m.for_each(|k, v| {
1642 if let Value::Var(vp) = k {
1643 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1644 }
1645 });
1646 } else {
1647 return Err(EvalError::Runtime(
1648 "with-bindings*: first arg must be a map".into(),
1649 ));
1650 }
1651 let _guard = crate::env::dynamics::push_frame(frame);
1652 crate::env::apply::apply_value(&args[1], vec![], env)
1653}
1654
1655pub fn eval_send_to_agent(_args: Vec<Value>, _env: &mut Env) -> EvalResult {
1657 Err(EvalError::Runtime(
1658 "send/send-off: agents are not yet implemented".into(),
1659 ))
1660}
1661
1662fn ns_name_from_val(v: &Value) -> Result<String, EvalError> {
1665 match v {
1666 Value::Symbol(s) => Ok(s.get().name.as_ref().to_string()),
1667 Value::Str(s) => Ok(s.get().clone()),
1668 Value::Namespace(ns) => Ok(ns.get().name.as_ref().to_string()),
1669 Value::Keyword(k) => Ok(k.get().name.as_ref().to_string()),
1670 other => Err(EvalError::Runtime(format!(
1671 "expected symbol, string, or namespace, got {}",
1672 other.type_name()
1673 ))),
1674 }
1675}
1676
1677fn the_ns(v: &Value, env: &Env) -> Result<GcPtr<cljrs_value::Namespace>, EvalError> {
1682 if let Value::Namespace(ns) = v {
1683 return Ok(ns.clone());
1684 }
1685 let name = ns_name_from_val(v)?;
1686 let map = env.globals.namespaces.read().unwrap();
1687 match map.get(name.as_str()) {
1688 Some(ns) => Ok(ns.clone()),
1689 None => Err(EvalError::Runtime(format!("No namespace: {name} found"))),
1690 }
1691}
1692
1693fn handle_ns_interns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1696 if arg_forms.is_empty() {
1697 return Err(EvalError::Arity {
1698 name: "ns-interns".into(),
1699 expected: "1".into(),
1700 got: 0,
1701 });
1702 }
1703 let arg = eval(&arg_forms[0], env)?;
1704 let ns = the_ns(&arg, env)?;
1705 crate::builtins::builtins::builtin_ns_interns(&[Value::Namespace(ns)])
1706 .map_err(crate::env::error::value_error_to_eval_error)
1707}
1708
1709fn handle_ns_refers(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1711 if arg_forms.is_empty() {
1712 return Err(EvalError::Arity {
1713 name: "ns-refers".into(),
1714 expected: "1".into(),
1715 got: 0,
1716 });
1717 }
1718 let arg = eval(&arg_forms[0], env)?;
1719 let ns = the_ns(&arg, env)?;
1720 crate::builtins::builtins::builtin_ns_refers(&[Value::Namespace(ns)])
1721 .map_err(crate::env::error::value_error_to_eval_error)
1722}
1723
1724fn handle_ns_map(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1726 if arg_forms.is_empty() {
1727 return Err(EvalError::Arity {
1728 name: "ns-map".into(),
1729 expected: "1".into(),
1730 got: 0,
1731 });
1732 }
1733 let arg = eval(&arg_forms[0], env)?;
1734 let ns = the_ns(&arg, env)?;
1735 crate::builtins::builtins::builtin_ns_map(&[Value::Namespace(ns)])
1736 .map_err(crate::env::error::value_error_to_eval_error)
1737}
1738
1739fn handle_find_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1741 if arg_forms.is_empty() {
1742 return Err(EvalError::Arity {
1743 name: "find-ns".into(),
1744 expected: "1".into(),
1745 got: 0,
1746 });
1747 }
1748 let arg = eval(&arg_forms[0], env)?;
1749 let name = ns_name_from_val(&arg)?;
1750 let map = env.globals.namespaces.read().unwrap();
1751 match map.get(name.as_str()) {
1752 Some(ns) => Ok(Value::Namespace(ns.clone())),
1753 None => Ok(Value::Nil),
1754 }
1755}
1756
1757fn handle_all_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1759 if !arg_forms.is_empty() {
1760 let _ = eval(&arg_forms[0], env)?; }
1762 let map = env.globals.namespaces.read().unwrap();
1763 let items: Vec<Value> = map
1764 .values()
1765 .map(|ns| Value::Namespace(ns.clone()))
1766 .collect();
1767 drop(map);
1768 Ok(Value::List(cljrs_gc::GcPtr::new(
1769 cljrs_value::PersistentList::from_iter(items),
1770 )))
1771}
1772
1773fn handle_create_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1775 if arg_forms.is_empty() {
1776 return Err(EvalError::Arity {
1777 name: "create-ns".into(),
1778 expected: "1".into(),
1779 got: 0,
1780 });
1781 }
1782 let arg = eval(&arg_forms[0], env)?;
1783 let name = ns_name_from_val(&arg)?;
1784 let ns = env.globals.get_or_create_ns(&name);
1785 Ok(Value::Namespace(ns))
1786}
1787
1788fn handle_ns_aliases(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1790 if arg_forms.is_empty() {
1791 return Err(EvalError::Arity {
1792 name: "ns-aliases".into(),
1793 expected: "1".into(),
1794 got: 0,
1795 });
1796 }
1797 let ns_val = eval(&arg_forms[0], env)?;
1798 let ns_name = ns_name_from_val(&ns_val)?;
1799 let map = env.globals.namespaces.read().unwrap();
1800 let ns = match map.get(ns_name.as_str()) {
1801 Some(ns) => ns.clone(),
1802 None => return Ok(Value::Map(cljrs_value::MapValue::empty())),
1803 };
1804 let aliases = ns.get().aliases.lock().unwrap().clone();
1805 drop(map);
1806 let mut m = cljrs_value::MapValue::empty();
1807 for (alias, full_ns_name) in &aliases {
1808 let sym = Value::symbol(cljrs_value::Symbol::simple(alias.clone()));
1809 let nsmap = env.globals.namespaces.read().unwrap();
1810 if let Some(target_ns) = nsmap.get(full_ns_name.as_ref()) {
1811 m = m.assoc(sym, Value::Namespace(target_ns.clone()));
1812 }
1813 }
1814 Ok(Value::Map(m))
1815}
1816
1817fn handle_remove_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1819 if arg_forms.is_empty() {
1820 return Err(EvalError::Arity {
1821 name: "remove-ns".into(),
1822 expected: "1".into(),
1823 got: 0,
1824 });
1825 }
1826 let arg = eval(&arg_forms[0], env)?;
1827 let name = ns_name_from_val(&arg)?;
1828 env.globals
1829 .namespaces
1830 .write()
1831 .unwrap()
1832 .remove(name.as_str());
1833 Ok(Value::Nil)
1834}
1835
1836fn handle_alter_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1838 if arg_forms.len() < 2 {
1839 return Err(EvalError::Arity {
1840 name: "alter-meta!".into(),
1841 expected: "2+".into(),
1842 got: arg_forms.len(),
1843 });
1844 }
1845 let obj = eval(&arg_forms[0], env)?;
1846 let f = eval(&arg_forms[1], env)?;
1847 let extra: Vec<Value> = arg_forms[2..]
1848 .iter()
1849 .map(|form| eval(form, env))
1850 .collect::<EvalResult<_>>()?;
1851
1852 let current_meta = match &obj {
1853 Value::Var(vp) => vp
1854 .get()
1855 .get_meta()
1856 .unwrap_or(Value::Map(cljrs_value::MapValue::empty())),
1857 _ => Value::Map(cljrs_value::MapValue::empty()),
1858 };
1859 let mut call_args = vec![current_meta];
1860 call_args.extend(extra);
1861 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1862 if let Value::Var(vp) = &obj {
1863 vp.get().set_meta(new_meta.clone());
1864 }
1865 Ok(new_meta)
1866}
1867
1868fn handle_ns_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1870 if arg_forms.len() < 2 {
1871 return Err(EvalError::Arity {
1872 name: "ns-resolve".into(),
1873 expected: "2".into(),
1874 got: arg_forms.len(),
1875 });
1876 }
1877 let ns_arg = eval(&arg_forms[0], env)?;
1878 let sym_arg = eval(&arg_forms[1], env)?;
1879 let ns_name = ns_name_from_val(&ns_arg)?;
1880 let sym_name = match &sym_arg {
1881 Value::Symbol(s) => s.get().name.as_ref().to_string(),
1882 Value::Str(s) => s.get().clone(),
1883 other => {
1884 return Err(EvalError::Runtime(format!(
1885 "ns-resolve: second arg must be symbol or string, got {}",
1886 other.type_name()
1887 )));
1888 }
1889 };
1890 match env.globals.lookup_var(&ns_name, &sym_name) {
1891 Some(var_ptr) => Ok(Value::Var(var_ptr)),
1892 None => Ok(Value::Nil),
1893 }
1894}
1895
1896fn resolve_current_ns(env: &Env) -> Arc<str> {
1900 if let Some(var) = env.globals.lookup_var("clojure.core", "*ns*") {
1901 let val = crate::env::dynamics::deref_var(&var);
1902 if let Some(Value::Namespace(ns_ptr)) = val {
1903 return ns_ptr.get().name.clone();
1904 }
1905 }
1906 env.current_ns.clone()
1907}
1908
1909fn handle_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1911 if arg_forms.len() != 1 {
1912 return Err(EvalError::Arity {
1913 name: "resolve".into(),
1914 expected: "1".into(),
1915 got: arg_forms.len(),
1916 });
1917 }
1918 let resolve_ns = resolve_current_ns(env);
1919 let sym_arg = eval(&arg_forms[0], env)?;
1920 let sym_name = match &sym_arg {
1921 Value::Symbol(s) => {
1922 let sym = s.get();
1923 if let Some(ns) = &sym.namespace {
1925 let full_ns = env.globals.resolve_ns_part_in(&resolve_ns, ns.as_ref());
1928 return Ok(
1929 match env.globals.lookup_var_in_ns(&full_ns, sym.name.as_ref()) {
1930 Some(var_ptr) => Value::Var(var_ptr),
1931 None => Value::Nil,
1932 },
1933 );
1934 }
1935 sym.name.as_ref().to_string()
1936 }
1937 Value::Str(s) => s.get().clone(),
1938 other => {
1939 return Err(EvalError::Runtime(format!(
1940 "resolve: arg must be symbol or string, got {}",
1941 other.type_name()
1942 )));
1943 }
1944 };
1945 Ok(match env.globals.lookup_var_in_ns(&resolve_ns, &sym_name) {
1946 Some(var_ptr) => Value::Var(var_ptr),
1947 None => Value::Nil,
1948 })
1949}
1950
1951fn handle_intern(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1952 if arg_forms.len() < 2 || arg_forms.len() > 3 {
1953 return Err(EvalError::Runtime("intern expects 2 or 3 arguments".into()));
1954 }
1955 let ns_val = eval(&arg_forms[0], env)?;
1956 let ns_name: Arc<str> = match &ns_val {
1957 Value::Symbol(s) => s.get().name.clone(),
1958 Value::Namespace(ns) => ns.get().name.clone(),
1959 other => {
1960 return Err(EvalError::Runtime(format!(
1961 "intern: first arg must be namespace or symbol, got {}",
1962 other.type_name()
1963 )));
1964 }
1965 };
1966 let var_name: Arc<str> = match eval(&arg_forms[1], env)? {
1967 Value::Symbol(s) => s.get().name.clone(),
1968 other => {
1969 return Err(EvalError::Runtime(format!(
1970 "intern: second arg must be symbol, got {}",
1971 other.type_name()
1972 )));
1973 }
1974 };
1975 let ns = {
1977 let map = env.globals.namespaces.read().unwrap();
1978 map.get(ns_name.as_ref()).cloned()
1979 };
1980 let ns = ns.ok_or_else(|| EvalError::Runtime(format!("No namespace: {ns_name} found")))?;
1981 let var = if arg_forms.len() == 3 {
1982 #[cfg(feature = "no-gc")]
1985 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1986 let val = eval(&arg_forms[2], env)?;
1987 let mut interns = ns.get().interns.lock().unwrap();
1988 if let Some(var) = interns.get(&var_name) {
1989 var.get().bind(val);
1990 var.clone()
1991 } else {
1992 let var =
1993 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
1994 var.get().bind(val);
1995 interns.insert(var_name, var.clone());
1996 var
1997 }
1998 } else {
1999 let mut interns = ns.get().interns.lock().unwrap();
2000 if let Some(var) = interns.get(&var_name) {
2001 var.clone()
2002 } else {
2003 let var =
2004 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
2005 interns.insert(var_name, var.clone());
2006 var
2007 }
2008 };
2009 Ok(Value::Var(var))
2010}
2011
2012fn handle_bound_fn_star(arg_forms: &[Form], env: &mut Env) -> EvalResult {
2017 if arg_forms.len() != 1 {
2018 return Err(EvalError::Arity {
2019 name: "bound-fn*".into(),
2020 expected: "1".into(),
2021 got: arg_forms.len(),
2022 });
2023 }
2024 let f = eval(&arg_forms[0], env)?;
2025 let frames = crate::env::dynamics::capture_current();
2027 let mut merged = std::collections::HashMap::new();
2028 for frame in &frames {
2029 merged.extend(frame.iter().map(|(k, v)| (*k, v.clone())));
2030 }
2031 Ok(Value::BoundFn(cljrs_gc::GcPtr::new(cljrs_value::BoundFn {
2032 wrapped: f,
2033 captured_bindings: merged,
2034 })))
2035}