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};
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: &std::sync::Mutex<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}
61
62fn check_watch_error() -> EvalResult<()> {
64 WATCH_ERROR.with(|cell| {
65 if let Some(e) = cell.borrow_mut().take() {
66 Err(e)
67 } else {
68 Ok(())
69 }
70 })
71}
72
73#[derive(Debug)]
77pub struct ClosureThunk {
78 pub f: CljxFn,
79 pub globals: std::sync::Arc<crate::env::env::GlobalEnv>,
80 pub ns: std::sync::Arc<str>,
81}
82
83struct CallableValueThunk {
89 callee: Value,
90 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
91 ns: std::sync::Arc<str>,
92}
93
94impl std::fmt::Debug for CallableValueThunk {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 f.debug_struct("CallableValueThunk")
97 .field("callee", &self.callee.type_name())
98 .field("ns", &self.ns)
99 .finish()
100 }
101}
102
103impl cljrs_gc::Trace for CallableValueThunk {
104 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
105 self.callee.trace(visitor);
106 }
107}
108
109impl Thunk for CallableValueThunk {
110 fn force(&self) -> Result<Value, String> {
111 let mut env = Env::new(self.globals.clone(), &self.ns);
112 crate::env::apply::apply_value(&self.callee, Vec::new(), &mut env)
113 .map_err(|e| format!("{e}"))
114 }
115}
116
117pub fn make_lazy_seq_from_fn(
130 f_val: &Value,
131 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
132 ns: std::sync::Arc<str>,
133) -> EvalResult {
134 let unwrapped = f_val.unwrap_meta();
135 if let Value::Fn(g) = unwrapped {
136 let thunk = ClosureThunk {
137 f: g.get().clone(),
138 globals,
139 ns,
140 };
141 return Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))));
142 }
143 if unwrapped.type_name() != "fn" {
146 return Err(EvalError::Runtime(format!(
147 "make-lazy-seq requires a fn, got {}",
148 unwrapped.type_name(),
149 )));
150 }
151 let thunk = CallableValueThunk {
152 callee: unwrapped.clone(),
153 globals,
154 ns,
155 };
156 Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))))
157}
158
159impl cljrs_gc::Trace for ClosureThunk {
160 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
161 self.f.trace(visitor);
162 }
163}
164
165impl Thunk for ClosureThunk {
166 fn force(&self) -> Result<Value, String> {
167 let _closed_root = crate::env::gc_roots::root_values(&self.f.closed_over_vals);
171 let mut env = Env::with_closure(self.globals.clone(), &self.ns, &self.f);
172 call_cljrs_fn(&self.f, &[], &mut env).map_err(|e| format!("{e}"))
173 }
174}
175
176pub fn is_form_intercepted(name: &str) -> bool {
183 matches!(
184 name,
185 "apply"
186 | "atom"
187 | "reset!"
188 | "swap!"
189 | "volatile!"
190 | "vreset!"
191 | "agent"
192 | "make-lazy-seq"
193 | "make-delay"
194 | "vswap!"
195 | "send"
196 | "send-off"
197 | "with-bindings*"
198 | "alter-var-root"
199 | "vary-meta"
200 | "eval"
201 | "find-ns"
202 | "the-ns"
203 | "ns-interns"
204 | "ns-publics"
205 | "ns-refers"
206 | "ns-map"
207 | "all-ns"
208 | "create-ns"
209 | "ns-aliases"
210 | "remove-ns"
211 | "alter-meta!"
212 | "ns-resolve"
213 | "resolve"
214 | "intern"
215 | "bound-fn*"
216 )
217}
218
219pub fn eval_call(func_form: &Form, arg_forms: &[Form], env: &mut Env) -> EvalResult {
227 if let FormKind::Symbol(s) = &func_form.kind
229 && let Some(method) = s.strip_prefix('.')
230 && !method.is_empty()
231 && method != "."
232 {
233 return eval_method_call(method, arg_forms, env);
234 }
235
236 let callee = eval(func_form, env)?;
238
239 let _callee_root = crate::env::gc_roots::root_value(&callee);
241
242 if let Value::Macro(mfn) = &callee {
244 let expanded = macro_apply(mfn.get(), func_form, arg_forms, env)?;
245 return eval(&expanded, env);
246 }
247
248 if let Value::NativeFunction(nf) = &callee {
250 crate::env::policy::check_native(&nf.get().name)?;
251 match nf.get().name.as_ref() {
252 "apply" => return handle_apply_call(arg_forms, env),
253 "atom" => return handle_atom_call(arg_forms, env),
254 "reset!" => return handle_reset_bang(arg_forms, env),
255 "swap!" => return handle_swap_call(arg_forms, env),
256 "volatile!" => return handle_volatile(arg_forms, env),
257 "vreset!" => return handle_vreset(arg_forms, env),
258 "agent" => return handle_agent_call(arg_forms, env),
259 "make-lazy-seq" => return handle_make_lazy_seq(arg_forms, env),
260 "make-delay" => return handle_make_delay(arg_forms, env),
261 "vswap!" => return handle_vswap(arg_forms, env),
262 "send" | "send-off" => return handle_send(arg_forms, env),
263 "with-bindings*" => return handle_with_bindings(arg_forms, env),
264 "alter-var-root" => return handle_alter_var_root(arg_forms, env),
265 "vary-meta" => return handle_vary_meta(arg_forms, env),
266 "eval" => return handle_eval(arg_forms, env),
267 "find-ns" | "the-ns" => return handle_find_ns(arg_forms, env),
268 "ns-interns" | "ns-publics" => return handle_ns_interns(arg_forms, env),
269 "ns-refers" => return handle_ns_refers(arg_forms, env),
270 "ns-map" => return handle_ns_map(arg_forms, env),
271 "all-ns" => return handle_all_ns(arg_forms, env),
272 "create-ns" => return handle_create_ns(arg_forms, env),
273 "ns-aliases" => return handle_ns_aliases(arg_forms, env),
274 "remove-ns" => return handle_remove_ns(arg_forms, env),
275 "alter-meta!" => return handle_alter_meta(arg_forms, env),
276 "ns-resolve" => return handle_ns_resolve(arg_forms, env),
277 "resolve" => return handle_resolve(arg_forms, env),
278 "intern" => return handle_intern(arg_forms, env),
279 "bound-fn*" => return handle_bound_fn_star(arg_forms, env),
280 _ => {}
281 }
282 }
283
284 let mut args: Vec<Value> = Vec::with_capacity(arg_forms.len());
287 for f in arg_forms {
288 let _args_root = crate::env::gc_roots::root_values(&args);
290 args.push(eval(f, env)?);
291 }
292
293 if let Value::Fn(f) = &callee {
298 if let Some(fut) = crate::env::apply::dispatch_if_async(&callee, &args, env) {
301 return Ok(fut);
302 }
303 let _args_root = crate::env::gc_roots::root_values(&args);
304 crate::env::gc_roots::gc_safepoint(env);
305 return env.call_cljrs_fn(f.get(), &args);
306 }
307
308 crate::env::apply::apply_value(&callee, args, env)
309}
310
311fn eval_method_call(method: &str, arg_forms: &[Form], env: &mut Env) -> EvalResult {
321 if arg_forms.is_empty() {
322 return Err(EvalError::Runtime(format!(
323 ".{method} requires a target object"
324 )));
325 }
326 let target = eval(&arg_forms[0], env)?;
327 let args: Vec<Value> = arg_forms[1..]
328 .iter()
329 .map(|f| eval(f, env))
330 .collect::<EvalResult<_>>()?;
331
332 dispatch_method(method, &target, &args)
333}
334
335pub fn dispatch_method(method: &str, target: &Value, args: &[Value]) -> EvalResult {
341 match target {
342 Value::Str(s) => dispatch_string_method(method, s.get(), args),
343 Value::Vector(v) => dispatch_vector_method(method, v, args),
344 Value::List(_) | Value::Cons(_) | Value::LazySeq(_) => {
345 dispatch_seq_method(method, target, args)
346 }
347 _ => Err(EvalError::Runtime(format!(
348 ".{method} not supported on type {}",
349 target.type_name()
350 ))),
351 }
352}
353
354fn dispatch_string_method(method: &str, s: &str, args: &[Value]) -> EvalResult {
355 match method {
356 "indexOf" => {
357 let needle = match args.first() {
358 Some(Value::Str(s)) => s.get().to_string(),
359 Some(Value::Char(c)) => c.to_string(),
360 Some(v) => {
361 return Err(EvalError::Runtime(format!(
362 ".indexOf expects string or char argument, got {}",
363 v.type_name()
364 )));
365 }
366 None => return Err(EvalError::Runtime(".indexOf requires an argument".into())),
367 };
368 match s.find(&needle) {
369 Some(pos) => Ok(Value::Long(pos as i64)),
370 None => Ok(Value::Long(-1)),
371 }
372 }
373 "lastIndexOf" => {
374 let needle = match args.first() {
375 Some(Value::Str(s)) => s.get().to_string(),
376 Some(Value::Char(c)) => c.to_string(),
377 _ => {
378 return Err(EvalError::Runtime(
379 ".lastIndexOf requires a string or char argument".into(),
380 ));
381 }
382 };
383 match s.rfind(&needle) {
384 Some(pos) => Ok(Value::Long(pos as i64)),
385 None => Ok(Value::Long(-1)),
386 }
387 }
388 "startsWith" => {
389 let prefix = require_str_arg(args, ".startsWith")?;
390 Ok(Value::Bool(s.starts_with(&prefix)))
391 }
392 "endsWith" => {
393 let suffix = require_str_arg(args, ".endsWith")?;
394 Ok(Value::Bool(s.ends_with(&suffix)))
395 }
396 "contains" => {
397 let sub = require_str_arg(args, ".contains")?;
398 Ok(Value::Bool(s.contains(&sub)))
399 }
400 "length" => Ok(Value::Long(s.len() as i64)),
401 "isEmpty" => Ok(Value::Bool(s.is_empty())),
402 "charAt" => {
403 let idx = require_long_arg(args, ".charAt")? as usize;
404 s.chars()
405 .nth(idx)
406 .map(Value::Char)
407 .ok_or_else(|| EvalError::Runtime(format!(".charAt index {idx} out of bounds")))
408 }
409 "substring" => {
410 let start = require_long_arg(args, ".substring")? as usize;
411 let end = args
412 .get(1)
413 .map(|v| match v {
414 Value::Long(n) => Ok(*n as usize),
415 _ => Err(EvalError::Runtime(
416 ".substring end must be an integer".into(),
417 )),
418 })
419 .transpose()?;
420 let result = match end {
421 Some(e) => &s[start..e.min(s.len())],
422 None => &s[start..],
423 };
424 Ok(Value::Str(GcPtr::new(result.to_string())))
425 }
426 "toUpperCase" => Ok(Value::Str(GcPtr::new(s.to_uppercase()))),
427 "toLowerCase" => Ok(Value::Str(GcPtr::new(s.to_lowercase()))),
428 "trim" => Ok(Value::Str(GcPtr::new(s.trim().to_string()))),
429 "replace" => {
430 let from = require_str_arg(args, ".replace")?;
431 let to = match args.get(1) {
432 Some(Value::Str(s)) => s.get().to_string(),
433 Some(Value::Char(c)) => c.to_string(),
434 _ => {
435 return Err(EvalError::Runtime(
436 ".replace requires two string arguments".into(),
437 ));
438 }
439 };
440 Ok(Value::Str(GcPtr::new(s.replace(&from, &to))))
441 }
442 "split" => {
443 let sep = require_str_arg(args, ".split")?;
444 let parts: Vec<Value> = s
445 .split(&sep)
446 .map(|p| Value::Str(GcPtr::new(p.to_string())))
447 .collect();
448 Ok(Value::Vector(GcPtr::new(
449 cljrs_value::PersistentVector::from_iter(parts),
450 )))
451 }
452 _ => Err(EvalError::Runtime(format!(
453 ".{method} not supported on String"
454 ))),
455 }
456}
457
458fn dispatch_vector_method(
459 method: &str,
460 v: &GcPtr<cljrs_value::PersistentVector>,
461 args: &[Value],
462) -> EvalResult {
463 match method {
464 "indexOf" => {
465 let needle = args
466 .first()
467 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
468 for (i, item) in v.get().iter().enumerate() {
469 if item == needle {
470 return Ok(Value::Long(i as i64));
471 }
472 }
473 Ok(Value::Long(-1))
474 }
475 "size" | "count" => Ok(Value::Long(v.get().count() as i64)),
476 _ => Err(EvalError::Runtime(format!(
477 ".{method} not supported on Vector"
478 ))),
479 }
480}
481
482fn dispatch_seq_method(method: &str, target: &Value, args: &[Value]) -> EvalResult {
483 match method {
484 "indexOf" => {
485 let needle = args
486 .first()
487 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
488 let items = crate::interp::destructure::value_to_seq_vec(target);
489 for (i, item) in items.iter().enumerate() {
490 if item == needle {
491 return Ok(Value::Long(i as i64));
492 }
493 }
494 Ok(Value::Long(-1))
495 }
496 _ => Err(EvalError::Runtime(format!(
497 ".{method} not supported on {}",
498 target.type_name()
499 ))),
500 }
501}
502
503fn require_str_arg(args: &[Value], method: &str) -> Result<String, EvalError> {
504 match args.first() {
505 Some(Value::Str(s)) => Ok(s.get().to_string()),
506 Some(Value::Char(c)) => Ok(c.to_string()),
507 _ => Err(EvalError::Runtime(format!(
508 "{method} requires a string argument"
509 ))),
510 }
511}
512
513fn require_long_arg(args: &[Value], method: &str) -> Result<i64, EvalError> {
514 match args.first() {
515 Some(Value::Long(n)) => Ok(*n),
516 _ => Err(EvalError::Runtime(format!(
517 "{method} requires an integer argument"
518 ))),
519 }
520}
521
522pub fn resolve_type_tag(sym: &str) -> Arc<str> {
525 Arc::from(sym)
526}
527
528pub fn call_cljrs_fn(f: &CljxFn, args: &[Value], caller_env: &mut Env) -> EvalResult {
530 let arity = select_arity(f, args.len())?;
531
532 let _caller_root = crate::env::gc_roots::push_env_root(caller_env);
535
536 let mut env = Env::with_closure(caller_env.globals.clone(), &f.defining_ns, f);
539
540 let mut current_args = Vec::from(args);
541 loop {
542 let _args_root = crate::env::gc_roots::root_values(¤t_args);
545
546 crate::env::gc_roots::gc_safepoint(&env);
548
549 env.push_frame();
550
551 #[cfg(not(feature = "no-gc"))]
561 let _call_frame = cljrs_gc::push_alloc_frame();
562
563 bind_fn_params(arity, ¤t_args, &mut env)?;
565
566 if let Some(ref name) = f.name {
569 let self_val = if let Some(ref p) = f.self_ptr {
570 Value::Fn(p.clone())
571 } else {
572 Value::Fn(GcPtr::new(f.clone()))
573 };
574 env.bind(name.clone(), self_val);
575 }
576
577 #[cfg(not(feature = "no-gc"))]
582 let result = eval_body_recur_fn(&arity.body, &mut env);
583 #[cfg(feature = "no-gc")]
584 let result = {
585 let mut scratch = cljrs_gc::alloc_ctx::ScratchGuard::new();
586 eval_body_with_scratch(&arity.body, &mut scratch, &mut env)
588 };
589 env.pop_frame();
590 match result {
594 Ok(v) => return Ok(v),
595 Err(EvalError::Recur(new_args)) => {
596 if arity.rest_param.is_some() {
601 let n = arity.params.len();
602 if new_args.len() == n + 1 {
603 let mut flat = new_args[..n].to_vec();
604 let rest_val = &new_args[n];
606 match rest_val {
607 Value::Nil => {} _ => {
609 let rest_items = value_to_seq_vec(rest_val);
610 flat.extend(rest_items);
611 }
612 }
613 current_args = flat;
614 } else {
615 current_args = new_args;
616 }
617 } else {
618 current_args = new_args;
619 }
620 }
621 Err(e) => return Err(e),
622 }
623 }
624}
625
626pub fn bind_fn_params(arity: &CljxFnArity, args: &[Value], env: &mut Env) -> EvalResult<()> {
628 let n = arity.params.len();
629 for (i, name) in arity.params.iter().enumerate() {
631 let val = args.get(i).cloned().unwrap_or(Value::Nil);
632 env.bind(name.clone(), val);
633 }
634 if let Some(ref rest) = arity.rest_param {
636 let rest_items = args[n..].to_vec();
637 let rest_val = if rest_items.is_empty() {
638 Value::Nil
639 } else {
640 Value::List(GcPtr::new(PersistentList::from_iter(rest_items)))
641 };
642 env.bind(rest.clone(), rest_val.clone());
643 if let Some(ref pattern) = arity.destructure_rest {
645 let destructure_val = if matches!(pattern.kind, FormKind::Map(_)) {
649 let items = value_to_seq_vec(&rest_val);
650 Value::Map(MapValue::from_flat_entries(items))
651 } else {
652 rest_val
653 };
654 crate::interp::destructure::bind_pattern(pattern, destructure_val, env)?;
655 }
656 }
657 for (idx, pattern) in &arity.destructure_params {
659 let val = args.get(*idx).cloned().unwrap_or(Value::Nil);
660 crate::interp::destructure::bind_pattern(pattern, val, env)?;
661 }
662 Ok(())
663}
664
665#[cfg(not(feature = "no-gc"))]
667fn eval_body_recur_fn(body: &[cljrs_reader::Form], env: &mut Env) -> EvalResult {
668 let mut result = Value::Nil;
669 for form in body {
670 result = eval(form, env)?;
671 }
672 Ok(result)
673}
674
675#[cfg(feature = "no-gc")]
679fn eval_body_with_scratch(
680 body: &[cljrs_reader::Form],
681 scratch: &mut cljrs_gc::alloc_ctx::ScratchGuard,
682 env: &mut Env,
683) -> EvalResult {
684 if body.is_empty() {
685 scratch.pop_for_return();
686 return Ok(Value::Nil);
687 }
688 for form in &body[..body.len() - 1] {
690 eval(form, env)?;
691 }
692 scratch.pop_for_return();
694 eval(&body[body.len() - 1], env)
695}
696
697pub fn select_arity(f: &CljxFn, argc: usize) -> EvalResult<&CljxFnArity> {
699 let name = f.name.as_deref().unwrap_or("fn");
700 for arity in &f.arities {
702 if arity.rest_param.is_none() && arity.params.len() == argc {
703 return Ok(arity);
704 }
705 }
706 for arity in &f.arities {
708 if arity.rest_param.is_some() && argc >= arity.params.len() {
709 return Ok(arity);
710 }
711 }
712 let expected: Vec<String> = f
714 .arities
715 .iter()
716 .map(|a| {
717 if a.rest_param.is_some() {
718 format!("{}+", a.params.len())
719 } else {
720 a.params.len().to_string()
721 }
722 })
723 .collect();
724 Err(EvalError::Arity {
725 name: name.to_string(),
726 expected: expected.join(" or "),
727 got: argc,
728 })
729}
730
731fn macro_apply(
738 mfn: &CljxFn,
739 func_form: &Form,
740 arg_forms: &[Form],
741 env: &mut Env,
742) -> EvalResult<Form> {
743 let resolved_args: Vec<Form> = arg_forms
748 .iter()
749 .map(|f| crate::builtins::form::resolve_auto_forms(f, env))
750 .collect::<EvalResult<Vec<Form>>>()?;
751
752 let form_val = {
754 let mut items = vec![form_to_value(func_form)?];
755 for f in &resolved_args {
756 items.push(form_to_value(f)?);
757 }
758 Value::List(GcPtr::new(PersistentList::from_iter(items)))
759 };
760
761 let env_val = {
763 let (names, vals) = env.all_local_bindings();
764 let mut m = MapValue::empty();
765 for (name, val) in names.iter().zip(vals.iter()) {
766 m = m.assoc(Value::symbol(Symbol::simple(name.as_ref())), val.clone());
767 }
768 Value::Map(m)
769 };
770
771 let mut args = vec![form_val, env_val];
773 for f in &resolved_args {
774 args.push(form_to_value(f)?);
775 }
776
777 let expanded_val = call_cljrs_fn(mfn, args.as_ref(), env)?;
778 let dummy_span = cljrs_types::span::Span::new(Arc::new("<macro>".to_string()), 0, 0, 1, 1);
779 crate::interp::macros::value_to_form(&expanded_val, dummy_span)
780}
781
782fn handle_apply_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
784 let mut evaled: Vec<Value> = Vec::with_capacity(arg_forms.len());
785 for f in arg_forms {
786 let _root = crate::env::gc_roots::root_values(&evaled);
787 evaled.push(eval(f, env)?);
788 }
789
790 if evaled.len() < 2 {
791 return Err(EvalError::Arity {
792 name: "apply".into(),
793 expected: "2+".into(),
794 got: evaled.len(),
795 });
796 }
797
798 let f = evaled.remove(0);
799 let last = evaled.pop().unwrap();
800 let _f_root = crate::env::gc_roots::root_value(&f);
802 let _last_root = crate::env::gc_roots::root_value(&last);
803 let _evaled_root = crate::env::gc_roots::root_values(&evaled);
804 let spread = value_to_seq_vec(&last);
806 evaled.extend(spread);
807 crate::env::apply::apply_value(&f, evaled, env)
808}
809
810pub fn handle_make_lazy_seq(arg_forms: &[Form], env: &mut Env) -> EvalResult {
812 if arg_forms.len() != 1 {
813 return Err(EvalError::Arity {
814 name: "make-lazy-seq".into(),
815 expected: "1".into(),
816 got: arg_forms.len(),
817 });
818 }
819 let f_val = eval(&arg_forms[0], env)?;
820 let f = match f_val {
821 Value::Fn(f) => f.get().clone(),
822 other => {
823 return Err(EvalError::Runtime(format!(
824 "make-lazy-seq requires a fn, got {}",
825 other.type_name()
826 )));
827 }
828 };
829 let thunk = ClosureThunk {
830 f,
831 globals: env.globals.clone(),
832 ns: env.current_ns.clone(),
833 };
834 Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))))
835}
836
837fn handle_make_delay(arg_forms: &[Form], env: &mut Env) -> EvalResult {
839 if arg_forms.len() != 1 {
840 return Err(EvalError::Arity {
841 name: "make-delay".into(),
842 expected: "1".into(),
843 got: arg_forms.len(),
844 });
845 }
846 let f_val = eval(&arg_forms[0], env)?;
847 let f = match f_val {
848 Value::Fn(f) => f.get().clone(),
849 other => {
850 return Err(EvalError::Runtime(format!(
851 "make-delay requires a fn, got {}",
852 other.type_name()
853 )));
854 }
855 };
856 let thunk = ClosureThunk {
857 f,
858 globals: env.globals.clone(),
859 ns: env.current_ns.clone(),
860 };
861 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
862}
863
864fn handle_vswap(arg_forms: &[Form], env: &mut Env) -> EvalResult {
866 if arg_forms.len() < 2 {
867 return Err(EvalError::Arity {
868 name: "vswap!".into(),
869 expected: "2+".into(),
870 got: arg_forms.len(),
871 });
872 }
873 let vol_val = eval(&arg_forms[0], env)?;
874 let f = eval(&arg_forms[1], env)?;
875 let extra: Vec<Value> = arg_forms[2..]
876 .iter()
877 .map(|a| eval(a, env))
878 .collect::<EvalResult<_>>()?;
879
880 match vol_val {
881 Value::Volatile(v) => {
882 let cur = v.get().deref();
883 let mut call_args = vec![cur];
884 call_args.extend(extra);
885 #[cfg(feature = "no-gc")]
888 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
889 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
890 v.get().reset(new_val.clone());
891 Ok(new_val)
892 }
893 other => Err(EvalError::Runtime(format!(
894 "vswap!: expected volatile, got {}",
895 other.type_name()
896 ))),
897 }
898}
899
900fn handle_volatile(arg_forms: &[Form], env: &mut Env) -> EvalResult {
904 if arg_forms.is_empty() {
905 return Err(EvalError::Arity {
906 name: "volatile!".into(),
907 expected: "1".into(),
908 got: 0,
909 });
910 }
911 #[cfg(feature = "no-gc")]
914 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
915 let initial = eval(&arg_forms[0], env)?;
916 Ok(Value::Volatile(GcPtr::new(Volatile::new(initial))))
917}
918
919fn handle_vreset(arg_forms: &[Form], env: &mut Env) -> EvalResult {
923 if arg_forms.len() < 2 {
924 return Err(EvalError::Arity {
925 name: "vreset!".into(),
926 expected: "2".into(),
927 got: arg_forms.len(),
928 });
929 }
930 let vol_val = eval(&arg_forms[0], env)?;
931 #[cfg(feature = "no-gc")]
934 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
935 let new_val = eval(&arg_forms[1], env)?;
936 match &vol_val {
937 Value::Volatile(v) => {
938 v.get().reset(new_val.clone());
939 Ok(new_val)
940 }
941 other => Err(EvalError::Runtime(format!(
942 "vreset!: expected volatile, got {}",
943 other.type_name()
944 ))),
945 }
946}
947
948fn handle_agent_call(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
952 Err(EvalError::Runtime("agent is not yet implemented".into()))
953}
954
955fn handle_send(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
957 Err(EvalError::Runtime(
958 "send/send-off: agents are not yet implemented".into(),
959 ))
960}
961
962fn handle_atom_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
966 if arg_forms.is_empty() {
967 return Err(EvalError::Arity {
968 name: "atom".into(),
969 expected: "1+".into(),
970 got: 0,
971 });
972 }
973 #[cfg(feature = "no-gc")]
976 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
977 let initial = eval(&arg_forms[0], env)?;
978
979 let options: Vec<Value> = arg_forms[1..]
981 .iter()
982 .map(|f| eval(f, env))
983 .collect::<EvalResult<_>>()?;
984
985 let mut meta_opt: Option<Value> = None;
986 let mut validator_opt: Option<Value> = None;
987 let mut i = 0;
988 while i + 1 < options.len() {
989 match &options[i] {
990 Value::Keyword(k) if k.get().name.as_ref() == "meta" => {
991 meta_opt = Some(options[i + 1].clone());
992 i += 2;
993 }
994 Value::Keyword(k) if k.get().name.as_ref() == "validator" => {
995 let vf = options[i + 1].clone();
996 validator_opt = if vf == Value::Nil { None } else { Some(vf) };
997 i += 2;
998 }
999 _ => {
1000 i += 2;
1001 }
1002 }
1003 }
1004
1005 if let Some(ref m) = meta_opt
1007 && !matches!(m, Value::Nil | Value::Map(_))
1008 {
1009 return Err(EvalError::Thrown(Value::string(
1010 "Atom metadata must be a map or nil".to_string(),
1011 )));
1012 }
1013
1014 if let Some(ref vf) = validator_opt {
1016 let result = crate::env::apply::apply_value(vf, vec![initial.clone()], env)?;
1017 if result == Value::Nil || result == Value::Bool(false) {
1018 return Err(EvalError::Thrown(Value::string(
1019 "Invalid initial value for atom".to_string(),
1020 )));
1021 }
1022 }
1023
1024 let atom = GcPtr::new(Atom::new(initial));
1025 if let Some(m) = meta_opt {
1026 atom.get()
1027 .set_meta(if m == Value::Nil { None } else { Some(m) });
1028 }
1029 if let Some(vf) = validator_opt {
1030 atom.get().set_validator(Some(vf));
1031 }
1032 Ok(Value::Atom(atom))
1033}
1034
1035fn shared_atom_reset(sa: &Arc<cljrs_value::SharedAtom>, new_val: Value) -> EvalResult {
1047 let promoted = cljrs_value::promote(&new_val).map_err(|e| EvalError::Runtime(e.to_string()))?;
1048 sa.reset(promoted);
1049 Ok(new_val)
1050}
1051
1052fn shared_atom_swap(
1057 sa: &Arc<cljrs_value::SharedAtom>,
1058 f: &Value,
1059 extra: Vec<Value>,
1060 env: &mut Env,
1061) -> EvalResult {
1062 loop {
1063 let cur = sa.deref_val();
1064 let old_val = cljrs_value::demote(&cur);
1065 let mut call_args = Vec::with_capacity(1 + extra.len());
1066 call_args.push(old_val);
1067 call_args.extend(extra.iter().cloned());
1068 let new_val = crate::env::apply::apply_value(f, call_args, env)?;
1069 let promoted =
1070 cljrs_value::promote(&new_val).map_err(|e| EvalError::Runtime(e.to_string()))?;
1071 if sa.compare_and_set(&cur, promoted) {
1072 return Ok(new_val);
1073 }
1074 }
1076}
1077
1078fn handle_reset_bang(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1081 if arg_forms.len() < 2 {
1082 return Err(EvalError::Arity {
1083 name: "reset!".into(),
1084 expected: "2".into(),
1085 got: arg_forms.len(),
1086 });
1087 }
1088 let atom_val = eval(&arg_forms[0], env)?;
1089 #[cfg(feature = "no-gc")]
1092 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1093 let new_val = eval(&arg_forms[1], env)?;
1094
1095 let atom = match &atom_val {
1096 Value::Atom(a) => a.clone(),
1097 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1098 v => {
1099 return Err(EvalError::Runtime(format!(
1100 "reset! requires an atom, got {}",
1101 v.type_name()
1102 )));
1103 }
1104 };
1105
1106 validate_atom_value(&atom, &new_val, env)?;
1107 let old_val = atom.get().deref();
1108 atom.get().reset(new_val.clone());
1109 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1110 check_watch_error()?;
1111 Ok(new_val)
1112}
1113
1114fn validate_atom_value(atom: &GcPtr<Atom>, new_val: &Value, env: &mut Env) -> EvalResult<()> {
1116 if let Some(vf) = atom.get().get_validator() {
1117 let result = crate::env::apply::apply_value(&vf, vec![new_val.clone()], env)?;
1118 if result == Value::Nil || result == Value::Bool(false) {
1119 return Err(EvalError::Thrown(Value::string(
1120 "Invalid value for atom".to_string(),
1121 )));
1122 }
1123 }
1124 Ok(())
1125}
1126
1127fn handle_swap_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1130 let mut evaled: Vec<Value> = arg_forms
1131 .iter()
1132 .map(|f| eval(f, env))
1133 .collect::<EvalResult<_>>()?;
1134
1135 if evaled.len() < 2 {
1136 return Err(EvalError::Arity {
1137 name: "swap!".into(),
1138 expected: "2+".into(),
1139 got: evaled.len(),
1140 });
1141 }
1142
1143 let atom_val = evaled.remove(0);
1144 let f = evaled.remove(0);
1145
1146 let atom = match &atom_val {
1147 Value::Atom(a) => a.clone(),
1148 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, evaled, env),
1149 v => {
1150 return Err(EvalError::Runtime(format!(
1151 "swap! requires an atom, got {}",
1152 v.type_name()
1153 )));
1154 }
1155 };
1156
1157 let old_val = atom.get().deref();
1158 let mut args = vec![old_val.clone()];
1159 args.extend(evaled);
1160 #[cfg(feature = "no-gc")]
1163 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1164 let new_val = crate::env::apply::apply_value(&f, args, env)?;
1165 validate_atom_value(&atom, &new_val, env)?;
1166 atom.get().reset(new_val.clone());
1167 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1168 check_watch_error()?;
1169 Ok(new_val)
1170}
1171
1172fn handle_with_bindings(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1177 if arg_forms.len() < 2 {
1178 return Err(EvalError::Arity {
1179 name: "with-bindings*".into(),
1180 expected: "2".into(),
1181 got: arg_forms.len(),
1182 });
1183 }
1184 let map_val = eval(&arg_forms[0], env)?;
1185 let func_val = eval(&arg_forms[1], env)?;
1186
1187 let mut frame: HashMap<usize, Value> = HashMap::new();
1188 if let Value::Map(m) = &map_val {
1189 m.for_each(|k, v| {
1190 if let Value::Var(vp) = k {
1191 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1192 }
1193 });
1195 } else {
1196 return Err(EvalError::Runtime(
1197 "with-bindings*: first arg must be a map".into(),
1198 ));
1199 }
1200
1201 let _guard = crate::env::dynamics::push_frame(frame);
1202 crate::env::apply::apply_value(&func_val, vec![], env)
1203}
1204
1205fn handle_alter_var_root(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1209 if arg_forms.len() < 2 {
1210 return Err(EvalError::Arity {
1211 name: "alter-var-root".into(),
1212 expected: "2+".into(),
1213 got: arg_forms.len(),
1214 });
1215 }
1216 let var_val = eval(&arg_forms[0], env)?;
1217 let f = eval(&arg_forms[1], env)?;
1218 let extra: Vec<Value> = arg_forms[2..]
1219 .iter()
1220 .map(|form| eval(form, env))
1221 .collect::<EvalResult<_>>()?;
1222
1223 let vp = match &var_val {
1224 Value::Var(vp) => vp.clone(),
1225 v => {
1226 return Err(EvalError::Runtime(format!(
1227 "alter-var-root: expected var, got {}",
1228 v.type_name()
1229 )));
1230 }
1231 };
1232 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1233 let mut call_args = vec![old_val.clone()];
1234 call_args.extend(extra);
1235 #[cfg(feature = "no-gc")]
1238 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1239 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1240 vp.get().bind(new_val.clone());
1241 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1242 check_watch_error()?;
1243 Ok(new_val)
1244}
1245
1246fn handle_vary_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1250 if arg_forms.len() < 2 {
1251 return Err(EvalError::Arity {
1252 name: "vary-meta".into(),
1253 expected: "2+".into(),
1254 got: arg_forms.len(),
1255 });
1256 }
1257 let obj = eval(&arg_forms[0], env)?;
1258 let f = eval(&arg_forms[1], env)?;
1259 let extra: Vec<Value> = arg_forms[2..]
1260 .iter()
1261 .map(|form| eval(form, env))
1262 .collect::<EvalResult<_>>()?;
1263
1264 let current_meta = match &obj {
1265 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1266 _ => Value::Nil,
1267 };
1268 let mut call_args = vec![current_meta];
1269 call_args.extend(extra);
1270 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1271 if let Value::Var(vp) = &obj {
1272 vp.get().set_meta(new_meta);
1273 }
1274 Ok(obj)
1275}
1276
1277fn handle_eval(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1281 let [arg] = arg_forms else {
1282 return Err(EvalError::Arity {
1283 name: "eval".into(),
1284 expected: "1".into(),
1285 got: arg_forms.len(),
1286 });
1287 };
1288 let value = eval(arg, env)?;
1289 eval_eval(vec![value], env)
1290}
1291
1292pub fn eval_eval(args: Vec<Value>, env: &mut Env) -> EvalResult {
1297 let [value] = args.as_slice() else {
1298 return Err(EvalError::Arity {
1299 name: "eval".into(),
1300 expected: "1".into(),
1301 got: args.len(),
1302 });
1303 };
1304 let span = cljrs_types::span::Span::new(Arc::new("<eval>".to_string()), 0, 0, 1, 1);
1305 let form = crate::interp::macros::value_to_form(value, span)?;
1306 let mut top = Env::new(env.globals.clone(), &env.current_ns);
1307 eval(&form, &mut top)
1308}
1309
1310pub fn eval_reset_bang(args: Vec<Value>, env: &mut Env) -> EvalResult {
1318 if args.len() < 2 {
1319 return Err(EvalError::Arity {
1320 name: "reset!".into(),
1321 expected: "2".into(),
1322 got: args.len(),
1323 });
1324 }
1325 let atom_val = args[0].clone();
1326 let new_val = args[1].clone();
1327 let atom = match &atom_val {
1328 Value::Atom(a) => a.clone(),
1329 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1330 v => {
1331 return Err(EvalError::Runtime(format!(
1332 "reset! requires an atom, got {}",
1333 v.type_name()
1334 )));
1335 }
1336 };
1337 #[cfg(feature = "no-gc")]
1338 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1339 validate_atom_value(&atom, &new_val, env)?;
1340 let old_val = atom.get().deref();
1341 atom.get().reset(new_val.clone());
1342 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1343 check_watch_error()?;
1344 Ok(new_val)
1345}
1346
1347pub fn eval_swap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1349 if args.len() < 2 {
1350 return Err(EvalError::Arity {
1351 name: "swap!".into(),
1352 expected: "2+".into(),
1353 got: args.len(),
1354 });
1355 }
1356 let atom_val = args.remove(0);
1357 let f = args.remove(0);
1358 let atom = match &atom_val {
1359 Value::Atom(a) => a.clone(),
1360 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, args, env),
1361 v => {
1362 return Err(EvalError::Runtime(format!(
1363 "swap! requires an atom, got {}",
1364 v.type_name()
1365 )));
1366 }
1367 };
1368 let old_val = atom.get().deref();
1369 let mut call_args = vec![old_val.clone()];
1370 call_args.extend(args);
1371 #[cfg(feature = "no-gc")]
1372 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1373 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1374 validate_atom_value(&atom, &new_val, env)?;
1375 atom.get().reset(new_val.clone());
1376 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1377 check_watch_error()?;
1378 Ok(new_val)
1379}
1380
1381pub fn eval_volatile(args: Vec<Value>) -> EvalResult {
1383 if args.is_empty() {
1384 return Err(EvalError::Arity {
1385 name: "volatile!".into(),
1386 expected: "1".into(),
1387 got: 0,
1388 });
1389 }
1390 #[cfg(feature = "no-gc")]
1391 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1392 Ok(Value::Volatile(GcPtr::new(Volatile::new(
1393 args.into_iter().next().unwrap(),
1394 ))))
1395}
1396
1397pub fn eval_vreset_bang(args: Vec<Value>) -> EvalResult {
1399 if args.len() < 2 {
1400 return Err(EvalError::Arity {
1401 name: "vreset!".into(),
1402 expected: "2".into(),
1403 got: args.len(),
1404 });
1405 }
1406 #[cfg(feature = "no-gc")]
1407 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1408 let new_val = args[1].clone();
1409 match &args[0] {
1410 Value::Volatile(v) => {
1411 v.get().reset(new_val.clone());
1412 Ok(new_val)
1413 }
1414 other => Err(EvalError::Runtime(format!(
1415 "vreset!: expected volatile, got {}",
1416 other.type_name()
1417 ))),
1418 }
1419}
1420
1421pub fn eval_vswap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1423 if args.len() < 2 {
1424 return Err(EvalError::Arity {
1425 name: "vswap!".into(),
1426 expected: "2+".into(),
1427 got: args.len(),
1428 });
1429 }
1430 let vol_val = args.remove(0);
1431 let f = args.remove(0);
1432 match vol_val {
1433 Value::Volatile(v) => {
1434 let cur = v.get().deref();
1435 let mut call_args = vec![cur];
1436 call_args.extend(args);
1437 #[cfg(feature = "no-gc")]
1438 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1439 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1440 v.get().reset(new_val.clone());
1441 Ok(new_val)
1442 }
1443 other => Err(EvalError::Runtime(format!(
1444 "vswap!: expected volatile, got {}",
1445 other.type_name()
1446 ))),
1447 }
1448}
1449
1450pub fn make_delay_from_fn(
1455 f_val: &Value,
1456 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
1457 ns: std::sync::Arc<str>,
1458) -> EvalResult {
1459 let f = match f_val {
1460 Value::Fn(f) => f.get().clone(),
1461 other => {
1462 return Err(EvalError::Runtime(format!(
1463 "make-delay requires a fn, got {}",
1464 other.type_name()
1465 )));
1466 }
1467 };
1468 let thunk = ClosureThunk { f, globals, ns };
1469 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
1470}
1471
1472pub fn eval_alter_var_root(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1474 if args.len() < 2 {
1475 return Err(EvalError::Arity {
1476 name: "alter-var-root".into(),
1477 expected: "2+".into(),
1478 got: args.len(),
1479 });
1480 }
1481 let var_val = args.remove(0);
1482 let f = args.remove(0);
1483 let vp = match &var_val {
1484 Value::Var(vp) => vp.clone(),
1485 v => {
1486 return Err(EvalError::Runtime(format!(
1487 "alter-var-root: expected var, got {}",
1488 v.type_name()
1489 )));
1490 }
1491 };
1492 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1493 let mut call_args = vec![old_val.clone()];
1494 call_args.extend(args);
1495 #[cfg(feature = "no-gc")]
1496 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1497 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1498 vp.get().bind(new_val.clone());
1499 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1500 check_watch_error()?;
1501 Ok(new_val)
1502}
1503
1504pub fn eval_vary_meta(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1506 if args.len() < 2 {
1507 return Err(EvalError::Arity {
1508 name: "vary-meta".into(),
1509 expected: "2+".into(),
1510 got: args.len(),
1511 });
1512 }
1513 let obj = args.remove(0);
1514 let f = args.remove(0);
1515 let current_meta = match &obj {
1516 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1517 _ => Value::Nil,
1518 };
1519 let mut call_args = vec![current_meta];
1520 call_args.extend(args);
1521 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1522 if let Value::Var(vp) = &obj {
1523 vp.get().set_meta(new_meta);
1524 }
1525 Ok(obj)
1526}
1527
1528pub fn eval_with_bindings_star(args: Vec<Value>, env: &mut Env) -> EvalResult {
1530 if args.len() < 2 {
1531 return Err(EvalError::Arity {
1532 name: "with-bindings*".into(),
1533 expected: "2".into(),
1534 got: args.len(),
1535 });
1536 }
1537 let mut frame: HashMap<usize, Value> = HashMap::new();
1538 if let Value::Map(m) = &args[0] {
1539 m.for_each(|k, v| {
1540 if let Value::Var(vp) = k {
1541 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1542 }
1543 });
1544 } else {
1545 return Err(EvalError::Runtime(
1546 "with-bindings*: first arg must be a map".into(),
1547 ));
1548 }
1549 let _guard = crate::env::dynamics::push_frame(frame);
1550 crate::env::apply::apply_value(&args[1], vec![], env)
1551}
1552
1553pub fn eval_send_to_agent(_args: Vec<Value>, _env: &mut Env) -> EvalResult {
1555 Err(EvalError::Runtime(
1556 "send/send-off: agents are not yet implemented".into(),
1557 ))
1558}
1559
1560fn ns_name_from_val(v: &Value) -> Result<String, EvalError> {
1563 match v {
1564 Value::Symbol(s) => Ok(s.get().name.as_ref().to_string()),
1565 Value::Str(s) => Ok(s.get().clone()),
1566 Value::Namespace(ns) => Ok(ns.get().name.as_ref().to_string()),
1567 Value::Keyword(k) => Ok(k.get().name.as_ref().to_string()),
1568 other => Err(EvalError::Runtime(format!(
1569 "expected symbol, string, or namespace, got {}",
1570 other.type_name()
1571 ))),
1572 }
1573}
1574
1575fn the_ns(v: &Value, env: &Env) -> Result<GcPtr<cljrs_value::Namespace>, EvalError> {
1580 if let Value::Namespace(ns) = v {
1581 return Ok(ns.clone());
1582 }
1583 let name = ns_name_from_val(v)?;
1584 let map = env.globals.namespaces.read().unwrap();
1585 match map.get(name.as_str()) {
1586 Some(ns) => Ok(ns.clone()),
1587 None => Err(EvalError::Runtime(format!("No namespace: {name} found"))),
1588 }
1589}
1590
1591fn handle_ns_interns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1594 if arg_forms.is_empty() {
1595 return Err(EvalError::Arity {
1596 name: "ns-interns".into(),
1597 expected: "1".into(),
1598 got: 0,
1599 });
1600 }
1601 let arg = eval(&arg_forms[0], env)?;
1602 let ns = the_ns(&arg, env)?;
1603 crate::builtins::builtins::builtin_ns_interns(&[Value::Namespace(ns)])
1604 .map_err(crate::env::error::value_error_to_eval_error)
1605}
1606
1607fn handle_ns_refers(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1609 if arg_forms.is_empty() {
1610 return Err(EvalError::Arity {
1611 name: "ns-refers".into(),
1612 expected: "1".into(),
1613 got: 0,
1614 });
1615 }
1616 let arg = eval(&arg_forms[0], env)?;
1617 let ns = the_ns(&arg, env)?;
1618 crate::builtins::builtins::builtin_ns_refers(&[Value::Namespace(ns)])
1619 .map_err(crate::env::error::value_error_to_eval_error)
1620}
1621
1622fn handle_ns_map(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1624 if arg_forms.is_empty() {
1625 return Err(EvalError::Arity {
1626 name: "ns-map".into(),
1627 expected: "1".into(),
1628 got: 0,
1629 });
1630 }
1631 let arg = eval(&arg_forms[0], env)?;
1632 let ns = the_ns(&arg, env)?;
1633 crate::builtins::builtins::builtin_ns_map(&[Value::Namespace(ns)])
1634 .map_err(crate::env::error::value_error_to_eval_error)
1635}
1636
1637fn handle_find_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1639 if arg_forms.is_empty() {
1640 return Err(EvalError::Arity {
1641 name: "find-ns".into(),
1642 expected: "1".into(),
1643 got: 0,
1644 });
1645 }
1646 let arg = eval(&arg_forms[0], env)?;
1647 let name = ns_name_from_val(&arg)?;
1648 let map = env.globals.namespaces.read().unwrap();
1649 match map.get(name.as_str()) {
1650 Some(ns) => Ok(Value::Namespace(ns.clone())),
1651 None => Ok(Value::Nil),
1652 }
1653}
1654
1655fn handle_all_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1657 if !arg_forms.is_empty() {
1658 let _ = eval(&arg_forms[0], env)?; }
1660 let map = env.globals.namespaces.read().unwrap();
1661 let items: Vec<Value> = map
1662 .values()
1663 .map(|ns| Value::Namespace(ns.clone()))
1664 .collect();
1665 drop(map);
1666 Ok(Value::List(cljrs_gc::GcPtr::new(
1667 cljrs_value::PersistentList::from_iter(items),
1668 )))
1669}
1670
1671fn handle_create_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1673 if arg_forms.is_empty() {
1674 return Err(EvalError::Arity {
1675 name: "create-ns".into(),
1676 expected: "1".into(),
1677 got: 0,
1678 });
1679 }
1680 let arg = eval(&arg_forms[0], env)?;
1681 let name = ns_name_from_val(&arg)?;
1682 let ns = env.globals.get_or_create_ns(&name);
1683 Ok(Value::Namespace(ns))
1684}
1685
1686fn handle_ns_aliases(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1688 if arg_forms.is_empty() {
1689 return Err(EvalError::Arity {
1690 name: "ns-aliases".into(),
1691 expected: "1".into(),
1692 got: 0,
1693 });
1694 }
1695 let ns_val = eval(&arg_forms[0], env)?;
1696 let ns_name = ns_name_from_val(&ns_val)?;
1697 let map = env.globals.namespaces.read().unwrap();
1698 let ns = match map.get(ns_name.as_str()) {
1699 Some(ns) => ns.clone(),
1700 None => return Ok(Value::Map(cljrs_value::MapValue::empty())),
1701 };
1702 let aliases = ns.get().aliases.lock().unwrap().clone();
1703 drop(map);
1704 let mut m = cljrs_value::MapValue::empty();
1705 for (alias, full_ns_name) in &aliases {
1706 let sym = Value::symbol(cljrs_value::Symbol::simple(alias.clone()));
1707 let nsmap = env.globals.namespaces.read().unwrap();
1708 if let Some(target_ns) = nsmap.get(full_ns_name.as_ref()) {
1709 m = m.assoc(sym, Value::Namespace(target_ns.clone()));
1710 }
1711 }
1712 Ok(Value::Map(m))
1713}
1714
1715fn handle_remove_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1717 if arg_forms.is_empty() {
1718 return Err(EvalError::Arity {
1719 name: "remove-ns".into(),
1720 expected: "1".into(),
1721 got: 0,
1722 });
1723 }
1724 let arg = eval(&arg_forms[0], env)?;
1725 let name = ns_name_from_val(&arg)?;
1726 env.globals
1727 .namespaces
1728 .write()
1729 .unwrap()
1730 .remove(name.as_str());
1731 Ok(Value::Nil)
1732}
1733
1734fn handle_alter_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1736 if arg_forms.len() < 2 {
1737 return Err(EvalError::Arity {
1738 name: "alter-meta!".into(),
1739 expected: "2+".into(),
1740 got: arg_forms.len(),
1741 });
1742 }
1743 let obj = eval(&arg_forms[0], env)?;
1744 let f = eval(&arg_forms[1], env)?;
1745 let extra: Vec<Value> = arg_forms[2..]
1746 .iter()
1747 .map(|form| eval(form, env))
1748 .collect::<EvalResult<_>>()?;
1749
1750 let current_meta = match &obj {
1751 Value::Var(vp) => vp
1752 .get()
1753 .get_meta()
1754 .unwrap_or(Value::Map(cljrs_value::MapValue::empty())),
1755 _ => Value::Map(cljrs_value::MapValue::empty()),
1756 };
1757 let mut call_args = vec![current_meta];
1758 call_args.extend(extra);
1759 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1760 if let Value::Var(vp) = &obj {
1761 vp.get().set_meta(new_meta.clone());
1762 }
1763 Ok(new_meta)
1764}
1765
1766fn handle_ns_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1768 if arg_forms.len() < 2 {
1769 return Err(EvalError::Arity {
1770 name: "ns-resolve".into(),
1771 expected: "2".into(),
1772 got: arg_forms.len(),
1773 });
1774 }
1775 let ns_arg = eval(&arg_forms[0], env)?;
1776 let sym_arg = eval(&arg_forms[1], env)?;
1777 let ns_name = ns_name_from_val(&ns_arg)?;
1778 let sym_name = match &sym_arg {
1779 Value::Symbol(s) => s.get().name.as_ref().to_string(),
1780 Value::Str(s) => s.get().clone(),
1781 other => {
1782 return Err(EvalError::Runtime(format!(
1783 "ns-resolve: second arg must be symbol or string, got {}",
1784 other.type_name()
1785 )));
1786 }
1787 };
1788 match env.globals.lookup_var(&ns_name, &sym_name) {
1789 Some(var_ptr) => Ok(Value::Var(var_ptr)),
1790 None => Ok(Value::Nil),
1791 }
1792}
1793
1794fn resolve_current_ns(env: &Env) -> Arc<str> {
1798 if let Some(var) = env.globals.lookup_var("clojure.core", "*ns*") {
1799 let val = crate::env::dynamics::deref_var(&var);
1800 if let Some(Value::Namespace(ns_ptr)) = val {
1801 return ns_ptr.get().name.clone();
1802 }
1803 }
1804 env.current_ns.clone()
1805}
1806
1807fn handle_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1809 if arg_forms.len() != 1 {
1810 return Err(EvalError::Arity {
1811 name: "resolve".into(),
1812 expected: "1".into(),
1813 got: arg_forms.len(),
1814 });
1815 }
1816 let resolve_ns = resolve_current_ns(env);
1817 let sym_arg = eval(&arg_forms[0], env)?;
1818 let sym_name = match &sym_arg {
1819 Value::Symbol(s) => {
1820 let sym = s.get();
1821 if let Some(ns) = &sym.namespace {
1823 let full_ns = env
1824 .globals
1825 .resolve_alias(&resolve_ns, ns.as_ref())
1826 .unwrap_or_else(|| ns.clone());
1827 return Ok(
1828 match env.globals.lookup_var_in_ns(&full_ns, sym.name.as_ref()) {
1829 Some(var_ptr) => Value::Var(var_ptr),
1830 None => Value::Nil,
1831 },
1832 );
1833 }
1834 sym.name.as_ref().to_string()
1835 }
1836 Value::Str(s) => s.get().clone(),
1837 other => {
1838 return Err(EvalError::Runtime(format!(
1839 "resolve: arg must be symbol or string, got {}",
1840 other.type_name()
1841 )));
1842 }
1843 };
1844 Ok(match env.globals.lookup_var_in_ns(&resolve_ns, &sym_name) {
1845 Some(var_ptr) => Value::Var(var_ptr),
1846 None => Value::Nil,
1847 })
1848}
1849
1850fn handle_intern(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1851 if arg_forms.len() < 2 || arg_forms.len() > 3 {
1852 return Err(EvalError::Runtime("intern expects 2 or 3 arguments".into()));
1853 }
1854 let ns_val = eval(&arg_forms[0], env)?;
1855 let ns_name: Arc<str> = match &ns_val {
1856 Value::Symbol(s) => s.get().name.clone(),
1857 Value::Namespace(ns) => ns.get().name.clone(),
1858 other => {
1859 return Err(EvalError::Runtime(format!(
1860 "intern: first arg must be namespace or symbol, got {}",
1861 other.type_name()
1862 )));
1863 }
1864 };
1865 let var_name: Arc<str> = match eval(&arg_forms[1], env)? {
1866 Value::Symbol(s) => s.get().name.clone(),
1867 other => {
1868 return Err(EvalError::Runtime(format!(
1869 "intern: second arg must be symbol, got {}",
1870 other.type_name()
1871 )));
1872 }
1873 };
1874 let ns = {
1876 let map = env.globals.namespaces.read().unwrap();
1877 map.get(ns_name.as_ref()).cloned()
1878 };
1879 let ns = ns.ok_or_else(|| EvalError::Runtime(format!("No namespace: {ns_name} found")))?;
1880 let var = if arg_forms.len() == 3 {
1881 #[cfg(feature = "no-gc")]
1884 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1885 let val = eval(&arg_forms[2], env)?;
1886 let mut interns = ns.get().interns.lock().unwrap();
1887 if let Some(var) = interns.get(&var_name) {
1888 var.get().bind(val);
1889 var.clone()
1890 } else {
1891 let var =
1892 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
1893 var.get().bind(val);
1894 interns.insert(var_name, var.clone());
1895 var
1896 }
1897 } else {
1898 let mut interns = ns.get().interns.lock().unwrap();
1899 if let Some(var) = interns.get(&var_name) {
1900 var.clone()
1901 } else {
1902 let var =
1903 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
1904 interns.insert(var_name, var.clone());
1905 var
1906 }
1907 };
1908 Ok(Value::Var(var))
1909}
1910
1911fn handle_bound_fn_star(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1916 if arg_forms.len() != 1 {
1917 return Err(EvalError::Arity {
1918 name: "bound-fn*".into(),
1919 expected: "1".into(),
1920 got: arg_forms.len(),
1921 });
1922 }
1923 let f = eval(&arg_forms[0], env)?;
1924 let frames = crate::env::dynamics::capture_current();
1926 let mut merged = std::collections::HashMap::new();
1927 for frame in &frames {
1928 merged.extend(frame.iter().map(|(k, v)| (*k, v.clone())));
1929 }
1930 Ok(Value::BoundFn(cljrs_gc::GcPtr::new(cljrs_value::BoundFn {
1931 wrapped: f,
1932 captured_bindings: merged,
1933 })))
1934}