1use crate::builtins::form::form_to_value;
4use cljrs_gc::GcPtr;
5use cljrs_reader::{Form, FormKind};
6use cljrs_value::{
7 Atom, CljxFn, CljxFnArity, Delay, LazySeq, MapValue, PersistentList, Symbol, Thunk, Value,
8 Volatile,
9};
10use std::collections::HashMap;
11use std::sync::Arc;
12
13use crate::env::env::Env;
14use crate::env::error::{EvalError, EvalResult};
15use crate::interp::destructure::value_to_seq_vec;
16use crate::interp::eval::eval;
17
18#[allow(dead_code)]
21fn eval_error_to_value(e: EvalError) -> Value {
22 match e {
23 EvalError::Thrown(v) => v,
24 other => Value::string(format!("{other}")),
25 }
26}
27
28fn fire_watches(
34 watches: &std::sync::Mutex<Vec<(Value, Value)>>,
35 reference: &Value,
36 old: &Value,
37 new: &Value,
38 env: &mut Env,
39) {
40 let ws: Vec<(Value, Value)> = watches.lock().unwrap().clone();
41 for (key, f) in &ws {
42 let args = vec![key.clone(), reference.clone(), old.clone(), new.clone()];
43 if let Err(e) = crate::env::apply::apply_value(f, args, env) {
44 WATCH_ERROR.with(|cell| {
51 cell.borrow_mut().replace(e);
52 });
53 return;
54 }
55 }
56}
57
58thread_local! {
59 static WATCH_ERROR: std::cell::RefCell<Option<EvalError>> = const { std::cell::RefCell::new(None) };
60}
61
62fn check_watch_error() -> EvalResult<()> {
64 WATCH_ERROR.with(|cell| {
65 if let Some(e) = cell.borrow_mut().take() {
66 Err(e)
67 } else {
68 Ok(())
69 }
70 })
71}
72
73#[derive(Debug)]
77pub struct ClosureThunk {
78 pub f: CljxFn,
79 pub globals: std::sync::Arc<crate::env::env::GlobalEnv>,
80 pub ns: std::sync::Arc<str>,
81}
82
83struct CallableValueThunk {
89 callee: Value,
90 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
91 ns: std::sync::Arc<str>,
92}
93
94impl std::fmt::Debug for CallableValueThunk {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 f.debug_struct("CallableValueThunk")
97 .field("callee", &self.callee.type_name())
98 .field("ns", &self.ns)
99 .finish()
100 }
101}
102
103impl cljrs_gc::Trace for CallableValueThunk {
104 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
105 self.callee.trace(visitor);
106 }
107}
108
109impl Thunk for CallableValueThunk {
110 fn force(&self) -> Result<Value, String> {
111 let mut env = Env::new(self.globals.clone(), &self.ns);
112 crate::env::apply::apply_value(&self.callee, Vec::new(), &mut env)
113 .map_err(|e| format!("{e}"))
114 }
115}
116
117pub fn make_lazy_seq_from_fn(
130 f_val: &Value,
131 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
132 ns: std::sync::Arc<str>,
133) -> EvalResult {
134 let unwrapped = f_val.unwrap_meta();
135 if let Value::Fn(g) = unwrapped {
136 let thunk = ClosureThunk {
137 f: g.get().clone(),
138 globals,
139 ns,
140 };
141 return Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))));
142 }
143 if unwrapped.type_name() != "fn" {
146 return Err(EvalError::Runtime(format!(
147 "make-lazy-seq requires a fn, got {}",
148 unwrapped.type_name(),
149 )));
150 }
151 let thunk = CallableValueThunk {
152 callee: unwrapped.clone(),
153 globals,
154 ns,
155 };
156 Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))))
157}
158
159impl cljrs_gc::Trace for ClosureThunk {
160 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
161 self.f.trace(visitor);
162 }
163}
164
165impl Thunk for ClosureThunk {
166 fn force(&self) -> Result<Value, String> {
167 let _closed_root = crate::env::gc_roots::root_values(&self.f.closed_over_vals);
171 let mut env = Env::with_closure(self.globals.clone(), &self.ns, &self.f);
172 call_cljrs_fn(&self.f, &[], &mut env).map_err(|e| format!("{e}"))
173 }
174}
175
176pub fn eval_call(func_form: &Form, arg_forms: &[Form], env: &mut Env) -> EvalResult {
184 if let FormKind::Symbol(s) = &func_form.kind
186 && let Some(method) = s.strip_prefix('.')
187 && !method.is_empty()
188 && method != "."
189 {
190 return eval_method_call(method, arg_forms, env);
191 }
192
193 let callee = eval(func_form, env)?;
195
196 let _callee_root = crate::env::gc_roots::root_value(&callee);
198
199 if let Value::Macro(mfn) = &callee {
201 let expanded = macro_apply(mfn.get(), func_form, arg_forms, env)?;
202 return eval(&expanded, env);
203 }
204
205 if let Value::NativeFunction(nf) = &callee {
207 crate::env::policy::check_native(&nf.get().name)?;
208 match nf.get().name.as_ref() {
209 "apply" => return handle_apply_call(arg_forms, env),
210 "atom" => return handle_atom_call(arg_forms, env),
211 "reset!" => return handle_reset_bang(arg_forms, env),
212 "swap!" => return handle_swap_call(arg_forms, env),
213 "volatile!" => return handle_volatile(arg_forms, env),
214 "vreset!" => return handle_vreset(arg_forms, env),
215 "agent" => return handle_agent_call(arg_forms, env),
216 "make-lazy-seq" => return handle_make_lazy_seq(arg_forms, env),
217 "make-delay" => return handle_make_delay(arg_forms, env),
218 "vswap!" => return handle_vswap(arg_forms, env),
219 "send" | "send-off" => return handle_send(arg_forms, env),
220 "with-bindings*" => return handle_with_bindings(arg_forms, env),
221 "alter-var-root" => return handle_alter_var_root(arg_forms, env),
222 "vary-meta" => return handle_vary_meta(arg_forms, env),
223 "find-ns" | "the-ns" => return handle_find_ns(arg_forms, env),
224 "ns-interns" | "ns-publics" => return handle_ns_interns(arg_forms, env),
225 "ns-refers" => return handle_ns_refers(arg_forms, env),
226 "ns-map" => return handle_ns_map(arg_forms, env),
227 "all-ns" => return handle_all_ns(arg_forms, env),
228 "create-ns" => return handle_create_ns(arg_forms, env),
229 "ns-aliases" => return handle_ns_aliases(arg_forms, env),
230 "remove-ns" => return handle_remove_ns(arg_forms, env),
231 "alter-meta!" => return handle_alter_meta(arg_forms, env),
232 "ns-resolve" => return handle_ns_resolve(arg_forms, env),
233 "resolve" => return handle_resolve(arg_forms, env),
234 "intern" => return handle_intern(arg_forms, env),
235 "bound-fn*" => return handle_bound_fn_star(arg_forms, env),
236 _ => {}
237 }
238 }
239
240 let mut args: Vec<Value> = Vec::with_capacity(arg_forms.len());
243 for f in arg_forms {
244 let _args_root = crate::env::gc_roots::root_values(&args);
246 args.push(eval(f, env)?);
247 }
248
249 if let Value::Fn(f) = &callee {
258 if let Some(fut) = crate::env::apply::dispatch_if_async(&callee, &args, env) {
261 return Ok(fut);
262 }
263 let _args_root = crate::env::gc_roots::root_values(&args);
264 crate::env::gc_roots::gc_safepoint(env);
265 let call_fn = env.globals.call_cljrs_fn;
266 return call_fn(f.get(), &args, env);
267 }
268
269 crate::env::apply::apply_value(&callee, args, env)
270}
271
272fn eval_method_call(method: &str, arg_forms: &[Form], env: &mut Env) -> EvalResult {
282 if arg_forms.is_empty() {
283 return Err(EvalError::Runtime(format!(
284 ".{method} requires a target object"
285 )));
286 }
287 let target = eval(&arg_forms[0], env)?;
288 let args: Vec<Value> = arg_forms[1..]
289 .iter()
290 .map(|f| eval(f, env))
291 .collect::<EvalResult<_>>()?;
292
293 dispatch_method(method, &target, &args)
294}
295
296pub fn dispatch_method(method: &str, target: &Value, args: &[Value]) -> EvalResult {
302 match target {
303 Value::Str(s) => dispatch_string_method(method, s.get(), args),
304 Value::Vector(v) => dispatch_vector_method(method, v, args),
305 Value::List(_) | Value::Cons(_) | Value::LazySeq(_) => {
306 dispatch_seq_method(method, target, args)
307 }
308 _ => Err(EvalError::Runtime(format!(
309 ".{method} not supported on type {}",
310 target.type_name()
311 ))),
312 }
313}
314
315fn dispatch_string_method(method: &str, s: &str, args: &[Value]) -> EvalResult {
316 match method {
317 "indexOf" => {
318 let needle = match args.first() {
319 Some(Value::Str(s)) => s.get().to_string(),
320 Some(Value::Char(c)) => c.to_string(),
321 Some(v) => {
322 return Err(EvalError::Runtime(format!(
323 ".indexOf expects string or char argument, got {}",
324 v.type_name()
325 )));
326 }
327 None => return Err(EvalError::Runtime(".indexOf requires an argument".into())),
328 };
329 match s.find(&needle) {
330 Some(pos) => Ok(Value::Long(pos as i64)),
331 None => Ok(Value::Long(-1)),
332 }
333 }
334 "lastIndexOf" => {
335 let needle = match args.first() {
336 Some(Value::Str(s)) => s.get().to_string(),
337 Some(Value::Char(c)) => c.to_string(),
338 _ => {
339 return Err(EvalError::Runtime(
340 ".lastIndexOf requires a string or char argument".into(),
341 ));
342 }
343 };
344 match s.rfind(&needle) {
345 Some(pos) => Ok(Value::Long(pos as i64)),
346 None => Ok(Value::Long(-1)),
347 }
348 }
349 "startsWith" => {
350 let prefix = require_str_arg(args, ".startsWith")?;
351 Ok(Value::Bool(s.starts_with(&prefix)))
352 }
353 "endsWith" => {
354 let suffix = require_str_arg(args, ".endsWith")?;
355 Ok(Value::Bool(s.ends_with(&suffix)))
356 }
357 "contains" => {
358 let sub = require_str_arg(args, ".contains")?;
359 Ok(Value::Bool(s.contains(&sub)))
360 }
361 "length" => Ok(Value::Long(s.len() as i64)),
362 "isEmpty" => Ok(Value::Bool(s.is_empty())),
363 "charAt" => {
364 let idx = require_long_arg(args, ".charAt")? as usize;
365 s.chars()
366 .nth(idx)
367 .map(Value::Char)
368 .ok_or_else(|| EvalError::Runtime(format!(".charAt index {idx} out of bounds")))
369 }
370 "substring" => {
371 let start = require_long_arg(args, ".substring")? as usize;
372 let end = args
373 .get(1)
374 .map(|v| match v {
375 Value::Long(n) => Ok(*n as usize),
376 _ => Err(EvalError::Runtime(
377 ".substring end must be an integer".into(),
378 )),
379 })
380 .transpose()?;
381 let result = match end {
382 Some(e) => &s[start..e.min(s.len())],
383 None => &s[start..],
384 };
385 Ok(Value::Str(GcPtr::new(result.to_string())))
386 }
387 "toUpperCase" => Ok(Value::Str(GcPtr::new(s.to_uppercase()))),
388 "toLowerCase" => Ok(Value::Str(GcPtr::new(s.to_lowercase()))),
389 "trim" => Ok(Value::Str(GcPtr::new(s.trim().to_string()))),
390 "replace" => {
391 let from = require_str_arg(args, ".replace")?;
392 let to = match args.get(1) {
393 Some(Value::Str(s)) => s.get().to_string(),
394 Some(Value::Char(c)) => c.to_string(),
395 _ => {
396 return Err(EvalError::Runtime(
397 ".replace requires two string arguments".into(),
398 ));
399 }
400 };
401 Ok(Value::Str(GcPtr::new(s.replace(&from, &to))))
402 }
403 "split" => {
404 let sep = require_str_arg(args, ".split")?;
405 let parts: Vec<Value> = s
406 .split(&sep)
407 .map(|p| Value::Str(GcPtr::new(p.to_string())))
408 .collect();
409 Ok(Value::Vector(GcPtr::new(
410 cljrs_value::PersistentVector::from_iter(parts),
411 )))
412 }
413 _ => Err(EvalError::Runtime(format!(
414 ".{method} not supported on String"
415 ))),
416 }
417}
418
419fn dispatch_vector_method(
420 method: &str,
421 v: &GcPtr<cljrs_value::PersistentVector>,
422 args: &[Value],
423) -> EvalResult {
424 match method {
425 "indexOf" => {
426 let needle = args
427 .first()
428 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
429 for (i, item) in v.get().iter().enumerate() {
430 if item == needle {
431 return Ok(Value::Long(i as i64));
432 }
433 }
434 Ok(Value::Long(-1))
435 }
436 "size" | "count" => Ok(Value::Long(v.get().count() as i64)),
437 _ => Err(EvalError::Runtime(format!(
438 ".{method} not supported on Vector"
439 ))),
440 }
441}
442
443fn dispatch_seq_method(method: &str, target: &Value, args: &[Value]) -> EvalResult {
444 match method {
445 "indexOf" => {
446 let needle = args
447 .first()
448 .ok_or_else(|| EvalError::Runtime(".indexOf requires an argument".into()))?;
449 let items = crate::interp::destructure::value_to_seq_vec(target);
450 for (i, item) in items.iter().enumerate() {
451 if item == needle {
452 return Ok(Value::Long(i as i64));
453 }
454 }
455 Ok(Value::Long(-1))
456 }
457 _ => Err(EvalError::Runtime(format!(
458 ".{method} not supported on {}",
459 target.type_name()
460 ))),
461 }
462}
463
464fn require_str_arg(args: &[Value], method: &str) -> Result<String, EvalError> {
465 match args.first() {
466 Some(Value::Str(s)) => Ok(s.get().to_string()),
467 Some(Value::Char(c)) => Ok(c.to_string()),
468 _ => Err(EvalError::Runtime(format!(
469 "{method} requires a string argument"
470 ))),
471 }
472}
473
474fn require_long_arg(args: &[Value], method: &str) -> Result<i64, EvalError> {
475 match args.first() {
476 Some(Value::Long(n)) => Ok(*n),
477 _ => Err(EvalError::Runtime(format!(
478 "{method} requires an integer argument"
479 ))),
480 }
481}
482
483pub fn resolve_type_tag(sym: &str) -> Arc<str> {
486 Arc::from(sym)
487}
488
489pub fn call_cljrs_fn(f: &CljxFn, args: &[Value], caller_env: &mut Env) -> EvalResult {
491 let arity = select_arity(f, args.len())?;
492
493 let _caller_root = crate::env::gc_roots::push_env_root(caller_env);
496
497 let mut env = Env::with_closure(caller_env.globals.clone(), &f.defining_ns, f);
500
501 let mut current_args = Vec::from(args);
502 loop {
503 let _args_root = crate::env::gc_roots::root_values(¤t_args);
506
507 crate::env::gc_roots::gc_safepoint(&env);
509
510 env.push_frame();
511
512 #[cfg(not(feature = "no-gc"))]
522 let _call_frame = cljrs_gc::push_alloc_frame();
523
524 bind_fn_params(arity, ¤t_args, &mut env)?;
526
527 if let Some(ref name) = f.name {
530 let self_val = if let Some(ref p) = f.self_ptr {
531 Value::Fn(p.clone())
532 } else {
533 Value::Fn(GcPtr::new(f.clone()))
534 };
535 env.bind(name.clone(), self_val);
536 }
537
538 #[cfg(not(feature = "no-gc"))]
543 let result = eval_body_recur_fn(&arity.body, &mut env);
544 #[cfg(feature = "no-gc")]
545 let result = {
546 let mut scratch = cljrs_gc::alloc_ctx::ScratchGuard::new();
547 eval_body_with_scratch(&arity.body, &mut scratch, &mut env)
549 };
550 env.pop_frame();
551 match result {
555 Ok(v) => return Ok(v),
556 Err(EvalError::Recur(new_args)) => {
557 if arity.rest_param.is_some() {
562 let n = arity.params.len();
563 if new_args.len() == n + 1 {
564 let mut flat = new_args[..n].to_vec();
565 let rest_val = &new_args[n];
567 match rest_val {
568 Value::Nil => {} _ => {
570 let rest_items = value_to_seq_vec(rest_val);
571 flat.extend(rest_items);
572 }
573 }
574 current_args = flat;
575 } else {
576 current_args = new_args;
577 }
578 } else {
579 current_args = new_args;
580 }
581 }
582 Err(e) => return Err(e),
583 }
584 }
585}
586
587pub fn bind_fn_params(arity: &CljxFnArity, args: &[Value], env: &mut Env) -> EvalResult<()> {
589 let n = arity.params.len();
590 for (i, name) in arity.params.iter().enumerate() {
592 let val = args.get(i).cloned().unwrap_or(Value::Nil);
593 env.bind(name.clone(), val);
594 }
595 if let Some(ref rest) = arity.rest_param {
597 let rest_items = args[n..].to_vec();
598 let rest_val = if rest_items.is_empty() {
599 Value::Nil
600 } else {
601 Value::List(GcPtr::new(PersistentList::from_iter(rest_items)))
602 };
603 env.bind(rest.clone(), rest_val.clone());
604 if let Some(ref pattern) = arity.destructure_rest {
606 let destructure_val = if matches!(pattern.kind, FormKind::Map(_)) {
610 let items = value_to_seq_vec(&rest_val);
611 Value::Map(MapValue::from_flat_entries(items))
612 } else {
613 rest_val
614 };
615 crate::interp::destructure::bind_pattern(pattern, destructure_val, env)?;
616 }
617 }
618 for (idx, pattern) in &arity.destructure_params {
620 let val = args.get(*idx).cloned().unwrap_or(Value::Nil);
621 crate::interp::destructure::bind_pattern(pattern, val, env)?;
622 }
623 Ok(())
624}
625
626#[cfg(not(feature = "no-gc"))]
628fn eval_body_recur_fn(body: &[cljrs_reader::Form], env: &mut Env) -> EvalResult {
629 let mut result = Value::Nil;
630 for form in body {
631 result = eval(form, env)?;
632 }
633 Ok(result)
634}
635
636#[cfg(feature = "no-gc")]
640fn eval_body_with_scratch(
641 body: &[cljrs_reader::Form],
642 scratch: &mut cljrs_gc::alloc_ctx::ScratchGuard,
643 env: &mut Env,
644) -> EvalResult {
645 if body.is_empty() {
646 scratch.pop_for_return();
647 return Ok(Value::Nil);
648 }
649 for form in &body[..body.len() - 1] {
651 eval(form, env)?;
652 }
653 scratch.pop_for_return();
655 eval(&body[body.len() - 1], env)
656}
657
658pub fn select_arity(f: &CljxFn, argc: usize) -> EvalResult<&CljxFnArity> {
660 let name = f.name.as_deref().unwrap_or("fn");
661 for arity in &f.arities {
663 if arity.rest_param.is_none() && arity.params.len() == argc {
664 return Ok(arity);
665 }
666 }
667 for arity in &f.arities {
669 if arity.rest_param.is_some() && argc >= arity.params.len() {
670 return Ok(arity);
671 }
672 }
673 let expected: Vec<String> = f
675 .arities
676 .iter()
677 .map(|a| {
678 if a.rest_param.is_some() {
679 format!("{}+", a.params.len())
680 } else {
681 a.params.len().to_string()
682 }
683 })
684 .collect();
685 Err(EvalError::Arity {
686 name: name.to_string(),
687 expected: expected.join(" or "),
688 got: argc,
689 })
690}
691
692fn macro_apply(
699 mfn: &CljxFn,
700 func_form: &Form,
701 arg_forms: &[Form],
702 env: &mut Env,
703) -> EvalResult<Form> {
704 let resolved_args: Vec<Form> = arg_forms
709 .iter()
710 .map(|f| crate::builtins::form::resolve_auto_forms(f, env))
711 .collect::<EvalResult<Vec<Form>>>()?;
712
713 let form_val = {
715 let mut items = vec![form_to_value(func_form)?];
716 for f in &resolved_args {
717 items.push(form_to_value(f)?);
718 }
719 Value::List(GcPtr::new(PersistentList::from_iter(items)))
720 };
721
722 let env_val = {
724 let (names, vals) = env.all_local_bindings();
725 let mut m = MapValue::empty();
726 for (name, val) in names.iter().zip(vals.iter()) {
727 m = m.assoc(Value::symbol(Symbol::simple(name.as_ref())), val.clone());
728 }
729 Value::Map(m)
730 };
731
732 let mut args = vec![form_val, env_val];
734 for f in &resolved_args {
735 args.push(form_to_value(f)?);
736 }
737
738 let expanded_val = call_cljrs_fn(mfn, args.as_ref(), env)?;
739 let dummy_span = cljrs_types::span::Span::new(Arc::new("<macro>".to_string()), 0, 0, 1, 1);
740 crate::interp::macros::value_to_form(&expanded_val, dummy_span)
741}
742
743fn handle_apply_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
745 let mut evaled: Vec<Value> = Vec::with_capacity(arg_forms.len());
746 for f in arg_forms {
747 let _root = crate::env::gc_roots::root_values(&evaled);
748 evaled.push(eval(f, env)?);
749 }
750
751 if evaled.len() < 2 {
752 return Err(EvalError::Arity {
753 name: "apply".into(),
754 expected: "2+".into(),
755 got: evaled.len(),
756 });
757 }
758
759 let f = evaled.remove(0);
760 let last = evaled.pop().unwrap();
761 let _f_root = crate::env::gc_roots::root_value(&f);
763 let _last_root = crate::env::gc_roots::root_value(&last);
764 let _evaled_root = crate::env::gc_roots::root_values(&evaled);
765 let spread = value_to_seq_vec(&last);
767 evaled.extend(spread);
768 crate::env::apply::apply_value(&f, evaled, env)
769}
770
771pub fn handle_make_lazy_seq(arg_forms: &[Form], env: &mut Env) -> EvalResult {
773 if arg_forms.len() != 1 {
774 return Err(EvalError::Arity {
775 name: "make-lazy-seq".into(),
776 expected: "1".into(),
777 got: arg_forms.len(),
778 });
779 }
780 let f_val = eval(&arg_forms[0], env)?;
781 let f = match f_val {
782 Value::Fn(f) => f.get().clone(),
783 other => {
784 return Err(EvalError::Runtime(format!(
785 "make-lazy-seq requires a fn, got {}",
786 other.type_name()
787 )));
788 }
789 };
790 let thunk = ClosureThunk {
791 f,
792 globals: env.globals.clone(),
793 ns: env.current_ns.clone(),
794 };
795 Ok(Value::LazySeq(GcPtr::new(LazySeq::new(Box::new(thunk)))))
796}
797
798fn handle_make_delay(arg_forms: &[Form], env: &mut Env) -> EvalResult {
800 if arg_forms.len() != 1 {
801 return Err(EvalError::Arity {
802 name: "make-delay".into(),
803 expected: "1".into(),
804 got: arg_forms.len(),
805 });
806 }
807 let f_val = eval(&arg_forms[0], env)?;
808 let f = match f_val {
809 Value::Fn(f) => f.get().clone(),
810 other => {
811 return Err(EvalError::Runtime(format!(
812 "make-delay requires a fn, got {}",
813 other.type_name()
814 )));
815 }
816 };
817 let thunk = ClosureThunk {
818 f,
819 globals: env.globals.clone(),
820 ns: env.current_ns.clone(),
821 };
822 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
823}
824
825fn handle_vswap(arg_forms: &[Form], env: &mut Env) -> EvalResult {
827 if arg_forms.len() < 2 {
828 return Err(EvalError::Arity {
829 name: "vswap!".into(),
830 expected: "2+".into(),
831 got: arg_forms.len(),
832 });
833 }
834 let vol_val = eval(&arg_forms[0], env)?;
835 let f = eval(&arg_forms[1], env)?;
836 let extra: Vec<Value> = arg_forms[2..]
837 .iter()
838 .map(|a| eval(a, env))
839 .collect::<EvalResult<_>>()?;
840
841 match vol_val {
842 Value::Volatile(v) => {
843 let cur = v.get().deref();
844 let mut call_args = vec![cur];
845 call_args.extend(extra);
846 #[cfg(feature = "no-gc")]
849 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
850 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
851 v.get().reset(new_val.clone());
852 Ok(new_val)
853 }
854 other => Err(EvalError::Runtime(format!(
855 "vswap!: expected volatile, got {}",
856 other.type_name()
857 ))),
858 }
859}
860
861fn handle_volatile(arg_forms: &[Form], env: &mut Env) -> EvalResult {
865 if arg_forms.is_empty() {
866 return Err(EvalError::Arity {
867 name: "volatile!".into(),
868 expected: "1".into(),
869 got: 0,
870 });
871 }
872 #[cfg(feature = "no-gc")]
875 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
876 let initial = eval(&arg_forms[0], env)?;
877 Ok(Value::Volatile(GcPtr::new(Volatile::new(initial))))
878}
879
880fn handle_vreset(arg_forms: &[Form], env: &mut Env) -> EvalResult {
884 if arg_forms.len() < 2 {
885 return Err(EvalError::Arity {
886 name: "vreset!".into(),
887 expected: "2".into(),
888 got: arg_forms.len(),
889 });
890 }
891 let vol_val = eval(&arg_forms[0], env)?;
892 #[cfg(feature = "no-gc")]
895 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
896 let new_val = eval(&arg_forms[1], env)?;
897 match &vol_val {
898 Value::Volatile(v) => {
899 v.get().reset(new_val.clone());
900 Ok(new_val)
901 }
902 other => Err(EvalError::Runtime(format!(
903 "vreset!: expected volatile, got {}",
904 other.type_name()
905 ))),
906 }
907}
908
909fn handle_agent_call(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
913 Err(EvalError::Runtime("agent is not yet implemented".into()))
914}
915
916fn handle_send(_arg_forms: &[Form], _env: &mut Env) -> EvalResult {
918 Err(EvalError::Runtime(
919 "send/send-off: agents are not yet implemented".into(),
920 ))
921}
922
923fn handle_atom_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
927 if arg_forms.is_empty() {
928 return Err(EvalError::Arity {
929 name: "atom".into(),
930 expected: "1+".into(),
931 got: 0,
932 });
933 }
934 #[cfg(feature = "no-gc")]
937 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
938 let initial = eval(&arg_forms[0], env)?;
939
940 let options: Vec<Value> = arg_forms[1..]
942 .iter()
943 .map(|f| eval(f, env))
944 .collect::<EvalResult<_>>()?;
945
946 let mut meta_opt: Option<Value> = None;
947 let mut validator_opt: Option<Value> = None;
948 let mut i = 0;
949 while i + 1 < options.len() {
950 match &options[i] {
951 Value::Keyword(k) if k.get().name.as_ref() == "meta" => {
952 meta_opt = Some(options[i + 1].clone());
953 i += 2;
954 }
955 Value::Keyword(k) if k.get().name.as_ref() == "validator" => {
956 let vf = options[i + 1].clone();
957 validator_opt = if vf == Value::Nil { None } else { Some(vf) };
958 i += 2;
959 }
960 _ => {
961 i += 2;
962 }
963 }
964 }
965
966 if let Some(ref m) = meta_opt
968 && !matches!(m, Value::Nil | Value::Map(_))
969 {
970 return Err(EvalError::Thrown(Value::string(
971 "Atom metadata must be a map or nil".to_string(),
972 )));
973 }
974
975 if let Some(ref vf) = validator_opt {
977 let result = crate::env::apply::apply_value(vf, vec![initial.clone()], env)?;
978 if result == Value::Nil || result == Value::Bool(false) {
979 return Err(EvalError::Thrown(Value::string(
980 "Invalid initial value for atom".to_string(),
981 )));
982 }
983 }
984
985 let atom = GcPtr::new(Atom::new(initial));
986 if let Some(m) = meta_opt {
987 atom.get()
988 .set_meta(if m == Value::Nil { None } else { Some(m) });
989 }
990 if let Some(vf) = validator_opt {
991 atom.get().set_validator(Some(vf));
992 }
993 Ok(Value::Atom(atom))
994}
995
996fn shared_atom_reset(sa: &Arc<cljrs_value::SharedAtom>, new_val: Value) -> EvalResult {
1008 let promoted = cljrs_value::promote(&new_val).map_err(|e| EvalError::Runtime(e.to_string()))?;
1009 sa.reset(promoted);
1010 Ok(new_val)
1011}
1012
1013fn shared_atom_swap(
1018 sa: &Arc<cljrs_value::SharedAtom>,
1019 f: &Value,
1020 extra: Vec<Value>,
1021 env: &mut Env,
1022) -> EvalResult {
1023 loop {
1024 let cur = sa.deref_val();
1025 let old_val = cljrs_value::demote(&cur);
1026 let mut call_args = Vec::with_capacity(1 + extra.len());
1027 call_args.push(old_val);
1028 call_args.extend(extra.iter().cloned());
1029 let new_val = crate::env::apply::apply_value(f, call_args, env)?;
1030 let promoted =
1031 cljrs_value::promote(&new_val).map_err(|e| EvalError::Runtime(e.to_string()))?;
1032 if sa.compare_and_set(&cur, promoted) {
1033 return Ok(new_val);
1034 }
1035 }
1037}
1038
1039fn handle_reset_bang(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1042 if arg_forms.len() < 2 {
1043 return Err(EvalError::Arity {
1044 name: "reset!".into(),
1045 expected: "2".into(),
1046 got: arg_forms.len(),
1047 });
1048 }
1049 let atom_val = eval(&arg_forms[0], env)?;
1050 #[cfg(feature = "no-gc")]
1053 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1054 let new_val = eval(&arg_forms[1], env)?;
1055
1056 let atom = match &atom_val {
1057 Value::Atom(a) => a.clone(),
1058 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1059 v => {
1060 return Err(EvalError::Runtime(format!(
1061 "reset! requires an atom, got {}",
1062 v.type_name()
1063 )));
1064 }
1065 };
1066
1067 validate_atom_value(&atom, &new_val, env)?;
1068 let old_val = atom.get().deref();
1069 atom.get().reset(new_val.clone());
1070 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1071 check_watch_error()?;
1072 Ok(new_val)
1073}
1074
1075fn validate_atom_value(atom: &GcPtr<Atom>, new_val: &Value, env: &mut Env) -> EvalResult<()> {
1077 if let Some(vf) = atom.get().get_validator() {
1078 let result = crate::env::apply::apply_value(&vf, vec![new_val.clone()], env)?;
1079 if result == Value::Nil || result == Value::Bool(false) {
1080 return Err(EvalError::Thrown(Value::string(
1081 "Invalid value for atom".to_string(),
1082 )));
1083 }
1084 }
1085 Ok(())
1086}
1087
1088fn handle_swap_call(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1091 let mut evaled: Vec<Value> = arg_forms
1092 .iter()
1093 .map(|f| eval(f, env))
1094 .collect::<EvalResult<_>>()?;
1095
1096 if evaled.len() < 2 {
1097 return Err(EvalError::Arity {
1098 name: "swap!".into(),
1099 expected: "2+".into(),
1100 got: evaled.len(),
1101 });
1102 }
1103
1104 let atom_val = evaled.remove(0);
1105 let f = evaled.remove(0);
1106
1107 let atom = match &atom_val {
1108 Value::Atom(a) => a.clone(),
1109 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, evaled, env),
1110 v => {
1111 return Err(EvalError::Runtime(format!(
1112 "swap! requires an atom, got {}",
1113 v.type_name()
1114 )));
1115 }
1116 };
1117
1118 let old_val = atom.get().deref();
1119 let mut args = vec![old_val.clone()];
1120 args.extend(evaled);
1121 #[cfg(feature = "no-gc")]
1124 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1125 let new_val = crate::env::apply::apply_value(&f, args, env)?;
1126 validate_atom_value(&atom, &new_val, env)?;
1127 atom.get().reset(new_val.clone());
1128 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1129 check_watch_error()?;
1130 Ok(new_val)
1131}
1132
1133fn handle_with_bindings(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1138 if arg_forms.len() < 2 {
1139 return Err(EvalError::Arity {
1140 name: "with-bindings*".into(),
1141 expected: "2".into(),
1142 got: arg_forms.len(),
1143 });
1144 }
1145 let map_val = eval(&arg_forms[0], env)?;
1146 let func_val = eval(&arg_forms[1], env)?;
1147
1148 let mut frame: HashMap<usize, Value> = HashMap::new();
1149 if let Value::Map(m) = &map_val {
1150 m.for_each(|k, v| {
1151 if let Value::Var(vp) = k {
1152 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1153 }
1154 });
1156 } else {
1157 return Err(EvalError::Runtime(
1158 "with-bindings*: first arg must be a map".into(),
1159 ));
1160 }
1161
1162 let _guard = crate::env::dynamics::push_frame(frame);
1163 crate::env::apply::apply_value(&func_val, vec![], env)
1164}
1165
1166fn handle_alter_var_root(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1170 if arg_forms.len() < 2 {
1171 return Err(EvalError::Arity {
1172 name: "alter-var-root".into(),
1173 expected: "2+".into(),
1174 got: arg_forms.len(),
1175 });
1176 }
1177 let var_val = eval(&arg_forms[0], env)?;
1178 let f = eval(&arg_forms[1], env)?;
1179 let extra: Vec<Value> = arg_forms[2..]
1180 .iter()
1181 .map(|form| eval(form, env))
1182 .collect::<EvalResult<_>>()?;
1183
1184 let vp = match &var_val {
1185 Value::Var(vp) => vp.clone(),
1186 v => {
1187 return Err(EvalError::Runtime(format!(
1188 "alter-var-root: expected var, got {}",
1189 v.type_name()
1190 )));
1191 }
1192 };
1193 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1194 let mut call_args = vec![old_val.clone()];
1195 call_args.extend(extra);
1196 #[cfg(feature = "no-gc")]
1199 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1200 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1201 vp.get().bind(new_val.clone());
1202 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1203 check_watch_error()?;
1204 Ok(new_val)
1205}
1206
1207fn handle_vary_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1211 if arg_forms.len() < 2 {
1212 return Err(EvalError::Arity {
1213 name: "vary-meta".into(),
1214 expected: "2+".into(),
1215 got: arg_forms.len(),
1216 });
1217 }
1218 let obj = eval(&arg_forms[0], env)?;
1219 let f = eval(&arg_forms[1], env)?;
1220 let extra: Vec<Value> = arg_forms[2..]
1221 .iter()
1222 .map(|form| eval(form, env))
1223 .collect::<EvalResult<_>>()?;
1224
1225 let current_meta = match &obj {
1226 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1227 _ => Value::Nil,
1228 };
1229 let mut call_args = vec![current_meta];
1230 call_args.extend(extra);
1231 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1232 if let Value::Var(vp) = &obj {
1233 vp.get().set_meta(new_meta);
1234 }
1235 Ok(obj)
1236}
1237
1238pub fn eval_reset_bang(args: Vec<Value>, env: &mut Env) -> EvalResult {
1246 if args.len() < 2 {
1247 return Err(EvalError::Arity {
1248 name: "reset!".into(),
1249 expected: "2".into(),
1250 got: args.len(),
1251 });
1252 }
1253 let atom_val = args[0].clone();
1254 let new_val = args[1].clone();
1255 let atom = match &atom_val {
1256 Value::Atom(a) => a.clone(),
1257 Value::SharedAtom(sa) => return shared_atom_reset(sa, new_val),
1258 v => {
1259 return Err(EvalError::Runtime(format!(
1260 "reset! requires an atom, got {}",
1261 v.type_name()
1262 )));
1263 }
1264 };
1265 #[cfg(feature = "no-gc")]
1266 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1267 validate_atom_value(&atom, &new_val, env)?;
1268 let old_val = atom.get().deref();
1269 atom.get().reset(new_val.clone());
1270 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1271 check_watch_error()?;
1272 Ok(new_val)
1273}
1274
1275pub fn eval_swap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1277 if args.len() < 2 {
1278 return Err(EvalError::Arity {
1279 name: "swap!".into(),
1280 expected: "2+".into(),
1281 got: args.len(),
1282 });
1283 }
1284 let atom_val = args.remove(0);
1285 let f = args.remove(0);
1286 let atom = match &atom_val {
1287 Value::Atom(a) => a.clone(),
1288 Value::SharedAtom(sa) => return shared_atom_swap(sa, &f, args, env),
1289 v => {
1290 return Err(EvalError::Runtime(format!(
1291 "swap! requires an atom, got {}",
1292 v.type_name()
1293 )));
1294 }
1295 };
1296 let old_val = atom.get().deref();
1297 let mut call_args = vec![old_val.clone()];
1298 call_args.extend(args);
1299 #[cfg(feature = "no-gc")]
1300 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1301 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1302 validate_atom_value(&atom, &new_val, env)?;
1303 atom.get().reset(new_val.clone());
1304 fire_watches(&atom.get().watches, &atom_val, &old_val, &new_val, env);
1305 check_watch_error()?;
1306 Ok(new_val)
1307}
1308
1309pub fn eval_volatile(args: Vec<Value>) -> EvalResult {
1311 if args.is_empty() {
1312 return Err(EvalError::Arity {
1313 name: "volatile!".into(),
1314 expected: "1".into(),
1315 got: 0,
1316 });
1317 }
1318 #[cfg(feature = "no-gc")]
1319 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1320 Ok(Value::Volatile(GcPtr::new(Volatile::new(
1321 args.into_iter().next().unwrap(),
1322 ))))
1323}
1324
1325pub fn eval_vreset_bang(args: Vec<Value>) -> EvalResult {
1327 if args.len() < 2 {
1328 return Err(EvalError::Arity {
1329 name: "vreset!".into(),
1330 expected: "2".into(),
1331 got: args.len(),
1332 });
1333 }
1334 #[cfg(feature = "no-gc")]
1335 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1336 let new_val = args[1].clone();
1337 match &args[0] {
1338 Value::Volatile(v) => {
1339 v.get().reset(new_val.clone());
1340 Ok(new_val)
1341 }
1342 other => Err(EvalError::Runtime(format!(
1343 "vreset!: expected volatile, got {}",
1344 other.type_name()
1345 ))),
1346 }
1347}
1348
1349pub fn eval_vswap_bang(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1351 if args.len() < 2 {
1352 return Err(EvalError::Arity {
1353 name: "vswap!".into(),
1354 expected: "2+".into(),
1355 got: args.len(),
1356 });
1357 }
1358 let vol_val = args.remove(0);
1359 let f = args.remove(0);
1360 match vol_val {
1361 Value::Volatile(v) => {
1362 let cur = v.get().deref();
1363 let mut call_args = vec![cur];
1364 call_args.extend(args);
1365 #[cfg(feature = "no-gc")]
1366 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1367 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1368 v.get().reset(new_val.clone());
1369 Ok(new_val)
1370 }
1371 other => Err(EvalError::Runtime(format!(
1372 "vswap!: expected volatile, got {}",
1373 other.type_name()
1374 ))),
1375 }
1376}
1377
1378pub fn make_delay_from_fn(
1383 f_val: &Value,
1384 globals: std::sync::Arc<crate::env::env::GlobalEnv>,
1385 ns: std::sync::Arc<str>,
1386) -> EvalResult {
1387 let f = match f_val {
1388 Value::Fn(f) => f.get().clone(),
1389 other => {
1390 return Err(EvalError::Runtime(format!(
1391 "make-delay requires a fn, got {}",
1392 other.type_name()
1393 )));
1394 }
1395 };
1396 let thunk = ClosureThunk { f, globals, ns };
1397 Ok(Value::Delay(GcPtr::new(Delay::new(Box::new(thunk)))))
1398}
1399
1400pub fn eval_alter_var_root(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1402 if args.len() < 2 {
1403 return Err(EvalError::Arity {
1404 name: "alter-var-root".into(),
1405 expected: "2+".into(),
1406 got: args.len(),
1407 });
1408 }
1409 let var_val = args.remove(0);
1410 let f = args.remove(0);
1411 let vp = match &var_val {
1412 Value::Var(vp) => vp.clone(),
1413 v => {
1414 return Err(EvalError::Runtime(format!(
1415 "alter-var-root: expected var, got {}",
1416 v.type_name()
1417 )));
1418 }
1419 };
1420 let old_val = vp.get().deref().unwrap_or(Value::Nil);
1421 let mut call_args = vec![old_val.clone()];
1422 call_args.extend(args);
1423 #[cfg(feature = "no-gc")]
1424 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1425 let new_val = crate::env::apply::apply_value(&f, call_args, env)?;
1426 vp.get().bind(new_val.clone());
1427 fire_watches(&vp.get().watches, &var_val, &old_val, &new_val, env);
1428 check_watch_error()?;
1429 Ok(new_val)
1430}
1431
1432pub fn eval_vary_meta(mut args: Vec<Value>, env: &mut Env) -> EvalResult {
1434 if args.len() < 2 {
1435 return Err(EvalError::Arity {
1436 name: "vary-meta".into(),
1437 expected: "2+".into(),
1438 got: args.len(),
1439 });
1440 }
1441 let obj = args.remove(0);
1442 let f = args.remove(0);
1443 let current_meta = match &obj {
1444 Value::Var(vp) => vp.get().get_meta().unwrap_or(Value::Nil),
1445 _ => Value::Nil,
1446 };
1447 let mut call_args = vec![current_meta];
1448 call_args.extend(args);
1449 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1450 if let Value::Var(vp) = &obj {
1451 vp.get().set_meta(new_meta);
1452 }
1453 Ok(obj)
1454}
1455
1456pub fn eval_with_bindings_star(args: Vec<Value>, env: &mut Env) -> EvalResult {
1458 if args.len() < 2 {
1459 return Err(EvalError::Arity {
1460 name: "with-bindings*".into(),
1461 expected: "2".into(),
1462 got: args.len(),
1463 });
1464 }
1465 let mut frame: HashMap<usize, Value> = HashMap::new();
1466 if let Value::Map(m) = &args[0] {
1467 m.for_each(|k, v| {
1468 if let Value::Var(vp) = k {
1469 frame.insert(crate::env::dynamics::var_key_of(vp), v.clone());
1470 }
1471 });
1472 } else {
1473 return Err(EvalError::Runtime(
1474 "with-bindings*: first arg must be a map".into(),
1475 ));
1476 }
1477 let _guard = crate::env::dynamics::push_frame(frame);
1478 crate::env::apply::apply_value(&args[1], vec![], env)
1479}
1480
1481pub fn eval_send_to_agent(_args: Vec<Value>, _env: &mut Env) -> EvalResult {
1483 Err(EvalError::Runtime(
1484 "send/send-off: agents are not yet implemented".into(),
1485 ))
1486}
1487
1488fn ns_name_from_val(v: &Value) -> Result<String, EvalError> {
1491 match v {
1492 Value::Symbol(s) => Ok(s.get().name.as_ref().to_string()),
1493 Value::Str(s) => Ok(s.get().clone()),
1494 Value::Namespace(ns) => Ok(ns.get().name.as_ref().to_string()),
1495 Value::Keyword(k) => Ok(k.get().name.as_ref().to_string()),
1496 other => Err(EvalError::Runtime(format!(
1497 "expected symbol, string, or namespace, got {}",
1498 other.type_name()
1499 ))),
1500 }
1501}
1502
1503fn the_ns(v: &Value, env: &Env) -> Result<GcPtr<cljrs_value::Namespace>, EvalError> {
1508 if let Value::Namespace(ns) = v {
1509 return Ok(ns.clone());
1510 }
1511 let name = ns_name_from_val(v)?;
1512 let map = env.globals.namespaces.read().unwrap();
1513 match map.get(name.as_str()) {
1514 Some(ns) => Ok(ns.clone()),
1515 None => Err(EvalError::Runtime(format!("No namespace: {name} found"))),
1516 }
1517}
1518
1519fn handle_ns_interns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1522 if arg_forms.is_empty() {
1523 return Err(EvalError::Arity {
1524 name: "ns-interns".into(),
1525 expected: "1".into(),
1526 got: 0,
1527 });
1528 }
1529 let arg = eval(&arg_forms[0], env)?;
1530 let ns = the_ns(&arg, env)?;
1531 crate::builtins::builtins::builtin_ns_interns(&[Value::Namespace(ns)])
1532 .map_err(crate::env::error::value_error_to_eval_error)
1533}
1534
1535fn handle_ns_refers(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1537 if arg_forms.is_empty() {
1538 return Err(EvalError::Arity {
1539 name: "ns-refers".into(),
1540 expected: "1".into(),
1541 got: 0,
1542 });
1543 }
1544 let arg = eval(&arg_forms[0], env)?;
1545 let ns = the_ns(&arg, env)?;
1546 crate::builtins::builtins::builtin_ns_refers(&[Value::Namespace(ns)])
1547 .map_err(crate::env::error::value_error_to_eval_error)
1548}
1549
1550fn handle_ns_map(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1552 if arg_forms.is_empty() {
1553 return Err(EvalError::Arity {
1554 name: "ns-map".into(),
1555 expected: "1".into(),
1556 got: 0,
1557 });
1558 }
1559 let arg = eval(&arg_forms[0], env)?;
1560 let ns = the_ns(&arg, env)?;
1561 crate::builtins::builtins::builtin_ns_map(&[Value::Namespace(ns)])
1562 .map_err(crate::env::error::value_error_to_eval_error)
1563}
1564
1565fn handle_find_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1567 if arg_forms.is_empty() {
1568 return Err(EvalError::Arity {
1569 name: "find-ns".into(),
1570 expected: "1".into(),
1571 got: 0,
1572 });
1573 }
1574 let arg = eval(&arg_forms[0], env)?;
1575 let name = ns_name_from_val(&arg)?;
1576 let map = env.globals.namespaces.read().unwrap();
1577 match map.get(name.as_str()) {
1578 Some(ns) => Ok(Value::Namespace(ns.clone())),
1579 None => Ok(Value::Nil),
1580 }
1581}
1582
1583fn handle_all_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1585 if !arg_forms.is_empty() {
1586 let _ = eval(&arg_forms[0], env)?; }
1588 let map = env.globals.namespaces.read().unwrap();
1589 let items: Vec<Value> = map
1590 .values()
1591 .map(|ns| Value::Namespace(ns.clone()))
1592 .collect();
1593 drop(map);
1594 Ok(Value::List(cljrs_gc::GcPtr::new(
1595 cljrs_value::PersistentList::from_iter(items),
1596 )))
1597}
1598
1599fn handle_create_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1601 if arg_forms.is_empty() {
1602 return Err(EvalError::Arity {
1603 name: "create-ns".into(),
1604 expected: "1".into(),
1605 got: 0,
1606 });
1607 }
1608 let arg = eval(&arg_forms[0], env)?;
1609 let name = ns_name_from_val(&arg)?;
1610 let ns = env.globals.get_or_create_ns(&name);
1611 Ok(Value::Namespace(ns))
1612}
1613
1614fn handle_ns_aliases(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1616 if arg_forms.is_empty() {
1617 return Err(EvalError::Arity {
1618 name: "ns-aliases".into(),
1619 expected: "1".into(),
1620 got: 0,
1621 });
1622 }
1623 let ns_val = eval(&arg_forms[0], env)?;
1624 let ns_name = ns_name_from_val(&ns_val)?;
1625 let map = env.globals.namespaces.read().unwrap();
1626 let ns = match map.get(ns_name.as_str()) {
1627 Some(ns) => ns.clone(),
1628 None => return Ok(Value::Map(cljrs_value::MapValue::empty())),
1629 };
1630 let aliases = ns.get().aliases.lock().unwrap().clone();
1631 drop(map);
1632 let mut m = cljrs_value::MapValue::empty();
1633 for (alias, full_ns_name) in &aliases {
1634 let sym = Value::symbol(cljrs_value::Symbol::simple(alias.clone()));
1635 let nsmap = env.globals.namespaces.read().unwrap();
1636 if let Some(target_ns) = nsmap.get(full_ns_name.as_ref()) {
1637 m = m.assoc(sym, Value::Namespace(target_ns.clone()));
1638 }
1639 }
1640 Ok(Value::Map(m))
1641}
1642
1643fn handle_remove_ns(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1645 if arg_forms.is_empty() {
1646 return Err(EvalError::Arity {
1647 name: "remove-ns".into(),
1648 expected: "1".into(),
1649 got: 0,
1650 });
1651 }
1652 let arg = eval(&arg_forms[0], env)?;
1653 let name = ns_name_from_val(&arg)?;
1654 env.globals
1655 .namespaces
1656 .write()
1657 .unwrap()
1658 .remove(name.as_str());
1659 Ok(Value::Nil)
1660}
1661
1662fn handle_alter_meta(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1664 if arg_forms.len() < 2 {
1665 return Err(EvalError::Arity {
1666 name: "alter-meta!".into(),
1667 expected: "2+".into(),
1668 got: arg_forms.len(),
1669 });
1670 }
1671 let obj = eval(&arg_forms[0], env)?;
1672 let f = eval(&arg_forms[1], env)?;
1673 let extra: Vec<Value> = arg_forms[2..]
1674 .iter()
1675 .map(|form| eval(form, env))
1676 .collect::<EvalResult<_>>()?;
1677
1678 let current_meta = match &obj {
1679 Value::Var(vp) => vp
1680 .get()
1681 .get_meta()
1682 .unwrap_or(Value::Map(cljrs_value::MapValue::empty())),
1683 _ => Value::Map(cljrs_value::MapValue::empty()),
1684 };
1685 let mut call_args = vec![current_meta];
1686 call_args.extend(extra);
1687 let new_meta = crate::env::apply::apply_value(&f, call_args, env)?;
1688 if let Value::Var(vp) = &obj {
1689 vp.get().set_meta(new_meta.clone());
1690 }
1691 Ok(new_meta)
1692}
1693
1694fn handle_ns_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1696 if arg_forms.len() < 2 {
1697 return Err(EvalError::Arity {
1698 name: "ns-resolve".into(),
1699 expected: "2".into(),
1700 got: arg_forms.len(),
1701 });
1702 }
1703 let ns_arg = eval(&arg_forms[0], env)?;
1704 let sym_arg = eval(&arg_forms[1], env)?;
1705 let ns_name = ns_name_from_val(&ns_arg)?;
1706 let sym_name = match &sym_arg {
1707 Value::Symbol(s) => s.get().name.as_ref().to_string(),
1708 Value::Str(s) => s.get().clone(),
1709 other => {
1710 return Err(EvalError::Runtime(format!(
1711 "ns-resolve: second arg must be symbol or string, got {}",
1712 other.type_name()
1713 )));
1714 }
1715 };
1716 match env.globals.lookup_var(&ns_name, &sym_name) {
1717 Some(var_ptr) => Ok(Value::Var(var_ptr)),
1718 None => Ok(Value::Nil),
1719 }
1720}
1721
1722fn resolve_current_ns(env: &Env) -> Arc<str> {
1726 if let Some(var) = env.globals.lookup_var("clojure.core", "*ns*") {
1727 let val = crate::env::dynamics::deref_var(&var);
1728 if let Some(Value::Namespace(ns_ptr)) = val {
1729 return ns_ptr.get().name.clone();
1730 }
1731 }
1732 env.current_ns.clone()
1733}
1734
1735fn handle_resolve(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1737 if arg_forms.len() != 1 {
1738 return Err(EvalError::Arity {
1739 name: "resolve".into(),
1740 expected: "1".into(),
1741 got: arg_forms.len(),
1742 });
1743 }
1744 let resolve_ns = resolve_current_ns(env);
1745 let sym_arg = eval(&arg_forms[0], env)?;
1746 let sym_name = match &sym_arg {
1747 Value::Symbol(s) => {
1748 let sym = s.get();
1749 if let Some(ns) = &sym.namespace {
1751 let full_ns = env
1752 .globals
1753 .resolve_alias(&resolve_ns, ns.as_ref())
1754 .unwrap_or_else(|| ns.clone());
1755 return Ok(
1756 match env.globals.lookup_var_in_ns(&full_ns, sym.name.as_ref()) {
1757 Some(var_ptr) => Value::Var(var_ptr),
1758 None => Value::Nil,
1759 },
1760 );
1761 }
1762 sym.name.as_ref().to_string()
1763 }
1764 Value::Str(s) => s.get().clone(),
1765 other => {
1766 return Err(EvalError::Runtime(format!(
1767 "resolve: arg must be symbol or string, got {}",
1768 other.type_name()
1769 )));
1770 }
1771 };
1772 Ok(match env.globals.lookup_var_in_ns(&resolve_ns, &sym_name) {
1773 Some(var_ptr) => Value::Var(var_ptr),
1774 None => Value::Nil,
1775 })
1776}
1777
1778fn handle_intern(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1779 if arg_forms.len() < 2 || arg_forms.len() > 3 {
1780 return Err(EvalError::Runtime("intern expects 2 or 3 arguments".into()));
1781 }
1782 let ns_val = eval(&arg_forms[0], env)?;
1783 let ns_name: Arc<str> = match &ns_val {
1784 Value::Symbol(s) => s.get().name.clone(),
1785 Value::Namespace(ns) => ns.get().name.clone(),
1786 other => {
1787 return Err(EvalError::Runtime(format!(
1788 "intern: first arg must be namespace or symbol, got {}",
1789 other.type_name()
1790 )));
1791 }
1792 };
1793 let var_name: Arc<str> = match eval(&arg_forms[1], env)? {
1794 Value::Symbol(s) => s.get().name.clone(),
1795 other => {
1796 return Err(EvalError::Runtime(format!(
1797 "intern: second arg must be symbol, got {}",
1798 other.type_name()
1799 )));
1800 }
1801 };
1802 let ns = {
1804 let map = env.globals.namespaces.read().unwrap();
1805 map.get(ns_name.as_ref()).cloned()
1806 };
1807 let ns = ns.ok_or_else(|| EvalError::Runtime(format!("No namespace: {ns_name} found")))?;
1808 let var = if arg_forms.len() == 3 {
1809 #[cfg(feature = "no-gc")]
1812 let _static_ctx = cljrs_gc::alloc_ctx::StaticCtxGuard::new();
1813 let val = eval(&arg_forms[2], env)?;
1814 let mut interns = ns.get().interns.lock().unwrap();
1815 if let Some(var) = interns.get(&var_name) {
1816 var.get().bind(val);
1817 var.clone()
1818 } else {
1819 let var =
1820 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
1821 var.get().bind(val);
1822 interns.insert(var_name, var.clone());
1823 var
1824 }
1825 } else {
1826 let mut interns = ns.get().interns.lock().unwrap();
1827 if let Some(var) = interns.get(&var_name) {
1828 var.clone()
1829 } else {
1830 let var =
1831 cljrs_gc::GcPtr::new(cljrs_value::Var::new(ns_name.clone(), var_name.clone()));
1832 interns.insert(var_name, var.clone());
1833 var
1834 }
1835 };
1836 Ok(Value::Var(var))
1837}
1838
1839fn handle_bound_fn_star(arg_forms: &[Form], env: &mut Env) -> EvalResult {
1844 if arg_forms.len() != 1 {
1845 return Err(EvalError::Arity {
1846 name: "bound-fn*".into(),
1847 expected: "1".into(),
1848 got: arg_forms.len(),
1849 });
1850 }
1851 let f = eval(&arg_forms[0], env)?;
1852 let frames = crate::env::dynamics::capture_current();
1854 let mut merged = std::collections::HashMap::new();
1855 for frame in &frames {
1856 merged.extend(frame.iter().map(|(k, v)| (*k, v.clone())));
1857 }
1858 Ok(Value::BoundFn(cljrs_gc::GcPtr::new(cljrs_value::BoundFn {
1859 wrapped: f,
1860 captured_bindings: merged,
1861 })))
1862}