pype 0.2.1

Python oneliner helper
Documentation
use super::lisp_exp::*;

use std::cell::RefCell;
use std::rc::Rc;

pub struct LispArena {
    arena: Vec<LispExpRefStrong>,
    symbols: std::collections::HashMap<String, LispExpRef>,
}

impl LispArena {
    pub fn alloc(&mut self, exp: LispExp) -> LispExpRef {
        let exp_ref = Rc::new(RefCell::new(exp));
        self.arena.push(exp_ref.clone());
        Rc::downgrade(&exp_ref)
    }

    pub fn alloc_symbol(&mut self, sym: &str) -> LispExpRef {
        if let Some(exp) = self.symbols.get(sym) {
            exp.clone()
        } else {
            let exp = self.alloc(LispExp::new_symbol(sym));
            self.symbols.insert(sym.to_string(), exp.clone());
            exp
        }
    }
}

#[macro_export]
macro_rules! alloc {
    ($arena: expr, []) => {
        $arena.alloc_symbol("nil")
    };
    ($arena: expr, [$exp: tt]) => {{
        let e = $crate::alloc!($arena, $exp);
        let nil = $crate::alloc!($arena, []);
        $arena.alloc((e, nil).into())
    }};
    ($arena: expr, [$car: tt ; $cdr: tt]) => {{
        let car = $crate::alloc!($arena, $car);
        let cdr = $crate::alloc!($arena, $cdr);
        $arena.alloc((car, cdr).into())
    }};
    ($arena: expr, [$car: tt, $($rest: tt),* $( ; $last_cdr: tt )?]) => {{
        let car = $crate::alloc!($arena, $car);
        let cdr = $crate::alloc!($arena, [$($rest),* $( ; $last_cdr )?]);
        $arena.alloc((car, cdr).into())
    }};
    ($arena: expr, $exp: tt) => {
        $exp.clone()
    };
}

impl Default for LispArena {
    fn default() -> Self {
        Self {
            arena: Vec::with_capacity(10000),
            symbols: std::collections::HashMap::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_alloc() {
        let mut arena = LispArena::default();
        let nil = arena.alloc_symbol("nil");
        let c1 = arena.alloc(1.into());
        let c2 = arena.alloc(2.into());
        let c3 = arena.alloc(3.into());

        let e1 = arena.alloc((&c1, &nil).into());
        let e2 = arena.alloc((&c2, &e1).into());
        let e3 = arena.alloc((&c3, &e2).into());
        assert_eq!(e3.upgrade().unwrap().borrow().to_string(), "(3 2 1)");
    }

    #[test]
    fn test_alloc_macro() {
        let mut arena = LispArena::default();
        let c1 = arena.alloc(1.into());
        let c2 = arena.alloc(2.into());
        let c3 = arena.alloc(3.into());

        let e1 = alloc!(arena, [c1]);
        assert_eq!(e1.upgrade().unwrap().borrow().to_string(), "(1)");

        let e2 = alloc!(arena, [c1, c2, c3]);
        assert_eq!(e2.upgrade().unwrap().borrow().to_string(), "(1 2 3)");

        let e3 = alloc!(arena, [[c1, c2], c3]);
        assert_eq!(e3.upgrade().unwrap().borrow().to_string(), "((1 2) 3)");

        let e4 = alloc!(arena, [c1, [c2, c3]]);
        assert_eq!(e4.upgrade().unwrap().borrow().to_string(), "(1 (2 3))");
    }

    #[test]
    fn test_alloc_macro_dotlist() {
        let mut arena = LispArena::default();
        let c1 = arena.alloc(1.into());
        let c2 = arena.alloc(2.into());
        let c3 = arena.alloc(3.into());

        let e1 = alloc!(arena, [c1]);
        assert_eq!(e1.upgrade().unwrap().borrow().to_string(), "(1)");

        let e2 = alloc!(arena, [c1; c2]);
        assert_eq!(e2.upgrade().unwrap().borrow().to_string(), "(1 . 2)");

        let e3 = alloc!(arena, [c1, c2, c3; e1]);
        assert_eq!(e3.upgrade().unwrap().borrow().to_string(), "(1 2 3 1)");

        let e4 = alloc!(arena, [c1, c2, c3; [c1, c2]]);
        assert_eq!(e4.upgrade().unwrap().borrow().to_string(), "(1 2 3 1 2)");

        let e5 = alloc!(arena, [c1, c2, c3; [c1, c2; c3]]);
        assert_eq!(
            e5.upgrade().unwrap().borrow().to_string(),
            "(1 2 3 1 2 . 3)"
        );
    }
}