Created
January 9, 2020 22:31
-
-
Save mbillingr/e871907c08011b6b56a525f3a1f35c19 to your computer and use it in GitHub Desktop.
(WIP) A little Scheme implementation within Rust macros
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::cell::Cell; | |
| macro_rules! scm_meaning { | |
| (()) => {Scm::Nil}; | |
| (<) => {Scm::from(Scm::less)}; | |
| (>) => {Scm::from(Scm::greater)}; | |
| (+) => {Scm::from(Scm::add)}; | |
| (-) => {Scm::from(Scm::sub)}; | |
| (*) => {Scm::from(Scm::mul)}; | |
| (/) => {Scm::from(Scm::div)}; | |
| ((quote $expr:tt)) => { scm_quotation!($expr) }; | |
| ((begin $($exprs:tt)*)) => { scm_sequence!($($exprs)*) }; | |
| ((if $condition:tt $consequence:tt $alternative:tt)) => { | |
| if !Scm::from(scm_meaning!{ $condition }).is_false() { | |
| Scm::from(scm_meaning!{ $consequence }) | |
| } else { | |
| Scm::from(scm_meaning!{ $alternative }) | |
| } | |
| }; | |
| ((set! $var:ident $form:tt)) => { $var = scm_meaning!{ $form }; }; | |
| ((lambda ($($params:tt)*) $($body:tt)*)) => { | |
| Scm::closure(move |args| -> Scm { | |
| let mut args = args.into_iter(); | |
| $( | |
| #[allow(unused_mut)] | |
| let mut $params = args.next().expect("Too few arguments"); | |
| )* | |
| if let Some(a) = args.next() { | |
| panic!("Too many arguments: {:?} ...", a); | |
| } | |
| scm_sequence!($($body)*) | |
| }) | |
| }; | |
| ((define $var:ident $form:tt)) => { | |
| #[allow(unused_mut)] | |
| let mut $var = scm_meaning!{ $form }; | |
| }; | |
| ((define ($var:ident $($params:ident)*) $($body:tt)*)) => { | |
| fn $var($($params: impl IntoScm<()>),*) -> Scm { | |
| $( | |
| #[allow(unused_mut)] | |
| let mut $params = $params.into_scm(); | |
| )* | |
| scm_sequence!($($body)*) | |
| }; | |
| }; | |
| (($($exprs:tt)*)) => { scm_application!($($exprs)*) }; | |
| ($symbol:ident) => { Scm::from($symbol) }; | |
| ($atom:expr) => { Scm::from($atom) }; | |
| } | |
| macro_rules! scm_sequence { | |
| ($($exprs:tt)*) => {{ | |
| $( | |
| scm_meaning!{ $exprs } | |
| );* | |
| }} | |
| } | |
| macro_rules! scm_application { | |
| ($func:tt $($args:tt)*) => {{ | |
| let mut f = scm_meaning!{ $func }; | |
| let args = vec![$(scm_meaning!{ $args }.into()),*]; | |
| Scm::from(f.invoke(args)) | |
| }}; | |
| } | |
| macro_rules! scm_quotation { | |
| (()) => { Scm::Nil }; | |
| (($next:tt $($rest:tt)*)) => { | |
| Scm::cons(scm_quotation!($next), scm_quotation!(($($rest)*))) | |
| }; | |
| ($expr:ident) => { Scm::symbol(stringify!($expr)) }; | |
| ($expr:expr) => { Scm::from($expr) }; | |
| } | |
| fn main() { | |
| scm_meaning! { | |
| (begin | |
| (define (bar) (say_hello)) | |
| (bar) | |
| (define (fib n) | |
| (if (< n 2) | |
| 1 | |
| (+ (fib (- n 1)) (fib (- n 2))))) | |
| (display (fib 20)) | |
| (define (make_number n) | |
| (lambda () n)) | |
| (display ((make_number 10))) | |
| (display (cons 1 2)) | |
| (define (make_numbers n) | |
| (cons (lambda () (+ n 1)) (lambda () (+ n 2)))) | |
| (define k (make_numbers 100)) | |
| (display ((car k))) | |
| (display ((cdr k))) | |
| (define n 0) | |
| (set! n (+ n 1)) | |
| (display n) | |
| (set! n (+ n 1)) | |
| (display n) | |
| (set! n (+ n 1)) | |
| (display n) | |
| // can't mutate variables | |
| /*(define (make_counter n) | |
| (lambda () (set! n (+ n 1)) n)) | |
| (define c (make_counter 0)) | |
| (display (c)) | |
| (display (c)) | |
| (display (c))*/ | |
| // workaround | |
| (define (make_counter n) | |
| (define n (cons n ())) | |
| (lambda () (set_car n (+ (car n) 1)) (car n))) | |
| (define c (make_counter 0)) | |
| (display (c)) | |
| (display (c)) | |
| (display (c)) | |
| (display ()) | |
| (display (quote (a b c))) | |
| (display (quote (a 1 b 2))) | |
| (define (reverse seq) | |
| (define (iter seq out) | |
| (if (is_null seq) | |
| out | |
| (iter (cdr seq) (cons (car seq) out)))) | |
| (iter seq ())) | |
| (display (reverse (quote (a b c)))) | |
| ) | |
| }; | |
| //println!("{:?}", fib(5)); | |
| } | |
| fn say_hello() { | |
| println!("hello!") | |
| } | |
| fn display(x: Scm) -> Scm { | |
| println!("{:?}", x); | |
| Scm::Nothing | |
| } | |
| fn is_null(x: Scm) -> Scm { | |
| x.is_null().into_scm() | |
| } | |
| fn cons(car: Scm, cdr: Scm) -> Scm { | |
| Scm::cons(car, cdr) | |
| } | |
| fn car(p: Scm) -> Scm { | |
| p.car() | |
| } | |
| fn cdr(p: Scm) -> Scm { | |
| p.cdr() | |
| } | |
| fn set_car(p: Scm, x: Scm) -> Scm { | |
| p.set_car(x); | |
| Scm::Nothing | |
| } | |
| fn set_cdr(p: Scm, x: Scm) -> Scm { | |
| p.set_cdr(x); | |
| Scm::Nothing | |
| } | |
| fn make_static<T>(value: T) -> &'static T { | |
| Box::leak(Box::new(value)) | |
| } | |
| #[derive(Debug, Copy, Clone)] | |
| enum Scm { | |
| Nothing, | |
| Nil, | |
| True, | |
| False, | |
| Int(i64), | |
| Symbol(&'static str), | |
| Pair(&'static (Cell<Scm>, Cell<Scm>)), | |
| Closure(Closure), | |
| } | |
| impl Scm { | |
| fn from<P, T: IntoScm<P>>(x: T) -> Self { | |
| x.into_scm() | |
| } | |
| fn is_null(&self) -> bool { | |
| match self { | |
| Scm::Nil => true, | |
| _ => false, | |
| } | |
| } | |
| fn is_false(&self) -> bool { | |
| match self { | |
| Scm::False => true, | |
| _ => false, | |
| } | |
| } | |
| fn symbol(s: &'static str) -> Self { | |
| Scm::Symbol(s) | |
| } | |
| fn cons(car: Scm, cdr: Scm) -> Self { | |
| Scm::Pair(make_static((Cell::new(car), Cell::new(cdr)))) | |
| } | |
| fn closure(f: impl Fn(Vec<Scm>)->Scm + 'static) -> Self { | |
| Scm::Closure(Closure(make_static(f))) | |
| } | |
| fn invoke(&mut self, args: Vec<Scm>) -> Scm { | |
| match self { | |
| Scm::Closure(cls) => cls.invoke(args), | |
| _ => panic!("Not callable: {:?}", self), | |
| } | |
| } | |
| fn car(&self) -> Self { | |
| match self { | |
| Scm::Pair(p) => p.0.get(), | |
| _ => panic!("Not a pair {:?}", self), | |
| } | |
| } | |
| fn cdr(&self) -> Self { | |
| match self { | |
| Scm::Pair(p) => p.1.get(), | |
| _ => panic!("Not a pair {:?}", self), | |
| } | |
| } | |
| fn set_car(&self, value: Scm) { | |
| match self { | |
| Scm::Pair(p) => p.0.set(value), | |
| _ => panic!("Not a pair {:?}", self), | |
| } | |
| } | |
| fn set_cdr(&self, value: Scm) { | |
| match self { | |
| Scm::Pair(p) => p.1.set(value), | |
| _ => panic!("Not a pair {:?}", self), | |
| } | |
| } | |
| fn add(&self, other: &Self) -> Self { | |
| match (self, other) { | |
| (Scm::Int(a), Scm::Int(b)) => Scm::Int(a + b), | |
| _ => panic!("Cannot add {:?} + {:?}", self, other), | |
| } | |
| } | |
| fn sub(&self, other: &Self) -> Self { | |
| match (self, other) { | |
| (Scm::Int(a), Scm::Int(b)) => Scm::Int(a - b), | |
| _ => panic!("Cannot subtract {:?} - {:?}", self, other), | |
| } | |
| } | |
| fn greater(&self, other: &Self) -> Self { | |
| match (self, other) { | |
| (Scm::Int(a), Scm::Int(b)) => (a > b).into_scm(), | |
| _ => panic!("Cannot compare {:?} > {:?}", self, other), | |
| } | |
| } | |
| fn less(&self, other: &Self) -> Self { | |
| match (self, other) { | |
| (Scm::Int(a), Scm::Int(b)) => (a < b).into_scm(), | |
| _ => panic!("Cannot compare {:?} < {:?}", self, other), | |
| } | |
| } | |
| } | |
| impl From<Scm> for i64 { | |
| fn from(x: Scm) -> i64 { | |
| match x { | |
| Scm::Int(x) => x, | |
| _ => panic!("Not an integer: {:?}", x) | |
| } | |
| } | |
| } | |
| trait IntoScm<T> { | |
| fn into_scm(self) -> Scm; | |
| } | |
| impl IntoScm<()> for Scm { | |
| fn into_scm(self) -> Scm { self } | |
| } | |
| impl IntoScm<()> for () { | |
| fn into_scm(self) -> Scm { Scm::Nothing } | |
| } | |
| impl IntoScm<()> for bool { | |
| fn into_scm(self) -> Scm { | |
| match self { | |
| true => Scm::True, | |
| false => Scm::False, | |
| } | |
| } | |
| } | |
| impl IntoScm<()> for i64 { | |
| fn into_scm(self) -> Scm { Scm::Int(self) } | |
| } | |
| impl<T> IntoScm<()> for T | |
| where | |
| T: Fn() +'static | |
| { | |
| fn into_scm(self) -> Scm { | |
| Scm::closure(move |args| { | |
| if !args.is_empty() { | |
| panic!("Expected no arguments; got {}", args.len()) | |
| } | |
| self().into_scm() | |
| }) | |
| } | |
| } | |
| impl<T> IntoScm<(i64, i64)> for T | |
| where | |
| T: Fn(i64)->i64 +'static | |
| { | |
| fn into_scm(self) -> Scm { | |
| Scm::closure(move |mut args| { | |
| let a = args.pop().expect("Expected 1 argument; got 0.").into(); | |
| if !args.is_empty() { | |
| panic!("Expected 1 argument; got {}", args.len() + 1) | |
| } | |
| self(a).into_scm() | |
| }) | |
| } | |
| } | |
| impl<T: Fn(i64, i64)->i64 +'static> IntoScm<(i64, i64, i64)> for T { | |
| fn into_scm(self) -> Scm { | |
| Scm::closure(move |mut args| { | |
| let b = args.pop().expect("Expected 2 argumets; got 0.").into(); | |
| let a = args.pop().expect("Expected 2 argumets; got 1.").into(); | |
| if !args.is_empty() { | |
| panic!("Expected 2 arguments; got {}", args.len() + 2) | |
| } | |
| self(a, b).into_scm() | |
| }) | |
| } | |
| } | |
| impl<T: Fn()->Scm +'static> IntoScm<Scm> for T { | |
| fn into_scm(self) -> Scm { | |
| Scm::closure(move |args| { | |
| if !args.is_empty() { | |
| panic!("Expected no arguments; got {}", args.len()) | |
| } | |
| self() | |
| }) | |
| } | |
| } | |
| impl<T: Fn(Scm)->Scm +'static> IntoScm<(Scm, Scm)> for T { | |
| fn into_scm(self) -> Scm { | |
| Scm::closure(move |mut args| { | |
| let a = args.pop().expect("Expected 1 argument; got 0."); | |
| if !args.is_empty() { | |
| panic!("Expected 1 argument; got {}", args.len() + 1) | |
| } | |
| self(a) | |
| }) | |
| } | |
| } | |
| impl<T: Fn(Scm, Scm)->Scm +'static> IntoScm<(Scm, Scm, Scm)> for T { | |
| fn into_scm(self) -> Scm { | |
| Scm::closure(move |mut args| { | |
| let b = args.pop().expect("Expected 2 argumets; got 0."); | |
| let a = args.pop().expect("Expected 2 argumets; got 1."); | |
| if !args.is_empty() { | |
| panic!("Expected 2 arguments; got {}", args.len() + 2) | |
| } | |
| self(a, b) | |
| }) | |
| } | |
| } | |
| impl<T: Fn(&Scm, &Scm)->Scm +'static> IntoScm<(&Scm, &Scm, Scm)> for T { | |
| fn into_scm(self) -> Scm { | |
| Scm::closure(move |mut args| { | |
| let b = args.pop().expect("Expected 2 argumets; got 0."); | |
| let a = args.pop().expect("Expected 2 argumets; got 1."); | |
| if !args.is_empty() { | |
| panic!("Expected 2 arguments; got {}", args.len() + 2) | |
| } | |
| self(&a, &b) | |
| }) | |
| } | |
| } | |
| #[derive(Copy, Clone)] | |
| struct Closure(&'static dyn Fn(Vec<Scm>)->Scm); | |
| impl std::fmt::Debug for Closure { | |
| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
| write!(f, "{:p}", &*self.0) | |
| } | |
| } | |
| impl Closure { | |
| fn invoke(&self, args: Vec<Scm>) -> Scm { | |
| self.0(args) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment