doughlang: let else, fn rety

ast:
- `let Pat (= Expr (else Expr)?)?`,
- `fn (args) -> Ty`, coagulating `do`, `if/while` formatting fixes

parser:
- int cleanup,
- fn rety parsing,
- experimental `for` desugar,
- experimental semicolon elision (TODO: it sucks),
- let-else parsing
- `do` coagulation impl (still not 100% there)

TODO:
- Fix `do` coagulation
- codify `do` elision rules
- `for` needs lib support
  - this is fine because we have no codegen yet
- Ty matching in macro_matcher
  - Or rip out macro_matcher? Who knows what the future holds.
This commit is contained in:
2025-10-16 01:51:14 -04:00
parent 95abb81f4a
commit 03d9682409
3 changed files with 227 additions and 52 deletions

View File

@@ -200,15 +200,14 @@ impl<'t> Parse<'t> for Literal {
.expect("should have one char in char literal"),
),
TKind::Integer => {
let Token { lexeme, kind: _, span } = p.take().expect("should have Token");
// TODO: more complex int parsing
let int = lexeme
.int()
.ok_or(ParseError::Expected(TKind::Integer, span))?;
Literal::Int(int as _)
let Token { lexeme, span, .. } = p.take().expect("should have Token");
let Lexeme::Integer(int, _) = lexeme else {
Err(ParseError::Expected(TKind::Integer, span))?
};
Literal::Int(int)
}
TKind::String => Literal::Str({
let Token { lexeme, kind: _, span } = p.take().expect("should have Token");
let Token { lexeme, span, .. } = p.take().expect("should have Token");
lexeme
.string()
.ok_or(ParseError::Expected(TKind::String, span))?
@@ -368,7 +367,10 @@ impl<'t> Parse<'t> for Ty {
_ => Err(ParseError::NotType(tok.kind, tok.span))?,
};
Ok(head)
Ok(match p.next_if(TKind::Arrow) {
Ok(_) => Ty::Fn(vec![head, p.parse(())?]),
_ => head,
})
}
}
@@ -435,21 +437,23 @@ impl Prec {
/// PseudoOperator: fake operators used to give certain tokens special behavior.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Ps {
Id, // Identifier
Mid, // MetaIdentifier
Lit, // Literal
Let, // let Pat = Expr
Const, // const Pat = Expr
Struct, // struct { Pat } | struct ( Pat )
Fn, // fn ( Pat,* ) Expr
Lambda0, // || Expr
Lambda, // | Pat,* | Expr
DoubleRef, // && Expr
Make, // Expr{ Expr,* }
Match, // match Expr { MatchArm,* }
Mod, // mod Ty Expr
End, // Produces an empty value.
Op(Op), // A normal [ast::Op]
Id, // Identifier
Mid, // MetaIdentifier
Lit, // Literal
Let, // let Pat = Expr
Const, // const Pat = Expr
Struct, // struct { Pat } | struct ( Pat )
For, // for Pat in Expr Expr else Expr
Fn, // fn ( Pat,* ) Expr
Lambda0, // || Expr
Lambda, // | Pat,* | Expr
DoubleRef, // && Expr
Make, // Expr{ Expr,* }
Match, // match Expr { MatchArm,* }
Mod, // mod Ty Expr
ImplicitDo, // An implicit semicolon
End, // Produces an empty value.
Op(Op), // A normal [ast::Op]
}
fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> {
@@ -464,6 +468,7 @@ fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> {
}
TKind::Public => (Ps::Op(Op::Pub), Prec::Body),
TKind::For => (Ps::For, Prec::Body),
TKind::Fn => (Ps::Fn, Prec::Body),
TKind::Match => (Ps::Match, Prec::Body),
TKind::Macro => (Ps::Op(Op::Macro), Prec::Assign),
@@ -533,6 +538,22 @@ fn from_infix(token: &Token) -> PResult<(Ps, Prec)> {
TKind::Star => (Ps::Op(Op::Mul), Prec::Term),
TKind::Slash => (Ps::Op(Op::Div), Prec::Term),
TKind::Rem => (Ps::Op(Op::Rem), Prec::Term),
TKind::True
| TKind::False
| TKind::Character
| TKind::Integer
| TKind::String
| TKind::Identifier
| TKind::Public
| TKind::Module
| TKind::Fn
| TKind::Do
| TKind::While
| TKind::If
| TKind::For
| TKind::Break
| TKind::Return => (Ps::ImplicitDo, Prec::Do),
kind => Err(ParseError::NotInfix(kind, token.span))?,
})
}
@@ -565,6 +586,7 @@ impl<'t> Parse<'t> for Fn {
Ok(Token { lexeme, .. }) => Ok(Self(
lexeme.string(),
p.parse(PPrec::Typed)?,
p.opt_if((), TKind::Arrow)?.unwrap_or_default(),
p.parse(Prec::Body.next())?,
)),
_ => Ok(Self(
@@ -575,6 +597,7 @@ impl<'t> Parse<'t> for Fn {
TKind::Comma,
TKind::RParen,
)?),
p.opt_if((), TKind::Arrow)?.unwrap_or_default(),
p.parse(Prec::Body.next())?,
)),
}
@@ -585,10 +608,17 @@ impl<'t> Parse<'t> for Let {
type Prec = ();
fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult<Self> {
Ok(Self(
p.consume().parse(PPrec::Alt)?,
p.opt_if(Prec::Tuple.value(), TKind::Eq)?,
))
let pat = p.consume().parse(PPrec::Tuple)?;
if p.next_if(TKind::Eq).is_err() {
return Ok(Self(pat, vec![]));
}
let body = p.parse(Prec::Tuple.value())?;
if p.next_if(TKind::Else).is_err() {
return Ok(Self(pat, vec![body]));
}
Ok(Self(pat, vec![body, p.parse(Prec::Body.next())?]))
}
}
@@ -634,10 +664,106 @@ impl<'t> Parse<'t> for MakeArm {
impl<'t> Parse<'t> for Mod {
type Prec = ();
fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult<Self> {
Ok(Mod(p.consume().parse(())?, p.parse(Prec::Body.value())?))
let ty = p.consume().parse(())?;
let body = p.parse(Prec::Body.value())?;
Ok(Mod(ty, body))
}
}
fn parse_for<'t>(p: &mut Parser<'t>, _level: ()) -> PResult<Expr> {
// for Pat
let pat = p.consume().parse(PPrec::Tuple)?;
// in Expr
let iter: Anno<Expr> = p.consume_if(TKind::In)?.parse(Prec::Logical.next())?;
let cspan = iter.1;
// Expr
let pass: Anno<Expr> = p.parse(Prec::Body.next())?;
let pspan = pass.1;
// else Expr?
let fail = match p.next_if(TKind::Else) {
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,
},
}
*/
// let mut tmp_p = Parser::new(Lexer::new(
// "match `iter.into_iter() {
// `iterator => loop match `iterator.next() {
// None => break `fail,
// Some(`pat) => `pass,
// },
// }",
// ));
// let mut template: Expr = tmp_p.parse(Prec::MIN)?;
// let mut subst = Subst::<Span> { exp: Default::default(), pat: Default::default() };
// subst.exp.extend([
// ("iterator".into(), Expr::Id("#iter".into())),
// ("iter".into(), iter.0),
// ("fail".into(), fail.0),
// ("pass".into(), pass.0),
// ]);
// subst.pat.extend([
// ("iterator".into(), Pat::Name("#iter".into())),
// ("pat".into(), pat),
// ]);
// template.apply(&subst);
// Ok(template)
Ok(Expr::Match(Box::new(Match(
Expr::Op(
Op::Dot,
vec![
iter,
Expr::Op(Op::Call, vec![Expr::Id("into_iter".into()).anno(cspan)]).anno(cspan),
],
)
.anno(cspan),
vec![MatchArm(
Pat::Name("#iter".into()),
Expr::Op(
Op::Loop,
vec![
Expr::Match(Box::new(Match(
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),
vec![
MatchArm(
Pat::Name("None".into()),
Expr::Op(Op::Break, vec![fail]).anno(fspan),
),
MatchArm(
Pat::TupStruct("Some".into(), Box::new(Pat::Tuple(vec![pat]))),
pass,
),
],
)))
.anno(pspan),
],
)
.anno(pspan),
)],
))))
}
impl<'t> Parse<'t> for Expr {
type Prec = usize;
@@ -662,6 +788,7 @@ impl<'t> Parse<'t> for Expr {
Ps::Mid => Expr::MetId(p.consume().next()?.lexeme.to_string()),
Ps::Lit => Expr::Lit(p.parse(())?),
Ps::Let => Expr::Let(p.parse(())?),
Ps::For => parse_for(p, ())?,
Ps::Const => Expr::Const(p.parse(())?),
Ps::Struct => Expr::Struct(p.parse(())?),
Ps::Match => Expr::Match(p.parse(())?),
@@ -694,11 +821,15 @@ impl<'t> Parse<'t> for Expr {
p.consume()
.opt(PPrec::Tuple, TKind::Bar)?
.unwrap_or(Pat::Tuple(vec![])),
p.opt_if((), TKind::Arrow)?.unwrap_or_default(),
p.parse(Prec::Body.next())?,
))),
Ps::Lambda0 => Expr::Fn(Box::new(Fn(
None,
Pat::Tuple(vec![]),
p.consume().opt_if((), TKind::Arrow)?.unwrap_or_default(),
p.parse(Prec::Body.next())?,
))),
Ps::Lambda0 => Expr::Fn(Box::new(Fn(None, Pat::Tuple(vec![]), {
p.consume().parse(Prec::Body.next())?
}))),
Ps::DoubleRef => p.consume().parse(prec.next()).map(|Anno(expr, span)| {
Expr::Op(
Op::Refer,
@@ -724,12 +855,14 @@ impl<'t> Parse<'t> for Expr {
Ps::Make => match &head {
Expr::Op(Op::Path, _) | Expr::Id(_) | Expr::MetId(_) => {
Expr::Make(Box::new(Make(
head.anno(span).into(),
head.anno(span),
p.consume().list(vec![], (), TKind::Comma, TKind::RCurly)?,
)))
}
_ => break,
},
Ps::Op(Op::Do) => head.and_do(span, p.consume().parse(prec.next())?),
Ps::ImplicitDo => head.and_do(span, p.parse(prec.next())?),
Ps::Op(Op::Index) => Expr::Op(
Op::Index,
p.consume()
@@ -740,13 +873,11 @@ impl<'t> Parse<'t> for Expr {
p.consume()
.list(vec![head.anno(span)], 0, TKind::Comma, TKind::RParen)?,
),
Ps::Op(op @ (Op::Do | Op::Tuple | Op::Dot | Op::Path | Op::LogAnd | Op::LogOr)) => {
Expr::Op(
op,
p.consume()
.list_bare(vec![head.anno(span)], prec.next(), kind)?,
)
}
Ps::Op(op @ (Op::Tuple | Op::Dot | Op::Path | Op::LogAnd | Op::LogOr)) => Expr::Op(
op,
p.consume()
.list_bare(vec![head.anno(span)], prec.next(), kind)?,
),
Ps::Op(op @ Op::Try) => {
p.consume();
Expr::Op(op, vec![head.anno(span)])