From 49a9d2de3b145636690f7d192f2980b3459ef6ea Mon Sep 17 00:00:00 2001 From: Mitul Agrawal Date: Thu, 21 Nov 2024 17:26:39 +0530 Subject: [PATCH 1/2] add record destructure spread test --- .../tests/snapshots/pass/record_destructure_spread.expr.roc | 2 ++ crates/compiler/test_syntax/tests/test_snapshots.rs | 1 + 2 files changed, 3 insertions(+) create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_spread.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_spread.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_spread.expr.roc new file mode 100644 index 00000000000..5a277f9cba9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_spread.expr.roc @@ -0,0 +1,2 @@ +{ x, y, .. } = 5 +y = 6 diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 05bf4d7e6a9..d437cb24ef5 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -456,6 +456,7 @@ mod test_snapshots { pass/record_builder.expr, pass/record_builder_ignored_fields.expr, pass/record_destructure_def.expr, + pass/record_destructure_spread.expr, pass/record_func_type_decl.expr, pass/record_type_with_function.expr, pass/record_update.expr, From c3f050887c4d80e29564193d6afab9902b1a6980 Mon Sep 17 00:00:00 2001 From: Mitul Agrawal Date: Thu, 21 Nov 2024 17:28:58 +0530 Subject: [PATCH 2/2] parsing support for record spread destructure --- crates/compiler/parse/src/ast.rs | 11 ++++++++ crates/compiler/parse/src/pattern.rs | 39 +++++++++++++++++++++------- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index f1860100470..59ded03c950 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -1687,6 +1687,10 @@ pub enum Pattern<'a> { /// In practice, these patterns will always be Identifier RecordDestructure(Collection<'a, Loc>>), + /// A record spread pattern ".." + /// Can only occur inside of a [Pattern::Record] + RecordRest(Option<(&'a [CommentOrNewline<'a>])>), + /// A required field pattern, e.g. { x: Just 0 } -> ... /// Can only occur inside of a RecordDestructure RequiredField(&'a str, &'a Loc>), @@ -1783,6 +1787,13 @@ impl<'a> Pattern<'a> { false } } + RecordRest(pattern_as) => match other { + RecordRest(other_pattern_as) => match (pattern_as, other_pattern_as) { + (Some((_, a)), Some((_, b))) => a.equivalent(b), + _ => false, + }, + _ => false, + } RequiredField(x, inner_x) => { if let RequiredField(y, inner_y) = other { x == y && inner_x.value.equivalent(&inner_y.value) diff --git a/crates/compiler/parse/src/pattern.rs b/crates/compiler/parse/src/pattern.rs index 74e443a5157..fa4fa5ba66f 100644 --- a/crates/compiler/parse/src/pattern.rs +++ b/crates/compiler/parse/src/pattern.rs @@ -475,20 +475,41 @@ fn lowercase_ident_pattern<'a>() -> impl Parser<'a, &'a str, EPattern<'a>> { #[inline(always)] fn record_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PRecord<'a>> { - map(record_pattern_fields(), Pattern::RecordDestructure) + map( + collection_trailing_sep_e( + byte(b'{', PRecord::Open), + record_element_pattern(), + byte(b',', PRecord::End), + byte(b'}', PRecord::End), + Pattern::SpaceBefore, + ), + Pattern::RecordDestructure + ) } -pub fn record_pattern_fields<'a>() -> impl Parser<'a, Collection<'a, Loc>>, PRecord<'a>> -{ - collection_trailing_sep_e( - byte(b'{', PRecord::Open), - record_pattern_field(), - byte(b',', PRecord::End), - byte(b'}', PRecord::End), - Pattern::SpaceBefore, +fn record_element_pattern<'a>() -> impl Parser<'a, Loc>, PRecord<'a>> { + one_of!( + three_record_rest_pattern_error(), + record_rest_pattern(), + record_pattern_fields(), ) } +fn three_record_rest_pattern_error<'a>() -> impl Parser<'a, Loc>, PRecord<'a>> { + fail_when(PRecord::Rest, loc(three_bytes(b'.', b'.', b'.', PRecord::Rest))) +} + +fn record_rest_pattern<'a>() -> impl Parser<'a, Loc>, PRecord<'a>> { + move |arena: &'a Bump, state: State<'a>, min_indent: u32| { + let (_, loc_word, state) = + loc(two_bytes(b'.', b'.', PRecord::Open)).parse(arena, state, min_indent)?; + + let no_as = Loc::at(loc_word.region, Pattern::RecordRest(None)); + + Ok((MadeProgress, no_as, state)) + } +} + fn record_pattern_field<'a>() -> impl Parser<'a, Loc>, PRecord<'a>> { use crate::parser::Either::*;