mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
checkpoint
This commit is contained in:
parent
e007430584
commit
f6fac092a4
4 changed files with 290 additions and 317 deletions
|
@ -13,7 +13,7 @@ use crate::parser::{
|
||||||
fail, map, newline_char, not, not_followed_by, optional, sep_by1, then, unexpected,
|
fail, map, newline_char, not, not_followed_by, optional, sep_by1, then, unexpected,
|
||||||
unexpected_eof, Either, ParseResult, Parser, State, SyntaxError,
|
unexpected_eof, Either, ParseResult, Parser, State, SyntaxError,
|
||||||
};
|
};
|
||||||
use crate::pattern::underscore_pattern;
|
use crate::pattern::{loc_closure_param, loc_pattern};
|
||||||
use crate::type_annotation;
|
use crate::type_annotation;
|
||||||
use bumpalo::collections::string::String;
|
use bumpalo::collections::string::String;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
|
@ -244,7 +244,10 @@ fn parse_expr<'a>(
|
||||||
|
|
||||||
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
||||||
/// Example: (foo) could be either an Expr::Var("foo") or Pattern::Identifier("foo")
|
/// Example: (foo) could be either an Expr::Var("foo") or Pattern::Identifier("foo")
|
||||||
fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>, SyntaxError<'a>> {
|
pub fn expr_to_pattern<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
expr: &Expr<'a>,
|
||||||
|
) -> Result<Pattern<'a>, SyntaxError<'a>> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Var { module_name, ident } => {
|
Expr::Var { module_name, ident } => {
|
||||||
if module_name.is_empty() {
|
if module_name.is_empty() {
|
||||||
|
@ -1009,306 +1012,6 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loc_closure_param<'a>(
|
|
||||||
min_indent: u16,
|
|
||||||
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
|
||||||
move |arena, state| parse_closure_param(arena, state, min_indent)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_closure_param<'a>(
|
|
||||||
arena: &'a Bump,
|
|
||||||
state: State<'a>,
|
|
||||||
min_indent: u16,
|
|
||||||
) -> ParseResult<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
|
||||||
one_of!(
|
|
||||||
// An ident is the most common param, e.g. \foo -> ...
|
|
||||||
loc_ident_pattern(min_indent, true),
|
|
||||||
// Underscore is also common, e.g. \_ -> ...
|
|
||||||
loc!(underscore_pattern()),
|
|
||||||
// You can destructure records in params, e.g. \{ x, y } -> ...
|
|
||||||
// loc!(record_destructure(min_indent)),
|
|
||||||
loc!(crate::pattern::record_pattern(min_indent)),
|
|
||||||
// If you wrap it in parens, you can match any arbitrary pattern at all.
|
|
||||||
// e.g. \User.UserId userId -> ...
|
|
||||||
between!(
|
|
||||||
ascii_char(b'('),
|
|
||||||
space0_around(loc_pattern(min_indent), min_indent),
|
|
||||||
ascii_char(b')')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.parse(arena, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
|
||||||
skip_first!(
|
|
||||||
// If this is a reserved keyword ("if", "then", "case, "when"), then
|
|
||||||
// it is not a pattern!
|
|
||||||
not(reserved_keyword()),
|
|
||||||
one_of!(
|
|
||||||
loc_parenthetical_pattern(min_indent),
|
|
||||||
loc!(underscore_pattern()),
|
|
||||||
loc_ident_pattern(min_indent, true),
|
|
||||||
// loc!(record_destructure(min_indent)),
|
|
||||||
loc!(crate::pattern::record_pattern(min_indent)),
|
|
||||||
loc!(string_pattern()),
|
|
||||||
loc!(number_pattern())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loc_tag_pattern_args<'a>(
|
|
||||||
min_indent: u16,
|
|
||||||
) -> impl Parser<'a, Vec<'a, Located<Pattern<'a>>>, SyntaxError<'a>> {
|
|
||||||
zero_or_more!(space1_before(loc_tag_pattern_arg(min_indent), min_indent))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loc_tag_pattern_arg<'a>(
|
|
||||||
min_indent: u16,
|
|
||||||
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
|
||||||
skip_first!(
|
|
||||||
// If this is a reserved keyword ("if", "then", "case, "when"), then
|
|
||||||
// it is not a function argument!
|
|
||||||
not(reserved_keyword()),
|
|
||||||
// Don't parse operators, because they have a higher precedence than function application.
|
|
||||||
// If we encounter one, we're done parsing function args!
|
|
||||||
move |arena, state| loc_parse_tag_pattern_arg(min_indent, arena, state)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loc_parse_tag_pattern_arg<'a>(
|
|
||||||
min_indent: u16,
|
|
||||||
arena: &'a Bump,
|
|
||||||
state: State<'a>,
|
|
||||||
) -> ParseResult<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
|
||||||
one_of!(
|
|
||||||
loc_parenthetical_pattern(min_indent),
|
|
||||||
loc!(underscore_pattern()),
|
|
||||||
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
|
||||||
loc_ident_pattern(min_indent, false),
|
|
||||||
// loc!(record_destructure(min_indent)),
|
|
||||||
loc!(crate::pattern::record_pattern(min_indent)),
|
|
||||||
loc!(string_pattern()),
|
|
||||||
loc!(number_pattern())
|
|
||||||
)
|
|
||||||
.parse(arena, state)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loc_parenthetical_pattern<'a>(
|
|
||||||
min_indent: u16,
|
|
||||||
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
|
||||||
between!(
|
|
||||||
ascii_char(b'('),
|
|
||||||
space0_around(
|
|
||||||
move |arena, state| loc_pattern(min_indent).parse(arena, state),
|
|
||||||
min_indent,
|
|
||||||
),
|
|
||||||
ascii_char(b')')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn number_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> {
|
|
||||||
map_with_arena!(number_literal(), |arena, expr| {
|
|
||||||
expr_to_pattern(arena, &expr).unwrap()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn string_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> {
|
|
||||||
map!(crate::string_literal::parse(), Pattern::StrLiteral)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> {
|
|
||||||
then(
|
|
||||||
collection!(
|
|
||||||
ascii_char(b'{'),
|
|
||||||
move |arena: &'a bumpalo::Bump,
|
|
||||||
state: crate::parser::State<'a>|
|
|
||||||
-> crate::parser::ParseResult<
|
|
||||||
'a,
|
|
||||||
Located<crate::ast::Pattern<'a>>,
|
|
||||||
SyntaxError,
|
|
||||||
> {
|
|
||||||
use crate::blankspace::{space0, space0_before};
|
|
||||||
use crate::ident::lowercase_ident;
|
|
||||||
use crate::parser::Either::*;
|
|
||||||
use roc_region::all::Region;
|
|
||||||
|
|
||||||
// You must have a field name, e.g. "email"
|
|
||||||
let (p1, loc_label, state) = loc!(lowercase_ident()).parse(arena, state)?;
|
|
||||||
|
|
||||||
let (p2, spaces, state) = space0(min_indent).parse(arena, state)?;
|
|
||||||
|
|
||||||
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
|
|
||||||
// (This is true in both literals and types.)
|
|
||||||
let (p3, opt_loc_val, state) = crate::parser::optional(either!(
|
|
||||||
skip_first!(
|
|
||||||
ascii_char(b':'),
|
|
||||||
space0_before(loc_pattern(min_indent), min_indent)
|
|
||||||
),
|
|
||||||
skip_first!(
|
|
||||||
ascii_char(b'?'),
|
|
||||||
space0_before(loc!(expr(min_indent)), min_indent)
|
|
||||||
)
|
|
||||||
))
|
|
||||||
.parse(arena, state)?;
|
|
||||||
|
|
||||||
let answer = match opt_loc_val {
|
|
||||||
Some(either) => match either {
|
|
||||||
First(loc_val) => Located {
|
|
||||||
region: Region::span_across(&loc_label.region, &loc_val.region),
|
|
||||||
value: Pattern::RequiredField(loc_label.value, arena.alloc(loc_val)),
|
|
||||||
},
|
|
||||||
Second(loc_val) => Located {
|
|
||||||
region: Region::span_across(&loc_label.region, &loc_val.region),
|
|
||||||
value: Pattern::OptionalField(loc_label.value, arena.alloc(loc_val)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// If no value was provided, record it as a Var.
|
|
||||||
// Canonicalize will know what to do with a Var later.
|
|
||||||
None => {
|
|
||||||
if !spaces.is_empty() {
|
|
||||||
Located {
|
|
||||||
region: loc_label.region,
|
|
||||||
value: Pattern::SpaceAfter(
|
|
||||||
arena.alloc(Pattern::Identifier(loc_label.value)),
|
|
||||||
spaces,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Located {
|
|
||||||
region: loc_label.region,
|
|
||||||
value: Pattern::Identifier(loc_label.value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let progress = p1.or(p2).or(p3);
|
|
||||||
debug_assert_eq!(progress, MadeProgress);
|
|
||||||
Ok((MadeProgress, answer, state))
|
|
||||||
},
|
|
||||||
ascii_char(b','),
|
|
||||||
ascii_char(b'}'),
|
|
||||||
min_indent
|
|
||||||
),
|
|
||||||
move |_arena, state, progress, loc_patterns| {
|
|
||||||
Ok((
|
|
||||||
progress,
|
|
||||||
Pattern::RecordDestructure(loc_patterns.into_bump_slice()),
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn loc_ident_pattern<'a>(
|
|
||||||
min_indent: u16,
|
|
||||||
can_have_arguments: bool,
|
|
||||||
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
|
||||||
move |arena: &'a Bump, state: State<'a>| {
|
|
||||||
let (_, loc_ident, state) = loc!(ident()).parse(arena, state)?;
|
|
||||||
|
|
||||||
match loc_ident.value {
|
|
||||||
Ident::GlobalTag(tag) => {
|
|
||||||
let loc_tag = Located {
|
|
||||||
region: loc_ident.region,
|
|
||||||
value: Pattern::GlobalTag(tag),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
|
||||||
if can_have_arguments {
|
|
||||||
let (_, loc_args, state) =
|
|
||||||
loc_tag_pattern_args(min_indent).parse(arena, state)?;
|
|
||||||
|
|
||||||
if loc_args.is_empty() {
|
|
||||||
Ok((MadeProgress, loc_tag, state))
|
|
||||||
} else {
|
|
||||||
let region = Region::across_all(
|
|
||||||
std::iter::once(&loc_ident.region)
|
|
||||||
.chain(loc_args.iter().map(|loc_arg| &loc_arg.region)),
|
|
||||||
);
|
|
||||||
let value =
|
|
||||||
Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
|
||||||
|
|
||||||
Ok((MadeProgress, Located { region, value }, state))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok((MadeProgress, loc_tag, state))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ident::PrivateTag(tag) => {
|
|
||||||
let loc_tag = Located {
|
|
||||||
region: loc_ident.region,
|
|
||||||
value: Pattern::PrivateTag(tag),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
|
||||||
if can_have_arguments {
|
|
||||||
let (_, loc_args, state) =
|
|
||||||
loc_tag_pattern_args(min_indent).parse(arena, state)?;
|
|
||||||
|
|
||||||
if loc_args.is_empty() {
|
|
||||||
Ok((MadeProgress, loc_tag, state))
|
|
||||||
} else {
|
|
||||||
let region = Region::across_all(
|
|
||||||
std::iter::once(&loc_ident.region)
|
|
||||||
.chain(loc_args.iter().map(|loc_arg| &loc_arg.region)),
|
|
||||||
);
|
|
||||||
let value =
|
|
||||||
Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
|
||||||
|
|
||||||
Ok((MadeProgress, Located { region, value }, state))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok((MadeProgress, loc_tag, state))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ident::Access { module_name, parts } => {
|
|
||||||
// Plain identifiers (e.g. `foo`) are allowed in patterns, but
|
|
||||||
// more complex ones (e.g. `Foo.bar` or `foo.bar.baz`) are not.
|
|
||||||
if module_name.is_empty() && parts.len() == 1 {
|
|
||||||
Ok((
|
|
||||||
MadeProgress,
|
|
||||||
Located {
|
|
||||||
region: loc_ident.region,
|
|
||||||
value: Pattern::Identifier(parts[0]),
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
let malformed_str = if module_name.is_empty() {
|
|
||||||
parts.join(".")
|
|
||||||
} else {
|
|
||||||
format!("{}.{}", module_name, parts.join("."))
|
|
||||||
};
|
|
||||||
Ok((
|
|
||||||
MadeProgress,
|
|
||||||
Located {
|
|
||||||
region: loc_ident.region,
|
|
||||||
value: Pattern::Malformed(
|
|
||||||
String::from_str_in(&malformed_str, &arena).into_bump_str(),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ident::AccessorFunction(string) => Ok((
|
|
||||||
MadeProgress,
|
|
||||||
Located {
|
|
||||||
region: loc_ident.region,
|
|
||||||
value: Pattern::Malformed(string),
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
)),
|
|
||||||
Ident::Malformed(malformed) => {
|
|
||||||
debug_assert!(!malformed.is_empty());
|
|
||||||
|
|
||||||
Err((MadeProgress, SyntaxError::InvalidPattern, state))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod when {
|
mod when {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::WhenBranch;
|
use crate::ast::WhenBranch;
|
||||||
|
|
|
@ -377,7 +377,8 @@ pub enum EPattern<'a> {
|
||||||
Start(Row, Col),
|
Start(Row, Col),
|
||||||
End(Row, Col),
|
End(Row, Col),
|
||||||
Space(BadInputError, Row, Col),
|
Space(BadInputError, Row, Col),
|
||||||
FunctionArgument(Row, Col),
|
|
||||||
|
PInParens(PInParens<'a>, Row, Col),
|
||||||
|
|
||||||
IndentStart(Row, Col),
|
IndentStart(Row, Col),
|
||||||
IndentEnd(Row, Col),
|
IndentEnd(Row, Col),
|
||||||
|
@ -392,7 +393,7 @@ pub enum PRecord<'a> {
|
||||||
Field(Row, Col),
|
Field(Row, Col),
|
||||||
Colon(Row, Col),
|
Colon(Row, Col),
|
||||||
Optional(Row, Col),
|
Optional(Row, Col),
|
||||||
Type(&'a EPattern<'a>, Row, Col),
|
Pattern(&'a EPattern<'a>, Row, Col),
|
||||||
// TODO remove
|
// TODO remove
|
||||||
Syntax(&'a SyntaxError<'a>, Row, Col),
|
Syntax(&'a SyntaxError<'a>, Row, Col),
|
||||||
|
|
||||||
|
@ -404,6 +405,21 @@ pub enum PRecord<'a> {
|
||||||
IndentEnd(Row, Col),
|
IndentEnd(Row, Col),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum PInParens<'a> {
|
||||||
|
End(Row, Col),
|
||||||
|
Open(Row, Col),
|
||||||
|
///
|
||||||
|
// TODO remove
|
||||||
|
Syntax(&'a SyntaxError<'a>, Row, Col),
|
||||||
|
|
||||||
|
///
|
||||||
|
Space(BadInputError, Row, Col),
|
||||||
|
///
|
||||||
|
IndentOpen(Row, Col),
|
||||||
|
IndentEnd(Row, Col),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Type<'a> {
|
pub enum Type<'a> {
|
||||||
TRecord(TRecord<'a>, Row, Col),
|
TRecord(TRecord<'a>, Row, Col),
|
||||||
|
|
|
@ -5,21 +5,17 @@ use crate::blankspace::{
|
||||||
line_comment, space0, space0_after, space0_around, space0_around_e, space0_before,
|
line_comment, space0, space0_after, space0_around, space0_around_e, space0_before,
|
||||||
space0_before_e, space0_e, space1, space1_around, space1_before, spaces_exactly,
|
space0_before_e, space0_e, space1, space1_around, space1_before, spaces_exactly,
|
||||||
};
|
};
|
||||||
use crate::expr::loc_pattern;
|
|
||||||
use crate::ident::{global_tag_or_ident, ident, lowercase_ident, Ident};
|
use crate::ident::{global_tag_or_ident, ident, lowercase_ident, Ident};
|
||||||
use crate::keyword;
|
|
||||||
use crate::number_literal::number_literal;
|
use crate::number_literal::number_literal;
|
||||||
use crate::parser::Progress::{self, *};
|
use crate::parser::Progress::{self, *};
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
self, allocated, fail, map, newline_char, not, not_followed_by, optional, peek_utf8_char_e,
|
self, map, newline_char, not, not_followed_by, optional, peek_utf8_char_e, sep_by1, specialize,
|
||||||
sep_by1, specialize, specialize_ref, then, unexpected, unexpected_eof, word1, BadInputError,
|
specialize_ref, unexpected, word1, BadInputError, EPattern, PInParens, PRecord, ParseResult,
|
||||||
EPattern, Either, PRecord, ParseResult, Parser, State, SyntaxError,
|
Parser, State, SyntaxError,
|
||||||
};
|
};
|
||||||
use crate::type_annotation;
|
|
||||||
use bumpalo::collections::string::String;
|
use bumpalo::collections::string::String;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_module::operator::{BinOp, CalledVia, UnaryOp};
|
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
|
||||||
/// Different patterns are supported in different circumstances.
|
/// Different patterns are supported in different circumstances.
|
||||||
|
@ -34,6 +30,262 @@ pub enum PatternType {
|
||||||
WhenBranch,
|
WhenBranch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn loc_closure_param<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
||||||
|
move |arena, state| parse_closure_param(arena, state, min_indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::parser::ascii_char;
|
||||||
|
|
||||||
|
fn parse_closure_param<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
state: State<'a>,
|
||||||
|
min_indent: u16,
|
||||||
|
) -> ParseResult<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
||||||
|
one_of!(
|
||||||
|
// An ident is the most common param, e.g. \foo -> ...
|
||||||
|
loc_ident_pattern(min_indent, true),
|
||||||
|
// Underscore is also common, e.g. \_ -> ...
|
||||||
|
loc!(underscore_pattern()),
|
||||||
|
// You can destructure records in params, e.g. \{ x, y } -> ...
|
||||||
|
loc!(crate::pattern::record_pattern(min_indent)),
|
||||||
|
// If you wrap it in parens, you can match any arbitrary pattern at all.
|
||||||
|
// e.g. \User.UserId userId -> ...
|
||||||
|
between!(
|
||||||
|
ascii_char(b'('),
|
||||||
|
space0_around(loc_pattern(min_indent), min_indent),
|
||||||
|
ascii_char(b')')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.parse(arena, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
||||||
|
one_of!(
|
||||||
|
loc_pattern_in_parens(min_indent),
|
||||||
|
loc!(underscore_pattern()),
|
||||||
|
loc_ident_pattern(min_indent, true),
|
||||||
|
loc!(crate::pattern::record_pattern(min_indent)),
|
||||||
|
loc!(string_pattern()),
|
||||||
|
loc!(number_pattern())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loc_tag_pattern_args<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
) -> impl Parser<'a, Vec<'a, Located<Pattern<'a>>>, SyntaxError<'a>> {
|
||||||
|
zero_or_more!(space1_before(loc_tag_pattern_arg(min_indent), min_indent))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loc_tag_pattern_arg<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
||||||
|
// Don't parse operators, because they have a higher precedence than function application.
|
||||||
|
// If we encounter one, we're done parsing function args!
|
||||||
|
specialize(
|
||||||
|
|e, _, _| SyntaxError::Pattern(e),
|
||||||
|
move |arena, state| loc_parse_tag_pattern_arg(min_indent, arena, state),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loc_parse_tag_pattern_arg<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
arena: &'a Bump,
|
||||||
|
state: State<'a>,
|
||||||
|
) -> ParseResult<'a, Located<Pattern<'a>>, EPattern<'a>> {
|
||||||
|
one_of!(
|
||||||
|
specialize(EPattern::PInParens, loc_pattern_in_parens_help(min_indent)),
|
||||||
|
loc!(underscore_pattern_help()),
|
||||||
|
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||||
|
loc_ident_pattern_help(min_indent, false),
|
||||||
|
loc!(specialize(
|
||||||
|
EPattern::Record,
|
||||||
|
crate::pattern::record_pattern_help(min_indent)
|
||||||
|
)),
|
||||||
|
loc!(string_pattern_help()),
|
||||||
|
loc!(number_pattern_help())
|
||||||
|
)
|
||||||
|
.parse(arena, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loc_pattern_in_parens<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
||||||
|
specialize(
|
||||||
|
|e, r, c| SyntaxError::Pattern(EPattern::PInParens(e, r, c)),
|
||||||
|
loc_pattern_in_parens_help(min_indent),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loc_pattern_in_parens_help<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
) -> impl Parser<'a, Located<Pattern<'a>>, PInParens<'a>> {
|
||||||
|
// between!(
|
||||||
|
// ascii_char(b'('),
|
||||||
|
// space0_around(
|
||||||
|
// move |arena, state| loc_pattern(min_indent).parse(arena, state),
|
||||||
|
// min_indent,
|
||||||
|
// ),
|
||||||
|
// ascii_char(b')')
|
||||||
|
// )
|
||||||
|
between!(
|
||||||
|
word1(b'(', PInParens::Open),
|
||||||
|
space0_around_e(
|
||||||
|
move |arena, state| specialize_ref(PInParens::Syntax, loc_pattern(min_indent))
|
||||||
|
.parse(arena, state),
|
||||||
|
min_indent,
|
||||||
|
PInParens::Space,
|
||||||
|
PInParens::IndentEnd,
|
||||||
|
),
|
||||||
|
word1(b')', PInParens::End)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn number_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> {
|
||||||
|
specialize(|e, _, _| SyntaxError::Pattern(e), number_pattern_help())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn number_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
|
||||||
|
specialize(
|
||||||
|
|_, r, c| EPattern::Start(r, c),
|
||||||
|
map_with_arena!(number_literal(), |arena, expr| {
|
||||||
|
crate::expr::expr_to_pattern(arena, &expr).unwrap()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn string_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> {
|
||||||
|
specialize(|e, _, _| SyntaxError::Pattern(e), string_pattern_help())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn string_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
|
||||||
|
specialize(
|
||||||
|
|_, r, c| EPattern::Start(r, c),
|
||||||
|
map!(crate::string_literal::parse(), Pattern::StrLiteral),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loc_ident_pattern_help<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
can_have_arguments: bool,
|
||||||
|
) -> impl Parser<'a, Located<Pattern<'a>>, EPattern<'a>> {
|
||||||
|
// TODO bad
|
||||||
|
specialize_ref(
|
||||||
|
|e, r, c| EPattern::PInParens(PInParens::Syntax(e, r, c), r, c),
|
||||||
|
loc_ident_pattern(min_indent, can_have_arguments),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loc_ident_pattern<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
can_have_arguments: bool,
|
||||||
|
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError<'a>> {
|
||||||
|
move |arena: &'a Bump, state: State<'a>| {
|
||||||
|
let (_, loc_ident, state) = loc!(ident()).parse(arena, state)?;
|
||||||
|
|
||||||
|
match loc_ident.value {
|
||||||
|
Ident::GlobalTag(tag) => {
|
||||||
|
let loc_tag = Located {
|
||||||
|
region: loc_ident.region,
|
||||||
|
value: Pattern::GlobalTag(tag),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||||
|
if can_have_arguments {
|
||||||
|
let (_, loc_args, state) =
|
||||||
|
loc_tag_pattern_args(min_indent).parse(arena, state)?;
|
||||||
|
|
||||||
|
if loc_args.is_empty() {
|
||||||
|
Ok((MadeProgress, loc_tag, state))
|
||||||
|
} else {
|
||||||
|
let region = Region::across_all(
|
||||||
|
std::iter::once(&loc_ident.region)
|
||||||
|
.chain(loc_args.iter().map(|loc_arg| &loc_arg.region)),
|
||||||
|
);
|
||||||
|
let value =
|
||||||
|
Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
||||||
|
|
||||||
|
Ok((MadeProgress, Located { region, value }, state))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok((MadeProgress, loc_tag, state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ident::PrivateTag(tag) => {
|
||||||
|
let loc_tag = Located {
|
||||||
|
region: loc_ident.region,
|
||||||
|
value: Pattern::PrivateTag(tag),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||||
|
if can_have_arguments {
|
||||||
|
let (_, loc_args, state) =
|
||||||
|
loc_tag_pattern_args(min_indent).parse(arena, state)?;
|
||||||
|
|
||||||
|
if loc_args.is_empty() {
|
||||||
|
Ok((MadeProgress, loc_tag, state))
|
||||||
|
} else {
|
||||||
|
let region = Region::across_all(
|
||||||
|
std::iter::once(&loc_ident.region)
|
||||||
|
.chain(loc_args.iter().map(|loc_arg| &loc_arg.region)),
|
||||||
|
);
|
||||||
|
let value =
|
||||||
|
Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
||||||
|
|
||||||
|
Ok((MadeProgress, Located { region, value }, state))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok((MadeProgress, loc_tag, state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ident::Access { module_name, parts } => {
|
||||||
|
// Plain identifiers (e.g. `foo`) are allowed in patterns, but
|
||||||
|
// more complex ones (e.g. `Foo.bar` or `foo.bar.baz`) are not.
|
||||||
|
if module_name.is_empty() && parts.len() == 1 {
|
||||||
|
Ok((
|
||||||
|
MadeProgress,
|
||||||
|
Located {
|
||||||
|
region: loc_ident.region,
|
||||||
|
value: Pattern::Identifier(parts[0]),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
let malformed_str = if module_name.is_empty() {
|
||||||
|
parts.join(".")
|
||||||
|
} else {
|
||||||
|
format!("{}.{}", module_name, parts.join("."))
|
||||||
|
};
|
||||||
|
Ok((
|
||||||
|
MadeProgress,
|
||||||
|
Located {
|
||||||
|
region: loc_ident.region,
|
||||||
|
value: Pattern::Malformed(
|
||||||
|
String::from_str_in(&malformed_str, &arena).into_bump_str(),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ident::AccessorFunction(string) => Ok((
|
||||||
|
MadeProgress,
|
||||||
|
Located {
|
||||||
|
region: loc_ident.region,
|
||||||
|
value: Pattern::Malformed(string),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
)),
|
||||||
|
Ident::Malformed(malformed) => {
|
||||||
|
debug_assert!(!malformed.is_empty());
|
||||||
|
|
||||||
|
Err((MadeProgress, SyntaxError::InvalidPattern, state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> {
|
pub fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> {
|
||||||
specialize(|e, _, _| SyntaxError::Pattern(e), underscore_pattern_help())
|
specialize(|e, _, _| SyntaxError::Pattern(e), underscore_pattern_help())
|
||||||
}
|
}
|
||||||
|
@ -166,13 +418,15 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRe
|
||||||
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon)
|
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon)
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
let Located { value, region } = loc_val;
|
// let Located { value, region } = loc_val;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
MadeProgress,
|
MadeProgress,
|
||||||
Pattern::RequiredField(
|
Pattern::RequiredField(
|
||||||
loc_label.value,
|
loc_label.value,
|
||||||
arena.alloc(arena.alloc(value).with_spaces_before(spaces, region)),
|
// TODO spaces are dropped here
|
||||||
|
// arena.alloc(arena.alloc(value).with_spaces_before(spaces, region)),
|
||||||
|
arena.alloc(loc_val),
|
||||||
),
|
),
|
||||||
state,
|
state,
|
||||||
))
|
))
|
||||||
|
@ -185,13 +439,15 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRe
|
||||||
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon)
|
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon)
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
let Located { value, region } = loc_val;
|
// let Located { value, region } = loc_val;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
MadeProgress,
|
MadeProgress,
|
||||||
Pattern::OptionalField(
|
Pattern::OptionalField(
|
||||||
loc_label.value,
|
loc_label.value,
|
||||||
arena.alloc(arena.alloc(value).with_spaces_before(spaces, region)),
|
// TODO spaces are dropped
|
||||||
|
// arena.alloc(arena.alloc(value).with_spaces_before(spaces, region)),
|
||||||
|
arena.alloc(loc_val),
|
||||||
),
|
),
|
||||||
state,
|
state,
|
||||||
))
|
))
|
||||||
|
@ -201,7 +457,6 @@ fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRe
|
||||||
None => {
|
None => {
|
||||||
let value = if !spaces.is_empty() {
|
let value = if !spaces.is_empty() {
|
||||||
let Located { value, .. } = loc_label;
|
let Located { value, .. } = loc_label;
|
||||||
|
|
||||||
Pattern::SpaceAfter(arena.alloc(Pattern::Identifier(value)), spaces)
|
Pattern::SpaceAfter(arena.alloc(Pattern::Identifier(value)), spaces)
|
||||||
} else {
|
} else {
|
||||||
let Located { value, .. } = loc_label;
|
let Located { value, .. } = loc_label;
|
||||||
|
|
|
@ -144,7 +144,6 @@ fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotatio
|
||||||
fn loc_type_in_parens<'a>(
|
fn loc_type_in_parens<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, TInParens<'a>> {
|
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, TInParens<'a>> {
|
||||||
// TODO what if the middle parser returns EOF?
|
|
||||||
between!(
|
between!(
|
||||||
word1(b'(', TInParens::Open),
|
word1(b'(', TInParens::Open),
|
||||||
space0_around_e(
|
space0_around_e(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue