#![allow(dead_code)]
use itertools::Itertools;
use lutra_bin::ir::{self, *};
type Result<T> = std::result::Result<T, ()>;
pub trait IrFold {
fn fold_program(&mut self, program: Program) -> Result<Program> {
Ok(Program {
main: self.fold_expr(program.main)?,
defs: program
.defs
.into_iter()
.map(|t| -> Result<TyDef> {
Ok(TyDef {
name: t.name,
ty: self.fold_ty(t.ty)?,
})
})
.collect::<Result<Vec<_>>>()?,
})
}
fn fold_expr(&mut self, expr: Expr) -> Result<Expr> {
let ty = self.fold_ty(expr.ty)?;
fold_expr_kind(self, expr.kind, ty)
}
fn fold_ptr(&mut self, ptr: Pointer, ty: Ty) -> Result<Expr> {
fold_ptr(ptr, ty)
}
fn fold_call(&mut self, call: Call, ty: Ty) -> Result<Expr> {
fold_call(self, call, ty)
}
fn fold_func(&mut self, func: Function, ty: Ty) -> Result<Expr> {
fold_func(self, func, ty)
}
fn fold_lookup(&mut self, lookup: TupleLookup, ty: Ty) -> Result<Expr> {
fold_lookup(self, lookup, ty)
}
fn fold_binding(&mut self, binding: Binding, ty: Ty) -> Result<Expr> {
fold_binding(self, binding, ty)
}
fn fold_switch(&mut self, branches: Vec<SwitchBranch>, ty: Ty) -> Result<Expr> {
fold_switch(self, branches, ty)
}
fn fold_enum_eq(&mut self, enum_eq: EnumEq, ty: Ty) -> Result<Expr> {
fold_enum_eq(self, enum_eq, ty)
}
fn fold_ty(&mut self, ty: Ty) -> Result<Ty> {
fold_ty(self, ty)
}
}
pub fn fold_expr_kind<T: ?Sized + IrFold>(fold: &mut T, kind: ExprKind, ty: Ty) -> Result<Expr> {
use ExprKind::*;
let kind = match kind {
Pointer(ptr) => return fold.fold_ptr(ptr, ty),
Literal(lit) => Literal(lit),
Call(call) => return fold.fold_call(*call, ty),
Function(func) => return fold.fold_func(*func, ty),
Tuple(fields) => Tuple(fold_tuple_fields(fold, fields)?),
Array(items) => Array(fold_exprs(fold, items)?),
EnumVariant(variant) => EnumVariant(Box::new(fold_enum_variant(fold, *variant)?)),
EnumEq(eq) => return fold.fold_enum_eq(*eq, ty),
EnumUnwrap(unwrap) => EnumUnwrap(Box::new(fold_enum_unwrap(fold, *unwrap)?)),
TupleLookup(lookup) => return fold.fold_lookup(*lookup, ty),
Binding(binding) => return fold.fold_binding(*binding, ty),
Switch(branches) => return fold.fold_switch(branches, ty),
};
Ok(Expr { kind, ty })
}
pub fn fold_exprs<F: ?Sized + IrFold>(fold: &mut F, exprs: Vec<Expr>) -> Result<Vec<Expr>> {
exprs.into_iter().map(|node| fold.fold_expr(node)).collect()
}
pub fn fold_tuple_fields<F: ?Sized + IrFold>(
fold: &mut F,
exprs: Vec<TupleField>,
) -> Result<Vec<TupleField>> {
exprs
.into_iter()
.map(|field| {
Ok(TupleField {
expr: fold.fold_expr(field.expr)?,
unpack: field.unpack,
})
})
.collect()
}
pub fn fold_call<T: ?Sized + IrFold>(fold: &mut T, call: Call, ty: Ty) -> Result<Expr> {
Ok(ir::Expr {
kind: ExprKind::Call(Box::new(Call {
function: fold.fold_expr(call.function)?,
args: fold_exprs(fold, call.args)?,
})),
ty,
})
}
pub fn fold_ptr(ptr: Pointer, ty: Ty) -> Result<Expr> {
Ok(Expr {
kind: ExprKind::Pointer(ptr),
ty,
})
}
pub fn fold_func<T: ?Sized + IrFold>(fold: &mut T, func: Function, ty: Ty) -> Result<Expr> {
Ok(ir::Expr {
kind: ExprKind::Function(Box::new(Function {
id: func.id,
body: fold.fold_expr(func.body)?,
})),
ty,
})
}
pub fn fold_enum_variant<T: ?Sized + IrFold>(
fold: &mut T,
variant: EnumVariant,
) -> Result<EnumVariant> {
Ok(EnumVariant {
tag: variant.tag,
inner: fold.fold_expr(variant.inner)?,
})
}
pub fn fold_enum_eq<T: ?Sized + IrFold>(fold: &mut T, eq: EnumEq, ty: Ty) -> Result<Expr> {
let eq = EnumEq {
tag: eq.tag,
subject: fold.fold_expr(eq.subject)?,
};
Ok(Expr::new(eq, ty))
}
pub fn fold_enum_unwrap<T: ?Sized + IrFold>(
fold: &mut T,
unwrap: EnumUnwrap,
) -> Result<EnumUnwrap> {
Ok(EnumUnwrap {
tag: unwrap.tag,
subject: fold.fold_expr(unwrap.subject)?,
})
}
pub fn fold_lookup<T: ?Sized + IrFold>(fold: &mut T, lookup: TupleLookup, ty: Ty) -> Result<Expr> {
Ok(ir::Expr {
kind: ExprKind::TupleLookup(Box::new(ir::TupleLookup {
base: fold.fold_expr(lookup.base)?,
position: lookup.position,
})),
ty,
})
}
pub fn fold_binding<T: ?Sized + IrFold>(fold: &mut T, binding: Binding, ty: Ty) -> Result<Expr> {
Ok(ir::Expr {
kind: ExprKind::Binding(Box::new(ir::Binding {
id: binding.id,
expr: fold.fold_expr(binding.expr)?,
main: fold.fold_expr(binding.main)?,
})),
ty,
})
}
pub fn fold_switch<T: ?Sized + IrFold>(
fold: &mut T,
branches: Vec<SwitchBranch>,
ty: Ty,
) -> Result<Expr> {
Ok(ir::Expr {
kind: ExprKind::Switch(
branches
.into_iter()
.map(|branch| {
Ok(SwitchBranch {
condition: fold.fold_expr(branch.condition)?,
value: fold.fold_expr(branch.value)?,
})
})
.collect::<Result<Vec<_>>>()?,
),
ty,
})
}
pub fn fold_ty<T: ?Sized + IrFold>(fold: &mut T, ty: Ty) -> Result<Ty> {
Ok(Ty {
kind: match ty.kind {
TyKind::Tuple(fields) => TyKind::Tuple(fold_ty_tuple_fields(fold, fields)?),
TyKind::Array(ty) => TyKind::Array(Box::new(fold.fold_ty(*ty)?)),
TyKind::Function(func) => TyKind::Function(Box::new(fold_ty_func(fold, *func)?)),
TyKind::Enum(variants) => TyKind::Enum(
variants
.into_iter()
.map(|variant| -> Result<_> {
Ok(TyEnumVariant {
name: variant.name,
ty: fold.fold_ty(variant.ty)?,
})
})
.try_collect()?,
),
TyKind::Ident(_) | TyKind::Primitive(_) => ty.kind,
},
name: ty.name,
layout: ty.layout,
variants_recursive: ty.variants_recursive,
})
}
pub fn fold_ty_func<F: ?Sized + IrFold>(fold: &mut F, f: TyFunction) -> Result<TyFunction> {
Ok(TyFunction {
params: f
.params
.into_iter()
.map(|p| fold.fold_ty(p))
.try_collect()?,
body: fold.fold_ty(f.body)?,
})
}
pub fn fold_ty_tuple_fields<F: ?Sized + IrFold>(
fold: &mut F,
fields: Vec<TyTupleField>,
) -> Result<Vec<TyTupleField>> {
fields
.into_iter()
.map(|field| -> Result<_> {
Ok(TyTupleField {
name: field.name,
ty: fold.fold_ty(field.ty)?,
})
})
.try_collect()
}