1use std::collections::BTreeMap;
19use std::sync::Arc;
20
21use tatara_lisp::{read, Atom, Sexp};
22
23use crate::builtins::builtin_table;
24use crate::env::Env;
25use crate::error::{EvalError, Result};
26use crate::value::{Lambda, Thunk, ThunkState, Value};
27
28pub struct Interpreter {
29 root: std::sync::RwLock<Env>,
31}
32
33impl Default for Interpreter {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl Interpreter {
40 pub fn new() -> Self {
41 let mut env = Env::new();
42 for (name, value) in builtin_table() {
43 env = env.extend(name, value);
44 }
45 Self {
46 root: std::sync::RwLock::new(env),
47 }
48 }
49
50 pub fn new_with_system() -> Self {
55 let mut env = Env::new();
56 for (name, value) in builtin_table() {
57 env = env.extend(name, value);
58 }
59 for (name, value) in crate::system::system_builtin_table() {
60 env = env.extend(name, value);
61 }
62 Self {
63 root: std::sync::RwLock::new(env),
64 }
65 }
66
67 pub fn eval_source(&self, src: &str) -> Result<Value> {
69 let forms = read(src)?;
70 self.eval_forms(&forms)
71 }
72
73 pub fn eval_forms(&self, forms: &[Sexp]) -> Result<Value> {
74 let mut last = Value::Nil;
75 for f in forms {
76 last = self.eval(f, &self.root_env())?;
77 }
78 Ok(last)
79 }
80
81 pub fn root_env(&self) -> Env {
82 self.root.read().unwrap().clone()
83 }
84
85 pub fn define(&self, name: impl Into<String>, value: Value) {
87 let mut root = self.root.write().unwrap();
88 *root = root.extend(name, value);
89 }
90
91 pub fn eval(&self, s: &Sexp, env: &Env) -> Result<Value> {
94 match s {
95 Sexp::Nil => Ok(Value::Nil),
96 Sexp::Atom(a) => self.eval_atom(a, env),
97 Sexp::Quote(inner) => Ok(self.sexp_to_value(inner)),
98 Sexp::Quasiquote(inner) => self.eval_quasiquote(inner, env),
99 Sexp::Unquote(_) | Sexp::UnquoteSplice(_) => Err(EvalError::Malformed {
100 form: "unquote".into(),
101 reason: "unquote outside quasiquote".into(),
102 }),
103 Sexp::List(items) => {
104 if items.is_empty() {
105 return Ok(Value::Nil);
106 }
107 if let Some(head) = items[0].as_symbol() {
109 match head {
110 "quote" => return self.sf_quote(items),
111 "if" => return self.sf_if(items, env),
112 "let" => return self.sf_let(items, env),
113 "letrec" => return self.sf_letrec(items, env),
114 "lambda" | "fn" => return self.sf_lambda(items, env),
115 "define" => return self.sf_define(items, env),
116 "begin" | "do" => return self.sf_begin(items, env),
117 "and" => return self.sf_and(items, env),
118 "or" => return self.sf_or(items, env),
119 _ => {}
120 }
121 }
122 let head_val = self.eval(&items[0], env)?;
124 let mut args = Vec::with_capacity(items.len() - 1);
125 for a in &items[1..] {
126 args.push(self.eval(a, env)?);
127 }
128 self.apply(&head_val, &args)
129 }
130 }
131 }
132
133 fn eval_atom(&self, a: &Atom, env: &Env) -> Result<Value> {
134 Ok(match a {
135 Atom::Int(n) => Value::Int(*n),
136 Atom::Float(n) => Value::Float(*n),
137 Atom::Str(s) => Value::Str(s.clone()),
138 Atom::Bool(b) => Value::Bool(*b),
139 Atom::Keyword(k) => Value::Keyword(k.clone()),
140 Atom::Symbol(name) => env
141 .lookup(name)
142 .ok_or_else(|| EvalError::Unbound(name.clone()))?,
143 })
144 }
145
146 fn sexp_to_value(&self, s: &Sexp) -> Value {
147 match s {
148 Sexp::Nil => Value::Nil,
149 Sexp::Atom(a) => match a {
150 Atom::Int(n) => Value::Int(*n),
151 Atom::Float(n) => Value::Float(*n),
152 Atom::Str(s) => Value::Str(s.clone()),
153 Atom::Bool(b) => Value::Bool(*b),
154 Atom::Keyword(k) => Value::Keyword(k.clone()),
155 Atom::Symbol(name) => Value::Symbol(name.clone()),
156 },
157 Sexp::List(xs) => {
158 Value::List(Arc::new(xs.iter().map(|x| self.sexp_to_value(x)).collect()))
159 }
160 other => Value::Quoted(Arc::new(other.clone())),
161 }
162 }
163
164 fn eval_quasiquote(&self, inner: &Sexp, env: &Env) -> Result<Value> {
165 match inner {
166 Sexp::Unquote(x) => self.eval(x, env),
167 Sexp::List(xs) => {
168 let mut out = Vec::with_capacity(xs.len());
169 for item in xs {
170 match item {
171 Sexp::UnquoteSplice(x) => {
172 let v = self.eval(x, env)?;
173 match v {
174 Value::List(items) => {
175 out.extend(items.iter().cloned());
176 }
177 other => {
178 return Err(EvalError::Type {
179 expected: "list (for ,@)".into(),
180 found: other.type_name().into(),
181 })
182 }
183 }
184 }
185 other => out.push(self.eval_quasiquote(other, env)?),
186 }
187 }
188 Ok(Value::List(Arc::new(out)))
189 }
190 Sexp::Atom(_) | Sexp::Nil => Ok(self.sexp_to_value(inner)),
191 Sexp::Quote(x) | Sexp::Quasiquote(x) => Ok(Value::Quoted(Arc::new((**x).clone()))),
192 Sexp::UnquoteSplice(_) => Err(EvalError::Malformed {
193 form: "quasiquote".into(),
194 reason: "bare ,@ outside of list".into(),
195 }),
196 }
197 }
198
199 fn sf_quote(&self, items: &[Sexp]) -> Result<Value> {
202 if items.len() != 2 {
203 return Err(EvalError::Malformed {
204 form: "quote".into(),
205 reason: "expected (quote x)".into(),
206 });
207 }
208 Ok(self.sexp_to_value(&items[1]))
209 }
210
211 fn sf_if(&self, items: &[Sexp], env: &Env) -> Result<Value> {
212 if items.len() < 3 || items.len() > 4 {
213 return Err(EvalError::Malformed {
214 form: "if".into(),
215 reason: "expected (if c t [e])".into(),
216 });
217 }
218 let cond = self.eval(&items[1], env)?;
219 if cond.is_truthy() {
220 self.eval(&items[2], env)
221 } else if items.len() == 4 {
222 self.eval(&items[3], env)
223 } else {
224 Ok(Value::Nil)
225 }
226 }
227
228 fn sf_let(&self, items: &[Sexp], env: &Env) -> Result<Value> {
229 if items.len() < 3 {
231 return Err(EvalError::Malformed {
232 form: "let".into(),
233 reason: "expected (let ((name val)...) body...)".into(),
234 });
235 }
236 let bindings = items[1].as_list().ok_or_else(|| EvalError::Malformed {
237 form: "let".into(),
238 reason: "bindings must be a list".into(),
239 })?;
240 let mut new_env = env.clone();
241 for b in bindings {
242 let pair = b.as_list().ok_or_else(|| EvalError::Malformed {
243 form: "let".into(),
244 reason: "each binding must be (name val)".into(),
245 })?;
246 if pair.len() != 2 {
247 return Err(EvalError::Malformed {
248 form: "let".into(),
249 reason: "each binding must be (name val)".into(),
250 });
251 }
252 let name = pair[0].as_symbol().ok_or_else(|| EvalError::Malformed {
253 form: "let".into(),
254 reason: "binding name must be a symbol".into(),
255 })?;
256 let value = self.eval(&pair[1], env)?;
257 new_env = new_env.extend(name, value);
258 }
259 self.eval_body(&items[2..], &new_env)
260 }
261
262 fn sf_letrec(&self, items: &[Sexp], env: &Env) -> Result<Value> {
263 if items.len() < 3 {
264 return Err(EvalError::Malformed {
265 form: "letrec".into(),
266 reason: "expected (letrec ((name val)...) body...)".into(),
267 });
268 }
269 let bindings = items[1].as_list().ok_or_else(|| EvalError::Malformed {
270 form: "letrec".into(),
271 reason: "bindings must be a list".into(),
272 })?;
273 let mut names: Vec<String> = Vec::new();
275 let mut exprs: Vec<Sexp> = Vec::new();
276 for b in bindings {
277 let pair = b.as_list().ok_or_else(|| EvalError::Malformed {
278 form: "letrec".into(),
279 reason: "each binding must be (name val)".into(),
280 })?;
281 let name = pair[0]
282 .as_symbol()
283 .ok_or_else(|| EvalError::Malformed {
284 form: "letrec".into(),
285 reason: "binding name must be a symbol".into(),
286 })?
287 .to_string();
288 names.push(name);
289 exprs.push(pair[1].clone());
290 }
291 let mut new_env = env.clone();
294 let thunks: Vec<Arc<Thunk>> = exprs
295 .iter()
296 .map(|e| Thunk::new(e.clone(), new_env.clone()))
297 .collect();
298 for (name, t) in names.iter().zip(&thunks) {
299 new_env = new_env.extend(name.clone(), Value::Thunk(t.clone()));
300 }
301 for t in &thunks {
304 let mut state = t.cell.lock().unwrap();
305 if let ThunkState::Unevaluated { env: e, .. } = &mut *state {
306 *e = new_env.clone();
307 }
308 }
309 self.eval_body(&items[2..], &new_env)
310 }
311
312 fn sf_lambda(&self, items: &[Sexp], env: &Env) -> Result<Value> {
313 if items.len() < 3 {
314 return Err(EvalError::Malformed {
315 form: "lambda".into(),
316 reason: "expected (lambda (params...) body...)".into(),
317 });
318 }
319 let (params, rest) = parse_params(&items[1])?;
320 let body = items[2..].to_vec();
321 Ok(Value::Lambda(Arc::new(Lambda {
322 params,
323 rest,
324 body,
325 env: env.clone(),
326 name: None,
327 })))
328 }
329
330 fn sf_define(&self, items: &[Sexp], env: &Env) -> Result<Value> {
331 if items.len() != 3 {
332 return Err(EvalError::Malformed {
333 form: "define".into(),
334 reason: "expected (define name expr)".into(),
335 });
336 }
337 let name = items[1]
338 .as_symbol()
339 .ok_or_else(|| EvalError::Malformed {
340 form: "define".into(),
341 reason: "name must be a symbol".into(),
342 })?
343 .to_string();
344 let value = self.eval(&items[2], env)?;
345 self.define(name.clone(), value.clone());
346 Ok(value)
347 }
348
349 fn sf_begin(&self, items: &[Sexp], env: &Env) -> Result<Value> {
350 self.eval_body(&items[1..], env)
351 }
352
353 fn sf_and(&self, items: &[Sexp], env: &Env) -> Result<Value> {
354 let mut last = Value::Bool(true);
355 for e in &items[1..] {
356 last = self.eval(e, env)?;
357 if !last.is_truthy() {
358 return Ok(last);
359 }
360 }
361 Ok(last)
362 }
363
364 fn sf_or(&self, items: &[Sexp], env: &Env) -> Result<Value> {
365 for e in &items[1..] {
366 let v = self.eval(e, env)?;
367 if v.is_truthy() {
368 return Ok(v);
369 }
370 }
371 Ok(Value::Bool(false))
372 }
373
374 fn eval_body(&self, forms: &[Sexp], env: &Env) -> Result<Value> {
375 let mut last = Value::Nil;
376 for f in forms {
377 last = self.eval(f, env)?;
378 }
379 Ok(last)
380 }
381
382 pub fn apply(&self, f: &Value, args: &[Value]) -> Result<Value> {
385 let forced_args: Vec<Value> = args
388 .iter()
389 .map(|v| self.force(v.clone()))
390 .collect::<Result<_>>()?;
391
392 match self.force(f.clone())? {
393 Value::Builtin(b) => {
394 if !b.arity.check(forced_args.len()) {
395 return Err(EvalError::Arity {
396 name: b.name.clone(),
397 expected: b.arity.describe(),
398 got: forced_args.len(),
399 });
400 }
401 (b.func)(&forced_args)
402 }
403 Value::Lambda(l) => self.apply_lambda(&l, &forced_args),
404 other => Err(EvalError::Type {
405 expected: "callable".into(),
406 found: other.type_name().into(),
407 }),
408 }
409 }
410
411 fn apply_lambda(&self, l: &Lambda, args: &[Value]) -> Result<Value> {
412 let mut env = l.env.clone();
413 match &l.rest {
414 None => {
415 if args.len() != l.params.len() {
416 return Err(EvalError::Arity {
417 name: l.name.clone().unwrap_or_else(|| "<lambda>".into()),
418 expected: format!("{}", l.params.len()),
419 got: args.len(),
420 });
421 }
422 for (p, a) in l.params.iter().zip(args.iter()) {
423 env = env.extend(p.clone(), a.clone());
424 }
425 }
426 Some(rest_name) => {
427 if args.len() < l.params.len() {
428 return Err(EvalError::Arity {
429 name: l.name.clone().unwrap_or_else(|| "<lambda>".into()),
430 expected: format!("at least {}", l.params.len()),
431 got: args.len(),
432 });
433 }
434 for (p, a) in l.params.iter().zip(args.iter()) {
435 env = env.extend(p.clone(), a.clone());
436 }
437 let rest = args[l.params.len()..].to_vec();
438 env = env.extend(rest_name.clone(), Value::List(Arc::new(rest)));
439 }
440 }
441 self.eval_body(&l.body, &env)
442 }
443
444 pub fn force(&self, v: Value) -> Result<Value> {
446 let t = match v {
447 Value::Thunk(t) => t,
448 other => return Ok(other),
449 };
450 let mut state = t.cell.lock().unwrap();
451 match std::mem::replace(&mut *state, ThunkState::Evaluating) {
452 ThunkState::Forced(v) => {
453 *state = ThunkState::Forced(v.clone());
454 Ok(v)
455 }
456 ThunkState::Evaluating => Err(EvalError::Other(
457 "thunk cycle: forcing an in-progress thunk".into(),
458 )),
459 ThunkState::Unevaluated { body, env } => {
460 drop(state);
461 let v = self.eval(&body, &env)?;
462 let v = self.force(v)?;
463 *t.cell.lock().unwrap() = ThunkState::Forced(v.clone());
464 Ok(v)
465 }
466 }
467 }
468}
469
470fn parse_params(s: &Sexp) -> Result<(Vec<String>, Option<String>)> {
471 let items = s.as_list().ok_or_else(|| EvalError::Malformed {
472 form: "lambda".into(),
473 reason: "params must be a list".into(),
474 })?;
475 let mut params = Vec::new();
476 let mut rest = None;
477 let mut saw_rest = false;
478 for (i, p) in items.iter().enumerate() {
479 let name = p.as_symbol().ok_or_else(|| EvalError::Malformed {
480 form: "lambda".into(),
481 reason: "param must be a symbol".into(),
482 })?;
483 if name == "&rest" || name == "&" {
484 if i + 2 != items.len() {
485 return Err(EvalError::Malformed {
486 form: "lambda".into(),
487 reason: "&rest must be followed by exactly one name".into(),
488 });
489 }
490 saw_rest = true;
491 continue;
492 }
493 if saw_rest {
494 rest = Some(name.to_string());
495 break;
496 }
497 params.push(name.to_string());
498 }
499 let _ = BTreeMap::<String, Value>::new(); Ok((params, rest))
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506
507 #[test]
508 fn evaluates_literals() {
509 let i = Interpreter::new();
510 assert!(matches!(i.eval_source("42").unwrap(), Value::Int(42)));
511 assert!(matches!(i.eval_source("#t").unwrap(), Value::Bool(true)));
512 match i.eval_source(r#""hi""#).unwrap() {
513 Value::Str(s) => assert_eq!(s, "hi"),
514 _ => panic!(),
515 }
516 }
517
518 #[test]
519 fn arithmetic() {
520 let i = Interpreter::new();
521 assert!(matches!(i.eval_source("(+ 1 2 3)").unwrap(), Value::Int(6)));
522 assert!(matches!(
523 i.eval_source("(* 2 3 4)").unwrap(),
524 Value::Int(24)
525 ));
526 assert!(matches!(i.eval_source("(- 10 3)").unwrap(), Value::Int(7)));
527 assert!(matches!(i.eval_source("(- 5)").unwrap(), Value::Int(-5)));
528 }
529
530 #[test]
531 fn if_and_booleans() {
532 let i = Interpreter::new();
533 assert!(matches!(
534 i.eval_source("(if (< 1 2) 'yes 'no)").unwrap(),
535 Value::Symbol(s) if s == "yes"
536 ));
537 }
538
539 #[test]
540 fn let_binds() {
541 let i = Interpreter::new();
542 let v = i.eval_source("(let ((x 10) (y 20)) (+ x y))").unwrap();
543 assert!(matches!(v, Value::Int(30)));
544 }
545
546 #[test]
547 fn lambda_and_apply() {
548 let i = Interpreter::new();
549 let v = i.eval_source("((lambda (x y) (+ x y)) 3 4)").unwrap();
550 assert!(matches!(v, Value::Int(7)));
551 }
552
553 #[test]
554 fn closures_capture_env() {
555 let i = Interpreter::new();
556 let v = i
557 .eval_source(
558 "(let ((make-add (lambda (x) (lambda (y) (+ x y))))) \
559 ((make-add 3) 4))",
560 )
561 .unwrap();
562 assert!(matches!(v, Value::Int(7)));
563 }
564
565 #[test]
566 fn letrec_enables_mutual_reference() {
567 let i = Interpreter::new();
568 let v = i
569 .eval_source(
570 "(letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1))))) \
571 (odd? (lambda (n) (if (= n 0) #f (even? (- n 1)))))) \
572 (even? 10))",
573 )
574 .unwrap();
575 assert!(matches!(v, Value::Bool(true)));
576 }
577
578 #[test]
579 fn quasiquote_and_unquote() {
580 let i = Interpreter::new();
581 let v = i.eval_source("(let ((x 5)) `(1 ,x 3))").unwrap();
582 match v {
583 Value::List(xs) => {
584 assert_eq!(xs.len(), 3);
585 assert!(matches!(xs[1], Value::Int(5)));
586 }
587 _ => panic!(),
588 }
589 }
590
591 #[test]
592 fn string_append_and_tostring() {
593 let i = Interpreter::new();
594 let v = i
595 .eval_source(r#"(string-append "hello-" (toString 42))"#)
596 .unwrap();
597 assert!(matches!(v, Value::Str(s) if s == "hello-42"));
598 }
599
600 #[test]
601 fn derivation_builtin_produces_typed_value() {
602 let i = Interpreter::new();
603 let v = i
604 .eval_source(
605 r#"(derivation
606 (attrs
607 "name" "hello"
608 "version" "1.0"))"#,
609 )
610 .unwrap();
611 match v {
612 Value::Derivation(d) => {
613 assert_eq!(d.name, "hello");
614 assert_eq!(d.version.as_deref(), Some("1.0"));
615 }
616 _ => panic!("expected Derivation, got {v:?}"),
617 }
618 }
619
620 #[test]
621 fn derivation_store_path_is_deterministic() {
622 let i = Interpreter::new();
623 let v1 = i
624 .eval_source(r#"(derivation (attrs "name" "x" "version" "1"))"#)
625 .unwrap();
626 let v2 = i
627 .eval_source(r#"(derivation (attrs "name" "x" "version" "1"))"#)
628 .unwrap();
629 let (Value::Derivation(a), Value::Derivation(b)) = (v1, v2) else {
630 panic!("expected derivations");
631 };
632 assert_eq!(a.store_path(), b.store_path());
633 }
634}