doughlang: Expressions in patterns

ast:
- Document operators
- Parameterize Pat with Annotation
- Consolidate Path and Lit into "Value" (Expr)
- Consolidate NamedRecord/Namedtuple into PatOp::TypePrefixed
- Allow Pat<Pat,*> patterns
- Additional helper functions on Expr and Pat

lexer:
- Support inner-doc comment syntax `//!`
  - Cleans up `///` or `//!` prefix

parser:
- Make Parse::Prec `Default`
- Allow expression elision after `..`/`..=`
- Fix Parser::consume not updating elide_do state
- Add token splitting, to make `&&Expr` and `&&Pat` easier
- error: Embed Pat precedence in ParseError
This commit is contained in:
2026-01-05 15:17:22 -05:00
parent d6104b6a0b
commit e4c008bd4b
16 changed files with 700 additions and 322 deletions

View File

@@ -65,78 +65,137 @@ pub enum Expr<A: Annotation = Span> {
/// - Traditional binary and unary operators (add, sub, neg, assign)
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Op {
// -- true operators
Do, // Expr ; Expr
As, // Expr as Expr
Macro, // macro { (Pat => Expr)* }
Block, // { Expr }
Array, // [ Expr,* ]
ArRep, // [ Expr ; Expr ]
Group, // ( Expr ,?)
Tuple, // Expr (, Expr)*
Meta, // #[ Expr ]
/// `Expr (; Expr)*`
Do,
/// `Expr as Expr`
As,
/// `macro { (Pat => Expr)* }`
Macro,
/// `{ Expr }`
Block,
/// `[ Expr,* ]`
Array,
/// `[ Expr ; Expr ]`
ArRep,
/// `( Expr )`
Group,
/// `Expr (, Expr)*`
Tuple,
/// `#[ Expr ]`
Meta,
Try, // Expr '?'
Index, // Expr [ Expr,* ]
Call, // Expr ( Expr,* )
/// `Expr '?'`
Try,
/// `Expr [ Expr,* ]`
Index,
/// `Expr ( Expr,* )`
Call,
Pub, // pub Expr
Const, // const Expr
Static, // static Expr
Loop, // loop Expr
Match, // match Expr { <Bind(Match, ..)>,* }
If, // if Expr Expr (else Expr)?
While, // while Expr Expr (else Expr)?
Break, // break Expr
Return, // return Expr
Continue, // continue
/// `pub Expr`
Pub,
/// `const Expr`
Const,
/// `static Expr`
Static,
/// `loop Expr`
Loop,
/// `match Expr { <Bind(Match, ..)>,* }`
Match,
/// `if Expr Expr (else Expr)?`
If,
/// `while Expr Expr (else Expr)?`
While,
/// `break Expr`
Break,
/// `return Expr`
Return,
/// `continue`
Continue,
Dot, // Expr . Expr
/// `Expr . Expr`
Dot,
RangeEx, // Expr? ..Expr
RangeIn, // Expr? ..=Expr
Neg, // -Expr
Not, // !Expr
Identity, // !!Expr
Refer, // &Expr
Deref, // *Expr
/// `Expr? ..Expr`
RangeEx,
/// `Expr? ..=Expr`
RangeIn,
/// `-Expr`
Neg,
/// `!Expr`
Not,
/// `!!Expr`
Identity,
/// `&Expr`
Refer,
/// `*Expr`
Deref,
Mul, // Expr * Expr
Div, // Expr / Expr
Rem, // Expr % Expr
/// `Expr * Expr`
Mul,
/// `Expr / Expr`
Div,
/// `Expr % Expr`
Rem,
Add, // Expr + Expr
Sub, // Expr - Expr
/// `Expr + Expr`
Add,
/// `Expr - Expr`
Sub,
Shl, // Expr << Expr
Shr, // Expr >> Expr
/// `Expr << Expr`
Shl,
/// `Expr >> Expr`
Shr,
And, // Expr & Expr
Xor, // Expr ^ Expr
Or, // Expr | Expr
/// `Expr & Expr`
And,
/// `Expr ^ Expr`
Xor,
/// `Expr | Expr`
Or,
Lt, // Expr < Expr
Leq, // Expr <= Expr
Eq, // Expr == Expr
Neq, // Expr != Expr
Geq, // Expr >= Expr
Gt, // Expr > Expr
/// `Expr < Expr`
Lt,
/// `Expr <= Expr`
Leq,
/// `Expr == Expr`
Eq,
/// `Expr != Expr`
Neq,
/// `Expr >= Expr`
Geq,
/// `Expr > Expr`
Gt,
LogAnd, // Expr && Expr
LogXor, // Expr ^^ Expr
LogOr, // Expr || Expr
/// `Expr && Expr`
LogAnd,
/// `Expr ^^ Expr`
LogXor,
/// `Expr || Expr`
LogOr,
Set, // Expr = Expr
MulSet, // Expr *= Expr
DivSet, // Expr /= Expr
RemSet, // Expr %= Expr
AddSet, // Expr += Expr
SubSet, // Expr -= Expr
ShlSet, // Expr <<= Expr
ShrSet, // Expr >>= Expr
AndSet, // Expr &= Expr
XorSet, // Expr ^= Expr
OrSet, // Expr |= Expr
/// `Expr = Expr`
Set,
/// `Expr *= Expr`
MulSet,
/// `Expr /= Expr`
DivSet,
/// `Expr %= Expr`
RemSet,
/// `Expr += Expr`
AddSet,
/// `Expr -= Expr`
SubSet,
/// `Expr <<= Expr`
ShlSet,
/// `Expr >>= Expr`
ShrSet,
/// `Expr &= Expr`
AndSet,
/// `Expr ^= Expr`
XorSet,
/// `Expr |= Expr`
OrSet,
}
/// A qualified identifier
@@ -181,11 +240,11 @@ pub enum Use {
/// ```ignore
/// let Pat (= Expr (else Expr)?)?
/// type Pat (= Expr)?
/// struct Pat
/// enum Pat
/// fn Pat Expr
/// mod Pat Expr
/// impl Pat Expr
/// struct Pat
/// enum Pat
/// for Pat in Expr Expr (else Expr)?
/// Pat => Expr // in match
/// ```
@@ -193,7 +252,7 @@ pub enum Use {
pub struct Bind<A: Annotation = Span>(
pub BindOp,
pub Vec<Path>,
pub Pat,
pub Pat<A>,
pub Vec<Anno<Expr<A>, A>>,
);
@@ -237,7 +296,7 @@ pub struct MakeArm<A: Annotation = Span>(pub String, pub Option<Anno<Expr<A>, A>
///
/// This covers both patterns in Match expressions, and type annotations.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Pat {
pub enum Pat<A: Annotation = Span> {
/// Matches anything without binding
Ignore,
/// Matches nothing, ever
@@ -246,16 +305,10 @@ pub enum Pat {
MetId(String),
/// Matches anything, and binds it to a name
Name(String),
/// Matches against a named const value
Path(Path),
/// Matches a Struct Expression `Ident { Pat }`
NamedRecord(Path, Box<Pat>),
/// Matches a Tuple Struct Expression `Ident ( Pat )`
NamedTuple(Path, Box<Pat>),
/// Matches a literal value by equality comparison
Lit(Literal),
/// Matches a value by equality comparison
Value(Box<Anno<Expr<A>, A>>),
/// Matches a compound pattern
Op(PatOp, Vec<Pat>),
Op(PatOp, Vec<Pat<A>>),
}
/// Operators on lists of patterns
@@ -285,6 +338,10 @@ pub enum PatOp {
ArRep,
/// Matches a type annotation or struct member
Typed,
/// Matches a prefix-type-annotated structure
TypePrefixed,
/// Matches a generic specialization annotation
Generic,
/// Changes the binding mode to "function-body"
Fn,
/// Matches one of a list of alternatives
@@ -321,6 +378,13 @@ impl<A: Annotation> Expr<A> {
Self::Op(Op::Do, exprs)
}
pub fn to_tuple(self, annotation: A) -> Self {
match self {
Self::Op(Op::Tuple, _) => self,
_ => Self::Op(Op::Tuple, vec![self.anno(annotation)]),
}
}
pub const fn is_place(&self) -> bool {
matches!(
self,
@@ -328,6 +392,10 @@ impl<A: Annotation> Expr<A> {
)
}
pub const fn is_value(&self) -> bool {
!self.is_place()
}
#[allow(clippy::type_complexity)]
pub const fn as_slice(&self) -> Option<(Op, &[Anno<Expr<A>, A>])> {
match self {
@@ -337,6 +405,15 @@ impl<A: Annotation> Expr<A> {
}
}
impl<A: Annotation> Pat<A> {
pub fn to_tuple(self) -> Self {
match self {
Self::Op(PatOp::Tuple, _) => self,
_ => Self::Op(PatOp::Tuple, vec![self]),
}
}
}
impl From<&str> for Path {
fn from(value: &str) -> Self {
Self { parts: vec![value.to_owned()] }