// -- Questions -------------------------------------------------------------- // It may be an idea to have extra enum's for our predefined symbols. // Macros help us perhaps to provide the default symbol name and symbol // value for these. // We don't want to use any unsafe code. // But: The are no global variables. Pass the world around all the time? // --------------------------------------------------------------------------- #![allow(dead_code)] #![allow(unused_assignments)] #![allow(unused_variables)] #![allow(unused_macros)] use std::rc::Rc; use std::cell::RefCell; use std::io::{self, Write}; use std::fmt; // I needed some time and struggled with making a macro expand to sth // including a $ until I came up with the solution to pass that $ to the // macro. This is the sole purpose of the $dollar parameter. // The task here is to define macros, which will insert a .clone() // applied to each argument. We hope to later take advantage of our // macros to compile the SUBR list. macro_rules! define_clonies { ($($i:ident),*) => { $(define_clonie!($i);)* }} macro_rules! define_clonie { ($i:ident) => { define_clonie_1!($i,$); }} macro_rules! define_clonie_1 { ($i:ident, $dollar:tt) => { macro_rules! $i { ($dollar($x :expr),*) => { $i ($dollar($x.clone()),*) }}}} macro_rules! defun { ($i:ident ($($p:ident),*) $body:block) => { define_clonie!($i); fn $i ($($p :Q),*) -> Q $body }; ($i:ident ($($p:ident),*) -> $r:ty $body:block) => { define_clonie!($i); fn $i ($($p :Q),*) -> $r $body }} type Q = Node; type Flonum = f64; struct Cons { car :Node, cdr :Node } struct Symbol { val :Node, nam: String, plist :Node } #[derive(Clone)] enum Node { Flonum (Flonum), Cons (Rc>), Symbol (Rc>), Unbound } // -- Conses ----------------------------------------------------------------- defun! (cons (x, y) { Node::Cons (Rc::new(RefCell::new(Cons { car: x, cdr: y }))) }); defun! (car (x) { match x { Node::Cons(x) => { let a = Rc::clone(&x); let b = a.borrow(); b.car.clone() } _ => panic! ("Not a cons"), } }); defun! (cdr (x) { match x { Node::Cons(x) => { let a = Rc::clone(&x); let b = a.borrow(); b.cdr.clone() } _ => panic! ("Not a cons"), } }); defun! (caar (x) { car!(car!(x)) }); defun! (cadr (x) { car!(cdr!(x)) }); defun! (cdar (x) { cdr!(car!(x)) }); defun! (cddr (x) { cdr!(cdr!(x)) }); defun! (rplaca (x, y) { match x.clone() { Node::Cons(x) => { let a = Rc::clone(&x); let mut b = a.borrow_mut(); b.car = y } _ => panic! ("Not a cons"), }; x }); defun! (rplacd (x, y) { match x.clone() { Node::Cons(x) => { let a = Rc::clone(&x); let mut b = a.borrow_mut(); b.cdr = y } _ => panic! ("Not a cons"), }; x }); // -- Numbers ---------------------------------------------------------------- fn maknum (x :Flonum) -> Q { Node::Flonum(x) } defun! (getnum (x) -> Flonum { match x { Node::Flonum(x) => x, _ => panic! ("Not a number"), } }); // -- Symbols ---------------------------------------------------------------- // Ok, we would need to have an oblist here. I wonder if it would be // feasible to have extra enums for our builtin symbols. The interpreter // also must dispatch on this. fn maknam_0 (nam :String) -> Q { Node::Symbol (Rc::new(RefCell::new(Symbol { val: Node::Unbound, nam: nam, plist: Node::Unbound }))) } // -- Predicates ------------------------------------------------------------- defun! (consp (x) -> bool { match x { Node::Cons(_) => true, _ => false, } }); defun! (atom (x) -> bool { !consp(x) }); defun! (numberp (x) -> bool { match x { Node::Flonum(_) => true, _ => false, } }); defun! (symbolp (x) -> bool { match x { Node::Symbol(_) => true, _ => false, } }); defun! (eq (x, y) -> bool { match (x, y) { (Node::Cons(x), Node::Cons(y)) => Rc::ptr_eq(&x,&y), (Node::Symbol(x), Node::Symbol(y)) => Rc::ptr_eq(&x,&y), (Node::Flonum(x), Node::Flonum(y)) => x == y, (Node::Unbound, Node::Unbound) => true, _ => false, }}); defun! (eql (x, y) -> bool { eq (x, y) }); defun! (eqn (x, y) -> bool { match (x,y) { (Node::Flonum(x), Node::Flonum(y)) => x == y, _ => panic!("Not two numbers"), }}); defun! (lessp (x, y) -> bool { match (x,y) { (Node::Flonum(x), Node::Flonum(y)) => x < y, _ => panic!("Not two numbers"), }}); defun! (plus (x,y) { match (x,y) { (Node::Flonum(x), Node::Flonum(y)) => maknum (x + y), _ => panic!("Not two numbers"), }}); // -- I/O -------------------------------------------------------------------- // I cannot see how to do character based I/O in Rust. We want a layer // inbetween here anyway boiling down to TYI and TYO essentially. So get // over it. defun! (tyo (x) { let ch = getnum!(x) as u8; io::stdout().write(&[ch]).unwrap(); maknum(0 as Flonum) }); fn tyos (x :String) { for c in x.as_bytes().into_iter() { tyo! (maknum(*c as Flonum)); } } defun! (tyi (x) { maknum(0 as Flonum) }); fn tyo2 (c :u8, f: &mut impl Write) { f.write (&[c]).unwrap(); } // -- Printer ---------------------------------------------------------------- defun! (null (x) -> bool { false }); defun! (drucke (x) { if consp!(x) { let mut q = x.clone(); tyos ("(".to_string()); loop { drucke! (car!(q)); q = cdr! (q); if !consp!(q) { break; } tyos (" ".to_string()); } if !null!(q) { tyos (" . ".to_string()); drucke! (q); } tyos (")".to_string()); } else if numberp!(x) { tyos (getnum!(x).to_string()); } else if symbolp!(x) { } else { tyos ("#".to_string()); } x }); // --------------------------------------------------------------------------- fn rc (x :&Q) -> usize { match x { Node::Cons(x) => Rc::strong_count(x), Node::Symbol(x) => Rc::strong_count(x), _ => 0 as usize }} // --------------------------------------------------------------------------- impl fmt::Display for Node { fn fmt (&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // tyo2 (42, f); f.write_str("Hey") } } // --------------------------------------------------------------------------- fn main () { { let f = &mut io::stdout(); tyo2 (42, f); } tyo (maknum (42 as Flonum)); tyo (maknum (10 as Flonum)); let s = "fooäß\n"; for c in s.as_bytes().into_iter() { tyo! (maknum(*c as Flonum)); } let f = maknum (42 as Flonum); let g = maknum (69 as Flonum); let a = cons!(f,g); { let b = cons! (a, f); let c = a.clone(); println! ("a = ({} . {})", getnum!(car!(a)), getnum!(cdr!(a))); rplaca! (a, g); println! ("a = ({} . {})", getnum!(car!(a)), getnum!(cdr!(a))); println! ("c = ({} . {})", getnum!(car!(c)), getnum!(cdr!(c))); drucke!(cons!(maknum(30 as Flonum), a)); println! ("a = {}", a); println! ("rc(a) = {}", rc(&a)); rplaca! (b, g); println! ("rc(a) = {}", rc(&a)); } println! ("rc(a) = {}", rc(&a)); }