[fine] Starting on pattern matching, make patterns explicit

This commit is contained in:
John Doty 2024-02-04 07:47:16 -08:00
parent f00b9e22e7
commit deed9d9a45
4 changed files with 178 additions and 50 deletions

View file

@ -138,6 +138,7 @@ pub enum TreeKind {
GroupingExpression,
Identifier,
IfStatement,
IsExpression,
LetStatement,
ListConstructor,
ListConstructorElement,
@ -156,8 +157,12 @@ pub enum TreeKind {
TypeParameterList,
UnaryExpression,
IsExpression,
Pattern,
VariableBinding,
MatchExpression,
MatchBody,
MatchArm,
}
pub struct Tree<'a> {
@ -902,6 +907,7 @@ const EXPRESSION_FIRST: &[TokenKind] = &[
TokenKind::Selff,
TokenKind::LeftBracket,
TokenKind::New,
TokenKind::Match,
];
fn expression(p: &mut CParser) {
@ -979,20 +985,26 @@ fn is_expression(p: &mut CParser, left: MarkClosed, right_power: u8) -> MarkClos
let m = p.start_before(left);
p.advance(); // Consume the operator
// This is hard to do with just, like, no lookahead.
pattern(p, right_power);
p.end(m, TreeKind::IsExpression)
}
fn pattern(p: &mut CParser, right_power: u8) {
let m = p.start();
// patterns are very simple.
if p.peek() == TokenKind::Identifier && p.peek_next() == TokenKind::Colon {
// This is a variable binding.
variable_binding(p);
} else {
type_expr(p);
}
// Additional predicates go into the right-hand-side.
type_expr(p);
if p.eat(TokenKind::And) {
expression_with_power(p, right_power);
}
p.end(m, TreeKind::IsExpression)
p.end(m, TreeKind::Pattern);
}
fn variable_binding(p: &mut CParser) {
@ -1000,7 +1012,6 @@ fn variable_binding(p: &mut CParser) {
p.expect_start(TokenKind::Identifier);
p.expect_start(TokenKind::Colon);
type_expr(p);
p.end(m, TreeKind::VariableBinding);
}
@ -1055,9 +1066,14 @@ fn prefix_expression(p: &mut CParser) -> Option<MarkClosed> {
TokenKind::LeftBracket => list_constructor(p),
TokenKind::New => object_constructor(p),
TokenKind::Match => match_expression(p),
_ => {
assert!(!p.at_any(EXPRESSION_FIRST));
assert!(
!p.at_any(EXPRESSION_FIRST),
"TokenKind::{:?} is in EXPRESSION_FIRST but not handled; is this a new kind of prefix?",
p.peek()
);
return None;
}
};
@ -1214,6 +1230,60 @@ fn field_value(p: &mut CParser) {
p.end(m, TreeKind::FieldValue);
}
fn match_expression(p: &mut CParser) -> MarkClosed {
let m = p.start();
p.expect_start(TokenKind::Match);
expression(p); // ?
if p.at(TokenKind::LeftBrace) {
match_body(p);
} else {
p.error("expected a '{' to start the alternatives after `match`");
}
p.end(m, TreeKind::MatchExpression)
}
fn match_body(p: &mut CParser) {
let m = p.start();
p.expect_start(TokenKind::LeftBrace);
while !p.at(TokenKind::RightBrace) && !p.eof() {
if p.at(TokenKind::Identifier) {
// TODO: type_expr_first ?
match_arm(p);
} else {
if p.at_any(STATEMENT_RECOVERY) {
break;
}
p.advance_with_error("expected a type expression to start a match arm");
}
}
p.expect(
TokenKind::RightBrace,
"expected a '}' to end the alternatives in a match",
);
p.end(m, TreeKind::MatchBody);
}
fn match_arm(p: &mut CParser) {
let m = p.start();
pattern(p, 0);
if p.eat(TokenKind::Arrow) {
expression(p);
} else {
p.error("expected an arrow after the pattern in a match arm");
}
if !p.at(TokenKind::RightBrace) {
p.expect(TokenKind::Comma, "expected a comma between match arms");
}
p.end(m, TreeKind::MatchArm);
}
#[cfg(test)]
mod tests {
use super::*;