ast: Give for loops a dedicated node and remove eager desugar

This commit is contained in:
2025-12-01 23:16:47 -05:00
parent 7ea483fe26
commit e566ea62e5
3 changed files with 19 additions and 73 deletions

View File

@@ -184,6 +184,7 @@ pub enum Use {
/// fn Pat Expr /// fn Pat Expr
/// mod Pat Expr /// mod Pat Expr
/// impl Pat Expr /// impl Pat Expr
/// for Pat in Expr Expr (else Expr)?
/// Pat => Expr // in match /// Pat => Expr // in match
/// ``` /// ```
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
@@ -214,6 +215,8 @@ pub enum BindOp {
Struct, Struct,
/// An enum definition /// An enum definition
Enum, Enum,
/// A `for Pat in Expr Expr (else Expr)?` binding
For,
/// A `Pat => Expr` binding /// A `Pat => Expr` binding
Match, Match,
} }

View File

@@ -190,6 +190,13 @@ impl<A: Annotation> Display for Bind<A> {
}, },
_ => pat.fmt(f), _ => pat.fmt(f),
}, },
BindOp::For => match exprs.as_slice() {
[iter, pass, Anno(Expr::Op(Op::Tuple, e), _)] if e.is_empty() => {
write!(f, "{pat} in {iter} {pass}")
}
[iter, pass, fail] => write!(f, "{pat} in {iter} {pass} else {fail}"),
other => f.delimit(fmt!("{pat} in [["), "]]!?").list(other, ", "),
},
_ => match exprs.as_slice() { _ => match exprs.as_slice() {
[] => write!(f, "{pat}"), [] => write!(f, "{pat}"),
[value] => write!(f, "{pat} = {value}"), [value] => write!(f, "{pat} = {value}"),
@@ -212,6 +219,7 @@ impl Display for BindOp {
Self::Fn => "fn ", Self::Fn => "fn ",
Self::Mod => "mod ", Self::Mod => "mod ",
Self::Impl => "impl ", Self::Impl => "impl ",
Self::For => "for ",
Self::Match => "", Self::Match => "",
}) })
} }

View File

@@ -398,19 +398,12 @@ fn parse_match(p: &mut Parser<'_>) -> PResult<Expr> {
Ok(expr) Ok(expr)
} }
/// Parses and desugars a `for` loop expression /// Parses a `for` loop expression
///
/// Assumes the existence of the following items:
///
/// 1. `enum<T> Option { None, Some(T) }`
/// 2. `fn T::into_iter(&mut self) -> U`
/// 3. `U::next() -> Option<V>`
fn parse_for(p: &mut Parser<'_>, _level: ()) -> PResult<Expr> { fn parse_for(p: &mut Parser<'_>, _level: ()) -> PResult<Expr> {
// for Pat // for Pat
let pat = p.consume().parse(PPrec::Tuple)?; let pat = p.consume().parse(PPrec::Tuple)?;
// in Expr // in Expr
let iter: Anno<Expr> = p.expect(TKind::In)?.parse(Prec::Logical.next())?; let iter: Anno<Expr> = p.expect(TKind::In)?.parse(Prec::Logical.next())?;
let cspan = iter.1;
// Expr // Expr
let pass: Anno<Expr> = p.parse(Prec::Body.next())?; let pass: Anno<Expr> = p.parse(Prec::Body.next())?;
let pspan = pass.1; let pspan = pass.1;
@@ -419,8 +412,8 @@ fn parse_for(p: &mut Parser<'_>, _level: ()) -> PResult<Expr> {
Some(Ok(_)) => p.parse(Prec::Body.next())?, Some(Ok(_)) => p.parse(Prec::Body.next())?,
_ => Expr::Op(Op::Tuple, vec![]).anno(pspan), _ => Expr::Op(Op::Tuple, vec![]).anno(pspan),
}; };
let fspan = fail.1;
/* /*
TODO: desugar for into loop-match:
for `pat in `iter `pass else `fail for `pat in `iter `pass else `fail
==> ==>
match (`iter).into_iter() { match (`iter).into_iter() {
@@ -431,70 +424,12 @@ fn parse_for(p: &mut Parser<'_>, _level: ()) -> PResult<Expr> {
} }
*/ */
// TODO: A better way to do this kind of substitution desugaring Ok(Expr::Bind(Box::new(Bind(
// without losing span information! BindOp::For,
Ok(Expr::Op( vec![],
Op::Match, pat,
vec![ vec![iter, pass, fail],
Expr::Op( ))))
Op::Dot,
vec![
iter,
Expr::Op(Op::Call, vec![Expr::Id("into_iter".into()).anno(cspan)]).anno(cspan),
],
)
.anno(cspan),
Expr::Bind(Box::new(Bind(
BindOp::Match,
vec![],
Pat::Name("#iter".into()),
vec![
Expr::Op(
Op::Loop,
vec![
Expr::Op(
Op::Match,
vec![
Expr::Op(
Op::Dot,
vec![
Expr::Id("#iter".into()).anno(cspan),
Expr::Op(
Op::Call,
vec![Expr::Id("next".into()).anno(cspan)],
)
.anno(cspan),
],
)
.anno(cspan),
Expr::Bind(Box::new(Bind(
BindOp::Match,
vec![],
Pat::Name("None".into()),
vec![Expr::Op(Op::Break, vec![fail]).anno(fspan)],
)))
.anno(fspan),
Expr::Bind(Box::new(Bind(
BindOp::Match,
vec![],
Pat::NamedTuple(
"Some".into(),
Box::new(Pat::Op(PatOp::Tuple, vec![pat])),
),
vec![pass],
)))
.anno(pspan),
],
)
.anno(pspan),
],
)
.anno(pspan),
],
)))
.anno(pspan),
],
))
} }
/// Returns the [BindOp], [pattern precedence](PPrec), [arrow TKind](TKind), [body precedence](Prec), /// Returns the [BindOp], [pattern precedence](PPrec), [arrow TKind](TKind), [body precedence](Prec),