From 058cf26e893a6cadca088c5008c521df7c72f070 Mon Sep 17 00:00:00 2001 From: mehbark Date: Wed, 14 Jun 2023 22:51:42 -0400 Subject: [PATCH] add lambda-like special form --- src/main.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/main.rs b/src/main.rs index c79c8dd..742854d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -611,6 +611,7 @@ pub fn simp(rules: &[Rule], mut expr: Sexp, step_limit: usize, complexity_limit: for _ in 0..step_limit { expr = expr .apply_special_form::() + .apply_special_form::() .apply_special_form::() .apply_special_form::() .apply_special_form::() @@ -792,6 +793,80 @@ impl<'src> SpecialForm<'src> for App<'src> { } } +static LAMBDA_LIKE: Lazy = + Lazy::new(|| TABLE.lock().unwrap().intern("lambda-like").unwrap()); + +#[derive(Debug, Clone)] +struct LambdaLike<'src> { + lambda: &'src Sexp, + bindings: &'src [Sexp], + body: Option<&'src Sexp>, +} + +impl<'src> SpecialForm<'src> for LambdaLike<'src> { + fn from_sexp(sexp: &'src Sexp) -> Option { + match sexp { + Sexp::Atom(_) => None, + Sexp::List(xs) => match &xs[..] { + [Sexp::Atom(lambda_like), lambda, Sexp::List(bindings), body] + if *lambda_like == *LAMBDA_LIKE => + { + Some(Self { + lambda, + bindings, + body: Some(body), + }) + } + [Sexp::Atom(lambda_like), lambda, Sexp::List(bindings)] + if *lambda_like == *LAMBDA_LIKE => + { + Some(Self { + lambda, + bindings, + body: None, + }) + } + _ => None, + }, + } + } + + // (lambda () body) -> (lambda () body) + // it's sort of incorrect (do zero arg functions exist? actual question), but i think i'll actually do: + // (lambda () body) -> body + // (lambda (a) body) -> (lambda a body) + // (lambda (a) body) -> (lambda a body) + // (lambda (a b) body) -> (lambda a (lambda b body)) + fn eval(self) -> Sexp { + let Self { + lambda, + bindings, + body, + } = self; + + if let Some(body) = body { + let mut out = body.clone(); + + for binding in bindings.iter().rev() { + out = Sexp::List(vec![lambda.clone(), binding.clone(), out]); + } + + out + } else if let Some(body) = bindings.last() { + let mut out = body.clone(); + + for binding in bindings.iter().rev().skip(1) { + out = Sexp::List(vec![lambda.clone(), binding.clone(), out]); + } + + out + } else { + // HACK: bad? + Sexp::nil() + } + } +} + pub fn make_rule(sexp: &Sexp) -> Option { match sexp { Sexp::List(xs) => match &xs[..] {