just getting started
This commit is contained in:
commit
e9b352bc0a
3 changed files with 91 additions and 0 deletions
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lc-to-iota"
|
||||||
|
version = "0.1.0"
|
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "lc-to-iota"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
78
src/lib.rs
Normal file
78
src/lib.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//! Compiles the [lambda calculus](https://en.wikipedia.org/wiki/Lambda_calculus),
|
||||||
|
//! represented by the [`LC`] type,
|
||||||
|
//! to a tree of [Iota (ι) combinators](https://en.wikipedia.org/wiki/Iota_and_Jot),
|
||||||
|
//! represented by the [`Iota`] type.
|
||||||
|
|
||||||
|
use core::fmt;
|
||||||
|
use std::{collections::HashSet, rc::Rc};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum Iota {
|
||||||
|
App(Rc<Iota>, Rc<Iota>),
|
||||||
|
Iota,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum LC {
|
||||||
|
Abs(Rc<LC>),
|
||||||
|
App(Rc<LC>, Rc<LC>),
|
||||||
|
Var(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LC {
|
||||||
|
pub fn app(self, x: impl Into<Self>) -> Self {
|
||||||
|
Self::App(self.into(), x.into().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn abs(self) -> Self {
|
||||||
|
Self::Abs(self.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_free_vars(&self) -> bool {
|
||||||
|
// TODO: faster
|
||||||
|
!self.free_vars().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// contextless free vars aren't that useful?
|
||||||
|
pub fn free_vars(&self) -> HashSet<usize> {
|
||||||
|
self.free_vars_past(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free_vars_past(&self, bound: usize) -> HashSet<usize> {
|
||||||
|
// TODO: faster
|
||||||
|
match self {
|
||||||
|
LC::Abs(x) => x.free_vars_past(bound + 1),
|
||||||
|
LC::App(f, x) => {
|
||||||
|
let f_vars = f.free_vars_past(bound);
|
||||||
|
let x_vars = x.free_vars_past(bound);
|
||||||
|
&f_vars | &x_vars
|
||||||
|
}
|
||||||
|
LC::Var(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for LC {
|
||||||
|
fn from(n: usize) -> Self {
|
||||||
|
Self::Var(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for LC {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
LC::Abs(x) => write!(f, "(λ {x})"),
|
||||||
|
LC::App(g, x) => write!(f, "({g} {x})"),
|
||||||
|
LC::Var(n) => write!(f, "{n}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lc_display() {
|
||||||
|
let identity = LC::Var(0).abs();
|
||||||
|
assert_eq!(identity.to_string(), "(λ 0)");
|
||||||
|
|
||||||
|
let apply = LC::Var(1).app(0).abs().abs();
|
||||||
|
assert_eq!(apply.to_string(), "(λ (λ (1 0)))");
|
||||||
|
}
|
Loading…
Reference in a new issue