From 0bf5a41498dc32d85cf660f7b1c320a6054f1ee8 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 28 Oct 2025 02:27:56 -0400 Subject: [PATCH] syntax: Merge ALL definitions into Bind, and allow type variable introducers at every Bind --- src/ast.rs | 98 +++++++++++++++++---------------- src/ast/macro_matcher.rs | 24 +++------ src/parser.rs | 114 +++++++++++++++++++++------------------ src/span.rs | 2 +- 4 files changed, 116 insertions(+), 122 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index b0798a4..0500ce0 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -114,30 +114,26 @@ pub enum PatOp { Alt, } -/// A record-type definition -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Typedef(pub TypedefKind, pub Pat); - -/// The type of type being defined -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum TypedefKind { - Struct, - Enum, -} - /// A pattern binding /// ```ignore /// let Pat (= Expr (else Expr)?)? /// const Pat (= Expr (else Expr)?)? /// static Pat (= Expr (else Expr)?)? /// type Pat (= Expr)? +/// struct Pat +/// enum Pat /// fn Pat Expr /// mod Pat Expr /// impl Pat Expr /// Pat => Expr // in match -/// `````` +/// ``` #[derive(Clone, Debug, PartialEq, Eq)] -pub struct Bind(pub BindKind, pub Pat, pub Vec, A>>); +pub struct Bind( + pub BindKind, + pub Vec, + pub Pat, + pub Vec, A>>, +); #[derive(Clone, Debug, PartialEq, Eq)] pub enum BindKind { @@ -149,6 +145,10 @@ pub enum BindKind { Static, /// A type-alias binding Type, + /// A struct definition + Struct, + /// An enum definition + Enum, /// A `fn Pat Expr` binding Fn, /// A `mod Pat Expr` binding @@ -176,6 +176,8 @@ pub struct MakeArm(pub String, pub Option, A> /// Expressions: The beating heart of Dough #[derive(Clone, Debug, PartialEq, Eq)] pub enum Expr { + /// Omitted by semicolon insertion-elision rules + Omitted, /// An identifier Id(FqPath), /// An escaped token for macro binding @@ -187,8 +189,6 @@ pub enum Expr { /// `(let | const | static) Pat::NoTopAlt (= expr (else expr)?)?` | /// `(fn | mod | impl) Pat::Fn Expr` Bind(Box>), - /// (struct | enum | type) Pat::NoTopAlt - Struct(Box), /// Expr { (Ident (: Expr)?),* } Make(Box>), /// Op Expr | Expr Op | Expr (Op Expr)+ | Op Expr Expr else Expr @@ -212,13 +212,14 @@ pub enum Op { Index, // Expr [ Expr,* ] Call, // Expr ( Expr,* ) - Pub, // pub Expr - Loop, // loop Expr - Match, // match Expr { ,* } - If, // if Expr Expr (else Expr)? - While, // while Expr Expr (else Expr)? - Break, // break Expr - Return, // return Expr + Pub, // pub Expr + Loop, // loop Expr + Match, // match Expr { ,* } + If, // if Expr Expr (else Expr)? + While, // while Expr Expr (else Expr)? + Break, // break Expr + Return, // return Expr + Continue, // continue Dot, // Expr . Expr @@ -356,18 +357,31 @@ impl Display for Use { impl Display for Bind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Self(op, pat, exprs) = self; + let Self(op, gens, pat, exprs) = self; + op.fmt(f)?; + if !gens.is_empty() { + f.delimit("<", "> ").list(gens, ", ")?; + } match op { BindKind::Match => f.delimit(fmt!("{pat} => "), "").list(exprs, ",!? "), BindKind::Fn | BindKind::Mod | BindKind::Impl => { - f.delimit(fmt!("{op}{pat} "), "").list(exprs, ",!? ") + f.delimit(fmt!("{pat} "), "").list(exprs, ",!? ") } + BindKind::Struct | BindKind::Enum => match pat { + Pat::NamedStruct(name, bind) => match bind.as_ref() { + Pat::Op(PatOp::Tuple, parts) => f + .delimit_indented(fmt!("{name} {{"), "}") + .list_wrap("\n", parts, ",\n", ",\n"), + other => write!(f, "{name} {{ {other} }}"), + }, + _ => pat.fmt(f), + }, _ => match exprs.as_slice() { - [] => write!(f, "{op}{pat}"), - [value] => write!(f, "{op}{pat} = {value}"), - [value, fail] => write!(f, "{op} {pat} = {value} else {fail}"), - other => f.delimit(fmt!("{op}{pat} ("), ")").list(other, ", "), + [] => write!(f, "{pat}"), + [value] => write!(f, "{pat} = {value}"), + [value, fail] => write!(f, "{pat} = {value} else {fail}"), + other => f.delimit(fmt!("{pat} ("), ")").list(other, ", "), }, } } @@ -380,10 +394,12 @@ impl Display for BindKind { Self::Const => "const ", Self::Static => "static ", Self::Type => "type ", + Self::Struct => "struct ", + Self::Enum => "enum ", Self::Fn => "fn ", Self::Mod => "mod ", Self::Impl => "impl ", - Self::Match => "| ", + Self::Match => "", }) } } @@ -395,34 +411,15 @@ impl Display for Make { } } -impl Display for Typedef { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Self(kind, pat) = self; - f.write_str(match kind { - TypedefKind::Struct => "struct ", - TypedefKind::Enum => "enum ", - })?; - match pat { - Pat::NamedStruct(name, bind) => match bind.as_ref() { - Pat::Op(PatOp::Tuple, parts) => f - .delimit_indented(fmt!("{name} {{"), "}") - .list_wrap("\n", parts, ",\n", ",\n"), - other => write!(f, "{name} {{ {other} }}"), - }, - _ => pat.fmt(f), - } - } -} - impl Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + Self::Omitted => "/* omitted */".fmt(f), Self::Id(id) => id.fmt(f), Self::MetId(id) => write!(f, "`{id}"), Self::Lit(literal) => literal.fmt(f), Self::Use(v) => write!(f, "use {v}"), Self::Bind(v) => v.fmt(f), - Self::Struct(v) => v.fmt(f), Self::Make(v) => v.fmt(f), Self::Op(op @ (Op::If | Op::While), exprs) => match exprs.as_slice() { @@ -446,7 +443,7 @@ impl Display for Expr { Self::Op(Op::Tuple, exprs) => f.delimit("(", ")").list(exprs, ", "), Self::Op(Op::Group, exprs) => f.list(exprs, ", "), Self::Op(Op::Meta, exprs) => match exprs.as_slice() { - [meta, expr @ ..] => f.delimit(fmt!("#[{meta}] "), "").list(expr, ","), + [meta, expr @ ..] => f.delimit(fmt!("#[{meta}]\n"), "").list(expr, ","), [] => write!(f, "#[]"), }, @@ -492,6 +489,7 @@ impl Display for Op { Op::While => "while ", Op::Break => "break ", Op::Return => "return ", + Op::Continue => "continue", Op::Dot => ".", Op::RangeEx => "..", Op::RangeIn => "..=", diff --git a/src/ast/macro_matcher.rs b/src/ast/macro_matcher.rs index 4f36d8e..2f5df6b 100644 --- a/src/ast/macro_matcher.rs +++ b/src/ast/macro_matcher.rs @@ -80,14 +80,15 @@ impl + Annotation, A: Annotation> Match for Anno { impl Match for Bind { fn recurse(sub: &mut Subst, pat: &Self, expr: &Self) -> bool { - let (Self(pat_kind, pat_pat, pat_expr), Self(expr_kind, expr_pat, expr_expr)) = (pat, expr); + let (Self(pat_kind, _, pat_pat, pat_expr), Self(expr_kind, _, expr_pat, expr_expr)) = + (pat, expr); pat_kind == expr_kind && Match::recurse(sub, pat_pat, expr_pat) && Match::recurse(sub, pat_expr, expr_expr) } fn apply(&mut self, sub: &Subst) { - let Self(_, pat, expr) = self; + let Self(_, _, pat, expr) = self; pat.apply(sub); expr.apply(sub); } @@ -106,21 +107,11 @@ impl Match for crate::ast::Make { } } -impl Match for Typedef { - fn recurse(sub: &mut Subst, pat: &Self, expr: &Self) -> bool { - let (Self(pat_kind, pat_pat), Self(expr_kind, expr_pat)) = (pat, expr); - pat_kind == expr_kind && Match::recurse(sub, pat_pat, expr_pat) - } - - fn apply(&mut self, sub: &Subst) { - let Self(_, pat) = self; - pat.apply(sub); - } -} - impl Match for Expr { fn recurse(sub: &mut Subst, pat: &Self, expr: &Self) -> bool { match (pat, expr) { + (Expr::Omitted, Expr::Omitted) => true, + (Expr::Omitted, _) => false, (Expr::MetId(name), _) if name == "_" => true, (Expr::MetId(name), _) => sub.add_expr(name.clone(), expr), (Expr::Id(pat), Expr::Id(expr)) => pat == expr, @@ -131,8 +122,6 @@ impl Match for Expr { (Expr::Use(_), _) => false, (Expr::Bind(pat), Expr::Bind(expr)) => Match::recurse(sub, pat, expr), (Expr::Bind(..), _) => false, - (Expr::Struct(pat), Expr::Struct(expr)) => Match::recurse(sub, pat, expr), - (Expr::Struct(_), _) => false, (Expr::Make(pat), Expr::Make(expr)) => Match::recurse(sub, pat, expr), (Expr::Make(..), _) => false, (Expr::Op(pat_op, pat_exprs), Expr::Op(expr_op, expr_exprs)) => { @@ -149,9 +138,8 @@ impl Match for Expr { *self = expr.clone() } } - Expr::Id(_) | Expr::Lit(_) | Expr::Use(_) => {} + Expr::Omitted | Expr::Id(_) | Expr::Lit(_) | Expr::Use(_) => {} Expr::Bind(expr) => expr.apply(sub), - Expr::Struct(expr) => expr.apply(sub), Expr::Make(expr) => expr.apply(sub), Expr::Op(op, exprs) => { op.apply(sub); diff --git a/src/parser.rs b/src/parser.rs index f536b2f..6a8176c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -535,19 +535,12 @@ pub enum Ps { Mid, // MetaIdentifier Lit, // Literal Use, // use Use - Let, // let Pat = Expr (else Expr)? - Const, // const Pat = Expr - Static, // static Pat = Expr - Type, // type Pat = Expr - Typedef, // struct { Pat } | struct ( Pat ) + Def, // any definition (let, const, static, struct, enum, fn, ...) For, // for Pat in Expr Expr else Expr - Fn, // fn ( Pat,* ) Expr Lambda0, // || Expr Lambda, // | Pat,* | Expr DoubleRef, // && Expr Make, // Expr{ Expr,* } - Mod, // mod Pat Expr - Impl, // impl Pat Expr ImplicitDo, // An implicit semicolon ExplicitDo, // An explicit leading semicolon End, // Produces an empty value. @@ -568,16 +561,19 @@ fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> { TKind::Pub => (Ps::Op(Op::Pub), Prec::Body), TKind::For => (Ps::For, Prec::Body), - TKind::Fn => (Ps::Fn, Prec::Body), TKind::Match => (Ps::Op(Op::Match), Prec::Body), TKind::Macro => (Ps::Op(Op::Macro), Prec::Assign), - TKind::Mod => (Ps::Mod, Prec::Body), - TKind::Impl => (Ps::Impl, Prec::Body), - TKind::Let => (Ps::Let, Prec::Tuple), - TKind::Const => (Ps::Const, Prec::Body), - TKind::Static => (Ps::Static, Prec::Body), - TKind::Type => (Ps::Type, Prec::Body), - TKind::Struct | TKind::Enum => (Ps::Typedef, Prec::Body), + + TKind::Fn + | TKind::Mod + | TKind::Impl + | TKind::Let + | TKind::Const + | TKind::Static + | TKind::Type + | TKind::Struct + | TKind::Enum => (Ps::Def, Prec::Body), + TKind::Loop => (Ps::Op(Op::Loop), Prec::Body), TKind::If => (Ps::Op(Op::If), Prec::Body), TKind::While => (Ps::Op(Op::While), Prec::Body), @@ -658,56 +654,66 @@ fn from_infix(token: &Token) -> PResult<(Ps, Prec)> { }) } -impl<'t> Parse<'t> for Typedef { +impl<'t> Parse<'t> for BindKind { type Prec = (); fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult { - let tok = p.next()?; - match tok.kind { - TKind::Enum => Ok(Self(TypedefKind::Enum, p.parse(PPrec::Tuple)?)), - TKind::Struct => Ok(Self(TypedefKind::Struct, p.parse(PPrec::Tuple)?)), - _ => Err(ParseError::NotType(tok.kind, tok.span)), - } + let bk = match p.peek()?.kind { + TKind::Let => BindKind::Let, + TKind::Const => BindKind::Const, + TKind::Static => BindKind::Static, + TKind::Type => BindKind::Type, + TKind::Struct => BindKind::Struct, + TKind::Enum => BindKind::Enum, + TKind::Fn => BindKind::Fn, + TKind::Mod => BindKind::Mod, + TKind::Impl => BindKind::Impl, + TKind::Bar => BindKind::Match, + // no consume! + _ => return Ok(BindKind::Match), + }; + p.consume(); + Ok(bk) } } impl<'t> Parse<'t> for Bind { - type Prec = BindKind; + type Prec = (); + + fn parse(p: &mut Parser<'t>, _level: Self::Prec) -> PResult { + let level = p.parse(())?; + let generics = match p.next_if(TKind::Lt)? { + Ok(_) => p.list(vec![], (), TKind::Comma, TKind::Gt)?, + Err(_) => vec![], + }; - fn parse(p: &mut Parser<'t>, level: Self::Prec) -> PResult { match level { BindKind::Match => { // |? Pat => Expr - p.next_if(TKind::Bar)?.ok(); // and discard Ok(Self( level, + generics, p.parse(PPrec::Alt)?, vec![p.expect(TKind::FatArrow)?.parse(Prec::Body.next())?], )) } BindKind::Mod | BindKind::Impl => Ok(Self( level, - p.consume().parse(PPrec::Max)?, + generics, + p.parse(PPrec::Max)?, + vec![p.parse(Prec::Body.next())?], + )), + BindKind::Fn => Ok(Self( + level, + generics, + p.parse(PPrec::Fn)?, vec![p.parse(Prec::Body.next())?], )), - BindKind::Fn => { - if p.consume().next_if(TKind::Lt)?.is_ok() { - let _gty: Vec = p.list(vec![], (), TKind::Comma, TKind::Gt)?; - } - Ok(Self( - level, - p.parse(PPrec::Fn)?, - vec![p.parse(Prec::Body.next())?], - )) - } _ => { - if p.consume().next_if(TKind::Lt)?.is_ok() { - let _gty: Vec = p.list(vec![], (), TKind::Comma, TKind::Gt)?; - } // let Pat let pat = p.parse(PPrec::Tuple)?; if p.next_if(TKind::Eq).allow_eof()?.is_none_or(|v| v.is_err()) { - return Ok(Self(level, pat, vec![])); + return Ok(Self(level, generics, pat, vec![])); } // = Expr @@ -716,11 +722,16 @@ impl<'t> Parse<'t> for Bind { .allow_eof()? .is_none_or(|v| v.is_err()) { - return Ok(Self(level, pat, vec![body])); + return Ok(Self(level, generics, pat, vec![body])); } // else Expr - Ok(Self(level, pat, vec![body, p.parse(Prec::Body.next())?])) + Ok(Self( + level, + generics, + pat, + vec![body, p.parse(Prec::Body.next())?], + )) } } } @@ -760,21 +771,14 @@ impl<'t> Parse<'t> for Expr { // "End" is produced when an "empty" expression is syntactically required. // This happens when a semi or closing delimiter begins an expression. // The token which emitted "End" cannot be consumed, as it is expected elsewhere. - Ps::End if level <= prec.next() => Expr::Op(Op::Tuple, vec![]), + Ps::End if level <= prec.next() => Expr::Omitted, Ps::End => Err(ParseError::NotPrefix(kind, span))?, Ps::Id => Expr::Id(p.parse(())?), Ps::Mid => Expr::MetId(p.consume().next()?.lexeme.to_string()), Ps::Lit => Expr::Lit(p.parse(())?), Ps::Use => Expr::Use(p.consume().parse(())?), - Ps::Typedef => Expr::Struct(p.parse(())?), - Ps::Let => Expr::Bind(p.parse(BindKind::Let)?), - Ps::Const => Expr::Bind(p.parse(BindKind::Const)?), - Ps::Static => Expr::Bind(p.parse(BindKind::Static)?), - Ps::Type => Expr::Bind(p.parse(BindKind::Type)?), - Ps::Mod => Expr::Bind(p.parse(BindKind::Mod)?), - Ps::Impl => Expr::Bind(p.parse(BindKind::Impl)?), - Ps::Fn => Expr::Bind(p.parse(BindKind::Fn)?), + Ps::Def => Expr::Bind(p.parse(())?), Ps::Lambda | Ps::Lambda0 => { p.consume(); @@ -789,6 +793,7 @@ impl<'t> Parse<'t> for Expr { Expr::Bind(Box::new(Bind( BindKind::Fn, + vec![], Pat::Op(PatOp::Fn, vec![args, rety]), vec![p.parse(Prec::Body.next())?], ))) @@ -928,7 +933,7 @@ fn parse_match<'t>(p: &mut Parser<'t>) -> PResult { let arms = p .expect(TKind::LCurly)? - .list(vec![], BindKind::Match, TKind::Comma, TKind::RCurly)? + .list(vec![], (), TKind::Comma, TKind::RCurly)? .into_iter() .map(|Anno(arm, span)| Anno(Expr::Bind(Box::new(arm)), span)); @@ -978,6 +983,7 @@ fn parse_for<'t>(p: &mut Parser<'t>, _level: ()) -> PResult { .anno(cspan), Expr::Bind(Box::new(Bind( BindKind::Match, + vec![], Pat::Name("#iter".into()), vec![ Expr::Op( @@ -1000,12 +1006,14 @@ fn parse_for<'t>(p: &mut Parser<'t>, _level: ()) -> PResult { .anno(cspan), Expr::Bind(Box::new(Bind( BindKind::Match, + vec![], Pat::Name("None".into()), vec![Expr::Op(Op::Break, vec![fail]).anno(fspan)], ))) .anno(fspan), Expr::Bind(Box::new(Bind( BindKind::Match, + vec![], Pat::NamedTuple( "Some".into(), Box::new(Pat::Op(PatOp::Tuple, vec![pat])), diff --git a/src/span.rs b/src/span.rs index 206c09a..d37e8f1 100644 --- a/src/span.rs +++ b/src/span.rs @@ -14,7 +14,7 @@ impl std::fmt::Debug for Span { } } -#[allow(non_snake_case)] +#[expect(non_snake_case)] /// Stores the start and end byte position pub fn Span(head: u32, tail: u32) -> Span { Span { head, tail }