Skip to content

Instantly share code, notes, and snippets.

@carrotflakes
Last active November 29, 2019 16:37
Show Gist options
  • Save carrotflakes/0f9c35955bb8dec8d8fadbd17058f8ed to your computer and use it in GitHub Desktop.
Save carrotflakes/0f9c35955bb8dec8d8fadbd17058f8ed to your computer and use it in GitHub Desktop.
[WIP] A Lisp implementation in Rust =>https://github.com/carrotflakes/gluten
use std::rc::Rc;
use std::cell::RefCell;
use std::str::Chars;
type R<T> = Rc<RefCell<T>>;
#[derive(Debug, Clone)]
enum V {
Symbol(String),
Int(i64),
Cons(R<V>, R<V>),
Nil
}
fn r(v: V) -> R<V> {
Rc::new(RefCell::new(v))
}
fn parse(src: &str) -> Result<R<V>, String> {
parse_value(&src.chars()).map(|x| x.0)
}
fn skip_whitespace<'a> (cs: &Chars<'a>) -> Chars<'a> {
let mut ncs = cs.clone();
match ncs.next() {
Some(c) if c.is_whitespace() =>
skip_whitespace(&ncs),
_ => cs.clone()
}
}
fn parse_value<'a>(cs: &Chars<'a>) -> Result<(R<V>, Chars<'a>), String> {
let mut cs = skip_whitespace(cs);
match cs.next() {
Some('(') => {
let mut vec = vec![];
loop {
match parse_value(&cs) {
Ok((rv, ncs)) => {
vec.push(rv);
cs = ncs.clone();
},
_ => {
break;
}
}
}
cs = skip_whitespace(&cs);
let mut ret_rv = r(V::Nil);
while let Some(c) = cs.next() {
if c == ')' {
break;
} else if c == '.' {
cs = skip_whitespace(&cs);
if let Ok((rv, ncs)) = parse_value(&cs) {
ret_rv = rv;
cs = ncs.clone();
cs = skip_whitespace(&cs);
if let Some(')') = cs.next() {
break;
}
}
}
return Err("fail".to_string());
}
for rv in vec.into_iter().rev() {
ret_rv = r(V::Cons(rv, ret_rv));
}
Ok((ret_rv, cs))
},
Some(c) if c.is_alphanumeric() => {
let mut vec = vec![c];
let mut ncs = cs.clone();
loop {
match ncs.next() {
Some(c) if c.is_alphanumeric() => {
vec.push(c);
cs = ncs.clone();
},
_ => {
break;
}
}
}
let s = vec.iter().collect();
Ok((r(if &s == "nil" { V::Nil } else {V::Symbol(s)}), cs))
},
_ => Err("fail".to_string())
}
}
#[derive(Debug)]
struct RVC<'a>(&'a V);
struct RVCTail<'a>(&'a V);
impl <'a> std::fmt::Display for RVC<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
V::Symbol(ref s) => write!(f, "{}", s),
v@V::Cons(_, _) =>
write!(f, "({})", RVCTail(v)),
V::Nil => write!(f, "nil"),
v => write!(f, "{:?}!", v)
}
}
}
impl <'a> std::fmt::Display for RVCTail<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
V::Cons(ref car, ref cdr) =>
match *cdr.borrow() {
V::Nil => write!(f, "{}", RVC(&car.borrow())),
V::Cons(_, _) =>
write!(f, "{} {}", RVC(&car.borrow()), RVCTail(&cdr.borrow())),
_ => write!(f, "{} . {}", RVC(&car.borrow()), RVC(&cdr.borrow())),
}
rv => write!(f, "{}", RVC(rv))
}
}
}
// macro_rules! pm_arm {
// (nil => $e:expr) => (V::Nil => $e);
// // (cons($car:pat, $cdr:pat)) => (V::Cons(
// ($p:pat => $e:expr) => ($p => $e);
// }
// macro_rules! pm {
// ($v:expr, $($x:pat => $y:expr),*) => {
// match $v {
// $(
// pm_arm!($x => $y)
// ),*
// }
// };
// }
fn eval(rv: R<V>) -> R<V> {
match *rv.borrow() {
V::Cons(ref car, ref cdr) =>
match *car.borrow() {
V::Symbol(ref s) if &*s == "quote" =>
match *cdr.borrow() {
V::Cons(ref car, ref cdr) =>
match *cdr.borrow() {
V::Nil => car.clone(),
_ => r(V::Nil)
},
_ => r(V::Nil)
},
_ => r(V::Nil)
},
_ => r(V::Nil)
}
}
fn main() {
println!("Hello, world!");
println!("{:?}", V::Cons(r(V::Int(1)), r(V::Int(2))));
println!("{:?}", parse(" (abc 東京) "));
println!("{}", RVC(&parse(" (abc 東京) ").unwrap().borrow()));
println!("{}", RVC(&parse(" ( abc () . 東京 ) ").unwrap().borrow()));
println!("{}", RVC(&parse(" ( abc (a) . 東京 ) ").unwrap().borrow()));
println!("{}", RVC(&parse(" ( abc (a b) . 東京 ) ").unwrap().borrow()));
println!("{}", RVC(&parse(" ( abc ( hello world .nil) . 東京 ) ").unwrap().borrow()));
println!("{}", RVC(&eval(parse("(quote a)").unwrap()).borrow()));
//println!("{}", pm!(1, 1 => 2, _ => 3));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment