From 3fd089ad11b7ad56f1fd44f556ce4ffd6251a4d2 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 21 Oct 2025 07:44:13 -0400 Subject: [PATCH] parser: Move `parse_for` below `impl Parse<'t> for Expr`, and allow a single semi at EOF --- src/parser.rs | 185 ++++++++++++++++++++++++++------------------------ 1 file changed, 97 insertions(+), 88 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 54bdd06..a215ae3 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -731,93 +731,6 @@ impl<'t> Parse<'t> for MakeArm { } } -fn parse_for<'t>(p: &mut Parser<'t>, _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; - // else Expr? - let fail = match p.next_if(TKind::Else).allow_eof()? { - Some(Ok(_)) => p.parse(Prec::Body.next())?, - _ => Expr::Op(Op::Tuple, vec![]).anno(pspan), - }; - let fspan = fail.1; - /* - for `pat in `iter `pass else `fail - ==> - match (`iter).into_iter() { - #iter => loop match #iter.next() { - None => break `fail, - Some(`pat) => `pass, - }, - } - */ - - 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( - BindKind::Match, - 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( - BindKind::Match, - Pat::Name("None".into()), - vec![Expr::Op(Op::Break, vec![fail]).anno(fspan)], - ))) - .anno(fspan), - Expr::Bind(Box::new(Bind( - BindKind::Match, - Pat::NamedTuple( - "Some".into(), - Box::new(Pat::Op(PatOp::Tuple, vec![pat])), - ), - vec![pass], - ))) - .anno(pspan), - ], - ) - .anno(pspan), - ], - ) - .anno(pspan), - ], - ))) - .anno(pspan), - ], - )) -} - impl<'t> Parse<'t> for Expr { type Prec = usize; @@ -939,7 +852,14 @@ impl<'t> Parse<'t> for Expr { // As is ImplicitDo (semicolon elision) Ps::ImplicitDo if p.elide_do => head.and_do(span, p.parse(prec.next())?), Ps::ImplicitDo => break, - Ps::Op(Op::Do) => head.and_do(span, p.consume().parse(prec.next())?), + // Allow `;` at end of file + Ps::Op(Op::Do) => head.and_do( + span, + match p.consume().peek().allow_eof()? { + Some(_) => p.parse(prec.next())?, + None => Anno(Default::default(), span), + }, + ), Ps::Op(Op::Index) => Expr::Op( Op::Index, p.consume() @@ -1008,6 +928,95 @@ fn parse_match<'t>(p: &mut Parser<'t>) -> PResult { Ok(expr) } +fn parse_for<'t>(p: &mut Parser<'t>, _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; + // else Expr? + let fail = match p.next_if(TKind::Else).allow_eof()? { + Some(Ok(_)) => p.parse(Prec::Body.next())?, + _ => Expr::Op(Op::Tuple, vec![]).anno(pspan), + }; + let fspan = fail.1; + /* + for `pat in `iter `pass else `fail + ==> + match (`iter).into_iter() { + #iter => loop match #iter.next() { + None => break `fail, + Some(`pat) => `pass, + }, + } + */ + + // 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( + BindKind::Match, + 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( + BindKind::Match, + Pat::Name("None".into()), + vec![Expr::Op(Op::Break, vec![fail]).anno(fspan)], + ))) + .anno(fspan), + Expr::Bind(Box::new(Bind( + BindKind::Match, + Pat::NamedTuple( + "Some".into(), + Box::new(Pat::Op(PatOp::Tuple, vec![pat])), + ), + vec![pass], + ))) + .anno(pspan), + ], + ) + .anno(pspan), + ], + ) + .anno(pspan), + ], + ))) + .anno(pspan), + ], + )) +} + impl<'t, P: Parse<'t> + Annotation> Parse<'t> for Anno

{ type Prec = P::Prec; fn parse(p: &mut Parser<'t>, level: P::Prec) -> PResult