conlang: PATTERN MATCHING AND DESTRUCTURED BINDINGS WOOOOO
- Integrate the Match and Pattern nodes into the AST
- TODO: `let x: T` is ambiguous with `let x: {}`. Currently the latter takes precedence in the parser.
- Implement pattern matching through unification in the interpreter.
- It's not fast, but it works!
- Refactor destructuring assignments to use the new pattern functionality
This commit is contained in:
@@ -119,6 +119,10 @@ pub enum Parsing {
|
||||
Break,
|
||||
Return,
|
||||
Continue,
|
||||
|
||||
Pattern,
|
||||
Match,
|
||||
MatchArm,
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
@@ -225,6 +229,10 @@ impl Display for Parsing {
|
||||
Parsing::Break => "a break expression",
|
||||
Parsing::Return => "a return expression",
|
||||
Parsing::Continue => "a continue expression",
|
||||
|
||||
Parsing::Pattern => "a pattern",
|
||||
Parsing::Match => "a match expression",
|
||||
Parsing::MatchArm => "a match arm",
|
||||
}
|
||||
.fmt(f)
|
||||
}
|
||||
|
||||
@@ -917,7 +917,7 @@ impl Parse<'_> for Let {
|
||||
p.consume_peeked();
|
||||
Ok(Let {
|
||||
mutable: Mutability::parse(p)?,
|
||||
name: Sym::parse(p)?,
|
||||
name: Pattern::parse(p)?,
|
||||
ty: if p.match_type(TokenKind::Colon, Parsing::Let).is_ok() {
|
||||
Some(Ty::parse(p)?.into())
|
||||
} else {
|
||||
@@ -1075,6 +1075,38 @@ impl Parse<'_> for Return {
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse<'_> for Pattern {
|
||||
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
|
||||
let value = prec::exprkind(p, prec::Precedence::Highest.level())?;
|
||||
Pattern::try_from(value)
|
||||
.map_err(|_| p.error(ExpectedParsing { want: Parsing::Pattern }, Parsing::Pattern))
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse<'_> for Match {
|
||||
/// [Match] = `match` [Expr] `{` [MatchArm],* `}`
|
||||
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
|
||||
p.match_type(TokenKind::Match, Parsing::Match)?;
|
||||
let scrutinee = Expr::parse(p)?.into();
|
||||
let arms = delim(
|
||||
sep(MatchArm::parse, TokenKind::Comma, CURLIES.1, Parsing::Match),
|
||||
CURLIES,
|
||||
Parsing::Match,
|
||||
)(p)?;
|
||||
Ok(Match { scrutinee, arms })
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse<'_> for MatchArm {
|
||||
/// [MatchArm] = [Pattern] `=>` [Expr]
|
||||
fn parse(p: &mut Parser<'_>) -> PResult<Self> {
|
||||
let pat = Pattern::parse(p)?;
|
||||
p.match_type(TokenKind::FatArrow, Parsing::MatchArm)?;
|
||||
let expr = Expr::parse(p)?;
|
||||
Ok(MatchArm(pat, expr))
|
||||
}
|
||||
}
|
||||
|
||||
/// ret_body = (*unconsumed* `;` | [Expr])
|
||||
fn ret_body(p: &mut Parser, while_parsing: Parsing) -> PResult<Option<Box<Expr>>> {
|
||||
Ok(match p.peek_kind(while_parsing)? {
|
||||
|
||||
@@ -21,6 +21,7 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
|
||||
TokenKind::LBrack => exprkind_arraylike(p)?,
|
||||
TokenKind::LParen => exprkind_tuplelike(p)?,
|
||||
TokenKind::Let => Let::parse(p)?.into(),
|
||||
TokenKind::Match => Match::parse(p)?.into(),
|
||||
TokenKind::While => ExprKind::While(While::parse(p)?),
|
||||
TokenKind::If => ExprKind::If(If::parse(p)?),
|
||||
TokenKind::For => ExprKind::For(For::parse(p)?),
|
||||
@@ -32,8 +33,7 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
|
||||
}
|
||||
|
||||
op => {
|
||||
let (kind, prec) =
|
||||
from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?;
|
||||
let (kind, prec) = from_prefix(op).ok_or_else(|| p.error(Unexpected(op), parsing))?;
|
||||
let ((), after) = prec.prefix().expect("should have a precedence");
|
||||
p.consume_peeked();
|
||||
Unary { kind, tail: exprkind(p, after)?.into() }.into()
|
||||
@@ -65,14 +65,10 @@ pub fn exprkind(p: &mut Parser, power: u8) -> PResult<ExprKind> {
|
||||
ExprKind::Index(Index { head: head.into(), indices })
|
||||
}
|
||||
TokenKind::LParen => {
|
||||
let exprs =
|
||||
sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
|
||||
let exprs = sep(Expr::parse, TokenKind::Comma, TokenKind::RParen, parsing)(p)?;
|
||||
p.match_type(TokenKind::RParen, parsing)?;
|
||||
Binary {
|
||||
kind: BinaryKind::Call,
|
||||
parts: (head, Tuple { exprs }.into()).into(),
|
||||
}
|
||||
.into()
|
||||
Binary { kind: BinaryKind::Call, parts: (head, Tuple { exprs }.into()).into() }
|
||||
.into()
|
||||
}
|
||||
TokenKind::Dot => {
|
||||
let kind = MemberKind::parse(p)?;
|
||||
@@ -260,6 +256,7 @@ pub enum Precedence {
|
||||
Cast,
|
||||
Member, // left-associative
|
||||
Call,
|
||||
Highest,
|
||||
}
|
||||
|
||||
impl Precedence {
|
||||
@@ -310,9 +307,7 @@ impl From<BinaryKind> for Precedence {
|
||||
Op::BitAnd | Op::BitOr | Op::BitXor => Precedence::Bitwise,
|
||||
Op::LogAnd | Op::LogOr | Op::LogXor => Precedence::Logic,
|
||||
Op::RangeExc | Op::RangeInc => Precedence::Range,
|
||||
Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => {
|
||||
Precedence::Compare
|
||||
}
|
||||
Op::Lt | Op::LtEq | Op::Equal | Op::NotEq | Op::GtEq | Op::Gt => Precedence::Compare,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user