#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Position(usize);
impl Position {
#[must_use]
pub fn value(&self) -> usize {
self.0
}
}
impl From<usize> for Position {
fn from(value: usize) -> Self {
Self(value)
}
}
impl std::fmt::Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct VarName(String);
impl VarName {
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<String> for VarName {
fn from(value: String) -> Self {
Self(value)
}
}
impl From<&str> for VarName {
fn from(value: &str) -> Self {
Self(value.to_owned())
}
}
impl std::fmt::Display for VarName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Expr {
Var(VarName),
Lam {
param: VarName,
body: Box<Expr>,
},
App {
func: Box<Expr>,
arg: Box<Expr>,
},
Let {
name: VarName,
value: Box<Expr>,
body: Box<Expr>,
},
Fix {
name: VarName,
body: Box<Expr>,
},
Ref {
inner: Box<Expr>,
},
Deref {
inner: Box<Expr>,
},
Assign {
target: Box<Expr>,
value: Box<Expr>,
},
Seq {
first: Box<Expr>,
second: Box<Expr>,
},
Object {
entries: Vec<(VarName, Expr)>,
prototype: Option<Box<Expr>>,
},
Field {
object: Box<Expr>,
name: VarName,
},
Throw {
inner: Box<Expr>,
},
TryCatch {
body: Box<Expr>,
catch_param: VarName,
handler: Box<Expr>,
},
}
impl Expr {
#[must_use]
pub fn var(name: impl Into<VarName>) -> Self {
Self::Var(name.into())
}
#[must_use]
pub fn lam(param: impl Into<VarName>, body: Self) -> Self {
Self::Lam {
param: param.into(),
body: Box::new(body),
}
}
#[must_use]
pub fn app(func: Self, arg: Self) -> Self {
Self::App {
func: Box::new(func),
arg: Box::new(arg),
}
}
#[must_use]
pub fn bind(name: impl Into<VarName>, value: Self, body: Self) -> Self {
Self::Let {
name: name.into(),
value: Box::new(value),
body: Box::new(body),
}
}
#[must_use]
pub fn fix(name: impl Into<VarName>, body: Self) -> Self {
Self::Fix {
name: name.into(),
body: Box::new(body),
}
}
#[must_use]
pub fn alloc(inner: Self) -> Self {
Self::Ref {
inner: Box::new(inner),
}
}
#[must_use]
pub fn deref(inner: Self) -> Self {
Self::Deref {
inner: Box::new(inner),
}
}
#[must_use]
pub fn assign(target: Self, value: Self) -> Self {
Self::Assign {
target: Box::new(target),
value: Box::new(value),
}
}
#[must_use]
pub fn seq(first: Self, second: Self) -> Self {
Self::Seq {
first: Box::new(first),
second: Box::new(second),
}
}
#[must_use]
pub fn object(entries: Vec<(VarName, Self)>) -> Self {
Self::Object {
entries,
prototype: None,
}
}
#[must_use]
pub fn object_with_proto(entries: Vec<(VarName, Self)>, prototype: Self) -> Self {
Self::Object {
entries,
prototype: Some(Box::new(prototype)),
}
}
#[must_use]
pub fn field(object: Self, name: impl Into<VarName>) -> Self {
Self::Field {
object: Box::new(object),
name: name.into(),
}
}
#[must_use]
pub fn throw(inner: Self) -> Self {
Self::Throw {
inner: Box::new(inner),
}
}
#[must_use]
pub fn try_catch(body: Self, catch_param: impl Into<VarName>, handler: Self) -> Self {
Self::TryCatch {
body: Box::new(body),
catch_param: catch_param.into(),
handler: Box::new(handler),
}
}
}
impl std::fmt::Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Var(name) => write!(f, "{name}"),
Self::Lam { param, body } => write!(f, "(\\{param}. {body})"),
Self::App { func, arg } => write!(f, "({func} {arg})"),
Self::Let { name, value, body } => {
write!(f, "(let {name} = {value} in {body})")
}
Self::Fix { name, body } => write!(f, "(fix {name}. {body})"),
Self::Ref { inner } => write!(f, "(ref {inner})"),
Self::Deref { inner } => write!(f, "(!{inner})"),
Self::Assign { target, value } => write!(f, "({target} := {value})"),
Self::Seq { first, second } => write!(f, "({first} ; {second})"),
Self::Object { entries, prototype } => write_object(f, entries, prototype.as_deref()),
Self::Field { object, name } => write!(f, "({object}.{name})"),
Self::Throw { inner } => write!(f, "(throw {inner})"),
Self::TryCatch {
body,
catch_param,
handler,
} => write!(f, "(try {body} catch {catch_param}. {handler})"),
}
}
}
fn write_object(
f: &mut std::fmt::Formatter<'_>,
entries: &[(VarName, Expr)],
prototype: Option<&Expr>,
) -> std::fmt::Result {
let body = entries
.iter()
.map(|(k, v)| format!("{k} = {v}"))
.collect::<Vec<_>>()
.join(", ");
let formatted = prototype.map_or_else(
|| format!("{{{body}}}"),
|proto| format!("(extend {proto} {{{body}}})"),
);
f.write_str(&formatted)
}