Parse all pattern types

This commit is contained in:
Alex Bethel 2022-08-07 14:04:35 -05:00
parent 00a9ac7cf7
commit 8331e4f341
2 changed files with 97 additions and 26 deletions

View file

@ -541,22 +541,95 @@ fn parse_record_type(
}
fn parse_pattern(m: &ParserMeta) -> impl Parser<char, Pattern, Error = Simple<char>> {
// TODO: add all the patterns
choice((parse_capture_pattern(m),))
recursive(|rec| {
choice((
parse_ignore_pattern(m),
parse_capture_pattern(m),
parse_literal_pattern(m),
parse_tuple_pattern(m, rec.clone()),
parse_record_pattern(m, rec.clone()),
))
.repeated()
.at_least(1)
.map(|pats| {
pats.into_iter()
.reduce(|l, r| Pattern::Destructure(Box::new(l), Box::new(r)))
.unwrap()
})
.then(
just(':')
.then(whitespace())
.ignore_then(parse_type(m))
.or_not(),
)
.map(|(pat, typ)| match typ {
Some(typ) => Pattern::TypeAnnotated {
pat: Box::new(pat),
typ: Box::new(typ),
},
None => pat,
})
})
}
fn parse_ignore_pattern(_m: &ParserMeta) -> impl Parser<char, Pattern, Error = Simple<char>> {
keyword("_").then_ignore(whitespace()).to(Pattern::Ignore)
}
fn parse_capture_pattern(_m: &ParserMeta) -> impl Parser<char, Pattern, Error = Simple<char>> {
ident()
.try_map(|iden: String, span| {
if iden.starts_with(|c: char| c.is_lowercase()) {
Ok(iden)
ident().then_ignore(whitespace()).map(Pattern::Capture)
}
fn parse_tuple_pattern(
_m: &ParserMeta,
rec: impl Parser<char, Pattern, Error = Simple<char>>,
) -> impl Parser<char, Pattern, Error = Simple<char>> {
rec.separated_by(just(',').then(whitespace()))
.allow_trailing()
.delimited_by(just('(').then(whitespace()), just(')').then(whitespace()))
.map(|pats| {
if pats.len() == 1 {
// `(Int)` is the same as `Int`
pats.into_iter().next().unwrap()
} else {
Err(Simple::custom(
span,
"capture must start with lowercase letter",
))
Pattern::Tuple(pats)
}
})
.then_ignore(whitespace())
.map(Pattern::Exact)
}
fn parse_record_pattern(
_m: &ParserMeta,
rec: impl Parser<char, Pattern, Error = Simple<char>>,
) -> impl Parser<char, Pattern, Error = Simple<char>> {
let item = ident().then_ignore(whitespace()).then(
just(':')
.ignore_then(whitespace())
.ignore_then(rec)
.or_not(),
);
let items = item
.separated_by(just(',').then(whitespace()))
.allow_trailing();
let ellipsis = just("...")
.ignore_then(whitespace())
.or_not()
.map(|ellipsis| ellipsis.is_some());
items
.then(ellipsis)
.delimited_by(just('{').then(whitespace()), just('}').then(whitespace()))
.map(|(members, inexhaustive)| Pattern::Record {
members,
inexhaustive,
})
}
fn parse_literal_pattern(m: &ParserMeta) -> impl Parser<char, Pattern, Error = Simple<char>> {
// TODO: factor out literal parsing so we don't have to do this ugly `unreachable` stuff.
parse_literal(m).map(|e| match e {
Expr::Literal(lit) => Pattern::Literal(lit),
_ => unreachable!(),
})
}

View file

@ -215,6 +215,15 @@ pub enum Pattern {
/// `(a, b)`
Tuple(Vec<Pattern>),
/// `{a: x, b: y}`
Record {
/// The named members of the record, in order of occurrence.
members: Vec<(String, Option<Pattern>)>,
/// Whether the record ends with "...", for ignoring unlisted elements.
inexhaustive: bool,
},
/// `a: String`
TypeAnnotated {
/// The pattern being annotated.
@ -224,11 +233,10 @@ pub enum Pattern {
typ: Box<Type>,
},
/// `Foo`
Exact(String),
/// `Foo { a: x, b: y, ... }`
Destructure(String, Record),
// Note that the left side here *must* be just one word, semantically; but we let it be a
// Pattern to make parsing easier.
Destructure(Box<Pattern>, Box<Pattern>),
/// `_`
Ignore,
@ -237,16 +245,6 @@ pub enum Pattern {
Literal(Literal),
}
/// Record syntax blocks, e.g., "{a: b, c: d, ...}".
#[derive(Clone, Debug)]
pub struct Record {
/// The named members of the record, in order of occurrence.
pub members: Vec<(String, Expr)>,
/// Whether the record ends with "..."; this allows ignoring blocks.
pub inexhaustive: bool,
}
/// Literal values included in source code.
#[derive(Clone, Debug)]
pub enum Literal {