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 && 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 Value::TypeInstance(ti) => {
348 if let Some(field) = method.strip_prefix('-') {
352 let key = Value::keyword(cljrs_value::Keyword::simple(field));
353 let inst = ti.get();
354 if let Some(atom) = &inst.mutable
357 && let Value::Map(m) = atom.get().deref()
358 && let Some(v) = m.get(&key)
359 {
360 return Ok(v);
361 }
362 Ok(inst.fields.get(&key).unwrap_or(Value::Nil))
363 } else {
364 Err(EvalError::Runtime(format!(
365 ".{method} not supported on {} (only .-field access is)",
366 target.type_name()
367 )))
368 }
369 }
370 _ => Err(EvalError::Runtime(format!(
371 ".{method} not supported on type {}",
372 target.type_name()
373 ))),
374 }
375}
376
377fn dispatch_string_method(method: &str, s: &str, args: &[Value]) -> EvalResult {
378 match method {
379 "indexOf" => {
380 let needle = match args.first() {
381 Some(Value::Str(s)) => s.get().to_string(),
382 Some(Value::Char(c)) => c.to_string(),
383 Some(v) => {
384 return Err(EvalError::Runtime(format!(
385 ".indexOf expects string or char argument, got {}",
386 v.type_name()
387 )));
388 }
389 None => return Err(EvalError::Runtime(".indexOf requires an argument".into())),
390 };
391 match s.find(&needle) {
392 Some(pos) => Ok(Value::Long(pos as i64)),
393 None => Ok(Value::Long(-1)),
394 }
395 }
396 "lastIndexOf" => {
397 let needle = match args.first() {
398 Some(Value::Str(s)) => s.get().to_string(),
399 Some(Value::Char(c)) => c.to_string(),
400 _ => {
401 return Err(EvalError::Runtime(
402 ".lastIndexOf requires a string or char argument".into(),
403 ));
404 }
405 };
406 match s.rfind(&needle) {
407 Some(pos) => Ok(Value::Long(pos as i64)),
408 None => Ok(Value::Long(-1)),
409 }
410 }
411 "startsWith" => {
412 let prefix = require_str_arg(args, ".startsWith")?;
413 Ok(Value::Bool(s.starts_with(&prefix)))
414 }
415 "endsWith" => {
416 let suffix = require_str_arg(args, ".endsWith")?;
417 Ok(Value::Bool(s.ends_with(&suffix)))
418 }
419 "contains" => {
420 let sub = require_str_arg(args, ".contains")?;
421 Ok(Value::Bool(s.contains(&sub)))
422 }
423 "length" => Ok(Value::Long(s.len() as i64)),
424 "isEmpty" => Ok(Value::Bool(s.is_empty())),
425 "charAt" => {
426 let idx = require_long_arg(args, ".charAt")? as usize;
427 s.chars()
428 .nth(idx)
429 .map(Value::Char)
430 .ok_or_else(|| EvalError::Runtime(format!(".charAt index {idx} out of bounds")))
431 }
432 "substring" => {
433 let start = require_long_arg(args, ".substring")? as usize;
434 let end = args
435 .get(1)
436 .map(|v| match v {
437 Value::Long(n) => Ok(*n as usize),
438 _ => Err(EvalError::Runtime(
439 ".substring end must be an integer".into(),
440 )),
441 })
442 .transpose()?;
443 let result = match end {
444 Some(e) => &s[start..e.min(s.len())],
445 None => &s[start..],
446 };
447 Ok(Value::Str(GcPtr::new(result.to_string())))
448 }
449 "toUpperCase" => Ok(Value::Str(GcPtr::new(s.to_uppercase()))),
450 "toLowerCase" => Ok(Value::Str(GcPtr::new(s.to_lowercase()))),
451 "trim" => Ok(Value::Str(GcPtr::new(s.trim().to_string()))),
452 "replace" => {
453 let from = require_str_arg(args, ".replace")?;
454 let to = match args.get(1) {
455 Some(Value::Str(s)) => s.get().to_string(),
456 Some(Value::Char(c)) => c.to_string(),
457 _ => {
458 return Err(EvalError::Runtime(
459 ".replace requires two string arguments".into(),
460 ));
461 }
462 };
463 Ok(Value::Str(GcPtr::new(s.replace(&from, &to))))
464 }
465 "split" => {
466 let sep = require_str_arg(args, ".split")?;
467 let parts: Vec<Value> = s
468 .split(&sep)
469 .map(|p| Value::Str(GcPtr::new(p.to_string())))
470 .collect();
471 Ok(Value::Vector(GcPtr::new(
472 cljrs_value::PersistentVector::from_iter(parts),
473 )))
474 }
475 _ => Err(EvalError::Runtime(format!(
476 ".{method} not supported on String"
477 ))),
478 }
479}
480
481fn dispatch_vector_method(
482 method: &str,
483 v: &GcPtr<cljrs_value::PersistentVector>,
484 args: &[Value],
485) -> EvalResult {
486 match method {
487 "indexOf" => {
488 let needle = args
489 .first()
490 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
491 for (i, item) in v.get().iter().enumerate() {
492 if item == needle {
493 return Ok(Value::Long(i as i64));
494 }
495 }
496 Ok(Value::Long(-1))
497 }
498 "size" | "count" => Ok(Value::Long(v.get().count() as i64)),
499 _ => Err(EvalError::Runtime(format!(
500 ".{method} not supported on Vector"
501 ))),
502 }
503}
504
505fn dispatch_seq_method(method: &str, target: &Value, args: &[Value]) -> EvalResult {
506 match method {
507 "indexOf" => {
508 let needle = args
509 .first()
510 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
511 let items = crate::interp::destructure::value_to_seq_vec(target);
512 for (i, item) in items.iter().enumerate() {
513 if item == needle {
514 return Ok(Value::Long(i as i64));
515 }
516 }
517 Ok(Value::Long(-1))
518 }
519 _ => Err(EvalError::Runtime(format!(
520 ".{method} not supported on {}",
521 target.type_name()
522 ))),
523 }
524}
525
526fn require_str_arg(args: &[Value], method: &str) -> Result<String, EvalError> {
527 match args.first() {
528 Some(Value::Str(s)) => Ok(s.get().to_string()),
529 Some(Value::Char(c)) => Ok(c.to_string()),
530 _ => Err(EvalError::Runtime(format!(
531 "{method} requires a string argument"
532 ))),
533 }
534}
535
536fn require_long_arg(args: &[Value], method: &str) -> Result<i64, EvalError> {
537 match args.first() {
538 Some(Value::Long(n)) => Ok(*n),
539 _ => Err(EvalError::Runtime(format!(
540 "{method} requires an integer argument"
541 ))),
542 }
543}
544
545pub fn resolve_type_tag(sym: &str) -> Arc<str> {
548 Arc::from(sym)
549}
550
551pub fn call_cljrs_fn(f: &CljxFn, args: &[Value], caller_env: &mut Env) -> EvalResult {
553 let arity = select_arity(f, args.len())?;
554
555 let _caller_root = crate::env::gc_roots::push_env_root(caller_env);
558
559 let mut env = Env::with_closure(caller_env.globals.clone(), &f.defining_ns, f);
562
563 let mut current_args = Vec::from(args);
564 loop {
565 let _args_root = crate::env::gc_roots::root_values(¤t_args);
568
569 crate::env::gc_roots::gc_safepoint(&env);
571
572 env.push_frame();
573
574 #[cfg(not(feature = "no-gc"))]
584 let _call_frame = cljrs_gc::push_alloc_frame();
585
586 if let Some(ref name) = f.name {
595 let self_val = if let Some(ref p) = f.self_ptr {
596 Value::Fn(p.clone())
597 } else {
598 Value::Fn(GcPtr::new(f.clone()))
599 };
600 env.bind(name.clone(), self_val);
601 }
602
603 bind_fn_params(arity, ¤t_args, &mut env)?;
605
606 #[cfg(not(feature = "no-gc"))]
611 let result = eval_body_recur_fn(&arity.body, &mut env);
612 #[cfg(feature = "no-gc")]
613 let result = {
614 let mut scratch = cljrs_gc::alloc_ctx::ScratchGuard::new();
615 eval_body_with_scratch(&arity.body, &mut scratch, &mut env)
617 };
618 env.pop_frame();
619 match result {
623 Ok(v) => return Ok(v),
624 Err(EvalError::Recur(new_args)) => {
625 if arity.rest_param.is_some() {
630 let n = arity.params.len();
631 if new_args.len() == n + 1 {
632 let mut flat = new_args[..n].to_vec();
633 let rest_val = &new_args[n];
635 match rest_val {
636 Value::Nil => {} _ => {
638 let rest_items = value_to_seq_vec(rest_val);
639 flat.extend(rest_items);
640 }
641 }
642 current_args = flat;
643 } else {
644 current_args = new_args;
645 }
646 } else {
647 current_args = new_args;
648 }
649 }
650 Err(e) => return Err(e),
651 }
652 }
653}
654
655pub fn bind_fn_params(arity: &CljxFnArity, args: &[Value], env: &mut Env) -> EvalResult<()> {
658 bind_fn_params_impl(arity, args, env, true)
659}
660
661pub fn bind_fn_params_positional(
672 arity: &CljxFnArity,
673 args: &[Value],
674 env: &mut Env,
675) -> EvalResult<()> {
676 bind_fn_params_impl(arity, args, env, false)
677}
678
679fn bind_fn_params_impl(
680 arity: &CljxFnArity,
681 args: &[Value],
682 env: &mut Env,
683 destructure: bool,
684) -> EvalResult<()> {
685 let n = arity.params.len();
686 for (i, name) in arity.params.iter().enumerate() {
688 let val = args.get(i).cloned().unwrap_or(Value::Nil);
689 env.bind(name.clone(), val);
690 }
691 if let Some(ref rest) = arity.rest_param {
693 let rest_items = args[n..].to_vec();
694 let rest_val = if rest_items.is_empty() {
695 Value::Nil
696 } else {
697 Value::List(GcPtr::new(PersistentList::from_iter(rest_items)))
698 };
699 env.bind(rest.clone(), rest_val.clone());
700 if destructure && let Some(ref pattern) = arity.destructure_rest {
702 let destructure_val = if pattern.is_kwargs_rest_pattern() {
706 let items = value_to_seq_vec(&rest_val);
707 Value::from_kwargs_rest(items).map_err(value_error_to_eval_error)?
708 } else {
709 rest_val
710 };
711 crate::interp::destructure::bind_pattern(pattern, destructure_val, env)?;
712 }
713 }
714 if destructure {
716 for (idx, pattern) in &arity.destructure_params {
717 let val = args.get(*idx).cloned().unwrap_or(Value::Nil);
718 crate::interp::destructure::bind_pattern(pattern, val, env)?;
719 }
720 }
721 Ok(())
722}
723
724#[cfg(not(feature = "no-gc"))]
726fn eval_body_recur_fn(body: &[cljrs_reader::Form], env: &mut Env) -> EvalResult {
727 let mut result = Value::Nil;
728 for form in body {
729 result = eval(form, env)?;
730 }
731 Ok(result)
732}
733
734#[cfg(feature = "no-gc")]
738fn eval_body_with_scratch(
739 body: &[cljrs_reader::Form],
740 scratch: &mut cljrs_gc::alloc_ctx::ScratchGuard,
741 env: &mut Env,
742) -> EvalResult {
743 if body.is_empty() {
744 scratch.pop_for_return();
745 return Ok(Value::Nil);
746 }
747 for form in &body[..body.len() - 1] {
749 eval(form, env)?;
750 }
751 scratch.pop_for_return();
753 eval(&body[body.len() - 1], env)
754}
755
756pub fn select_arity(f: &CljxFn, argc: usize) -> EvalResult<&CljxFnArity> {
758 let name = f.name.as_deref().unwrap_or("fn");
759 for arity in &f.arities {
761 if arity.rest_param.is_none() && arity.params.len() == argc {
762 return Ok(arity);
763 }
764 }
765 for arity in &f.arities {
767 if arity.rest_param.is_some() && argc >= arity.params.len() {
768 return Ok(arity);
769 }
770 }
771 let expected: Vec<String> = f
773 .arities
774 .iter()
775 .map(|a| {
776 if a.rest_param.is_some() {
777 format!("{}+", a.params.len())
778 } else {
779 a.params.len().to_string()
780 }
781 })
782 .collect();
783 Err(EvalError::Arity {
784 name: name.to_string(),
785 expected: expected.join(" or "),
786 got: argc,
787 })
788}
789
790fn macro_apply(
797 mfn: &CljxFn,
798 func_form: &Form,
799 arg_forms: &[Form],
800 env: &mut Env,
801) -> EvalResult<Form> {
802 let resolved_args: Vec<Form> = arg_forms
807 .iter()
808 .map(|f| crate::builtins::form::resolve_auto_forms(f, env))
809 .collect::<EvalResult<Vec<Form>>>()?;
810
811 let form_val = {
813 let mut items = vec![form_to_value(func_form)?];
814 for f in &resolved_args {
815 items.push(form_to_value(f)?);
816 }
817 Value::List(GcPtr::new(PersistentList::from_iter(items)))
818 };
819
820 let env_val = {
822 let (names, vals) = env.all_local_bindings();
823 let mut m = MapValue::empty();
824 for (name, val) in names.iter().zip(vals.iter()) {
825 m = m.assoc(Value::symbol(Symbol::simple(name.as_ref())), val.clone());
826 }
827 Value::Map(m)
828 };
829
830 let mut args = vec![form_val, env_val];
832 for f in &resolved_args {
833 args.push(form_to_value(f)?);
834 }
835
836 let expanded_val = call_cljrs_fn(mfn, args.as_ref(), env)?;
837 let dummy_span = cljrs_types::span::Span::new(Arc::new("<macro>".to_string()), 0, 0, 1, 1);
838 crate::interp::macros::value_to_form(&expanded_val, dummy_span)
839}
840
841fn handle_apply_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
843 let mut evaled: Vec<Value> = Vec::with_capacity(arg_forms.len());
844 for f in arg_forms {
845 let _root = crate::env::gc_roots::root_values(&evaled);
846 evaled.push(eval(f, env)?);
847 }
848
849 if evaled.len() < 2 {
850 return Err(EvalError::Arity {
851 name: "apply".into(),
852 expected: "2+".into(),
853 got: evaled.len(),
854 });
855 }
856
857 let f = evaled.remove(0);
858 let last = evaled.pop().unwrap();
859 let _f_root = crate::env::gc_roots::root_value(&f);
861 let _last_root = crate::env::gc_roots::root_value(&last);
862 let _evaled_root = crate::env::gc_roots::root_values(&evaled);
863 let spread = value_to_seq_vec(&last);
865 evaled.extend(spread);
866 crate::env::apply::apply_value(&f, evaled, env)
867}
868
869pub fn handle_make_lazy_seq(arg_forms: &[Form], env: &mut Env) -> EvalResult {
871 if arg_forms.len() != 1 {
872 return Err(EvalError::Arity {
873 name: "make-lazy-seq".into(),
874 expected: "1".into(),
875 got: arg_forms.len(),
876 });
877 }
878 let f_val = eval(&arg_forms[0], env)?;
879 let f = match f_val {
880 Value::Fn(f) => f.get().clone(),
881 other => {
882 return Err(EvalError::Runtime(format!(
883 "make-lazy-seq requires a fn, got {}",
884 other.type_name()
885 )));
886 }
887 };
888 let thunk = ClosureThunk {
889 f,
890 globals: env.globals.clone(),
891 ns: env.current_ns.clone(),
892 };
893 Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))))
894}
895
896fn handle_make_delay(arg_forms: &[Form], env: &mut Env) -> EvalResult {
898 if arg_forms.len() != 1 {
899 return Err(EvalError::Arity {
900 name: "make-delay".into(),
901 expected: "1".into(),
902 got: arg_forms.len(),
903 });
904 }
905 let f_val = eval(&arg_forms[0], env)?;
906 let f = match f_val {
907 Value::Fn(f) => f.get().clone(),
908 other => {
909 return Err(EvalError::Runtime(format!(
910 "make-delay requires a fn, got {}",
911 other.type_name()
912 )));
913 }
914 };
915 let thunk = ClosureThunk {
916 f,
917 globals: env.globals.clone(),
918 ns: env.current_ns.clone(),
919 };
920 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
921}
922
923fn handle_vswap(arg_forms: &[Form], env: &mut Env) -> EvalResult {
925 if arg_forms.len() < 2 {
926 return Err(EvalError::Arity {
927 name: "vswap!".into(),
928 expected: "2+".into(),
929 got: arg_forms.len(),
930 });
931 }
932 let vol_val = eval(&arg_forms[0], env)?;
933 let f = eval(&arg_forms[1], env)?;
934 let extra: Vec<Value> = arg_forms[2..]
935 .iter()
936 .map(|a| eval(a, env))
937 .collect::<EvalResult<_>>()?;
938
939 match vol_val {
940 Value::Volatile(v) => {
941 let cur = v.get().deref();
942 let mut call_args = vec![cur];
943 call_args.extend(extra);
944 #[cfg(feature = "no-gc")]
947 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
948 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
949 v.get().reset(new_val.clone());
950 Ok(new_val)
951 }
952 other => Err(EvalError::Runtime(format!(
953 "vswap!: expected volatile, got {}",
954 other.type_name()
955 ))),
956 }
957}
958
959fn handle_volatile(arg_forms: &[Form], env: &mut Env) -> EvalResult {
963 if arg_forms.is_empty() {
964 return Err(EvalError::Arity {
965 name: "volatile!".into(),
966 expected: "1".into(),
967 got: 0,
968 });
969 }
970 #[cfg(feature = "no-gc")]
973 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
974 let initial = eval(&arg_forms[0], env)?;
975 Ok(Value::Volatile(GcPtr::new(Volatile::new(initial))))
976}
977
978fn handle_vreset(arg_forms: &[Form], env: &mut Env) -> EvalResult {
982 if arg_forms.len() < 2 {
983 return Err(EvalError::Arity {
984 name: "vreset!".into(),
985 expected: "2".into(),
986 got: arg_forms.len(),
987 });
988 }
989 let vol_val = eval(&arg_forms[0], env)?;
990 #[cfg(feature = "no-gc")]
993 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
994 let new_val = eval(&arg_forms[1], env)?;
995 match &vol_val {
996 Value::Volatile(v) => {
997 v.get().reset(new_val.clone());
998 Ok(new_val)
999 }
1000 other => Err(EvalError::Runtime(format!(
1001 "vreset!: expected volatile, got {}",
1002 other.type_name()
1003 ))),
1004 }
1005}
1006
1007fn handle_agent_call(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
1011 Err(EvalError::Runtime("agent is not yet implemented".into()))
1012}
1013
1014fn handle_send(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
1016 Err(EvalError::Runtime(
1017 "send/send-off: agents are not yet implemented".into(),
1018 ))
1019}
1020
1021fn handle_atom_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1025 if arg_forms.is_empty() {
1026 return Err(EvalError::Arity {
1027 name: "atom".into(),
1028 expected: "1+".into(),
1029 got: 0,
1030 });
1031 }
1032 #[cfg(feature = "no-gc")]
1035 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1036 let initial = eval(&arg_forms[0], env)?;
1037
1038 let options: Vec<Value> = arg_forms[1..]
1040 .iter()
1041 .map(|f| eval(f, env))
1042 .collect::<EvalResult<_>>()?;
1043
1044 let mut meta_opt: Option<Value> = None;
1045 let mut validator_opt: Option<Value> = None;
1046 let mut i = 0;
1047 while i + 1 < options.len() {
1048 match &options[i] {
1049 Value::Keyword(k) if k.get().name.as_ref() == "meta" => {
1050 meta_opt = Some(options[i + 1].clone());
1051 i += 2;
1052 }
1053 Value::Keyword(k) if k.get().name.as_ref() == "validator" => {
1054 let vf = options[i + 1].clone();
1055 validator_opt = if vf == Value::Nil { None } else { Some(vf) };
1056 i += 2;
1057 }
1058 _ => {
1059 i += 2;
1060 }
1061 }
1062 }
1063
1064 if let Some(ref m) = meta_opt
1066 && !matches!(m, Value::Nil | Value::Map(_))
1067 {
1068 return Err(EvalError::Thrown(Value::string(
1069 "Atom metadata must be a map or nil".to_string(),
1070 )));
1071 }
1072
1073 if let Some(ref vf) = validator_opt {
1075 let result = crate::env::apply::apply_value(vf, vec![initial.clone()], env)?;
1076 if result == Value::Nil || result == Value::Bool(false) {
1077 return Err(EvalError::Thrown(Value::string(
1078 "Invalid initial value for atom".to_string(),
1079 )));
1080 }
1081 }
1082
1083 let atom = GcPtr::new(Atom::new(initial));
1084 if let Some(m) = meta_opt {
1085 atom.get()
1086 .set_meta(if m == Value::Nil { None } else { Some(m) });
1087 }
1088 if let Some(vf) = validator_opt {
1089 atom.get().set_validator(Some(vf));
1090 }
1091 Ok(Value::Atom(atom))
1092}
1093
1094fn shared_atom_reset(sa: &Arc<cljrs_value::SharedAtom>, new_val: Value) -> EvalResult {
1106 let promoted = cljrs_value::promote(&new_val).map_err(|e| EvalError::Runtime(e.to_string()))?;
1107 sa.reset(promoted);
1108 Ok(new_val)
1109}
1110
1111fn shared_atom_swap(
1116 sa: &Arc<cljrs_value::SharedAtom>,
1117 f: &Value,
1118 extra: Vec<Value>,
1119 env: &mut Env,
1120) -> EvalResult {
1121 loop {
1122 let cur = sa.deref_val();
1123 let old_val = cljrs_value::demote(&cur);
1124 let mut call_args = Vec::with_capacity(1 + extra.len());
1125 call_args.push(old_val);
1126 call_args.extend(extra.iter().cloned());
1127 let new_val = crate::env::apply::apply_value(f, call_args, env)?;
1128 let promoted =
1129 cljrs_value::promote(&new_val).map_err(|e| EvalError::Runtime(e.to_string()))?;
1130 if sa.compare_and_set(&cur, promoted) {
1131 return Ok(new_val);
1132 }
1133 }
1135}
1136
1137fn handle_reset_bang(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1140 if arg_forms.len() < 2 {
1141 return Err(EvalError::Arity {
1142 name: "reset!".into(),
1143 expected: "2".into(),
1144 got: arg_forms.len(),
1145 });
1146 }
1147 let atom_val = eval(&arg_forms[0], env)?;
1148 #[cfg(feature = "no-gc")]
1151 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1152 let new_val = eval(&arg_forms[1], env)?;
1153
1154 let atom = match &atom_val {
1155 Value::Atom(a) => a.clone(),
1156 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1157 v => {
1158 return Err(EvalError::Runtime(format!(
1159 "reset! requires an atom, got {}",
1160 v.type_name()
1161 )));
1162 }
1163 };
1164
1165 validate_atom_value(&atom, &new_val, env)?;
1166 let old_val = atom.get().deref();
1167 atom.get().reset(new_val.clone());
1168 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1169 check_watch_error()?;
1170 Ok(new_val)
1171}
1172
1173fn validate_atom_value(atom: &GcPtr<Atom>, new_val: &Value, env: &mut Env) -> EvalResult<()> {
1175 if let Some(vf) = atom.get().get_validator() {
1176 let result = crate::env::apply::apply_value(&vf, vec![new_val.clone()], env)?;
1177 if result == Value::Nil || result == Value::Bool(false) {
1178 return Err(EvalError::Thrown(Value::string(
1179 "Invalid value for atom".to_string(),
1180 )));
1181 }
1182 }
1183 Ok(())
1184}
1185
1186fn handle_swap_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1189 let mut evaled: Vec<Value> = arg_forms
1190 .iter()
1191 .map(|f| eval(f, env))
1192 .collect::<EvalResult<_>>()?;
1193
1194 if evaled.len() < 2 {
1195 return Err(EvalError::Arity {
1196 name: "swap!".into(),
1197 expected: "2+".into(),
1198 got: evaled.len(),
1199 });
1200 }
1201
1202 let atom_val = evaled.remove(0);
1203 let f = evaled.remove(0);
1204
1205 let atom = match &atom_val {
1206 Value::Atom(a) => a.clone(),
1207 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, evaled, env),
1208 v => {
1209 return Err(EvalError::Runtime(format!(
1210 "swap! requires an atom, got {}",
1211 v.type_name()
1212 )));
1213 }
1214 };
1215
1216 let old_val = atom.get().deref();
1217 let mut args = vec![old_val.clone()];
1218 args.extend(evaled);
1219 #[cfg(feature = "no-gc")]
1222 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1223 let new_val = crate::env::apply::apply_value(&f, args, env)?;
1224 validate_atom_value(&atom, &new_val, env)?;
1225 atom.get().reset(new_val.clone());
1226 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1227 check_watch_error()?;
1228 Ok(new_val)
1229}
1230
1231fn handle_with_bindings(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1236 if arg_forms.len() < 2 {
1237 return Err(EvalError::Arity {
1238 name: "with-bindings*".into(),
1239 expected: "2".into(),
1240 got: arg_forms.len(),
1241 });
1242 }
1243 let map_val = eval(&arg_forms[0], env)?;
1244 let func_val = eval(&arg_forms[1], env)?;
1245
1246 let mut frame: HashMap<usize, Value> = HashMap::new();
1247 if let Value::Map(m) = &map_val {
1248 m.for_each(|k, v| {
1249 if let Value::Var(vp) = k {
1250 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1251 }
1252 });
1254 } else {
1255 return Err(EvalError::Runtime(
1256 "with-bindings*: first arg must be a map".into(),
1257 ));
1258 }
1259
1260 let _guard = crate::env::dynamics::push_frame(frame);
1261 crate::env::apply::apply_value(&func_val, vec![], env)
1262}
1263
1264fn handle_alter_var_root(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1268 if arg_forms.len() < 2 {
1269 return Err(EvalError::Arity {
1270 name: "alter-var-root".into(),
1271 expected: "2+".into(),
1272 got: arg_forms.len(),
1273 });
1274 }
1275 let var_val = eval(&arg_forms[0], env)?;
1276 let f = eval(&arg_forms[1], env)?;
1277 let extra: Vec<Value> = arg_forms[2..]
1278 .iter()
1279 .map(|form| eval(form, env))
1280 .collect::<EvalResult<_>>()?;
1281
1282 let vp = match &var_val {
1283 Value::Var(vp) => vp.clone(),
1284 v => {
1285 return Err(EvalError::Runtime(format!(
1286 "alter-var-root: expected var, got {}",
1287 v.type_name()
1288 )));
1289 }
1290 };
1291 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1292 let mut call_args = vec![old_val.clone()];
1293 call_args.extend(extra);
1294 #[cfg(feature = "no-gc")]
1297 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1298 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1299 vp.get().bind(new_val.clone());
1300 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1301 check_watch_error()?;
1302 Ok(new_val)
1303}
1304
1305fn handle_vary_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1309 if arg_forms.len() < 2 {
1310 return Err(EvalError::Arity {
1311 name: "vary-meta".into(),
1312 expected: "2+".into(),
1313 got: arg_forms.len(),
1314 });
1315 }
1316 let obj = eval(&arg_forms[0], env)?;
1317 let f = eval(&arg_forms[1], env)?;
1318 let extra: Vec<Value> = arg_forms[2..]
1319 .iter()
1320 .map(|form| eval(form, env))
1321 .collect::<EvalResult<_>>()?;
1322
1323 let current_meta = match &obj {
1324 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1325 _ => Value::Nil,
1326 };
1327 let mut call_args = vec![current_meta];
1328 call_args.extend(extra);
1329 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1330 if let Value::Var(vp) = &obj {
1331 vp.get().set_meta(new_meta);
1332 }
1333 Ok(obj)
1334}
1335
1336fn handle_eval(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1340 let [arg] = arg_forms else {
1341 return Err(EvalError::Arity {
1342 name: "eval".into(),
1343 expected: "1".into(),
1344 got: arg_forms.len(),
1345 });
1346 };
1347 let value = eval(arg, env)?;
1348 eval_eval(vec![value], env)
1349}
1350
1351pub fn eval_eval(args: Vec<Value>, env: &mut Env) -> EvalResult {
1356 let [value] = args.as_slice() else {
1357 return Err(EvalError::Arity {
1358 name: "eval".into(),
1359 expected: "1".into(),
1360 got: args.len(),
1361 });
1362 };
1363 let span = cljrs_types::span::Span::new(Arc::new("<eval>".to_string()), 0, 0, 1, 1);
1364 let form = crate::interp::macros::value_to_form(value, span)?;
1365 let mut top = Env::new(env.globals.clone(), &env.current_ns);
1366 eval(&form, &mut top)
1367}
1368
1369pub fn eval_reset_bang(args: Vec<Value>, env: &mut Env) -> EvalResult {
1377 if args.len() < 2 {
1378 return Err(EvalError::Arity {
1379 name: "reset!".into(),
1380 expected: "2".into(),
1381 got: args.len(),
1382 });
1383 }
1384 let atom_val = args[0].clone();
1385 let new_val = args[1].clone();
1386 let atom = match &atom_val {
1387 Value::Atom(a) => a.clone(),
1388 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1389 v => {
1390 return Err(EvalError::Runtime(format!(
1391 "reset! requires an atom, got {}",
1392 v.type_name()
1393 )));
1394 }
1395 };
1396 #[cfg(feature = "no-gc")]
1397 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1398 validate_atom_value(&atom, &new_val, env)?;
1399 let old_val = atom.get().deref();
1400 atom.get().reset(new_val.clone());
1401 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1402 check_watch_error()?;
1403 Ok(new_val)
1404}
1405
1406pub fn eval_swap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1408 if args.len() < 2 {
1409 return Err(EvalError::Arity {
1410 name: "swap!".into(),
1411 expected: "2+".into(),
1412 got: args.len(),
1413 });
1414 }
1415 let atom_val = args.remove(0);
1416 let f = args.remove(0);
1417 let atom = match &atom_val {
1418 Value::Atom(a) => a.clone(),
1419 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, args, env),
1420 v => {
1421 return Err(EvalError::Runtime(format!(
1422 "swap! requires an atom, got {}",
1423 v.type_name()
1424 )));
1425 }
1426 };
1427 let old_val = atom.get().deref();
1428 let mut call_args = vec![old_val.clone()];
1429 call_args.extend(args);
1430 #[cfg(feature = "no-gc")]
1431 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1432 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1433 validate_atom_value(&atom, &new_val, env)?;
1434 atom.get().reset(new_val.clone());
1435 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1436 check_watch_error()?;
1437 Ok(new_val)
1438}
1439
1440pub fn eval_volatile(args: Vec<Value>) -> EvalResult {
1442 if args.is_empty() {
1443 return Err(EvalError::Arity {
1444 name: "volatile!".into(),
1445 expected: "1".into(),
1446 got: 0,
1447 });
1448 }
1449 #[cfg(feature = "no-gc")]
1450 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1451 Ok(Value::Volatile(GcPtr::new(Volatile::new(
1452 args.into_iter().next().unwrap(),
1453 ))))
1454}
1455
1456pub fn eval_vreset_bang(args: Vec<Value>) -> EvalResult {
1458 if args.len() < 2 {
1459 return Err(EvalError::Arity {
1460 name: "vreset!".into(),
1461 expected: "2".into(),
1462 got: args.len(),
1463 });
1464 }
1465 #[cfg(feature = "no-gc")]
1466 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1467 let new_val = args[1].clone();
1468 match &args[0] {
1469 Value::Volatile(v) => {
1470 v.get().reset(new_val.clone());
1471 Ok(new_val)
1472 }
1473 other => Err(EvalError::Runtime(format!(
1474 "vreset!: expected volatile, got {}",
1475 other.type_name()
1476 ))),
1477 }
1478}
1479
1480pub fn eval_vswap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1482 if args.len() < 2 {
1483 return Err(EvalError::Arity {
1484 name: "vswap!".into(),
1485 expected: "2+".into(),
1486 got: args.len(),
1487 });
1488 }
1489 let vol_val = args.remove(0);
1490 let f = args.remove(0);
1491 match vol_val {
1492 Value::Volatile(v) => {
1493 let cur = v.get().deref();
1494 let mut call_args = vec![cur];
1495 call_args.extend(args);
1496 #[cfg(feature = "no-gc")]
1497 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1498 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1499 v.get().reset(new_val.clone());
1500 Ok(new_val)
1501 }
1502 other => Err(EvalError::Runtime(format!(
1503 "vswap!: expected volatile, got {}",
1504 other.type_name()
1505 ))),
1506 }
1507}
1508
1509pub fn make_delay_from_fn(
1514 f_val: &Value,
1515 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
1516 ns: std::sync::Arc<str>,
1517) -> EvalResult {
1518 let f = match f_val {
1519 Value::Fn(f) => f.get().clone(),
1520 other => {
1521 return Err(EvalError::Runtime(format!(
1522 "make-delay requires a fn, got {}",
1523 other.type_name()
1524 )));
1525 }
1526 };
1527 let thunk = ClosureThunk { f, globals, ns };
1528 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
1529}
1530
1531pub fn eval_alter_var_root(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1533 if args.len() < 2 {
1534 return Err(EvalError::Arity {
1535 name: "alter-var-root".into(),
1536 expected: "2+".into(),
1537 got: args.len(),
1538 });
1539 }
1540 let var_val = args.remove(0);
1541 let f = args.remove(0);
1542 let vp = match &var_val {
1543 Value::Var(vp) => vp.clone(),
1544 v => {
1545 return Err(EvalError::Runtime(format!(
1546 "alter-var-root: expected var, got {}",
1547 v.type_name()
1548 )));
1549 }
1550 };
1551 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1552 let mut call_args = vec![old_val.clone()];
1553 call_args.extend(args);
1554 #[cfg(feature = "no-gc")]
1555 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1556 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1557 vp.get().bind(new_val.clone());
1558 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1559 check_watch_error()?;
1560 Ok(new_val)
1561}
1562
1563pub fn eval_vary_meta(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1565 if args.len() < 2 {
1566 return Err(EvalError::Arity {
1567 name: "vary-meta".into(),
1568 expected: "2+".into(),
1569 got: args.len(),
1570 });
1571 }
1572 let obj = args.remove(0);
1573 let f = args.remove(0);
1574 let current_meta = match &obj {
1575 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1576 _ => Value::Nil,
1577 };
1578 let mut call_args = vec![current_meta];
1579 call_args.extend(args);
1580 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1581 if let Value::Var(vp) = &obj {
1582 vp.get().set_meta(new_meta);
1583 }
1584 Ok(obj)
1585}
1586
1587pub fn eval_with_bindings_star(args: Vec<Value>, env: &mut Env) -> EvalResult {
1589 if args.len() < 2 {
1590 return Err(EvalError::Arity {
1591 name: "with-bindings*".into(),
1592 expected: "2".into(),
1593 got: args.len(),
1594 });
1595 }
1596 let mut frame: HashMap<usize, Value> = HashMap::new();
1597 if let Value::Map(m) = &args[0] {
1598 m.for_each(|k, v| {
1599 if let Value::Var(vp) = k {
1600 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1601 }
1602 });
1603 } else {
1604 return Err(EvalError::Runtime(
1605 "with-bindings*: first arg must be a map".into(),
1606 ));
1607 }
1608 let _guard = crate::env::dynamics::push_frame(frame);
1609 crate::env::apply::apply_value(&args[1], vec![], env)
1610}
1611
1612pub fn eval_send_to_agent(_args: Vec<Value>, _env: &mut Env) -> EvalResult {
1614 Err(EvalError::Runtime(
1615 "send/send-off: agents are not yet implemented".into(),
1616 ))
1617}
1618
1619fn ns_name_from_val(v: &Value) -> Result<String, EvalError> {
1622 match v {
1623 Value::Symbol(s) => Ok(s.get().name.as_ref().to_string()),
1624 Value::Str(s) => Ok(s.get().clone()),
1625 Value::Namespace(ns) => Ok(ns.get().name.as_ref().to_string()),
1626 Value::Keyword(k) => Ok(k.get().name.as_ref().to_string()),
1627 other => Err(EvalError::Runtime(format!(
1628 "expected symbol, string, or namespace, got {}",
1629 other.type_name()
1630 ))),
1631 }
1632}
1633
1634fn the_ns(v: &Value, env: &Env) -> Result<GcPtr<cljrs_value::Namespace>, EvalError> {
1639 if let Value::Namespace(ns) = v {
1640 return Ok(ns.clone());
1641 }
1642 let name = ns_name_from_val(v)?;
1643 let map = env.globals.namespaces.read().unwrap();
1644 match map.get(name.as_str()) {
1645 Some(ns) => Ok(ns.clone()),
1646 None => Err(EvalError::Runtime(format!("No namespace: {name} found"))),
1647 }
1648}
1649
1650fn handle_ns_interns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1653 if arg_forms.is_empty() {
1654 return Err(EvalError::Arity {
1655 name: "ns-interns".into(),
1656 expected: "1".into(),
1657 got: 0,
1658 });
1659 }
1660 let arg = eval(&arg_forms[0], env)?;
1661 let ns = the_ns(&arg, env)?;
1662 crate::builtins::builtins::builtin_ns_interns(&[Value::Namespace(ns)])
1663 .map_err(crate::env::error::value_error_to_eval_error)
1664}
1665
1666fn handle_ns_refers(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1668 if arg_forms.is_empty() {
1669 return Err(EvalError::Arity {
1670 name: "ns-refers".into(),
1671 expected: "1".into(),
1672 got: 0,
1673 });
1674 }
1675 let arg = eval(&arg_forms[0], env)?;
1676 let ns = the_ns(&arg, env)?;
1677 crate::builtins::builtins::builtin_ns_refers(&[Value::Namespace(ns)])
1678 .map_err(crate::env::error::value_error_to_eval_error)
1679}
1680
1681fn handle_ns_map(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1683 if arg_forms.is_empty() {
1684 return Err(EvalError::Arity {
1685 name: "ns-map".into(),
1686 expected: "1".into(),
1687 got: 0,
1688 });
1689 }
1690 let arg = eval(&arg_forms[0], env)?;
1691 let ns = the_ns(&arg, env)?;
1692 crate::builtins::builtins::builtin_ns_map(&[Value::Namespace(ns)])
1693 .map_err(crate::env::error::value_error_to_eval_error)
1694}
1695
1696fn handle_find_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1698 if arg_forms.is_empty() {
1699 return Err(EvalError::Arity {
1700 name: "find-ns".into(),
1701 expected: "1".into(),
1702 got: 0,
1703 });
1704 }
1705 let arg = eval(&arg_forms[0], env)?;
1706 let name = ns_name_from_val(&arg)?;
1707 let map = env.globals.namespaces.read().unwrap();
1708 match map.get(name.as_str()) {
1709 Some(ns) => Ok(Value::Namespace(ns.clone())),
1710 None => Ok(Value::Nil),
1711 }
1712}
1713
1714fn handle_all_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1716 if !arg_forms.is_empty() {
1717 let _ = eval(&arg_forms[0], env)?; }
1719 let map = env.globals.namespaces.read().unwrap();
1720 let items: Vec<Value> = map
1721 .values()
1722 .map(|ns| Value::Namespace(ns.clone()))
1723 .collect();
1724 drop(map);
1725 Ok(Value::List(cljrs_gc::GcPtr::new(
1726 cljrs_value::PersistentList::from_iter(items),
1727 )))
1728}
1729
1730fn handle_create_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1732 if arg_forms.is_empty() {
1733 return Err(EvalError::Arity {
1734 name: "create-ns".into(),
1735 expected: "1".into(),
1736 got: 0,
1737 });
1738 }
1739 let arg = eval(&arg_forms[0], env)?;
1740 let name = ns_name_from_val(&arg)?;
1741 let ns = env.globals.get_or_create_ns(&name);
1742 Ok(Value::Namespace(ns))
1743}
1744
1745fn handle_ns_aliases(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1747 if arg_forms.is_empty() {
1748 return Err(EvalError::Arity {
1749 name: "ns-aliases".into(),
1750 expected: "1".into(),
1751 got: 0,
1752 });
1753 }
1754 let ns_val = eval(&arg_forms[0], env)?;
1755 let ns_name = ns_name_from_val(&ns_val)?;
1756 let map = env.globals.namespaces.read().unwrap();
1757 let ns = match map.get(ns_name.as_str()) {
1758 Some(ns) => ns.clone(),
1759 None => return Ok(Value::Map(cljrs_value::MapValue::empty())),
1760 };
1761 let aliases = ns.get().aliases.lock().unwrap().clone();
1762 drop(map);
1763 let mut m = cljrs_value::MapValue::empty();
1764 for (alias, full_ns_name) in &aliases {
1765 let sym = Value::symbol(cljrs_value::Symbol::simple(alias.clone()));
1766 let nsmap = env.globals.namespaces.read().unwrap();
1767 if let Some(target_ns) = nsmap.get(full_ns_name.as_ref()) {
1768 m = m.assoc(sym, Value::Namespace(target_ns.clone()));
1769 }
1770 }
1771 Ok(Value::Map(m))
1772}
1773
1774fn handle_remove_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1776 if arg_forms.is_empty() {
1777 return Err(EvalError::Arity {
1778 name: "remove-ns".into(),
1779 expected: "1".into(),
1780 got: 0,
1781 });
1782 }
1783 let arg = eval(&arg_forms[0], env)?;
1784 let name = ns_name_from_val(&arg)?;
1785 env.globals
1786 .namespaces
1787 .write()
1788 .unwrap()
1789 .remove(name.as_str());
1790 Ok(Value::Nil)
1791}
1792
1793fn handle_alter_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1795 if arg_forms.len() < 2 {
1796 return Err(EvalError::Arity {
1797 name: "alter-meta!".into(),
1798 expected: "2+".into(),
1799 got: arg_forms.len(),
1800 });
1801 }
1802 let obj = eval(&arg_forms[0], env)?;
1803 let f = eval(&arg_forms[1], env)?;
1804 let extra: Vec<Value> = arg_forms[2..]
1805 .iter()
1806 .map(|form| eval(form, env))
1807 .collect::<EvalResult<_>>()?;
1808
1809 let current_meta = match &obj {
1810 Value::Var(vp) => vp
1811 .get()
1812 .get_meta()
1813 .unwrap_or(Value::Map(cljrs_value::MapValue::empty())),
1814 _ => Value::Map(cljrs_value::MapValue::empty()),
1815 };
1816 let mut call_args = vec![current_meta];
1817 call_args.extend(extra);
1818 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1819 if let Value::Var(vp) = &obj {
1820 vp.get().set_meta(new_meta.clone());
1821 }
1822 Ok(new_meta)
1823}
1824
1825fn handle_ns_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1827 if arg_forms.len() < 2 {
1828 return Err(EvalError::Arity {
1829 name: "ns-resolve".into(),
1830 expected: "2".into(),
1831 got: arg_forms.len(),
1832 });
1833 }
1834 let ns_arg = eval(&arg_forms[0], env)?;
1835 let sym_arg = eval(&arg_forms[1], env)?;
1836 let ns_name = ns_name_from_val(&ns_arg)?;
1837 let sym_name = match &sym_arg {
1838 Value::Symbol(s) => s.get().name.as_ref().to_string(),
1839 Value::Str(s) => s.get().clone(),
1840 other => {
1841 return Err(EvalError::Runtime(format!(
1842 "ns-resolve: second arg must be symbol or string, got {}",
1843 other.type_name()
1844 )));
1845 }
1846 };
1847 match env.globals.lookup_var(&ns_name, &sym_name) {
1848 Some(var_ptr) => Ok(Value::Var(var_ptr)),
1849 None => Ok(Value::Nil),
1850 }
1851}
1852
1853fn resolve_current_ns(env: &Env) -> Arc<str> {
1857 if let Some(var) = env.globals.lookup_var("clojure.core", "*ns*") {
1858 let val = crate::env::dynamics::deref_var(&var);
1859 if let Some(Value::Namespace(ns_ptr)) = val {
1860 return ns_ptr.get().name.clone();
1861 }
1862 }
1863 env.current_ns.clone()
1864}
1865
1866fn handle_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1868 if arg_forms.len() != 1 {
1869 return Err(EvalError::Arity {
1870 name: "resolve".into(),
1871 expected: "1".into(),
1872 got: arg_forms.len(),
1873 });
1874 }
1875 let resolve_ns = resolve_current_ns(env);
1876 let sym_arg = eval(&arg_forms[0], env)?;
1877 let sym_name = match &sym_arg {
1878 Value::Symbol(s) => {
1879 let sym = s.get();
1880 if let Some(ns) = &sym.namespace {
1882 let full_ns = env.globals.resolve_ns_part_in(&resolve_ns, ns.as_ref());
1885 return Ok(
1886 match env.globals.lookup_var_in_ns(&full_ns, sym.name.as_ref()) {
1887 Some(var_ptr) => Value::Var(var_ptr),
1888 None => Value::Nil,
1889 },
1890 );
1891 }
1892 sym.name.as_ref().to_string()
1893 }
1894 Value::Str(s) => s.get().clone(),
1895 other => {
1896 return Err(EvalError::Runtime(format!(
1897 "resolve: arg must be symbol or string, got {}",
1898 other.type_name()
1899 )));
1900 }
1901 };
1902 Ok(match env.globals.lookup_var_in_ns(&resolve_ns, &sym_name) {
1903 Some(var_ptr) => Value::Var(var_ptr),
1904 None => Value::Nil,
1905 })
1906}
1907
1908fn handle_intern(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1909 if arg_forms.len() < 2 || arg_forms.len() > 3 {
1910 return Err(EvalError::Runtime("intern expects 2 or 3 arguments".into()));
1911 }
1912 let ns_val = eval(&arg_forms[0], env)?;
1913 let ns_name: Arc<str> = match &ns_val {
1914 Value::Symbol(s) => s.get().name.clone(),
1915 Value::Namespace(ns) => ns.get().name.clone(),
1916 other => {
1917 return Err(EvalError::Runtime(format!(
1918 "intern: first arg must be namespace or symbol, got {}",
1919 other.type_name()
1920 )));
1921 }
1922 };
1923 let var_name: Arc<str> = match eval(&arg_forms[1], env)? {
1924 Value::Symbol(s) => s.get().name.clone(),
1925 other => {
1926 return Err(EvalError::Runtime(format!(
1927 "intern: second arg must be symbol, got {}",
1928 other.type_name()
1929 )));
1930 }
1931 };
1932 let ns = {
1934 let map = env.globals.namespaces.read().unwrap();
1935 map.get(ns_name.as_ref()).cloned()
1936 };
1937 let ns = ns.ok_or_else(|| EvalError::Runtime(format!("No namespace: {ns_name} found")))?;
1938 let var = if arg_forms.len() == 3 {
1939 #[cfg(feature = "no-gc")]
1942 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1943 let val = eval(&arg_forms[2], env)?;
1944 let mut interns = ns.get().interns.lock().unwrap();
1945 if let Some(var) = interns.get(&var_name) {
1946 var.get().bind(val);
1947 var.clone()
1948 } else {
1949 let var =
1950 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
1951 var.get().bind(val);
1952 interns.insert(var_name, var.clone());
1953 var
1954 }
1955 } else {
1956 let mut interns = ns.get().interns.lock().unwrap();
1957 if let Some(var) = interns.get(&var_name) {
1958 var.clone()
1959 } else {
1960 let var =
1961 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
1962 interns.insert(var_name, var.clone());
1963 var
1964 }
1965 };
1966 Ok(Value::Var(var))
1967}
1968
1969fn handle_bound_fn_star(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1974 if arg_forms.len() != 1 {
1975 return Err(EvalError::Arity {
1976 name: "bound-fn*".into(),
1977 expected: "1".into(),
1978 got: arg_forms.len(),
1979 });
1980 }
1981 let f = eval(&arg_forms[0], env)?;
1982 let frames = crate::env::dynamics::capture_current();
1984 let mut merged = std::collections::HashMap::new();
1985 for frame in &frames {
1986 merged.extend(frame.iter().map(|(k, v)| (*k, v.clone())));
1987 }
1988 Ok(Value::BoundFn(cljrs_gc::GcPtr::new(cljrs_value::BoundFn {
1989 wrapped: f,
1990 captured_bindings: merged,
1991 })))
1992}