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