1use std::sync::Arc;
21
22use tatara_lisp::Span;
23
24use crate::error::{EvalError, Result};
25use crate::value::Value;
26
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
29pub enum Arity {
30 Exact(usize),
31 AtLeast(usize),
32 Range(usize, usize),
33 Any,
34}
35
36impl Arity {
37 pub fn check(&self, got: usize) -> std::result::Result<(), String> {
39 match *self {
40 Self::Exact(n) if got == n => Ok(()),
41 Self::Exact(n) => Err(format!("expected exactly {n}, got {got}")),
42 Self::AtLeast(n) if got >= n => Ok(()),
43 Self::AtLeast(n) => Err(format!("expected at least {n}, got {got}")),
44 Self::Range(lo, hi) if got >= lo && got <= hi => Ok(()),
45 Self::Range(lo, hi) => Err(format!("expected {lo}..={hi}, got {got}")),
46 Self::Any => Ok(()),
47 }
48 }
49}
50
51pub trait NativeCallable<H>: Send + Sync + 'static {
61 fn call(&self, args: &[Value], host: &mut H, call_span: Span) -> Result<Value>;
62}
63
64impl<H, F> NativeCallable<H> for F
65where
66 F: Fn(&[Value], &mut H, Span) -> Result<Value> + Send + Sync + 'static,
67{
68 fn call(&self, args: &[Value], host: &mut H, call_span: Span) -> Result<Value> {
69 (self)(args, host, call_span)
70 }
71}
72
73pub trait HigherOrderCallable<H>: Send + Sync + 'static {
77 fn call(
78 &self,
79 args: &[Value],
80 host: &mut H,
81 caller: &Caller<H>,
82 call_span: Span,
83 ) -> Result<Value>;
84}
85
86impl<H, F> HigherOrderCallable<H> for F
87where
88 F: Fn(&[Value], &mut H, &Caller<H>, Span) -> Result<Value> + Send + Sync + 'static,
89{
90 fn call(
91 &self,
92 args: &[Value],
93 host: &mut H,
94 caller: &Caller<H>,
95 call_span: Span,
96 ) -> Result<Value> {
97 (self)(args, host, caller, call_span)
98 }
99}
100
101pub struct Caller<'a, H> {
111 pub(crate) registry: &'a FnRegistry<H>,
112 pub(crate) expander: &'a tatara_lisp::SpannedExpander,
113}
114
115impl<'a, H: 'static> Caller<'a, H> {
116 pub fn apply_value(
121 &self,
122 callee: &Value,
123 args: Vec<Value>,
124 host: &mut H,
125 call_span: Span,
126 ) -> Result<Value> {
127 crate::eval::apply_external(callee, args, call_span, self.registry, self.expander, host)
128 }
129
130 pub fn expander(&self) -> &tatara_lisp::SpannedExpander {
133 self.expander
134 }
135
136 pub fn call1(&self, f: &Value, x: Value, host: &mut H, span: Span) -> Result<Value> {
139 self.apply_value(f, vec![x], host, span)
140 }
141
142 pub fn call2(&self, f: &Value, a: Value, b: Value, host: &mut H, span: Span) -> Result<Value> {
144 self.apply_value(f, vec![a, b], host, span)
145 }
146}
147
148pub trait AwaitableCallable<H>: Send + Sync + 'static {
177 fn ready(&self, args: &[Value], host: &H) -> bool;
182
183 fn call(&self, args: &[Value], host: &mut H, call_span: Span) -> Result<Value>;
185}
186
187pub struct Awaitable<R, C> {
189 pub ready: R,
190 pub call: C,
191}
192
193impl<H, R, C> AwaitableCallable<H> for Awaitable<R, C>
194where
195 R: Fn(&[Value], &H) -> bool + Send + Sync + 'static,
196 C: Fn(&[Value], &mut H, Span) -> Result<Value> + Send + Sync + 'static,
197{
198 fn ready(&self, args: &[Value], host: &H) -> bool {
199 (self.ready)(args, host)
200 }
201 fn call(&self, args: &[Value], host: &mut H, call_span: Span) -> Result<Value> {
202 (self.call)(args, host, call_span)
203 }
204}
205
206pub(crate) enum FnImpl<H> {
207 Native(Arc<dyn NativeCallable<H>>),
208 Higher(Arc<dyn HigherOrderCallable<H>>),
209 Awaitable(Arc<dyn AwaitableCallable<H>>),
210}
211
212impl<H> Clone for FnImpl<H> {
213 fn clone(&self) -> Self {
214 match self {
215 Self::Native(f) => Self::Native(Arc::clone(f)),
216 Self::Higher(f) => Self::Higher(Arc::clone(f)),
217 Self::Awaitable(f) => Self::Awaitable(Arc::clone(f)),
218 }
219 }
220}
221
222pub(crate) struct FnRegistry<H> {
224 entries: Vec<FnEntry<H>>,
225}
226
227pub(crate) struct FnEntry<H> {
228 pub name: Arc<str>,
229 #[allow(dead_code)]
232 pub arity: Arity,
233 pub callable: FnImpl<H>,
234}
235
236impl<H> Clone for FnEntry<H> {
242 fn clone(&self) -> Self {
243 Self {
244 name: Arc::clone(&self.name),
245 arity: self.arity,
246 callable: self.callable.clone(),
247 }
248 }
249}
250
251impl<H> Clone for FnRegistry<H> {
252 fn clone(&self) -> Self {
253 Self {
254 entries: self.entries.clone(),
255 }
256 }
257}
258
259impl<H> Default for FnRegistry<H> {
260 fn default() -> Self {
261 Self {
262 entries: Vec::new(),
263 }
264 }
265}
266
267impl<H> FnRegistry<H> {
268 pub(crate) fn new() -> Self {
269 Self::default()
270 }
271
272 pub(crate) fn insert(&mut self, entry: FnEntry<H>) {
273 if let Some(slot) = self.entries.iter_mut().find(|e| e.name == entry.name) {
275 *slot = entry;
276 } else {
277 self.entries.push(entry);
278 }
279 }
280
281 pub(crate) fn lookup(&self, name: &str) -> Option<&FnEntry<H>> {
282 self.entries.iter().find(|e| &*e.name == name)
283 }
284}
285
286pub trait FromValue: Sized {
292 fn from_value(v: &Value, at: Span) -> Result<Self>;
293}
294
295impl FromValue for Value {
296 fn from_value(v: &Value, _at: Span) -> Result<Self> {
297 Ok(v.clone())
298 }
299}
300
301impl FromValue for i64 {
302 fn from_value(v: &Value, at: Span) -> Result<Self> {
303 match v {
304 Value::Int(n) => Ok(*n),
305 other => Err(EvalError::type_mismatch("integer", other.type_name(), at)),
306 }
307 }
308}
309
310impl FromValue for f64 {
311 fn from_value(v: &Value, at: Span) -> Result<Self> {
312 match v {
313 Value::Int(n) => Ok(*n as f64),
314 Value::Float(n) => Ok(*n),
315 other => Err(EvalError::type_mismatch("number", other.type_name(), at)),
316 }
317 }
318}
319
320impl FromValue for bool {
321 fn from_value(v: &Value, at: Span) -> Result<Self> {
322 match v {
323 Value::Bool(b) => Ok(*b),
324 other => Err(EvalError::type_mismatch("bool", other.type_name(), at)),
325 }
326 }
327}
328
329impl FromValue for String {
330 fn from_value(v: &Value, at: Span) -> Result<Self> {
331 match v {
332 Value::Str(s) => Ok(s.to_string()),
333 other => Err(EvalError::type_mismatch("string", other.type_name(), at)),
334 }
335 }
336}
337
338impl FromValue for Arc<str> {
339 fn from_value(v: &Value, at: Span) -> Result<Self> {
340 match v {
341 Value::Str(s) => Ok(s.clone()),
342 Value::Symbol(s) => Ok(s.clone()),
343 Value::Keyword(s) => Ok(s.clone()),
344 other => Err(EvalError::type_mismatch(
345 "string/symbol",
346 other.type_name(),
347 at,
348 )),
349 }
350 }
351}
352
353impl FromValue for Vec<Value> {
354 fn from_value(v: &Value, at: Span) -> Result<Self> {
355 match v {
356 Value::Nil => Ok(Vec::new()),
357 Value::List(xs) => Ok(xs.as_ref().clone()),
358 other => Err(EvalError::type_mismatch("list", other.type_name(), at)),
359 }
360 }
361}
362
363impl<T: FromValue> FromValue for Option<T> {
364 fn from_value(v: &Value, at: Span) -> Result<Self> {
365 match v {
366 Value::Nil => Ok(None),
367 other => T::from_value(other, at).map(Some),
368 }
369 }
370}
371
372pub trait IntoValue {
377 fn into_value(self) -> Value;
378}
379
380impl IntoValue for Value {
381 fn into_value(self) -> Value {
382 self
383 }
384}
385
386impl IntoValue for () {
387 fn into_value(self) -> Value {
388 Value::Nil
389 }
390}
391
392impl IntoValue for bool {
393 fn into_value(self) -> Value {
394 Value::Bool(self)
395 }
396}
397
398impl IntoValue for i64 {
399 fn into_value(self) -> Value {
400 Value::Int(self)
401 }
402}
403
404impl IntoValue for f64 {
405 fn into_value(self) -> Value {
406 Value::Float(self)
407 }
408}
409
410impl IntoValue for String {
411 fn into_value(self) -> Value {
412 Value::Str(Arc::from(self))
413 }
414}
415
416impl IntoValue for &str {
417 fn into_value(self) -> Value {
418 Value::Str(Arc::from(self))
419 }
420}
421
422impl IntoValue for Arc<str> {
423 fn into_value(self) -> Value {
424 Value::Str(self)
425 }
426}
427
428impl<T: IntoValue> IntoValue for Option<T> {
429 fn into_value(self) -> Value {
430 match self {
431 None => Value::Nil,
432 Some(x) => x.into_value(),
433 }
434 }
435}
436
437impl<T: IntoValue> IntoValue for Vec<T> {
438 fn into_value(self) -> Value {
439 Value::list(self.into_iter().map(IntoValue::into_value))
440 }
441}
442
443#[cfg(test)]
444mod tests {
445 use super::*;
446
447 #[test]
448 fn arity_check() {
449 assert!(Arity::Exact(2).check(2).is_ok());
450 assert!(Arity::Exact(2).check(3).is_err());
451 assert!(Arity::AtLeast(1).check(5).is_ok());
452 assert!(Arity::AtLeast(1).check(0).is_err());
453 assert!(Arity::Range(1, 3).check(2).is_ok());
454 assert!(Arity::Range(1, 3).check(4).is_err());
455 assert!(Arity::Any.check(0).is_ok());
456 assert!(Arity::Any.check(1000).is_ok());
457 }
458
459 #[test]
460 fn from_value_round_trips_primitives() {
461 let sp = Span::synthetic();
462 assert_eq!(i64::from_value(&Value::Int(42), sp).unwrap(), 42);
463 assert_eq!(f64::from_value(&Value::Float(1.5), sp).unwrap(), 1.5);
464 assert!(bool::from_value(&Value::Bool(true), sp).unwrap());
465 assert_eq!(
466 String::from_value(&Value::Str(Arc::from("hi")), sp).unwrap(),
467 "hi"
468 );
469 }
470
471 #[test]
472 fn from_value_int_to_float_coerces() {
473 let sp = Span::synthetic();
474 assert_eq!(f64::from_value(&Value::Int(3), sp).unwrap(), 3.0);
475 }
476
477 #[test]
478 fn from_value_option_nil_is_none() {
479 let sp = Span::synthetic();
480 assert_eq!(
481 <Option<i64> as FromValue>::from_value(&Value::Nil, sp).unwrap(),
482 None
483 );
484 assert_eq!(
485 <Option<i64> as FromValue>::from_value(&Value::Int(7), sp).unwrap(),
486 Some(7)
487 );
488 }
489
490 #[test]
491 fn from_value_type_mismatch_reports_expected_kind() {
492 let sp = Span::synthetic();
493 let err = i64::from_value(&Value::Str(Arc::from("x")), sp).unwrap_err();
494 assert!(matches!(
495 err,
496 EvalError::TypeMismatch {
497 expected: "integer",
498 ..
499 }
500 ));
501 }
502
503 #[test]
504 fn into_value_round_trips() {
505 assert!(matches!(42i64.into_value(), Value::Int(42)));
506 assert!(matches!(true.into_value(), Value::Bool(true)));
507 assert!(matches!(().into_value(), Value::Nil));
508 match String::from("hello").into_value() {
509 Value::Str(s) => assert_eq!(&*s, "hello"),
510 other => panic!("{other:?}"),
511 }
512 }
513
514 #[test]
515 fn into_value_vec_produces_list() {
516 let v: Vec<i64> = vec![1, 2, 3];
517 match v.into_value() {
518 Value::List(xs) => {
519 assert_eq!(xs.len(), 3);
520 assert!(matches!(&xs[0], Value::Int(1)));
521 }
522 other => panic!("{other:?}"),
523 }
524 }
525}