You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
7.0 KiB
227 lines
7.0 KiB
#![feature(box_patterns, box_syntax)] |
|
|
|
use std::fmt::{self, Write}; |
|
use std::sync::atomic::{AtomicUsize, Ordering}; |
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)] |
|
pub struct Name(pub String); |
|
|
|
impl fmt::Display for Name { |
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
write!(f, "{}", self.0) |
|
} |
|
} |
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)] |
|
pub struct Function(pub Name, pub Expr); |
|
|
|
impl fmt::Display for Function { |
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
if let Expr::Function(box Function(..)) = self.1 { |
|
// handling multiple-argument functions |
|
let mut args = format!("{}", self.0); |
|
let mut expr = &self.1; |
|
while let Expr::Function(box Function(n, e)) = expr { |
|
write!(args, "{}", n)?; |
|
expr = e; |
|
} |
|
write!(f, "λ{}.{}", args, expr) |
|
} |
|
else { |
|
write!(f, "λ{}.{}", self.0, self.1) |
|
} |
|
} |
|
} |
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)] |
|
pub enum Expr { |
|
Name(Name), |
|
Function(Box<Function>), |
|
Application(Box<Expr>, Box<Expr>) |
|
} |
|
|
|
impl fmt::Display for Expr { |
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
|
match self { |
|
Expr::Name(n) => n.fmt(f), |
|
Expr::Function(n) => n.fmt(f), |
|
Expr::Application(box e1, box e2) => { |
|
if let Expr::Function(box Function(_, Expr::Name(_))) = e1 { |
|
write!(f, "({}){}", e1, e2) |
|
} |
|
else { |
|
write!(f, "[{}{}]", e1, e2) |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
static VARIABLE_COUNTER: AtomicUsize = AtomicUsize::new(0); |
|
|
|
pub fn new_var() -> Name { |
|
let cnt = VARIABLE_COUNTER.fetch_add(1, Ordering::SeqCst); |
|
Name(format!("a{}", cnt)) |
|
} |
|
|
|
pub fn expr_contains_name(expr: &Expr, name: &Name) -> bool { |
|
match expr { |
|
Expr::Name(n) if n == name => true, |
|
Expr::Function(box Function(n, e)) => |
|
n == name || expr_contains_name(e, name), |
|
Expr::Application(box left, box right) => |
|
expr_contains_name(left, name) || expr_contains_name(right, name), |
|
_ => false |
|
} |
|
} |
|
|
|
// https://open.spotify.com/track/6S0JlNqvdDJ6Oq5dSOjuE2?si=xNioxjqVTVCD7HCf67p0pw |
|
pub fn he_walk(old_name: Name, new_expr: Expr, ast: Expr) -> Expr { |
|
//println!("let {} = {} in {}", old_name, new_expr, ast); |
|
match ast { |
|
Expr::Name(n) if n == old_name => new_expr.clone(), |
|
x @ Expr::Name(..) => x, |
|
Expr::Application(box left, box right) => { |
|
Expr::Application( |
|
box he_walk(old_name.clone(), new_expr.clone(), left), |
|
box he_walk(old_name.clone(), new_expr.clone(), right)) |
|
}, |
|
Expr::Function(box Function(name, expr)) => { |
|
let Function(name_new, expr_new) = if name == old_name || expr_contains_name(&expr, &name) { |
|
//println!("swizzle time"); |
|
let new_name = new_var(); |
|
Function(new_name.clone(), he_walk(name, Expr::Name(new_name), expr)) |
|
} |
|
else { |
|
Function(name, expr) |
|
}; |
|
Expr::Function(box Function( |
|
name_new, |
|
he_walk(old_name, new_expr, expr_new))) |
|
} |
|
} |
|
} |
|
|
|
pub fn apply(e1: Expr, e2: Expr) -> Expr { |
|
Expr::Application(box e1, box e2) |
|
} |
|
pub fn var<T: Into<String>>(name: T) -> Expr { |
|
Expr::Name(Name(name.into())) |
|
} |
|
pub fn lambda<T: Into<String>>(name: T, e: Expr) -> Expr { |
|
Expr::Function(box Function(Name(name.into()), e)) |
|
} |
|
pub fn id() -> Expr { |
|
lambda("x", var("x")) |
|
} |
|
pub fn zero() -> Expr { |
|
lambda("s", lambda("z", var("z"))) |
|
} |
|
pub fn succ() -> Expr { |
|
lambda("w", lambda("y", lambda("x", apply(var("y"), apply(apply(var("w"), var("y")), var("x")))))) |
|
} |
|
pub fn asucc(e: Expr) -> Expr { |
|
apply(succ(), e) |
|
} |
|
pub fn apply_function_to_expr(fun: Function, e: Expr) -> Expr { |
|
//println!("funcall: {} = {} in {}", fun.0, e, fun.1); |
|
he_walk(fun.0, e, fun.1) |
|
} |
|
pub fn encode_num(num: usize) -> Expr { |
|
let mut ret = zero(); |
|
for _ in 0..num { |
|
ret = asucc(ret).eval() |
|
} |
|
ret |
|
} |
|
pub fn decode_num(e: Expr) -> Option<usize> { |
|
if let Expr::Function(box Function(s, Expr::Function(box Function(z, mut expr)))) = e { |
|
let mut ret = 0; |
|
while let Expr::Application(box Expr::Name(n), box new_exp) = expr { |
|
if n == s { |
|
ret += 1; |
|
} |
|
else { |
|
return None; |
|
} |
|
expr = new_exp; |
|
} |
|
if expr == Expr::Name(z) { |
|
Some(ret) |
|
} |
|
else { |
|
None |
|
} |
|
} |
|
else { |
|
None |
|
} |
|
} |
|
|
|
impl Expr { |
|
pub fn eval(self) -> Self { |
|
let mut this = self; |
|
loop { |
|
//println!("eval {}:", this); |
|
let ret = match this.clone() { |
|
Expr::Application(box left, box right) => { |
|
let mut expr = left; |
|
loop { |
|
match expr { |
|
x @ Expr::Application(..) => { |
|
expr = x.eval(); |
|
}, |
|
Expr::Function(box func) => { |
|
break apply_function_to_expr(func, right); |
|
}, |
|
x @ Expr::Name(..) => { |
|
break Expr::Application(box x, box right.eval()); |
|
} |
|
} |
|
} |
|
}, |
|
Expr::Function(box Function(name, expr)) => |
|
Expr::Function(box Function(name, expr.eval())), |
|
x => x |
|
}; |
|
//println!("eval {} => {}", this, ret); |
|
if this == ret { |
|
break ret; |
|
} |
|
this = ret; |
|
} |
|
} |
|
} |
|
|
|
fn main() { |
|
let ii = apply(id(), id()); |
|
let test2 = apply(lambda("x", lambda("y", apply(var("x"), var("y")))), var("y")); |
|
// (λx.(λy.(x(λx.xy))))y |
|
let test3 = apply(lambda("x", lambda("y", apply(var("x"), lambda("x", apply(var("x"), var("y")))))), var("y")); |
|
// (λx.xx)(λx.xx) |
|
let test4 = apply(lambda("x", apply(var("x"), var("x"))), lambda("x", apply(var("x"), var("x")))); |
|
println!("*** {}", lambda("x", lambda("y", lambda("z", var("k"))))); |
|
println!("*** {} => {}", ii, ii.clone().eval()); |
|
println!("*** {} => {}", test2, test2.clone().eval()); |
|
println!("*** {} => {}", test3, test3.clone().eval()); |
|
println!("*** {} => {}", test4, test4.clone().eval()); |
|
println!("zero is {}, succ is {}", zero(), succ()); |
|
println!("one is {}", asucc(zero()).eval()); |
|
println!("two is {}", asucc(asucc(zero())).eval()); |
|
println!("three is {}", asucc(asucc(asucc(zero()))).eval()); |
|
for x in 0..5 { |
|
println!("{} is {} => {:?}", x, encode_num(x), decode_num(encode_num(x))); |
|
} |
|
} |
|
|
|
#[cfg(test)] |
|
mod test { |
|
use crate::*; |
|
#[test] |
|
fn id_id() { |
|
assert_eq!( |
|
apply(id(), id()).eval(), |
|
id() |
|
); |
|
} |
|
}
|
|
|