1use crate::env::env::Env;
2use crate::env::error::{EvalError, EvalResult};
3use cljrs_value::{Arity, Value};
4use std::sync::Arc;
5
6fn check_arity(arity: &Arity, argc: usize, name: &str) -> EvalResult<()> {
7 match arity {
8 Arity::Fixed(n) if argc != *n => Err(EvalError::Arity {
9 name: name.to_string(),
10 expected: n.to_string(),
11 got: argc,
12 }),
13 Arity::Variadic { min } if argc < *min => Err(EvalError::Arity {
14 name: name.to_string(),
15 expected: format!("{}+", min),
16 got: argc,
17 }),
18 _ => Ok(()),
19 }
20}
21
22pub fn type_tag_of(val: &Value) -> Arc<str> {
24 match val {
25 Value::Nil => Arc::from("nil"),
26 Value::Bool(_) => Arc::from("Boolean"),
27 Value::Long(_) => Arc::from("Long"),
28 Value::Double(_) => Arc::from("Double"),
29 Value::BigInt(_) => Arc::from("BigInt"),
30 Value::BigDecimal(_) => Arc::from("BigDecimal"),
31 Value::Ratio(_) => Arc::from("Ratio"),
32 Value::Char(_) => Arc::from("Character"),
33 Value::Str(_) => Arc::from("String"),
34 Value::Keyword(_) => Arc::from("Keyword"),
35 Value::Symbol(_) => Arc::from("Symbol"),
36 Value::List(_) | Value::Cons(_) | Value::LazySeq(_) => Arc::from("List"),
37 Value::Vector(_) => Arc::from("Vector"),
38 Value::Map(_) => Arc::from("Map"),
39 Value::Set(_) => Arc::from("Set"),
40 Value::Fn(_) | Value::NativeFunction(_) | Value::ProtocolFn(_) | Value::MultiFn(_) => {
41 Arc::from("Fn")
42 }
43 Value::Atom(_) => Arc::from("Atom"),
44 Value::Var(_) => Arc::from("Var"),
45 Value::Protocol(_) => Arc::from("Protocol"),
46 Value::Volatile(_) => Arc::from("Volatile"),
47 Value::Delay(_) => Arc::from("Delay"),
48 Value::Promise(_) => Arc::from("Promise"),
49 Value::Future(_) => Arc::from("Future"),
50 Value::Agent(_) => Arc::from("Agent"),
51 Value::TypeInstance(ti) => ti.get().type_tag.clone(),
52 Value::NativeObject(obj) => Arc::from(obj.get().type_tag()),
53 Value::Resource(_) => Arc::from("Resource"),
54 _ => Arc::from("Object"),
55 }
56}
57
58pub fn type_tag_matches(val: &Value, tag: &str) -> bool {
64 match val {
65 Value::TypeInstance(ti) => &*ti.get().type_tag == tag,
66 Value::NativeObject(obj) => obj.get().type_tag() == tag,
67 _ => {
68 match val {
71 Value::Nil => "nil",
72 Value::Bool(_) => "Boolean",
73 Value::Long(_) => "Long",
74 Value::Double(_) => "Double",
75 Value::BigInt(_) => "BigInt",
76 Value::BigDecimal(_) => "BigDecimal",
77 Value::Ratio(_) => "Ratio",
78 Value::Char(_) => "Character",
79 Value::Str(_) => "String",
80 Value::Keyword(_) => "Keyword",
81 Value::Symbol(_) => "Symbol",
82 Value::List(_) | Value::Cons(_) | Value::LazySeq(_) => "List",
83 Value::Vector(_) => "Vector",
84 Value::Map(_) => "Map",
85 Value::Set(_) => "Set",
86 Value::Fn(_)
87 | Value::NativeFunction(_)
88 | Value::ProtocolFn(_)
89 | Value::MultiFn(_) => "Fn",
90 Value::Atom(_) => "Atom",
91 Value::Var(_) => "Var",
92 Value::Protocol(_) => "Protocol",
93 Value::Volatile(_) => "Volatile",
94 Value::Delay(_) => "Delay",
95 Value::Promise(_) => "Promise",
96 Value::Future(_) => "Future",
97 Value::Agent(_) => "Agent",
98 Value::Resource(_) => "Resource",
99 _ => "Object",
100 }
101 }
102 .eq(tag),
103 }
104}
105
106pub fn dispatch_if_async(callee: &Value, args: &[Value], env: &Env) -> Option<Value> {
114 let Value::Fn(f) = callee else { return None };
115 if !f.get().is_async {
116 return None;
117 }
118 let rt = env.globals.async_runtime()?;
119 let call_env = Env::new(env.globals.clone(), &env.current_ns);
120 Some(rt.spawn_async_call(callee.clone(), args.to_vec(), call_env))
121}
122
123pub fn apply_value(callee: &Value, args: Vec<Value>, env: &mut Env) -> EvalResult {
125 let _callee_root = crate::env::gc_roots::root_value(callee);
128 let _args_root = crate::env::gc_roots::root_values(&args);
129
130 crate::env::gc_roots::gc_safepoint(env);
133
134 match callee {
135 Value::NativeFunction(nf) => {
136 crate::env::policy::check_native(&nf.get().name)?;
137 check_arity(&nf.get().arity, args.len(), &nf.get().name)?;
138 let _caller_root = crate::env::gc_roots::push_env_root(env);
142 crate::env::callback::push_eval_context(env);
143 let result =
144 (nf.get().func)(&args).map_err(crate::env::error::value_error_to_eval_error);
145 crate::env::callback::pop_eval_context();
146 result
147 }
148 Value::Fn(f) => {
149 if let Some(fut) = dispatch_if_async(callee, &args, env) {
150 return Ok(fut);
151 }
152 env.call_cljrs_fn(f.get(), &args)
153 }
154 Value::BoundFn(bf) => {
155 let bf_ref = bf.get();
156 let _guard = crate::env::dynamics::push_frame(bf_ref.captured_bindings.clone());
160 apply_value(&bf_ref.wrapped, args, env)
161 }
162 Value::ProtocolFn(pf) => {
163 let pf_ref = pf.get();
164 let dispatch_val = args.first().ok_or_else(|| {
165 EvalError::Runtime(format!(
166 "{}: requires at least 1 argument",
167 pf_ref.method_name
168 ))
169 })?;
170
171 if pf_ref.protocol.get().extend_via_metadata
182 && let Some(Value::Map(m)) = dispatch_val.get_meta()
183 {
184 let proto = pf_ref.protocol.get();
185 let method_sym = Value::Symbol(cljrs_gc::GcPtr::new(
186 cljrs_value::Symbol::qualified(proto.ns.clone(), pf_ref.method_name.clone()),
187 ));
188 if let Some(impl_fn) = m.get(&method_sym) {
189 let _impl_root = crate::env::gc_roots::root_value(&impl_fn);
190 return apply_value(&impl_fn, args, env);
191 }
192 }
193
194 let tag = type_tag_of(dispatch_val);
195 let impls = pf_ref.protocol.get().impls.lock().unwrap();
196 let impl_fn = impls
197 .get(tag.as_ref())
198 .and_then(|m| m.get(pf_ref.method_name.as_ref()))
199 .cloned()
200 .ok_or_else(|| {
201 EvalError::Runtime(format!(
202 "No implementation of protocol {} for type {}",
203 pf_ref.protocol.get().name,
204 tag
205 ))
206 })?;
207 drop(impls);
208 let _impl_root = crate::env::gc_roots::root_value(&impl_fn);
209 apply_value(&impl_fn, args, env)
210 }
211 Value::MultiFn(mf) => {
212 let mf_ref = mf.get();
213 let dispatch_val = apply_value(&mf_ref.dispatch_fn, args.clone(), env)?;
214 let _dispatch_root = crate::env::gc_roots::root_value(&dispatch_val);
215 cljrs_gc::safepoint();
216 let key = format!("{}", dispatch_val);
217 let methods = mf_ref.methods.lock().unwrap();
218 let impl_fn = methods
219 .get(&key)
220 .or_else(|| methods.get(&mf_ref.default_dispatch))
221 .cloned()
222 .ok_or_else(|| {
223 EvalError::Runtime(format!(
224 "No method in multimethod '{}' for dispatch value {}",
225 mf_ref.name, key
226 ))
227 })?;
228 drop(methods);
229 let _impl_root = crate::env::gc_roots::root_value(&impl_fn);
230 apply_value(&impl_fn, args, env)
231 }
232 Value::Keyword(_kw) => {
233 let default = || args.get(1).cloned().unwrap_or(Value::Nil);
235 let target = args.first().map(|a| a.unwrap_meta());
236 match target {
237 Some(Value::Map(m)) => Ok(m.get(callee).unwrap_or_else(default)),
238 Some(Value::TypeInstance(ti)) => {
239 Ok(ti.get().fields.get(callee).unwrap_or_else(default))
240 }
241 Some(Value::Nil) => Ok(default()),
242 _ => Ok(Value::Nil),
243 }
244 }
245 Value::Map(m) => {
246 match args.first() {
248 Some(k) => Ok(m
249 .get(k)
250 .unwrap_or(args.get(1).cloned().unwrap_or(Value::Nil))),
251 None => Ok(Value::Nil),
252 }
253 }
254 Value::Set(s) => match args.first() {
255 Some(k) => {
256 if s.contains(k) {
257 Ok(k.clone())
258 } else {
259 Ok(Value::Nil)
260 }
261 }
262 None => Ok(Value::Nil),
263 },
264 Value::WithMeta(inner, _) => apply_value(inner, args, env),
265 Value::Var(v) => {
266 let inner = crate::env::dynamics::deref_var(v).ok_or_else(|| {
271 EvalError::Runtime(format!(
272 "unbound var {}/{} used as function",
273 v.get().namespace,
274 v.get().name,
275 ))
276 })?;
277 apply_value(&inner, args, env)
278 }
279 other => Err(EvalError::NotCallable(format!(
280 "<{}> is not callable",
281 other.type_name()
282 ))),
283 }
284}