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: &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 && is_method_sugar(s)
230 {
231 return eval_method_call(&s[1..], arg_forms, env);
232 }
233
234 let callee = eval(func_form, env)?;
236
237 let _callee_root = crate::env::gc_roots::root_value(&callee);
239
240 if let Value::Macro(mfn) = &callee {
242 let expanded = macro_apply(mfn.get(), func_form, arg_forms, env)?;
243 return eval(&expanded, env);
244 }
245
246 if let Value::NativeFunction(nf) = &callee {
248 crate::env::policy::check_native(&nf.get().name)?;
249 match nf.get().name.as_ref() {
250 "apply" => return handle_apply_call(arg_forms, env),
251 "atom" => return handle_atom_call(arg_forms, env),
252 "reset!" => return handle_reset_bang(arg_forms, env),
253 "swap!" => return handle_swap_call(arg_forms, env),
254 "volatile!" => return handle_volatile(arg_forms, env),
255 "vreset!" => return handle_vreset(arg_forms, env),
256 "agent" => return handle_agent_call(arg_forms, env),
257 "make-lazy-seq" => return handle_make_lazy_seq(arg_forms, env),
258 "make-delay" => return handle_make_delay(arg_forms, env),
259 "vswap!" => return handle_vswap(arg_forms, env),
260 "send" | "send-off" => return handle_send(arg_forms, env),
261 "with-bindings*" => return handle_with_bindings(arg_forms, env),
262 "alter-var-root" => return handle_alter_var_root(arg_forms, env),
263 "vary-meta" => return handle_vary_meta(arg_forms, env),
264 "eval" => return handle_eval(arg_forms, env),
265 "find-ns" | "the-ns" => return handle_find_ns(arg_forms, env),
266 "ns-interns" | "ns-publics" => return handle_ns_interns(arg_forms, env),
267 "ns-refers" => return handle_ns_refers(arg_forms, env),
268 "ns-map" => return handle_ns_map(arg_forms, env),
269 "all-ns" => return handle_all_ns(arg_forms, env),
270 "create-ns" => return handle_create_ns(arg_forms, env),
271 "ns-aliases" => return handle_ns_aliases(arg_forms, env),
272 "remove-ns" => return handle_remove_ns(arg_forms, env),
273 "alter-meta!" => return handle_alter_meta(arg_forms, env),
274 "ns-resolve" => return handle_ns_resolve(arg_forms, env),
275 "resolve" => return handle_resolve(arg_forms, env),
276 "intern" => return handle_intern(arg_forms, env),
277 "bound-fn*" => return handle_bound_fn_star(arg_forms, env),
278 _ => {}
279 }
280 }
281
282 let mut args: Vec<Value> = Vec::with_capacity(arg_forms.len());
285 for f in arg_forms {
286 let _args_root = crate::env::gc_roots::root_values(&args);
288 args.push(eval(f, env)?);
289 }
290
291 if let Value::Fn(f) = &callee {
296 if let Some(fut) = crate::env::apply::dispatch_if_async(&callee, &args, env) {
299 return Ok(fut);
300 }
301 let _args_root = crate::env::gc_roots::root_values(&args);
302 crate::env::gc_roots::gc_safepoint(env);
303 return env.call_cljrs_fn(f.get(), &args);
304 }
305
306 crate::env::apply::apply_value(&callee, args, env)
307}
308
309fn eval_method_call(method: &str, arg_forms: &[Form], env: &mut Env) -> EvalResult {
319 if arg_forms.is_empty() {
320 return Err(EvalError::Runtime(format!(
321 ".{method} requires a target object"
322 )));
323 }
324 let target = eval(&arg_forms[0], env)?;
325 let args: Vec<Value> = arg_forms[1..]
326 .iter()
327 .map(|f| eval(f, env))
328 .collect::<EvalResult<_>>()?;
329
330 dispatch_method(method, &target, &args)
331}
332
333pub use cljrs_ir::lower::is_method_sugar;
338
339pub fn dispatch_method(method: &str, target: &Value, args: &[Value]) -> EvalResult {
345 match target {
346 Value::Str(s) => dispatch_string_method(method, s.get(), args),
347 Value::Vector(v) => dispatch_vector_method(method, v, args),
348 Value::List(_) | Value::Cons(_) | Value::LazySeq(_) => {
349 dispatch_seq_method(method, target, args)
350 }
351 Value::TypeInstance(ti) => {
352 if let Some(field) = method.strip_prefix('-') {
356 let key = Value::keyword(cljrs_value::Keyword::simple(field));
357 let inst = ti.get();
358 if let Some(atom) = &inst.mutable
361 && let Value::Map(m) = atom.get().deref()
362 && let Some(v) = m.get(&key)
363 {
364 return Ok(v);
365 }
366 Ok(inst.fields.get(&key).unwrap_or(Value::Nil))
367 } else {
368 Err(EvalError::Runtime(format!(
369 ".{method} not supported on {} (only .-field access is)",
370 target.type_name()
371 )))
372 }
373 }
374 _ => Err(EvalError::Runtime(format!(
375 ".{method} not supported on type {}",
376 target.type_name()
377 ))),
378 }
379}
380
381fn dispatch_string_method(method: &str, s: &str, args: &[Value]) -> EvalResult {
382 match method {
383 "indexOf" => {
384 let needle = match args.first() {
385 Some(Value::Str(s)) => s.get().to_string(),
386 Some(Value::Char(c)) => c.to_string(),
387 Some(v) => {
388 return Err(EvalError::Runtime(format!(
389 ".indexOf expects string or char argument, got {}",
390 v.type_name()
391 )));
392 }
393 None => return Err(EvalError::Runtime(".indexOf requires an argument".into())),
394 };
395 match s.find(&needle) {
396 Some(pos) => Ok(Value::Long(pos as i64)),
397 None => Ok(Value::Long(-1)),
398 }
399 }
400 "lastIndexOf" => {
401 let needle = match args.first() {
402 Some(Value::Str(s)) => s.get().to_string(),
403 Some(Value::Char(c)) => c.to_string(),
404 _ => {
405 return Err(EvalError::Runtime(
406 ".lastIndexOf requires a string or char argument".into(),
407 ));
408 }
409 };
410 match s.rfind(&needle) {
411 Some(pos) => Ok(Value::Long(pos as i64)),
412 None => Ok(Value::Long(-1)),
413 }
414 }
415 "startsWith" => {
416 let prefix = require_str_arg(args, ".startsWith")?;
417 Ok(Value::Bool(s.starts_with(&prefix)))
418 }
419 "endsWith" => {
420 let suffix = require_str_arg(args, ".endsWith")?;
421 Ok(Value::Bool(s.ends_with(&suffix)))
422 }
423 "contains" => {
424 let sub = require_str_arg(args, ".contains")?;
425 Ok(Value::Bool(s.contains(&sub)))
426 }
427 "length" => Ok(Value::Long(s.len() as i64)),
428 "isEmpty" => Ok(Value::Bool(s.is_empty())),
429 "charAt" => {
430 let idx = require_long_arg(args, ".charAt")? as usize;
431 s.chars()
432 .nth(idx)
433 .map(Value::Char)
434 .ok_or_else(|| EvalError::Runtime(format!(".charAt index {idx} out of bounds")))
435 }
436 "substring" => {
437 let start = require_long_arg(args, ".substring")? as usize;
438 let end = args
439 .get(1)
440 .map(|v| match v {
441 Value::Long(n) => Ok(*n as usize),
442 _ => Err(EvalError::Runtime(
443 ".substring end must be an integer".into(),
444 )),
445 })
446 .transpose()?;
447 let result = match end {
448 Some(e) => &s[start..e.min(s.len())],
449 None => &s[start..],
450 };
451 Ok(Value::Str(GcPtr::new(result.to_string())))
452 }
453 "toUpperCase" => Ok(Value::Str(GcPtr::new(s.to_uppercase()))),
454 "toLowerCase" => Ok(Value::Str(GcPtr::new(s.to_lowercase()))),
455 "trim" => Ok(Value::Str(GcPtr::new(s.trim().to_string()))),
456 "replace" => {
457 let from = require_str_arg(args, ".replace")?;
458 let to = match args.get(1) {
459 Some(Value::Str(s)) => s.get().to_string(),
460 Some(Value::Char(c)) => c.to_string(),
461 _ => {
462 return Err(EvalError::Runtime(
463 ".replace requires two string arguments".into(),
464 ));
465 }
466 };
467 Ok(Value::Str(GcPtr::new(s.replace(&from, &to))))
468 }
469 "split" => {
470 let sep = require_str_arg(args, ".split")?;
471 let parts: Vec<Value> = s
472 .split(&sep)
473 .map(|p| Value::Str(GcPtr::new(p.to_string())))
474 .collect();
475 Ok(Value::Vector(GcPtr::new(
476 cljrs_value::PersistentVector::from_iter(parts),
477 )))
478 }
479 _ => Err(EvalError::Runtime(format!(
480 ".{method} not supported on String"
481 ))),
482 }
483}
484
485fn dispatch_vector_method(
486 method: &str,
487 v: &GcPtr<cljrs_value::PersistentVector>,
488 args: &[Value],
489) -> EvalResult {
490 match method {
491 "indexOf" => {
492 let needle = args
493 .first()
494 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
495 for (i, item) in v.get().iter().enumerate() {
496 if item == needle {
497 return Ok(Value::Long(i as i64));
498 }
499 }
500 Ok(Value::Long(-1))
501 }
502 "size" | "count" => Ok(Value::Long(v.get().count() as i64)),
503 _ => Err(EvalError::Runtime(format!(
504 ".{method} not supported on Vector"
505 ))),
506 }
507}
508
509fn dispatch_seq_method(method: &str, target: &Value, args: &[Value]) -> EvalResult {
510 match method {
511 "indexOf" => {
512 let needle = args
513 .first()
514 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
515 let items = crate::interp::destructure::value_to_seq_vec(target);
516 for (i, item) in items.iter().enumerate() {
517 if item == needle {
518 return Ok(Value::Long(i as i64));
519 }
520 }
521 Ok(Value::Long(-1))
522 }
523 _ => Err(EvalError::Runtime(format!(
524 ".{method} not supported on {}",
525 target.type_name()
526 ))),
527 }
528}
529
530fn require_str_arg(args: &[Value], method: &str) -> Result<String, EvalError> {
531 match args.first() {
532 Some(Value::Str(s)) => Ok(s.get().to_string()),
533 Some(Value::Char(c)) => Ok(c.to_string()),
534 _ => Err(EvalError::Runtime(format!(
535 "{method} requires a string argument"
536 ))),
537 }
538}
539
540fn require_long_arg(args: &[Value], method: &str) -> Result<i64, EvalError> {
541 match args.first() {
542 Some(Value::Long(n)) => Ok(*n),
543 _ => Err(EvalError::Runtime(format!(
544 "{method} requires an integer argument"
545 ))),
546 }
547}
548
549pub fn resolve_type_tag(sym: &str) -> Arc<str> {
552 Arc::from(sym)
553}
554
555pub fn call_cljrs_fn(f: &CljxFn, args: &[Value], caller_env: &mut Env) -> EvalResult {
557 let arity = select_arity(f, args.len())?;
558
559 let _caller_root = crate::env::gc_roots::push_env_root(caller_env);
562
563 let mut env = Env::with_closure(caller_env.globals.clone(), &f.defining_ns, f);
566
567 let mut current_args = Vec::from(args);
568 loop {
569 let _args_root = crate::env::gc_roots::root_values(¤t_args);
572
573 crate::env::gc_roots::gc_safepoint(&env);
575
576 env.push_frame();
577
578 #[cfg(not(feature = "no-gc"))]
588 let _call_frame = cljrs_gc::push_alloc_frame();
589
590 if let Some(ref name) = f.name {
599 let self_val = if let Some(ref p) = f.self_ptr {
600 Value::Fn(p.clone())
601 } else {
602 Value::Fn(GcPtr::new(f.clone()))
603 };
604 env.bind(name.clone(), self_val);
605 }
606
607 bind_fn_params(arity, ¤t_args, &mut env)?;
609
610 #[cfg(not(feature = "no-gc"))]
615 let result = eval_body_recur_fn(&arity.body, &mut env);
616 #[cfg(feature = "no-gc")]
617 let result = {
618 let mut scratch = cljrs_gc::alloc_ctx::ScratchGuard::new();
619 eval_body_with_scratch(&arity.body, &mut scratch, &mut env)
621 };
622 env.pop_frame();
623 match result {
627 Ok(v) => return Ok(v),
628 Err(EvalError::Recur(new_args)) => {
629 if arity.rest_param.is_some() {
634 let n = arity.params.len();
635 if new_args.len() == n + 1 {
636 let mut flat = new_args[..n].to_vec();
637 let rest_val = &new_args[n];
639 match rest_val {
640 Value::Nil => {} _ => {
642 let rest_items = value_to_seq_vec(rest_val);
643 flat.extend(rest_items);
644 }
645 }
646 current_args = flat;
647 } else {
648 current_args = new_args;
649 }
650 } else {
651 current_args = new_args;
652 }
653 }
654 Err(e) => return Err(e),
655 }
656 }
657}
658
659pub fn bind_fn_params(arity: &CljxFnArity, args: &[Value], env: &mut Env) -> EvalResult<()> {
662 bind_fn_params_impl(arity, args, env, true)
663}
664
665pub fn bind_fn_params_positional(
676 arity: &CljxFnArity,
677 args: &[Value],
678 env: &mut Env,
679) -> EvalResult<()> {
680 bind_fn_params_impl(arity, args, env, false)
681}
682
683fn bind_fn_params_impl(
684 arity: &CljxFnArity,
685 args: &[Value],
686 env: &mut Env,
687 destructure: bool,
688) -> EvalResult<()> {
689 let n = arity.params.len();
690 for (i, name) in arity.params.iter().enumerate() {
692 let val = args.get(i).cloned().unwrap_or(Value::Nil);
693 env.bind(name.clone(), val);
694 }
695 if let Some(ref rest) = arity.rest_param {
697 let rest_items = args[n..].to_vec();
698 let rest_val = if rest_items.is_empty() {
699 Value::Nil
700 } else {
701 Value::List(GcPtr::new(PersistentList::from_iter(rest_items)))
702 };
703 env.bind(rest.clone(), rest_val.clone());
704 if destructure && let Some(ref pattern) = arity.destructure_rest {
706 let destructure_val = if pattern.is_kwargs_rest_pattern() {
710 let items = value_to_seq_vec(&rest_val);
711 Value::from_kwargs_rest(items).map_err(value_error_to_eval_error)?
712 } else {
713 rest_val
714 };
715 crate::interp::destructure::bind_pattern(pattern, destructure_val, env)?;
716 }
717 }
718 if destructure {
720 for (idx, pattern) in &arity.destructure_params {
721 let val = args.get(*idx).cloned().unwrap_or(Value::Nil);
722 crate::interp::destructure::bind_pattern(pattern, val, env)?;
723 }
724 }
725 Ok(())
726}
727
728#[cfg(not(feature = "no-gc"))]
730fn eval_body_recur_fn(body: &[cljrs_reader::Form], env: &mut Env) -> EvalResult {
731 let mut result = Value::Nil;
732 for form in body {
733 result = eval(form, env)?;
734 }
735 Ok(result)
736}
737
738#[cfg(feature = "no-gc")]
742fn eval_body_with_scratch(
743 body: &[cljrs_reader::Form],
744 scratch: &mut cljrs_gc::alloc_ctx::ScratchGuard,
745 env: &mut Env,
746) -> EvalResult {
747 if body.is_empty() {
748 scratch.pop_for_return();
749 return Ok(Value::Nil);
750 }
751 for form in &body[..body.len() - 1] {
753 eval(form, env)?;
754 }
755 scratch.pop_for_return();
757 eval(&body[body.len() - 1], env)
758}
759
760pub fn select_arity(f: &CljxFn, argc: usize) -> EvalResult<&CljxFnArity> {
762 let name = f.name.as_deref().unwrap_or("fn");
763 for arity in &f.arities {
765 if arity.rest_param.is_none() && arity.params.len() == argc {
766 return Ok(arity);
767 }
768 }
769 for arity in &f.arities {
771 if arity.rest_param.is_some() && argc >= arity.params.len() {
772 return Ok(arity);
773 }
774 }
775 let implicit = if f.is_macro { IMPLICIT_MACRO_ARGS } else { 0 };
780 let expected: Vec<String> = f
781 .arities
782 .iter()
783 .map(|a| {
784 let fixed = a.params.len().saturating_sub(implicit);
785 if a.rest_param.is_some() {
786 format!("{fixed}+")
787 } else {
788 fixed.to_string()
789 }
790 })
791 .collect();
792 Err(EvalError::Arity {
793 name: name.to_string(),
794 expected: expected.join(" or "),
795 got: argc.saturating_sub(implicit),
796 })
797}
798
799const IMPLICIT_MACRO_ARGS: usize = 2;
801
802fn macro_apply(
809 mfn: &CljxFn,
810 func_form: &Form,
811 arg_forms: &[Form],
812 env: &mut Env,
813) -> EvalResult<Form> {
814 let resolved_args: Vec<Form> = arg_forms
819 .iter()
820 .map(|f| crate::builtins::form::resolve_auto_forms(f, env))
821 .collect::<EvalResult<Vec<Form>>>()?;
822
823 let form_val = {
825 let mut items = vec![form_to_value(func_form)?];
826 for f in &resolved_args {
827 items.push(form_to_value(f)?);
828 }
829 Value::List(GcPtr::new(PersistentList::from_iter(items)))
830 };
831
832 let env_val = {
834 let (names, vals) = env.all_local_bindings();
835 let mut m = MapValue::empty();
836 for (name, val) in names.iter().zip(vals.iter()) {
837 m = m.assoc(Value::symbol(Symbol::simple(name.as_ref())), val.clone());
838 }
839 Value::Map(m)
840 };
841
842 let mut args = vec![form_val, env_val];
844 for f in &resolved_args {
845 args.push(form_to_value(f)?);
846 }
847
848 let expanded_val = call_cljrs_fn(mfn, args.as_ref(), env)?;
849 let dummy_span = cljrs_types::span::Span::new(Arc::new("<macro>".to_string()), 0, 0, 1, 1);
850 crate::interp::macros::value_to_form(&expanded_val, dummy_span)
851}
852
853fn handle_apply_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
855 let mut evaled: Vec<Value> = Vec::with_capacity(arg_forms.len());
856 for f in arg_forms {
857 let _root = crate::env::gc_roots::root_values(&evaled);
858 evaled.push(eval(f, env)?);
859 }
860
861 if evaled.len() < 2 {
862 return Err(EvalError::Arity {
863 name: "apply".into(),
864 expected: "2+".into(),
865 got: evaled.len(),
866 });
867 }
868
869 let f = evaled.remove(0);
870 let last = evaled.pop().unwrap();
871 let _f_root = crate::env::gc_roots::root_value(&f);
873 let _last_root = crate::env::gc_roots::root_value(&last);
874 let _evaled_root = crate::env::gc_roots::root_values(&evaled);
875 let spread = value_to_seq_vec(&last);
877 evaled.extend(spread);
878 crate::env::apply::apply_value(&f, evaled, env)
879}
880
881pub fn handle_make_lazy_seq(arg_forms: &[Form], env: &mut Env) -> EvalResult {
883 if arg_forms.len() != 1 {
884 return Err(EvalError::Arity {
885 name: "make-lazy-seq".into(),
886 expected: "1".into(),
887 got: arg_forms.len(),
888 });
889 }
890 let f_val = eval(&arg_forms[0], env)?;
891 let f = match f_val {
892 Value::Fn(f) => f.get().clone(),
893 other => {
894 return Err(EvalError::Runtime(format!(
895 "make-lazy-seq requires a fn, got {}",
896 other.type_name()
897 )));
898 }
899 };
900 let thunk = ClosureThunk {
901 f,
902 globals: env.globals.clone(),
903 ns: env.current_ns.clone(),
904 };
905 Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))))
906}
907
908fn handle_make_delay(arg_forms: &[Form], env: &mut Env) -> EvalResult {
910 if arg_forms.len() != 1 {
911 return Err(EvalError::Arity {
912 name: "make-delay".into(),
913 expected: "1".into(),
914 got: arg_forms.len(),
915 });
916 }
917 let f_val = eval(&arg_forms[0], env)?;
918 let f = match f_val {
919 Value::Fn(f) => f.get().clone(),
920 other => {
921 return Err(EvalError::Runtime(format!(
922 "make-delay requires a fn, got {}",
923 other.type_name()
924 )));
925 }
926 };
927 let thunk = ClosureThunk {
928 f,
929 globals: env.globals.clone(),
930 ns: env.current_ns.clone(),
931 };
932 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
933}
934
935fn handle_vswap(arg_forms: &[Form], env: &mut Env) -> EvalResult {
937 if arg_forms.len() < 2 {
938 return Err(EvalError::Arity {
939 name: "vswap!".into(),
940 expected: "2+".into(),
941 got: arg_forms.len(),
942 });
943 }
944 let vol_val = eval(&arg_forms[0], env)?;
945 let f = eval(&arg_forms[1], env)?;
946 let extra: Vec<Value> = arg_forms[2..]
947 .iter()
948 .map(|a| eval(a, env))
949 .collect::<EvalResult<_>>()?;
950
951 match vol_val {
952 Value::Volatile(v) => {
953 let cur = v.get().deref();
954 let mut call_args = vec![cur];
955 call_args.extend(extra);
956 #[cfg(feature = "no-gc")]
959 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
960 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
961 v.get().reset(new_val.clone());
962 Ok(new_val)
963 }
964 other => Err(EvalError::Runtime(format!(
965 "vswap!: expected volatile, got {}",
966 other.type_name()
967 ))),
968 }
969}
970
971fn handle_volatile(arg_forms: &[Form], env: &mut Env) -> EvalResult {
975 if arg_forms.is_empty() {
976 return Err(EvalError::Arity {
977 name: "volatile!".into(),
978 expected: "1".into(),
979 got: 0,
980 });
981 }
982 #[cfg(feature = "no-gc")]
985 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
986 let initial = eval(&arg_forms[0], env)?;
987 Ok(Value::Volatile(GcPtr::new(Volatile::new(initial))))
988}
989
990fn handle_vreset(arg_forms: &[Form], env: &mut Env) -> EvalResult {
994 if arg_forms.len() < 2 {
995 return Err(EvalError::Arity {
996 name: "vreset!".into(),
997 expected: "2".into(),
998 got: arg_forms.len(),
999 });
1000 }
1001 let vol_val = eval(&arg_forms[0], env)?;
1002 #[cfg(feature = "no-gc")]
1005 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1006 let new_val = eval(&arg_forms[1], env)?;
1007 match &vol_val {
1008 Value::Volatile(v) => {
1009 v.get().reset(new_val.clone());
1010 Ok(new_val)
1011 }
1012 other => Err(EvalError::Runtime(format!(
1013 "vreset!: expected volatile, got {}",
1014 other.type_name()
1015 ))),
1016 }
1017}
1018
1019fn handle_agent_call(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
1023 Err(EvalError::Runtime("agent is not yet implemented".into()))
1024}
1025
1026fn handle_send(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
1028 Err(EvalError::Runtime(
1029 "send/send-off: agents are not yet implemented".into(),
1030 ))
1031}
1032
1033fn handle_atom_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1037 if arg_forms.is_empty() {
1038 return Err(EvalError::Arity {
1039 name: "atom".into(),
1040 expected: "1+".into(),
1041 got: 0,
1042 });
1043 }
1044 #[cfg(feature = "no-gc")]
1047 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1048 let initial = eval(&arg_forms[0], env)?;
1049
1050 let options: Vec<Value> = arg_forms[1..]
1052 .iter()
1053 .map(|f| eval(f, env))
1054 .collect::<EvalResult<_>>()?;
1055
1056 let mut meta_opt: Option<Value> = None;
1057 let mut validator_opt: Option<Value> = None;
1058 let mut i = 0;
1059 while i + 1 < options.len() {
1060 match &options[i] {
1061 Value::Keyword(k) if k.get().name.as_ref() == "meta" => {
1062 meta_opt = Some(options[i + 1].clone());
1063 i += 2;
1064 }
1065 Value::Keyword(k) if k.get().name.as_ref() == "validator" => {
1066 let vf = options[i + 1].clone();
1067 validator_opt = if vf == Value::Nil { None } else { Some(vf) };
1068 i += 2;
1069 }
1070 _ => {
1071 i += 2;
1072 }
1073 }
1074 }
1075
1076 if let Some(ref m) = meta_opt
1078 && !matches!(m, Value::Nil | Value::Map(_))
1079 {
1080 return Err(EvalError::Thrown(Value::string(
1081 "Atom metadata must be a map or nil".to_string(),
1082 )));
1083 }
1084
1085 if let Some(ref vf) = validator_opt {
1087 let result = crate::env::apply::apply_value(vf, vec![initial.clone()], env)?;
1088 if result == Value::Nil || result == Value::Bool(false) {
1089 return Err(EvalError::Thrown(Value::string(
1090 "Invalid initial value for atom".to_string(),
1091 )));
1092 }
1093 }
1094
1095 let atom = GcPtr::new(Atom::new(initial));
1096 if let Some(m) = meta_opt {
1097 atom.get()
1098 .set_meta(if m == Value::Nil { None } else { Some(m) });
1099 }
1100 if let Some(vf) = validator_opt {
1101 atom.get().set_validator(Some(vf));
1102 }
1103 Ok(Value::Atom(atom))
1104}
1105
1106fn shared_atom_reset(sa: &Arc<cljrs_value::SharedAtom>, new_val: Value) -> EvalResult {
1118 let promoted = cljrs_value::promote(&new_val).map_err(|e| EvalError::Runtime(e.to_string()))?;
1119 sa.reset(promoted);
1120 Ok(new_val)
1121}
1122
1123fn shared_atom_swap(
1128 sa: &Arc<cljrs_value::SharedAtom>,
1129 f: &Value,
1130 extra: Vec<Value>,
1131 env: &mut Env,
1132) -> EvalResult {
1133 loop {
1134 let cur = sa.deref_val();
1135 let old_val = cljrs_value::demote(&cur);
1136 let mut call_args = Vec::with_capacity(1 + extra.len());
1137 call_args.push(old_val);
1138 call_args.extend(extra.iter().cloned());
1139 let new_val = crate::env::apply::apply_value(f, call_args, env)?;
1140 let promoted =
1141 cljrs_value::promote(&new_val).map_err(|e| EvalError::Runtime(e.to_string()))?;
1142 if sa.compare_and_set(&cur, promoted) {
1143 return Ok(new_val);
1144 }
1145 }
1147}
1148
1149fn handle_reset_bang(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1152 if arg_forms.len() < 2 {
1153 return Err(EvalError::Arity {
1154 name: "reset!".into(),
1155 expected: "2".into(),
1156 got: arg_forms.len(),
1157 });
1158 }
1159 let atom_val = eval(&arg_forms[0], env)?;
1160 #[cfg(feature = "no-gc")]
1163 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1164 let new_val = eval(&arg_forms[1], env)?;
1165
1166 let atom = match &atom_val {
1167 Value::Atom(a) => a.clone(),
1168 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1169 v => {
1170 return Err(EvalError::Runtime(format!(
1171 "reset! requires an atom, got {}",
1172 v.type_name()
1173 )));
1174 }
1175 };
1176
1177 validate_atom_value(&atom, &new_val, env)?;
1178 let old_val = atom.get().deref();
1179 atom.get().reset(new_val.clone());
1180 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1181 check_watch_error()?;
1182 Ok(new_val)
1183}
1184
1185fn validate_atom_value(atom: &GcPtr<Atom>, new_val: &Value, env: &mut Env) -> EvalResult<()> {
1187 if let Some(vf) = atom.get().get_validator() {
1188 let result = crate::env::apply::apply_value(&vf, vec![new_val.clone()], env)?;
1189 if result == Value::Nil || result == Value::Bool(false) {
1190 return Err(EvalError::Thrown(Value::string(
1191 "Invalid value for atom".to_string(),
1192 )));
1193 }
1194 }
1195 Ok(())
1196}
1197
1198fn handle_swap_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1201 let mut evaled: Vec<Value> = arg_forms
1202 .iter()
1203 .map(|f| eval(f, env))
1204 .collect::<EvalResult<_>>()?;
1205
1206 if evaled.len() < 2 {
1207 return Err(EvalError::Arity {
1208 name: "swap!".into(),
1209 expected: "2+".into(),
1210 got: evaled.len(),
1211 });
1212 }
1213
1214 let atom_val = evaled.remove(0);
1215 let f = evaled.remove(0);
1216
1217 let atom = match &atom_val {
1218 Value::Atom(a) => a.clone(),
1219 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, evaled, env),
1220 v => {
1221 return Err(EvalError::Runtime(format!(
1222 "swap! requires an atom, got {}",
1223 v.type_name()
1224 )));
1225 }
1226 };
1227
1228 let old_val = atom.get().deref();
1229 let mut args = vec![old_val.clone()];
1230 args.extend(evaled);
1231 #[cfg(feature = "no-gc")]
1234 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1235 let new_val = crate::env::apply::apply_value(&f, args, env)?;
1236 validate_atom_value(&atom, &new_val, env)?;
1237 atom.get().reset(new_val.clone());
1238 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1239 check_watch_error()?;
1240 Ok(new_val)
1241}
1242
1243fn handle_with_bindings(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1248 if arg_forms.len() < 2 {
1249 return Err(EvalError::Arity {
1250 name: "with-bindings*".into(),
1251 expected: "2".into(),
1252 got: arg_forms.len(),
1253 });
1254 }
1255 let map_val = eval(&arg_forms[0], env)?;
1256 let func_val = eval(&arg_forms[1], env)?;
1257
1258 let mut frame: HashMap<usize, Value> = HashMap::new();
1259 if let Value::Map(m) = &map_val {
1260 m.for_each(|k, v| {
1261 if let Value::Var(vp) = k {
1262 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1263 }
1264 });
1266 } else {
1267 return Err(EvalError::Runtime(
1268 "with-bindings*: first arg must be a map".into(),
1269 ));
1270 }
1271
1272 let _guard = crate::env::dynamics::push_frame(frame);
1273 crate::env::apply::apply_value(&func_val, vec![], env)
1274}
1275
1276fn handle_alter_var_root(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1280 if arg_forms.len() < 2 {
1281 return Err(EvalError::Arity {
1282 name: "alter-var-root".into(),
1283 expected: "2+".into(),
1284 got: arg_forms.len(),
1285 });
1286 }
1287 let var_val = eval(&arg_forms[0], env)?;
1288 let f = eval(&arg_forms[1], env)?;
1289 let extra: Vec<Value> = arg_forms[2..]
1290 .iter()
1291 .map(|form| eval(form, env))
1292 .collect::<EvalResult<_>>()?;
1293
1294 let vp = match &var_val {
1295 Value::Var(vp) => vp.clone(),
1296 v => {
1297 return Err(EvalError::Runtime(format!(
1298 "alter-var-root: expected var, got {}",
1299 v.type_name()
1300 )));
1301 }
1302 };
1303 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1304 let mut call_args = vec![old_val.clone()];
1305 call_args.extend(extra);
1306 #[cfg(feature = "no-gc")]
1309 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1310 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1311 vp.get().bind(new_val.clone());
1312 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1313 check_watch_error()?;
1314 Ok(new_val)
1315}
1316
1317fn handle_vary_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1321 if arg_forms.len() < 2 {
1322 return Err(EvalError::Arity {
1323 name: "vary-meta".into(),
1324 expected: "2+".into(),
1325 got: arg_forms.len(),
1326 });
1327 }
1328 let obj = eval(&arg_forms[0], env)?;
1329 let f = eval(&arg_forms[1], env)?;
1330 let extra: Vec<Value> = arg_forms[2..]
1331 .iter()
1332 .map(|form| eval(form, env))
1333 .collect::<EvalResult<_>>()?;
1334
1335 let current_meta = match &obj {
1336 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1337 _ => Value::Nil,
1338 };
1339 let mut call_args = vec![current_meta];
1340 call_args.extend(extra);
1341 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1342 if let Value::Var(vp) = &obj {
1343 vp.get().set_meta(new_meta);
1344 }
1345 Ok(obj)
1346}
1347
1348fn handle_eval(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1352 let [arg] = arg_forms else {
1353 return Err(EvalError::Arity {
1354 name: "eval".into(),
1355 expected: "1".into(),
1356 got: arg_forms.len(),
1357 });
1358 };
1359 let value = eval(arg, env)?;
1360 eval_eval(vec![value], env)
1361}
1362
1363pub fn eval_eval(args: Vec<Value>, env: &mut Env) -> EvalResult {
1368 let [value] = args.as_slice() else {
1369 return Err(EvalError::Arity {
1370 name: "eval".into(),
1371 expected: "1".into(),
1372 got: args.len(),
1373 });
1374 };
1375 let span = cljrs_types::span::Span::new(Arc::new("<eval>".to_string()), 0, 0, 1, 1);
1376 let form = crate::interp::macros::value_to_form(value, span)?;
1377 let mut top = Env::new(env.globals.clone(), &env.current_ns);
1378 eval(&form, &mut top)
1379}
1380
1381pub fn eval_reset_bang(args: Vec<Value>, env: &mut Env) -> EvalResult {
1389 if args.len() < 2 {
1390 return Err(EvalError::Arity {
1391 name: "reset!".into(),
1392 expected: "2".into(),
1393 got: args.len(),
1394 });
1395 }
1396 let atom_val = args[0].clone();
1397 let new_val = args[1].clone();
1398 let atom = match &atom_val {
1399 Value::Atom(a) => a.clone(),
1400 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1401 v => {
1402 return Err(EvalError::Runtime(format!(
1403 "reset! requires an atom, got {}",
1404 v.type_name()
1405 )));
1406 }
1407 };
1408 #[cfg(feature = "no-gc")]
1409 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1410 validate_atom_value(&atom, &new_val, env)?;
1411 let old_val = atom.get().deref();
1412 atom.get().reset(new_val.clone());
1413 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1414 check_watch_error()?;
1415 Ok(new_val)
1416}
1417
1418pub fn eval_swap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1420 if args.len() < 2 {
1421 return Err(EvalError::Arity {
1422 name: "swap!".into(),
1423 expected: "2+".into(),
1424 got: args.len(),
1425 });
1426 }
1427 let atom_val = args.remove(0);
1428 let f = args.remove(0);
1429 let atom = match &atom_val {
1430 Value::Atom(a) => a.clone(),
1431 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, args, env),
1432 v => {
1433 return Err(EvalError::Runtime(format!(
1434 "swap! requires an atom, got {}",
1435 v.type_name()
1436 )));
1437 }
1438 };
1439 let old_val = atom.get().deref();
1440 let mut call_args = vec![old_val.clone()];
1441 call_args.extend(args);
1442 #[cfg(feature = "no-gc")]
1443 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1444 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1445 validate_atom_value(&atom, &new_val, env)?;
1446 atom.get().reset(new_val.clone());
1447 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1448 check_watch_error()?;
1449 Ok(new_val)
1450}
1451
1452pub fn eval_volatile(args: Vec<Value>) -> EvalResult {
1454 if args.is_empty() {
1455 return Err(EvalError::Arity {
1456 name: "volatile!".into(),
1457 expected: "1".into(),
1458 got: 0,
1459 });
1460 }
1461 #[cfg(feature = "no-gc")]
1462 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1463 Ok(Value::Volatile(GcPtr::new(Volatile::new(
1464 args.into_iter().next().unwrap(),
1465 ))))
1466}
1467
1468pub fn eval_vreset_bang(args: Vec<Value>) -> EvalResult {
1470 if args.len() < 2 {
1471 return Err(EvalError::Arity {
1472 name: "vreset!".into(),
1473 expected: "2".into(),
1474 got: args.len(),
1475 });
1476 }
1477 #[cfg(feature = "no-gc")]
1478 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1479 let new_val = args[1].clone();
1480 match &args[0] {
1481 Value::Volatile(v) => {
1482 v.get().reset(new_val.clone());
1483 Ok(new_val)
1484 }
1485 other => Err(EvalError::Runtime(format!(
1486 "vreset!: expected volatile, got {}",
1487 other.type_name()
1488 ))),
1489 }
1490}
1491
1492pub fn eval_vswap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1494 if args.len() < 2 {
1495 return Err(EvalError::Arity {
1496 name: "vswap!".into(),
1497 expected: "2+".into(),
1498 got: args.len(),
1499 });
1500 }
1501 let vol_val = args.remove(0);
1502 let f = args.remove(0);
1503 match vol_val {
1504 Value::Volatile(v) => {
1505 let cur = v.get().deref();
1506 let mut call_args = vec![cur];
1507 call_args.extend(args);
1508 #[cfg(feature = "no-gc")]
1509 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1510 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1511 v.get().reset(new_val.clone());
1512 Ok(new_val)
1513 }
1514 other => Err(EvalError::Runtime(format!(
1515 "vswap!: expected volatile, got {}",
1516 other.type_name()
1517 ))),
1518 }
1519}
1520
1521pub fn make_delay_from_fn(
1526 f_val: &Value,
1527 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
1528 ns: std::sync::Arc<str>,
1529) -> EvalResult {
1530 let f = match f_val {
1531 Value::Fn(f) => f.get().clone(),
1532 other => {
1533 return Err(EvalError::Runtime(format!(
1534 "make-delay requires a fn, got {}",
1535 other.type_name()
1536 )));
1537 }
1538 };
1539 let thunk = ClosureThunk { f, globals, ns };
1540 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
1541}
1542
1543pub fn eval_alter_var_root(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1545 if args.len() < 2 {
1546 return Err(EvalError::Arity {
1547 name: "alter-var-root".into(),
1548 expected: "2+".into(),
1549 got: args.len(),
1550 });
1551 }
1552 let var_val = args.remove(0);
1553 let f = args.remove(0);
1554 let vp = match &var_val {
1555 Value::Var(vp) => vp.clone(),
1556 v => {
1557 return Err(EvalError::Runtime(format!(
1558 "alter-var-root: expected var, got {}",
1559 v.type_name()
1560 )));
1561 }
1562 };
1563 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1564 let mut call_args = vec![old_val.clone()];
1565 call_args.extend(args);
1566 #[cfg(feature = "no-gc")]
1567 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1568 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1569 vp.get().bind(new_val.clone());
1570 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1571 check_watch_error()?;
1572 Ok(new_val)
1573}
1574
1575pub fn eval_vary_meta(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1577 if args.len() < 2 {
1578 return Err(EvalError::Arity {
1579 name: "vary-meta".into(),
1580 expected: "2+".into(),
1581 got: args.len(),
1582 });
1583 }
1584 let obj = args.remove(0);
1585 let f = args.remove(0);
1586 let current_meta = match &obj {
1587 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1588 _ => Value::Nil,
1589 };
1590 let mut call_args = vec![current_meta];
1591 call_args.extend(args);
1592 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1593 if let Value::Var(vp) = &obj {
1594 vp.get().set_meta(new_meta);
1595 }
1596 Ok(obj)
1597}
1598
1599pub fn eval_with_bindings_star(args: Vec<Value>, env: &mut Env) -> EvalResult {
1601 if args.len() < 2 {
1602 return Err(EvalError::Arity {
1603 name: "with-bindings*".into(),
1604 expected: "2".into(),
1605 got: args.len(),
1606 });
1607 }
1608 let mut frame: HashMap<usize, Value> = HashMap::new();
1609 if let Value::Map(m) = &args[0] {
1610 m.for_each(|k, v| {
1611 if let Value::Var(vp) = k {
1612 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1613 }
1614 });
1615 } else {
1616 return Err(EvalError::Runtime(
1617 "with-bindings*: first arg must be a map".into(),
1618 ));
1619 }
1620 let _guard = crate::env::dynamics::push_frame(frame);
1621 crate::env::apply::apply_value(&args[1], vec![], env)
1622}
1623
1624pub fn eval_send_to_agent(_args: Vec<Value>, _env: &mut Env) -> EvalResult {
1626 Err(EvalError::Runtime(
1627 "send/send-off: agents are not yet implemented".into(),
1628 ))
1629}
1630
1631fn ns_name_from_val(v: &Value) -> Result<String, EvalError> {
1634 match v {
1635 Value::Symbol(s) => Ok(s.get().name.as_ref().to_string()),
1636 Value::Str(s) => Ok(s.get().clone()),
1637 Value::Namespace(ns) => Ok(ns.get().name.as_ref().to_string()),
1638 Value::Keyword(k) => Ok(k.get().name.as_ref().to_string()),
1639 other => Err(EvalError::Runtime(format!(
1640 "expected symbol, string, or namespace, got {}",
1641 other.type_name()
1642 ))),
1643 }
1644}
1645
1646fn the_ns(v: &Value, env: &Env) -> Result<GcPtr<cljrs_value::Namespace>, EvalError> {
1651 if let Value::Namespace(ns) = v {
1652 return Ok(ns.clone());
1653 }
1654 let name = ns_name_from_val(v)?;
1655 let map = env.globals.namespaces.read().unwrap();
1656 match map.get(name.as_str()) {
1657 Some(ns) => Ok(ns.clone()),
1658 None => Err(EvalError::Runtime(format!("No namespace: {name} found"))),
1659 }
1660}
1661
1662fn handle_ns_interns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1665 if arg_forms.is_empty() {
1666 return Err(EvalError::Arity {
1667 name: "ns-interns".into(),
1668 expected: "1".into(),
1669 got: 0,
1670 });
1671 }
1672 let arg = eval(&arg_forms[0], env)?;
1673 let ns = the_ns(&arg, env)?;
1674 crate::builtins::builtins::builtin_ns_interns(&[Value::Namespace(ns)])
1675 .map_err(crate::env::error::value_error_to_eval_error)
1676}
1677
1678fn handle_ns_refers(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1680 if arg_forms.is_empty() {
1681 return Err(EvalError::Arity {
1682 name: "ns-refers".into(),
1683 expected: "1".into(),
1684 got: 0,
1685 });
1686 }
1687 let arg = eval(&arg_forms[0], env)?;
1688 let ns = the_ns(&arg, env)?;
1689 crate::builtins::builtins::builtin_ns_refers(&[Value::Namespace(ns)])
1690 .map_err(crate::env::error::value_error_to_eval_error)
1691}
1692
1693fn handle_ns_map(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1695 if arg_forms.is_empty() {
1696 return Err(EvalError::Arity {
1697 name: "ns-map".into(),
1698 expected: "1".into(),
1699 got: 0,
1700 });
1701 }
1702 let arg = eval(&arg_forms[0], env)?;
1703 let ns = the_ns(&arg, env)?;
1704 crate::builtins::builtins::builtin_ns_map(&[Value::Namespace(ns)])
1705 .map_err(crate::env::error::value_error_to_eval_error)
1706}
1707
1708fn handle_find_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1710 if arg_forms.is_empty() {
1711 return Err(EvalError::Arity {
1712 name: "find-ns".into(),
1713 expected: "1".into(),
1714 got: 0,
1715 });
1716 }
1717 let arg = eval(&arg_forms[0], env)?;
1718 let name = ns_name_from_val(&arg)?;
1719 let map = env.globals.namespaces.read().unwrap();
1720 match map.get(name.as_str()) {
1721 Some(ns) => Ok(Value::Namespace(ns.clone())),
1722 None => Ok(Value::Nil),
1723 }
1724}
1725
1726fn handle_all_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1728 if !arg_forms.is_empty() {
1729 let _ = eval(&arg_forms[0], env)?; }
1731 let map = env.globals.namespaces.read().unwrap();
1732 let items: Vec<Value> = map
1733 .values()
1734 .map(|ns| Value::Namespace(ns.clone()))
1735 .collect();
1736 drop(map);
1737 Ok(Value::List(cljrs_gc::GcPtr::new(
1738 cljrs_value::PersistentList::from_iter(items),
1739 )))
1740}
1741
1742fn handle_create_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1744 if arg_forms.is_empty() {
1745 return Err(EvalError::Arity {
1746 name: "create-ns".into(),
1747 expected: "1".into(),
1748 got: 0,
1749 });
1750 }
1751 let arg = eval(&arg_forms[0], env)?;
1752 let name = ns_name_from_val(&arg)?;
1753 let ns = env.globals.get_or_create_ns(&name);
1754 Ok(Value::Namespace(ns))
1755}
1756
1757fn handle_ns_aliases(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1759 if arg_forms.is_empty() {
1760 return Err(EvalError::Arity {
1761 name: "ns-aliases".into(),
1762 expected: "1".into(),
1763 got: 0,
1764 });
1765 }
1766 let ns_val = eval(&arg_forms[0], env)?;
1767 let ns_name = ns_name_from_val(&ns_val)?;
1768 let map = env.globals.namespaces.read().unwrap();
1769 let ns = match map.get(ns_name.as_str()) {
1770 Some(ns) => ns.clone(),
1771 None => return Ok(Value::Map(cljrs_value::MapValue::empty())),
1772 };
1773 let aliases = ns.get().aliases.lock().unwrap().clone();
1774 drop(map);
1775 let mut m = cljrs_value::MapValue::empty();
1776 for (alias, full_ns_name) in &aliases {
1777 let sym = Value::symbol(cljrs_value::Symbol::simple(alias.clone()));
1778 let nsmap = env.globals.namespaces.read().unwrap();
1779 if let Some(target_ns) = nsmap.get(full_ns_name.as_ref()) {
1780 m = m.assoc(sym, Value::Namespace(target_ns.clone()));
1781 }
1782 }
1783 Ok(Value::Map(m))
1784}
1785
1786fn handle_remove_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1788 if arg_forms.is_empty() {
1789 return Err(EvalError::Arity {
1790 name: "remove-ns".into(),
1791 expected: "1".into(),
1792 got: 0,
1793 });
1794 }
1795 let arg = eval(&arg_forms[0], env)?;
1796 let name = ns_name_from_val(&arg)?;
1797 env.globals
1798 .namespaces
1799 .write()
1800 .unwrap()
1801 .remove(name.as_str());
1802 Ok(Value::Nil)
1803}
1804
1805fn handle_alter_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1807 if arg_forms.len() < 2 {
1808 return Err(EvalError::Arity {
1809 name: "alter-meta!".into(),
1810 expected: "2+".into(),
1811 got: arg_forms.len(),
1812 });
1813 }
1814 let obj = eval(&arg_forms[0], env)?;
1815 let f = eval(&arg_forms[1], env)?;
1816 let extra: Vec<Value> = arg_forms[2..]
1817 .iter()
1818 .map(|form| eval(form, env))
1819 .collect::<EvalResult<_>>()?;
1820
1821 let current_meta = match &obj {
1822 Value::Var(vp) => vp
1823 .get()
1824 .get_meta()
1825 .unwrap_or(Value::Map(cljrs_value::MapValue::empty())),
1826 _ => Value::Map(cljrs_value::MapValue::empty()),
1827 };
1828 let mut call_args = vec![current_meta];
1829 call_args.extend(extra);
1830 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1831 if let Value::Var(vp) = &obj {
1832 vp.get().set_meta(new_meta.clone());
1833 }
1834 Ok(new_meta)
1835}
1836
1837fn handle_ns_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1839 if arg_forms.len() < 2 {
1840 return Err(EvalError::Arity {
1841 name: "ns-resolve".into(),
1842 expected: "2".into(),
1843 got: arg_forms.len(),
1844 });
1845 }
1846 let ns_arg = eval(&arg_forms[0], env)?;
1847 let sym_arg = eval(&arg_forms[1], env)?;
1848 let ns_name = ns_name_from_val(&ns_arg)?;
1849 let sym_name = match &sym_arg {
1850 Value::Symbol(s) => s.get().name.as_ref().to_string(),
1851 Value::Str(s) => s.get().clone(),
1852 other => {
1853 return Err(EvalError::Runtime(format!(
1854 "ns-resolve: second arg must be symbol or string, got {}",
1855 other.type_name()
1856 )));
1857 }
1858 };
1859 match env.globals.lookup_var(&ns_name, &sym_name) {
1860 Some(var_ptr) => Ok(Value::Var(var_ptr)),
1861 None => Ok(Value::Nil),
1862 }
1863}
1864
1865fn resolve_current_ns(env: &Env) -> Arc<str> {
1869 if let Some(var) = env.globals.lookup_var("clojure.core", "*ns*") {
1870 let val = crate::env::dynamics::deref_var(&var);
1871 if let Some(Value::Namespace(ns_ptr)) = val {
1872 return ns_ptr.get().name.clone();
1873 }
1874 }
1875 env.current_ns.clone()
1876}
1877
1878fn handle_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1880 if arg_forms.len() != 1 {
1881 return Err(EvalError::Arity {
1882 name: "resolve".into(),
1883 expected: "1".into(),
1884 got: arg_forms.len(),
1885 });
1886 }
1887 let resolve_ns = resolve_current_ns(env);
1888 let sym_arg = eval(&arg_forms[0], env)?;
1889 let sym_name = match &sym_arg {
1890 Value::Symbol(s) => {
1891 let sym = s.get();
1892 if let Some(ns) = &sym.namespace {
1894 let full_ns = env.globals.resolve_ns_part_in(&resolve_ns, ns.as_ref());
1897 return Ok(
1898 match env.globals.lookup_var_in_ns(&full_ns, sym.name.as_ref()) {
1899 Some(var_ptr) => Value::Var(var_ptr),
1900 None => Value::Nil,
1901 },
1902 );
1903 }
1904 sym.name.as_ref().to_string()
1905 }
1906 Value::Str(s) => s.get().clone(),
1907 other => {
1908 return Err(EvalError::Runtime(format!(
1909 "resolve: arg must be symbol or string, got {}",
1910 other.type_name()
1911 )));
1912 }
1913 };
1914 Ok(match env.globals.lookup_var_in_ns(&resolve_ns, &sym_name) {
1915 Some(var_ptr) => Value::Var(var_ptr),
1916 None => Value::Nil,
1917 })
1918}
1919
1920fn handle_intern(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1921 if arg_forms.len() < 2 || arg_forms.len() > 3 {
1922 return Err(EvalError::Runtime("intern expects 2 or 3 arguments".into()));
1923 }
1924 let ns_val = eval(&arg_forms[0], env)?;
1925 let ns_name: Arc<str> = match &ns_val {
1926 Value::Symbol(s) => s.get().name.clone(),
1927 Value::Namespace(ns) => ns.get().name.clone(),
1928 other => {
1929 return Err(EvalError::Runtime(format!(
1930 "intern: first arg must be namespace or symbol, got {}",
1931 other.type_name()
1932 )));
1933 }
1934 };
1935 let var_name: Arc<str> = match eval(&arg_forms[1], env)? {
1936 Value::Symbol(s) => s.get().name.clone(),
1937 other => {
1938 return Err(EvalError::Runtime(format!(
1939 "intern: second arg must be symbol, got {}",
1940 other.type_name()
1941 )));
1942 }
1943 };
1944 let ns = {
1946 let map = env.globals.namespaces.read().unwrap();
1947 map.get(ns_name.as_ref()).cloned()
1948 };
1949 let ns = ns.ok_or_else(|| EvalError::Runtime(format!("No namespace: {ns_name} found")))?;
1950 let var = if arg_forms.len() == 3 {
1951 #[cfg(feature = "no-gc")]
1954 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1955 let val = eval(&arg_forms[2], env)?;
1956 let mut interns = ns.get().interns.lock().unwrap();
1957 if let Some(var) = interns.get(&var_name) {
1958 var.get().bind(val);
1959 var.clone()
1960 } else {
1961 let var =
1962 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
1963 var.get().bind(val);
1964 interns.insert(var_name, var.clone());
1965 var
1966 }
1967 } else {
1968 let mut interns = ns.get().interns.lock().unwrap();
1969 if let Some(var) = interns.get(&var_name) {
1970 var.clone()
1971 } else {
1972 let var =
1973 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
1974 interns.insert(var_name, var.clone());
1975 var
1976 }
1977 };
1978 Ok(Value::Var(var))
1979}
1980
1981fn handle_bound_fn_star(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1986 if arg_forms.len() != 1 {
1987 return Err(EvalError::Arity {
1988 name: "bound-fn*".into(),
1989 expected: "1".into(),
1990 got: arg_forms.len(),
1991 });
1992 }
1993 let f = eval(&arg_forms[0], env)?;
1994 let frames = crate::env::dynamics::capture_current();
1996 let mut merged = std::collections::HashMap::new();
1997 for frame in &frames {
1998 merged.extend(frame.iter().map(|(k, v)| (*k, v.clone())));
1999 }
2000 Ok(Value::BoundFn(cljrs_gc::GcPtr::new(cljrs_value::BoundFn {
2001 wrapped: f,
2002 captured_bindings: merged,
2003 })))
2004}