mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
fix tag pattern argument bug
This commit is contained in:
parent
58a7ea5572
commit
78063f87d7
2 changed files with 144 additions and 27 deletions
|
@ -1002,7 +1002,7 @@ fn parse_closure_param<'a>(
|
||||||
) -> ParseResult<'a, Located<Pattern<'a>>> {
|
) -> ParseResult<'a, Located<Pattern<'a>>> {
|
||||||
one_of!(
|
one_of!(
|
||||||
// An ident is the most common param, e.g. \foo -> ...
|
// An ident is the most common param, e.g. \foo -> ...
|
||||||
loc_ident_pattern(min_indent),
|
loc_ident_pattern(min_indent, true),
|
||||||
// Underscore is also common, e.g. \_ -> ...
|
// Underscore is also common, e.g. \_ -> ...
|
||||||
loc!(underscore_pattern()),
|
loc!(underscore_pattern()),
|
||||||
// You can destructure records in params, e.g. \{ x, y } -> ...
|
// You can destructure records in params, e.g. \{ x, y } -> ...
|
||||||
|
@ -1026,7 +1026,7 @@ fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||||
one_of!(
|
one_of!(
|
||||||
loc_parenthetical_pattern(min_indent),
|
loc_parenthetical_pattern(min_indent),
|
||||||
loc!(underscore_pattern()),
|
loc!(underscore_pattern()),
|
||||||
loc_ident_pattern(min_indent),
|
loc_ident_pattern(min_indent, true),
|
||||||
loc!(record_destructure(min_indent)),
|
loc!(record_destructure(min_indent)),
|
||||||
loc!(string_pattern()),
|
loc!(string_pattern()),
|
||||||
loc!(number_pattern())
|
loc!(number_pattern())
|
||||||
|
@ -1034,6 +1034,38 @@ fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn loc_tag_pattern_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Pattern<'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>>> {
|
||||||
|
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>>> {
|
||||||
|
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!(string_pattern()),
|
||||||
|
loc!(number_pattern())
|
||||||
|
)
|
||||||
|
.parse(arena, state)
|
||||||
|
}
|
||||||
|
|
||||||
fn loc_parenthetical_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
fn loc_parenthetical_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||||
between!(
|
between!(
|
||||||
ascii_char(b'('),
|
ascii_char(b'('),
|
||||||
|
@ -1133,51 +1165,64 @@ fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn loc_ident_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
fn loc_ident_pattern<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
can_have_arguments: bool,
|
||||||
|
) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||||
move |arena: &'a Bump, state: State<'a>| {
|
move |arena: &'a Bump, state: State<'a>| {
|
||||||
let (loc_ident, state) = loc!(ident()).parse(arena, state)?;
|
let (loc_ident, state) = loc!(ident()).parse(arena, state)?;
|
||||||
|
|
||||||
match loc_ident.value {
|
match loc_ident.value {
|
||||||
Ident::GlobalTag(tag) => {
|
Ident::GlobalTag(tag) => {
|
||||||
let (loc_args, state) =
|
|
||||||
zero_or_more!(space1_before(loc_pattern(min_indent), min_indent))
|
|
||||||
.parse(arena, state)?;
|
|
||||||
let loc_tag = Located {
|
let loc_tag = Located {
|
||||||
region: loc_ident.region,
|
region: loc_ident.region,
|
||||||
value: Pattern::GlobalTag(tag),
|
value: Pattern::GlobalTag(tag),
|
||||||
};
|
};
|
||||||
|
|
||||||
if loc_args.is_empty() {
|
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||||
Ok((loc_tag, state))
|
if can_have_arguments {
|
||||||
} else {
|
let (loc_args, state) = loc_tag_pattern_args(min_indent).parse(arena, state)?;
|
||||||
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((Located { region, value }, state))
|
if loc_args.is_empty() {
|
||||||
|
Ok((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((Located { region, value }, state))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok((loc_tag, state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ident::PrivateTag(tag) => {
|
Ident::PrivateTag(tag) => {
|
||||||
let (loc_args, state) =
|
|
||||||
zero_or_more!(space1_before(loc_pattern(min_indent), min_indent))
|
|
||||||
.parse(arena, state)?;
|
|
||||||
let loc_tag = Located {
|
let loc_tag = Located {
|
||||||
region: loc_ident.region,
|
region: loc_ident.region,
|
||||||
value: Pattern::PrivateTag(tag),
|
value: Pattern::PrivateTag(tag),
|
||||||
};
|
};
|
||||||
|
|
||||||
if loc_args.is_empty() {
|
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||||
Ok((loc_tag, state))
|
if can_have_arguments {
|
||||||
} else {
|
let (loc_args, state) = loc_tag_pattern_args(min_indent).parse(arena, state)?;
|
||||||
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((Located { region, value }, state))
|
if loc_args.is_empty() {
|
||||||
|
Ok((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((Located { region, value }, state))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok((loc_tag, state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ident::Access { module_name, parts } => {
|
Ident::Access { module_name, parts } => {
|
||||||
|
|
|
@ -2980,4 +2980,76 @@ mod solve_expr {
|
||||||
"List x",
|
"List x",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn double_tag_application() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app Test provides [ main ] imports []
|
||||||
|
|
||||||
|
|
||||||
|
main =
|
||||||
|
if 1 == 1 then
|
||||||
|
Foo (Bar) 1
|
||||||
|
else
|
||||||
|
Foo Bar 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"[ Foo [ Bar ]* (Num *) ]*",
|
||||||
|
);
|
||||||
|
|
||||||
|
infer_eq_without_problem("Foo Bar 1", "[ Foo [ Bar ]* (Num *) ]*");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn double_tag_application_pattern_global() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app Test provides [ main ] imports []
|
||||||
|
|
||||||
|
Bar : [ Bar ]
|
||||||
|
Foo : [ Foo Bar Int, Empty ]
|
||||||
|
|
||||||
|
foo : Foo
|
||||||
|
foo = Foo Bar 1
|
||||||
|
|
||||||
|
main =
|
||||||
|
when foo is
|
||||||
|
Foo Bar 1 ->
|
||||||
|
Foo Bar 2
|
||||||
|
|
||||||
|
x ->
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"[ Empty, Foo [ Bar ] Int ]",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn double_tag_application_pattern_private() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app Test provides [ main ] imports []
|
||||||
|
|
||||||
|
Foo : [ @Foo [ @Bar ] Int, @Empty ]
|
||||||
|
|
||||||
|
foo : Foo
|
||||||
|
foo = @Foo @Bar 1
|
||||||
|
|
||||||
|
main =
|
||||||
|
when foo is
|
||||||
|
@Foo @Bar 1 ->
|
||||||
|
@Foo @Bar 2
|
||||||
|
|
||||||
|
x ->
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"[ @Empty, @Foo [ @Bar ] Int ]",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue