//! A folder (implementer of the [`Fold`] trait) maps ASTs to ASTs #![warn(clippy::all, clippy::pedantic)] #![allow(clippy::wildcard_imports, clippy::missing_errors_doc)] use std::mem::MaybeUninit; use super::*; /// Deconstructs an entire AST, and reconstructs it from parts. /// /// Each function acts as a customization point. /// /// Aside from [Annotation]s, each node in the AST implements the [`Foldable`] trait, /// which provides double dispatch. pub trait Fold { type Error; fn fold_annotation(&mut self, anno: A) -> Result { Ok(anno) } /// Consumes an [`Expr`], possibly transforms it, and produces a replacement [`Expr`] fn fold_expr(&mut self, expr: Expr) -> Result, Self::Error> { expr.children(self) } /// Consumes a Symbol, possibly transforms it, and produces a replacement Symbol fn fold_ident(&mut self, name: String) -> Result { name.children(self) // TODO: ^ this should be a symbol } /// Consumes a [`Path`], possibly transforms it, and produces a replacement [`Path`] fn fold_path(&mut self, path: Path) -> Result { path.children(self) } /// Consumes a [`Literal`], possibly transforms it, and produces a replacement [`Literal`] fn fold_literal(&mut self, lit: Literal) -> Result { lit.children(self) } /// Consumes a [`Use`], possibly transforms it, and produces a replacement [`Use`] fn fold_use(&mut self, item: Use) -> Result { item.children(self) } /// Consumes a [`Pat`], possibly transforms it, and produces a replacement [`Pat`] fn fold_pat(&mut self, pat: Pat) -> Result, Self::Error> { pat.children(self) } /// Consumes a [`Bind`], possibly transforms it, and produces a replacement [`Bind`] fn fold_bind(&mut self, bind: Bind) -> Result, Self::Error> { bind.children(self) } /// Consumes a [`Make`], possibly transforms it, and produces a replacement [`Make`] fn fold_make(&mut self, make: Make) -> Result, Self::Error> { make.children(self) } /// Consumes a [`MakeArm`], possibly transforms it, and produces a replacement [`MakeArm`] fn fold_makearm(&mut self, arm: MakeArm) -> Result, Self::Error> { arm.children(self) } } pub trait Foldable: Sized { /// Calls `Self`'s appropriate [Folder] function(s) fn fold_in + ?Sized>(self, folder: &mut F) -> Result; /// Destructures `self`, calling [`Foldable::fold_in`] on all foldable members, /// and rebuilds a `Self` out of the results. #[allow(unused_variables)] fn children + ?Sized>(self, folder: &mut F) -> Result { Ok(self) } } impl Foldable for Expr { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { folder.fold_expr(self) } fn children + ?Sized>(self, folder: &mut F) -> Result { Ok(match self { Self::Omitted => Self::Omitted, Self::Id(path) => Self::Id(path.fold_in(folder)?), Self::MetId(id) => Self::MetId(id.fold_in(folder)?), Self::Lit(literal) => Self::Lit(literal.fold_in(folder)?), Self::Use(item) => Self::Use(item.fold_in(folder)?), Self::Bind(bind) => Self::Bind(bind.fold_in(folder)?), Self::Make(make) => Self::Make(make.fold_in(folder)?), Self::Op(op, annos) => Self::Op(op, annos.fold_in(folder)?), }) } } impl Foldable for String { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { folder.fold_ident(self) } } impl Foldable for Path { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { folder.fold_path(self) } fn children + ?Sized>(self, _folder: &mut F) -> Result { Ok(self) } } impl Foldable for Literal { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { folder.fold_literal(self) } fn children + ?Sized>(self, _folder: &mut F) -> Result { Ok(self) } } impl Foldable for Use { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { folder.fold_use(self) } fn children + ?Sized>(self, folder: &mut F) -> Result { Ok(match self { Self::Glob => Self::Glob, Self::Name(name) => Self::Name(name), Self::Alias(name, alias) => Self::Alias(name, alias), Self::Path(name, rest) => Self::Path(name, rest.fold_in(folder)?), Self::Tree(items) => Self::Tree(items.fold_in(folder)?), }) } } impl Foldable for Pat { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { folder.fold_pat(self) } fn children + ?Sized>(self, folder: &mut F) -> Result { Ok(match self { Self::Ignore => Self::Ignore, Self::Never => Self::Never, Self::MetId(name) => Self::MetId(name.fold_in(folder)?), Self::Name(name) => Self::Name(name.fold_in(folder)?), Self::Value(expr) => Self::Value(expr.fold_in(folder)?), Self::Op(op, pats) => Self::Op(op, pats.fold_in(folder)?), }) } } impl Foldable for Bind { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { folder.fold_bind(self) } fn children + ?Sized>(self, folder: &mut F) -> Result { let Self(op, gens, pat, exprs) = self; Ok(Self( op, gens.fold_in(folder)?, pat.fold_in(folder)?, exprs.fold_in(folder)?, )) } } impl Foldable for Make { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { folder.fold_make(self) } fn children + ?Sized>(self, folder: &mut F) -> Result { let Self(expr, arms) = self; Ok(Self(expr.fold_in(folder)?, arms.fold_in(folder)?)) } } impl Foldable for MakeArm { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { folder.fold_makearm(self) } fn children + ?Sized>(self, folder: &mut F) -> Result { let Self(name, expr) = self; Ok(Self(name, expr.fold_in(folder)?)) } } impl, A: Annotation> Foldable for Anno { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { let Self(expr, anno) = self; let anno = folder.fold_annotation(anno)?; Ok(Self(expr.fold_in(folder)?, anno)) } fn children + ?Sized>(self, folder: &mut F) -> Result { let Self(expr, anno) = self; Ok(Self(expr.children(folder)?, anno)) } } ////////////////////////////////////////////// // GENERIC IMPLEMENTATIONS ON COLLECTIONS // ////////////////////////////////////////////// /// Maps the value in the box across `f()` without deallocating fn box_try_map(boxed: Box, f: impl FnOnce(T) -> Result) -> Result, E> { // TODO: replace with Box::take when it stabilizes. let rawbox = Box::into_raw(boxed); // Safety: `rawbox` came from a Box, so it is aligned and initialized. // To prevent further reuse and deallocate on failure, rawbox is // shadowed by a Box>. // Safety: MaybeUninit has the same size and alignment as T. let (value, rawbox) = (unsafe { rawbox.read() }, unsafe { Box::from_raw(rawbox.cast::>()) }); // rawbox is reinitialized with f(value) Ok(Box::write(rawbox, f(value)?)) } impl, A: Annotation> Foldable for Box { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { box_try_map(self, |t| t.fold_in(folder)) } fn children + ?Sized>(self, folder: &mut F) -> Result { box_try_map(self, |t| t.children(folder)) } } impl, A: Annotation> Foldable for Vec { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { self.into_iter().map(|e| e.fold_in(folder)).collect() } fn children + ?Sized>(self, folder: &mut F) -> Result { // TODO: is this correct for generic data structures? self.into_iter().map(|e| e.fold_in(folder)).collect() } } impl, A: Annotation> Foldable for Option { fn fold_in + ?Sized>(self, folder: &mut F) -> Result { Ok(match self { Self::Some(value) => Some(value.fold_in(folder)?), Self::None => None, }) } fn children + ?Sized>(self, folder: &mut F) -> Result { Ok(match self { Self::Some(value) => Some(value.children(folder)?), Self::None => None, }) } }