Parse all pattern types
This commit is contained in:
parent
00a9ac7cf7
commit
8331e4f341
|
@ -541,22 +541,95 @@ fn parse_record_type(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pattern(m: &ParserMeta) -> impl Parser<char, Pattern, Error = Simple<char>> {
|
fn parse_pattern(m: &ParserMeta) -> impl Parser<char, Pattern, Error = Simple<char>> {
|
||||||
// TODO: add all the patterns
|
recursive(|rec| {
|
||||||
choice((parse_capture_pattern(m),))
|
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>> {
|
fn parse_capture_pattern(_m: &ParserMeta) -> impl Parser<char, Pattern, Error = Simple<char>> {
|
||||||
ident()
|
ident().then_ignore(whitespace()).map(Pattern::Capture)
|
||||||
.try_map(|iden: String, span| {
|
}
|
||||||
if iden.starts_with(|c: char| c.is_lowercase()) {
|
|
||||||
Ok(iden)
|
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 {
|
} else {
|
||||||
Err(Simple::custom(
|
Pattern::Tuple(pats)
|
||||||
span,
|
|
||||||
"capture must start with lowercase letter",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.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!(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,6 +215,15 @@ pub enum Pattern {
|
||||||
/// `(a, b)`
|
/// `(a, b)`
|
||||||
Tuple(Vec<Pattern>),
|
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`
|
/// `a: String`
|
||||||
TypeAnnotated {
|
TypeAnnotated {
|
||||||
/// The pattern being annotated.
|
/// The pattern being annotated.
|
||||||
|
@ -224,11 +233,10 @@ pub enum Pattern {
|
||||||
typ: Box<Type>,
|
typ: Box<Type>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// `Foo`
|
|
||||||
Exact(String),
|
|
||||||
|
|
||||||
/// `Foo { a: x, b: y, ... }`
|
/// `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,
|
Ignore,
|
||||||
|
@ -237,16 +245,6 @@ pub enum Pattern {
|
||||||
Literal(Literal),
|
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.
|
/// Literal values included in source code.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Literal {
|
pub enum Literal {
|
||||||
|
|
Loading…
Reference in a new issue