1pub mod host;
2pub mod machine;
3pub mod read;
4
5use std::collections::HashMap;
6use std::fmt;
7
8use smol_str::SmolStr;
9
10pub use std::sync::Arc as Shared;
11
12pub use host::Atom;
13
14pub type RcExp = Shared<Exp>;
15
16#[derive(Clone)]
20pub struct RcVal(Shared<Val>);
21
22impl RcVal {
23 pub fn new(v: Val) -> RcVal {
24 RcVal(Shared::new(v))
25 }
26
27 pub fn refs(&self) -> usize {
30 Shared::strong_count(&self.0)
31 }
32
33 pub fn unwrap_or_clone(mut self) -> Val {
34 match Shared::get_mut(&mut self.0) {
35 Some(v) => std::mem::replace(v, Val::Nil),
36 None => Val::clone(&self.0),
37 }
38 }
39}
40
41fn detach(slot: &mut Shared<Val>, out: &mut Vec<RcVal>) {
44 if Shared::strong_count(slot) != 1 {
47 return;
48 }
49 let Some(v) = Shared::get_mut(slot) else {
50 return;
51 };
52 if !matches!(v, Val::Pair(_, _) | Val::Raise(_)) {
53 return;
54 }
55 let (a, b) = match std::mem::replace(v, Val::Nil) {
58 Val::Pair(a, b) => (a, Some(b)),
59 Val::Raise(a) => (a, None),
60 _ => unreachable!("guarded above"),
61 };
62 for c in std::iter::once(a).chain(b) {
63 if matches!(&*c, Val::Pair(_, _) | Val::Raise(_)) {
64 out.push(c);
65 }
66 }
67}
68
69impl Drop for RcVal {
70 fn drop(&mut self) {
71 let mut stack: Vec<RcVal> = Vec::new();
72 detach(&mut self.0, &mut stack);
73 while let Some(mut rc) = stack.pop() {
74 detach(&mut rc.0, &mut stack);
76 }
77 }
78}
79
80impl std::ops::Deref for RcVal {
81 type Target = Val;
82 #[inline]
83 fn deref(&self) -> &Val {
84 &self.0
85 }
86}
87
88impl From<Val> for RcVal {
89 fn from(v: Val) -> RcVal {
90 RcVal::new(v)
91 }
92}
93
94impl AsRef<Val> for RcVal {
95 #[inline]
96 fn as_ref(&self) -> &Val {
97 &self.0
98 }
99}
100
101impl PartialEq for RcVal {
102 fn eq(&self, other: &RcVal) -> bool {
103 Shared::ptr_eq(&self.0, &other.0) || **self == **other
104 }
105}
106
107impl fmt::Debug for RcVal {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 fmt::Debug::fmt(&**self, f)
110 }
111}
112
113impl fmt::Display for RcVal {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 fmt::Display::fmt(&**self, f)
116 }
117}
118
119const _: fn() = || {
122 fn assert_send_sync<T: Send + Sync>() {}
123 assert_send_sync::<Exp>();
124 assert_send_sync::<Val>();
125 assert_send_sync::<Env>();
126};
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub enum Prim1 {
133 Car,
134 Cdr,
135 IsNum,
136 IsSym,
137 IsNil,
138 IsPair,
139
140 CellNew,
141 CellRead,
142
143 Spawn,
144 Receive,
146 Monitor,
147 SpawnMonitor,
151
152 IsRaise,
157 RaiseValue,
158
159 IsCodeFun,
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165pub enum Prim2 {
166 Cons,
167 Plus,
168 Minus,
169 Times,
170 Div,
171 Mod,
172 Lt,
173 Eq,
174
175 CellSet,
176 Send,
177 LimitTurns,
180}
181
182impl Prim1 {
183 const NAMES: [(Prim1, &'static str); 15] = [
184 (Prim1::Car, "car"),
185 (Prim1::Cdr, "cdr"),
186 (Prim1::IsNum, "num?"),
187 (Prim1::IsSym, "sym?"),
188 (Prim1::IsNil, "nil?"),
189 (Prim1::IsPair, "pair?"),
190 (Prim1::CellNew, "cell-new"),
191 (Prim1::CellRead, "cell-read"),
192 (Prim1::Spawn, "spawn"),
193 (Prim1::Receive, "receive"),
194 (Prim1::Monitor, "monitor"),
195 (Prim1::SpawnMonitor, "spawn-monitor"),
196 (Prim1::IsRaise, "raise?"),
197 (Prim1::RaiseValue, "raise-value"),
198 (Prim1::IsCodeFun, "code-fun?"),
199 ];
200
201 pub fn from_name(s: &str) -> Option<Prim1> {
202 Prim1::NAMES.iter().find(|(_, n)| *n == s).map(|(p, _)| *p)
203 }
204
205 pub fn name(self) -> &'static str {
206 Prim1::NAMES
207 .iter()
208 .find_map(|(p, n)| (*p == self).then_some(*n))
209 .expect("every primitive is named")
210 }
211
212 pub fn pure(self) -> bool {
215 !matches!(
216 self,
217 Prim1::CellNew
218 | Prim1::CellRead
219 | Prim1::Spawn
220 | Prim1::Receive
221 | Prim1::Monitor
222 | Prim1::SpawnMonitor
223 )
224 }
225}
226
227impl Prim2 {
228 const NAMES: [(Prim2, &'static str); 11] = [
229 (Prim2::Cons, "cons"),
230 (Prim2::Plus, "+"),
231 (Prim2::Minus, "-"),
232 (Prim2::Times, "*"),
233 (Prim2::Div, "/"),
234 (Prim2::Mod, "%"),
235 (Prim2::Lt, "<"),
236 (Prim2::Eq, "eq?"),
237 (Prim2::CellSet, "cell-set!"),
238 (Prim2::Send, "send"),
239 (Prim2::LimitTurns, "limit-turns"),
240 ];
241
242 pub fn from_name(s: &str) -> Option<Prim2> {
243 Prim2::NAMES.iter().find(|(_, n)| *n == s).map(|(p, _)| *p)
244 }
245
246 pub fn name(self) -> &'static str {
247 Prim2::NAMES
248 .iter()
249 .find_map(|(p, n)| (*p == self).then_some(*n))
250 .expect("every primitive is named")
251 }
252
253 pub fn pure(self) -> bool {
254 !matches!(self, Prim2::CellSet | Prim2::Send | Prim2::LimitTurns)
255 }
256}
257
258#[derive(Debug, Clone, PartialEq)]
259pub enum Exp {
260 Lit(i64),
261 Flo(f64),
264 Sym(SmolStr),
265 Atom(Atom),
266 Nil,
267 Var(usize),
269 Lam(u16, RcExp),
273 App(RcExp, Box<[RcExp]>),
274 Apply(RcExp, RcExp),
278 Let(RcExp, RcExp),
279 If(RcExp, RcExp, RcExp),
280
281 Prim1(Prim1, RcExp),
282 Prim2(Prim2, RcExp, RcExp),
283 Op(u16, Vec<RcExp>),
285
286 Lift(RcExp),
287 LiftFun(RcExp, RcExp),
291 LiftRef(RcExp, RcExp),
294 Proc(RcVal),
297 Run(RcExp, RcExp),
298 IsCode(RcExp, RcExp),
299
300 Evalms(RcExp, RcExp),
303
304 Catch(RcExp),
305 Throw(RcExp),
306}
307
308impl Exp {
309 fn children(&self) -> ([Option<&RcExp>; 3], &[RcExp]) {
312 const NONE: [Option<&RcExp>; 3] = [None, None, None];
313 match self {
314 Exp::Lit(_)
315 | Exp::Flo(_)
316 | Exp::Sym(_)
317 | Exp::Atom(_)
318 | Exp::Nil
319 | Exp::Var(_)
320 | Exp::Proc(_) => (NONE, &[]),
321 Exp::Lam(_, a) | Exp::Prim1(_, a) | Exp::Lift(a) | Exp::Catch(a) | Exp::Throw(a) => {
322 ([Some(a), None, None], &[])
323 }
324 Exp::Let(a, b)
325 | Exp::Apply(a, b)
326 | Exp::LiftFun(a, b)
327 | Exp::Prim2(_, a, b)
328 | Exp::LiftRef(a, b)
329 | Exp::Run(a, b)
330 | Exp::IsCode(a, b)
331 | Exp::Evalms(a, b) => ([Some(a), Some(b), None], &[]),
332 Exp::If(c, t, e) => ([Some(c), Some(t), Some(e)], &[]),
333 Exp::App(f, args) => ([Some(f), None, None], args.as_ref()),
334 Exp::Op(_, args) => (NONE, args.as_slice()),
335 }
336 }
337
338 pub fn iter_children(&self) -> impl Iterator<Item = &RcExp> {
339 let (slots, rest) = self.children();
340 slots.into_iter().flatten().chain(rest)
341 }
342}
343
344impl Drop for Exp {
347 fn drop(&mut self) {
348 let mut stack: Vec<RcExp> = Vec::new();
349 take_children(self, &mut stack);
350 while let Some(rc) = stack.pop() {
351 if let Ok(mut e) = RcExp::try_unwrap(rc) {
352 take_children(&mut e, &mut stack);
353 }
354 }
355 }
356}
357
358fn take_children(e: &mut Exp, out: &mut Vec<RcExp>) {
359 let mut grab = |slot: &mut RcExp| out.push(std::mem::replace(slot, rc_exp(Exp::Nil)));
360 match e {
361 Exp::Lit(_)
362 | Exp::Flo(_)
363 | Exp::Sym(_)
364 | Exp::Atom(_)
365 | Exp::Nil
366 | Exp::Var(_)
367 | Exp::Proc(_) => {}
368 Exp::Op(_, args) => out.append(args),
369 Exp::Lam(_, a) | Exp::Prim1(_, a) | Exp::Lift(a) | Exp::Catch(a) | Exp::Throw(a) => grab(a),
370 Exp::App(f, args) => {
371 grab(f);
372 for slot in args.iter_mut() {
373 grab(slot);
374 }
375 }
376 Exp::Let(a, b)
377 | Exp::Apply(a, b)
378 | Exp::LiftFun(a, b)
379 | Exp::Prim2(_, a, b)
380 | Exp::LiftRef(a, b)
381 | Exp::Run(a, b)
382 | Exp::IsCode(a, b)
383 | Exp::Evalms(a, b) => {
384 grab(a);
385 grab(b);
386 }
387 Exp::If(c, t, f) => {
388 grab(c);
389 grab(t);
390 grab(f);
391 }
392 }
393}
394
395pub fn count_nodes(e: &Exp) -> usize {
396 let mut count = 0usize;
397 let mut stack: Vec<&Exp> = vec![e];
398 while let Some(node) = stack.pop() {
399 count += 1;
400 stack.extend(node.iter_children().map(|c| c.as_ref()));
401 }
402 count
403}
404
405#[derive(Debug, Clone)]
406pub enum Val {
407 Num(i64),
408 Flo(f64),
409 Sym(SmolStr),
410 Atom(Atom),
412 Nil,
413 Pair(RcVal, RcVal),
414 Clo(Env, RcExp),
415 Code(RcExp),
416 Cell(usize),
418 Raise(RcVal),
422}
423
424pub fn raise(v: Val) -> Val {
425 Val::Raise(rc_val(v))
426}
427
428impl PartialEq for Val {
429 fn eq(&self, other: &Self) -> bool {
432 let mut work: Vec<(&RcVal, &RcVal)> = Vec::new();
433 let (mut a, mut b) = (self, other);
434 loop {
435 match (a, b) {
436 (Val::Num(x), Val::Num(y)) if x == y => {}
437 (Val::Flo(x), Val::Flo(y)) if x == y => {}
438 (Val::Sym(x), Val::Sym(y)) if x == y => {}
439 (Val::Atom(x), Val::Atom(y)) if x == y => {}
440 (Val::Nil, Val::Nil) => {}
441 (Val::Cell(x), Val::Cell(y)) if x == y => {}
442 (Val::Pair(x1, x2), Val::Pair(y1, y2)) => {
443 work.push((x1, y1));
444 work.push((x2, y2));
445 }
446 (Val::Raise(x), Val::Raise(y)) => work.push((x, y)),
447 (Val::Code(x), Val::Code(y)) | (Val::Clo(_, x), Val::Clo(_, y)) => {
448 if !ptr_eq(x, y) && x != y {
449 return false;
450 }
451 }
452 _ => return false,
453 }
454 loop {
455 match work.pop() {
456 None => return true,
457 Some((x, y)) if ptr_eq(&x.0, &y.0) => {}
458 Some((x, y)) => {
459 (a, b) = (x, y);
460 break;
461 }
462 }
463 }
464 }
465 }
466}
467
468#[inline]
469pub fn rc_exp(e: Exp) -> RcExp {
470 RcExp::new(e)
471}
472
473#[inline]
474pub fn rc_val(v: Val) -> RcVal {
475 RcVal::new(v)
476}
477
478#[inline]
479pub fn ptr_eq<T: ?Sized>(a: &Shared<T>, b: &Shared<T>) -> bool {
480 Shared::ptr_eq(a, b)
481}
482
483#[inline]
484pub fn pair(a: Val, b: Val) -> Val {
485 Val::Pair(rc_val(a), rc_val(b))
486}
487
488#[inline]
489pub fn code(e: Exp) -> Val {
490 Val::Code(rc_exp(e))
491}
492
493pub fn list(items: &[Val]) -> Val {
494 items.iter().rev().fold(Val::Nil, |t, h| pair(h.clone(), t))
495}
496
497pub fn list_elements(v: &Val) -> Option<Vec<Val>> {
498 let mut out = Vec::new();
499 let mut cur = v;
500 loop {
501 match cur {
502 Val::Nil => return Some(out),
503 Val::Pair(h, t) => {
504 out.push((**h).clone());
505 cur = t;
506 }
507 _ => return None,
508 }
509 }
510}
511
512#[derive(Clone)]
516pub struct Env(Shared<EnvChunk>);
517
518impl fmt::Debug for Env {
521 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522 write!(f, "Env(len={})", self.len())
523 }
524}
525
526struct EnvChunk {
527 parent: Option<Env>,
528 base: usize,
529 vals: Vec<Val>,
530}
531
532impl Env {
533 pub fn new(vals: Vec<Val>) -> Env {
534 Env(Shared::new(EnvChunk {
535 parent: None,
536 base: 0,
537 vals,
538 }))
539 }
540
541 pub fn len(&self) -> usize {
542 self.0.base + self.0.vals.len()
543 }
544
545 pub fn is_empty(&self) -> bool {
546 self.len() == 0
547 }
548
549 pub fn get(&self, i: usize) -> Option<&Val> {
550 let mut cur = self;
551 loop {
552 let chunk = &*cur.0;
553 if i >= chunk.base {
554 return chunk.vals.get(i - chunk.base);
555 }
556 cur = chunk.parent.as_ref()?;
557 }
558 }
559
560 pub fn iter(&self) -> impl Iterator<Item = &Val> + '_ {
563 (0..self.len()).map(|i| self.get(i).expect("index below len"))
564 }
565
566 pub fn ptr_eq(a: &Env, b: &Env) -> bool {
567 Shared::ptr_eq(&a.0, &b.0)
568 }
569
570 fn link(self, val: Val) -> Env {
571 let base = self.len();
572 Env(Shared::new(EnvChunk {
573 parent: Some(self),
574 base,
575 vals: vec![val],
576 }))
577 }
578
579 pub fn push(&self, val: Val) -> Env {
582 Env::clone(self).link(val)
583 }
584
585 pub fn frame(self, vals: Vec<Val>) -> Env {
588 let base = self.len();
589 Env(Shared::new(EnvChunk {
590 parent: Some(self),
591 base,
592 vals,
593 }))
594 }
595
596 pub fn push_owned(mut self, val: Val) -> Env {
597 match Shared::get_mut(&mut self.0) {
598 Some(chunk) => {
599 chunk.vals.push(val);
600 self
601 }
602 None => self.link(val),
603 }
604 }
605}
606
607impl Default for Env {
608 fn default() -> Self {
609 Env::new(Vec::new())
610 }
611}
612
613fn repeats(v: &Val) -> HashMap<*const Val, usize> {
617 let mut seen: HashMap<*const Val, usize> = HashMap::new();
618 let mut work: Vec<&Val> = vec![v];
619 while let Some(x) = work.pop() {
620 let kids: &[&RcVal] = match x {
621 Val::Pair(a, b) => &[a, b],
622 Val::Raise(a) => &[a],
623 _ => continue,
624 };
625 for c in kids {
626 if c.refs() == 1 || !matches!(&***c, Val::Pair(_, _) | Val::Raise(_)) {
629 work.push(c);
630 continue;
631 }
632 let n = seen.entry(&***c as *const Val).or_insert(0);
633 *n += 1;
634 if *n == 1 {
636 work.push(c);
637 }
638 }
639 }
640 seen
641}
642
643enum Say<'a> {
644 Val(&'a Val),
645 Tail(&'a Val),
647 Text(&'static str),
648}
649
650impl fmt::Display for Val {
654 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655 let repeated = repeats(self);
656 let mut named: HashMap<*const Val, usize> = HashMap::new();
657 let mut stack = vec![Say::Val(self)];
658
659 while let Some(step) = stack.pop() {
660 let v = match step {
661 Say::Text(s) => {
662 f.write_str(s)?;
663 continue;
664 }
665 Say::Tail(v) => {
666 if !is_repeated(v, &repeated) {
669 match v {
670 Val::Nil => continue,
671 Val::Pair(h, t) => {
672 f.write_str(" ")?;
673 stack.push(Say::Tail(t));
674 stack.push(Say::Val(h));
675 continue;
676 }
677 _ => {}
678 }
679 }
680 f.write_str(" . ")?;
681 v
682 }
683 Say::Val(v) => v,
684 };
685
686 if is_repeated(v, &repeated) {
687 let at = v as *const Val;
688 if let Some(n) = named.get(&at) {
689 write!(f, "#{n}#")?;
690 continue;
691 }
692 let n = named.len();
693 named.insert(at, n);
694 write!(f, "#{n}=")?;
695 }
696
697 match v {
698 Val::Num(n) => write!(f, "{n}")?,
699 Val::Flo(x) => write!(f, "{x:?}")?,
700 Val::Nil => f.write_str("nil")?,
701 Val::Sym(s) => write!(f, "'{s}")?,
702 Val::Atom(a) => write!(f, "{a}")?,
703 Val::Clo(_, _) => f.write_str("#<closure>")?,
704 Val::Code(e) => write!(f, "#<code {e}>")?,
705 Val::Cell(i) => write!(f, "#<cell {i}>")?,
706 Val::Raise(a) => {
707 f.write_str("#<raised ")?;
708 stack.push(Say::Text(">"));
709 stack.push(Say::Val(a));
710 }
711 Val::Pair(h, t) => {
712 f.write_str("(")?;
713 stack.push(Say::Text(")"));
714 stack.push(Say::Tail(t));
715 stack.push(Say::Val(h));
716 }
717 }
718 }
719 Ok(())
720 }
721}
722
723fn is_repeated(v: &Val, repeated: &HashMap<*const Val, usize>) -> bool {
724 repeated.get(&(v as *const Val)).is_some_and(|&n| n > 1)
725}
726
727impl fmt::Display for Exp {
730 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
731 let mut stack = vec![Tok::Node(self)];
732 while let Some(tok) = stack.pop() {
733 let e = match tok {
734 Tok::Text(s) => {
735 f.write_str(s)?;
736 continue;
737 }
738 Tok::Node(e) => e,
739 };
740 match e {
741 Exp::Lit(n) => write!(f, "{n}")?,
742 Exp::Flo(x) => write!(f, "{x:?}")?,
743 Exp::Sym(s) => write!(f, "'{s}")?,
744 Exp::Atom(a) => write!(f, "{a}")?,
745 Exp::Nil => f.write_str("()")?,
746 Exp::Var(i) => write!(f, "x{i}")?,
747 Exp::Proc(v) => match &**v {
750 Val::Clo(_, _) | Val::Code(_) | Val::Cell(_) | Val::Raise(_) => {
751 write!(f, "{v}")?
752 }
753 v => write!(f, "#<{v}>")?,
754 },
755
756 Exp::Lam(1, b) => form(f, "lambda", &[b], &mut stack)?,
759 Exp::Lam(n, b) => {
760 write!(f, "(lambda/{n} ")?;
761 stack.push(Tok::Text(")"));
762 stack.push(Tok::Node(b));
763 }
764 Exp::App(g, args) => {
766 stack.push(Tok::Text(")"));
767 for a in args.iter().rev() {
768 stack.push(Tok::Node(a));
769 stack.push(Tok::Text(" "));
770 }
771 stack.push(Tok::Node(g));
772 f.write_str("(")?;
773 }
774 Exp::Apply(g, args) => form(f, "apply", &[g, args], &mut stack)?,
775 Exp::Let(a, b) => form(f, "let", &[a, b], &mut stack)?,
776 Exp::If(c, t, o) => form(f, "if", &[c, t, o], &mut stack)?,
777 Exp::Prim1(p, a) => form(f, p.name(), &[a], &mut stack)?,
778 Exp::Prim2(p, a, b) => form(f, p.name(), &[a, b], &mut stack)?,
779 Exp::Op(idx, args) => {
780 let kids: Vec<&RcExp> = args.iter().collect();
781 form(f, host::registry().get(*idx).name, &kids, &mut stack)?
782 }
783 Exp::Lift(a) => form(f, "lift", &[a], &mut stack)?,
784 Exp::LiftFun(a, b) => form(f, "lift-fun", &[a, b], &mut stack)?,
785 Exp::LiftRef(a, b) => form(f, "lift-ref", &[a, b], &mut stack)?,
786 Exp::Run(a, b) => form(f, "run", &[a, b], &mut stack)?,
787 Exp::IsCode(a, b) => form(f, "code?", &[a, b], &mut stack)?,
788 Exp::Evalms(a, b) => form(f, "evalms", &[a, b], &mut stack)?,
789 Exp::Catch(a) => form(f, "catch", &[a], &mut stack)?,
790 Exp::Throw(a) => form(f, "throw", &[a], &mut stack)?,
791 }
792 }
793 Ok(())
794 }
795}
796
797enum Tok<'a> {
798 Node(&'a Exp),
799 Text(&'static str),
800}
801
802fn form<'a>(
804 f: &mut fmt::Formatter<'_>,
805 head: &str,
806 kids: &[&'a RcExp],
807 stack: &mut Vec<Tok<'a>>,
808) -> fmt::Result {
809 stack.push(Tok::Text(")"));
810 for k in kids.iter().rev() {
811 stack.push(Tok::Node(k));
812 stack.push(Tok::Text(" "));
813 }
814 write!(f, "({head}")
815}
816
817#[cfg(test)]
818mod tests {
819 use super::*;
820
821 #[test]
822 fn a_long_list_costs_no_stack() {
823 let build = || (0..10_000).fold(Val::Nil, |t, i| pair(Val::Num(i), t));
824 assert_eq!(build(), build());
825 }
826
827 #[test]
828 fn a_deep_car_chain_costs_no_stack() {
829 let build = || (0..10_000).fold(Val::Nil, |t, _| pair(t, Val::Nil));
830 assert_eq!(build(), build());
831 }
832
833 #[test]
834 fn printing_costs_no_stack() {
835 let deep = (0..10_000).fold(Val::Nil, |t, _| pair(t, Val::Nil));
836 assert_eq!(deep.to_string().len(), 20_003);
837 let long = (0..10_000).fold(Val::Nil, |t, _| pair(Val::Num(1), t));
838 assert_eq!(long.to_string().len(), 20_001);
839 }
840
841 #[test]
842 fn a_shared_subgraph_is_named_rather_than_repeated() {
843 let one = pair(Val::Num(1), Val::Nil);
844 let wide = (0..20).fold(one, |v, _| pair(Val::clone(&v), v));
845 let s = wide.to_string();
846 assert!(s.len() < 600, "{} chars", s.len());
847 assert!(s.starts_with("((#0=(#1="), "{s}");
848 assert!(s.ends_with(" #0# . #37#)"), "{s}");
849
850 let leaf = rc_val(pair(Val::Sym("a".into()), Val::Nil));
853 let two = |a: &RcVal, b: &RcVal| Val::Pair(RcVal::clone(a), RcVal::clone(b));
854
855 let nil = rc_val(Val::Nil);
858 assert_eq!(two(&nil, &nil).to_string(), "(nil)");
859
860 assert_eq!(two(&leaf, &leaf).to_string(), "(#0=('a) . #0#)");
863 }
864}