diff --git a/src/ast.rs b/src/ast.rs index def78ed..75d9f48 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -184,6 +184,7 @@ pub enum Use { /// fn Pat Expr /// mod Pat Expr /// impl Pat Expr +/// for Pat in Expr Expr (else Expr)? /// Pat => Expr // in match /// ``` #[derive(Clone, Debug, PartialEq, Eq)] @@ -214,6 +215,8 @@ pub enum BindOp { Struct, /// An enum definition Enum, + /// A `for Pat in Expr Expr (else Expr)?` binding + For, /// A `Pat => Expr` binding Match, } diff --git a/src/ast/display.rs b/src/ast/display.rs index 5bc13ac..dde1556 100644 --- a/src/ast/display.rs +++ b/src/ast/display.rs @@ -190,6 +190,13 @@ impl Display for Bind { }, _ => 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() { [] => write!(f, "{pat}"), [value] => write!(f, "{pat} = {value}"), @@ -212,6 +219,7 @@ impl Display for BindOp { Self::Fn => "fn ", Self::Mod => "mod ", Self::Impl => "impl ", + Self::For => "for ", Self::Match => "", }) } diff --git a/src/parser/expr.rs b/src/parser/expr.rs index 77bb118..485ef59 100644 --- a/src/parser/expr.rs +++ b/src/parser/expr.rs @@ -398,19 +398,12 @@ fn parse_match(p: &mut Parser<'_>) -> PResult { Ok(expr) } -/// Parses and desugars a `for` loop expression -/// -/// Assumes the existence of the following items: -/// -/// 1. `enum Option { None, Some(T) }` -/// 2. `fn T::into_iter(&mut self) -> U` -/// 3. `U::next() -> Option` +/// Parses a `for` loop expression fn parse_for(p: &mut Parser<'_>, _level: ()) -> PResult { // for Pat let pat = p.consume().parse(PPrec::Tuple)?; // in Expr let iter: Anno = p.expect(TKind::In)?.parse(Prec::Logical.next())?; - let cspan = iter.1; // Expr let pass: Anno = p.parse(Prec::Body.next())?; let pspan = pass.1; @@ -419,8 +412,8 @@ fn parse_for(p: &mut Parser<'_>, _level: ()) -> PResult { Some(Ok(_)) => p.parse(Prec::Body.next())?, _ => Expr::Op(Op::Tuple, vec![]).anno(pspan), }; - let fspan = fail.1; /* + TODO: desugar for into loop-match: for `pat in `iter `pass else `fail ==> match (`iter).into_iter() { @@ -431,70 +424,12 @@ fn parse_for(p: &mut Parser<'_>, _level: ()) -> PResult { } */ - // TODO: A better way to do this kind of substitution desugaring - // without losing span information! - Ok(Expr::Op( - Op::Match, - vec![ - 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), - ], - )) + Ok(Expr::Bind(Box::new(Bind( + BindOp::For, + vec![], + pat, + vec![iter, pass, fail], + )))) } /// Returns the [BindOp], [pattern precedence](PPrec), [arrow TKind](TKind), [body precedence](Prec),