Skip to content

Instantly share code, notes, and snippets.

@mbillingr
Created January 9, 2020 22:31
Show Gist options
  • Select an option

  • Save mbillingr/e871907c08011b6b56a525f3a1f35c19 to your computer and use it in GitHub Desktop.

Select an option

Save mbillingr/e871907c08011b6b56a525f3a1f35c19 to your computer and use it in GitHub Desktop.
(WIP) A little Scheme implementation within Rust macros
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