diff --git a/src/fmt/expr.rs b/src/fmt/expr.rs index 85d39d7e1a..c96a1631d4 100644 --- a/src/fmt/expr.rs +++ b/src/fmt/expr.rs @@ -73,8 +73,9 @@ pub fn fmt_expr<'a>( } buf.push_str("\"\"\""); } - Int(string) => buf.push_str(string), - Float(string) => buf.push_str(string), + Int(string) | Float(string) | GlobalTag(string) | PrivateTag(string) => { + buf.push_str(string) + } NonBase10Int { base, string, @@ -480,7 +481,7 @@ pub fn fmt_closure<'a>( any_args_printed = true; } - fmt_pattern(buf, &loc_pattern.value, indent, true); + fmt_pattern(buf, &loc_pattern.value, indent, false); } if !arguments_are_multiline { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 8874a10401..e9abe3702b 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -430,7 +430,7 @@ pub fn loc_parenthetical_def<'a>(min_indent: u16) -> impl Parser<'a, Located( // e.g. \User.UserId userId -> ... between!( char('('), - space0_around(loc!(pattern(min_indent)), min_indent), + space0_around(loc_pattern(min_indent), min_indent), char(')') ), // The least common, but still allowed, e.g. \Foo -> ... - loc!(tag_pattern()) + loc_tag_pattern(min_indent) ) .parse(arena, state) } -fn pattern<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> { +fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located>> { one_of!( - underscore_pattern(), - tag_pattern(), - ident_pattern(), - record_destructure(min_indent), - string_pattern(), - int_pattern() + loc_parenthetical_pattern(min_indent), + loc!(underscore_pattern()), + loc_tag_pattern(min_indent), + loc!(ident_pattern()), + loc!(record_destructure(min_indent)), + loc!(string_pattern()), + loc!(int_pattern()) + ) +} + +fn loc_parenthetical_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located>> { + between!( + char('('), + move |arena, state| loc_pattern(min_indent).parse(arena, state), + char(')') ) } @@ -796,7 +805,7 @@ fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>> { fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> { then( - record_without_update!(loc!(pattern(min_indent)), min_indent), + record_without_update!(loc_pattern(min_indent), min_indent), move |arena, state, assigned_fields| { let mut patterns = Vec::with_capacity_in(assigned_fields.len(), arena); for assigned_field in assigned_fields { @@ -815,10 +824,29 @@ fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> { ) } -fn tag_pattern<'a>() -> impl Parser<'a, Pattern<'a>> { - one_of!( - map!(private_tag(), Pattern::PrivateTag), - map!(global_tag(), Pattern::GlobalTag) +fn loc_tag_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located>> { + map_with_arena!( + and!( + loc!(one_of!( + map!(private_tag(), Pattern::PrivateTag), + map!(global_tag(), Pattern::GlobalTag) + )), + // This can optionally be an applied pattern, e.g. (Foo bar) instead of (Foo) + zero_or_more!(space1_before(loc_pattern(min_indent), min_indent)) + ), + |arena: &'a Bump, + (loc_tag, loc_args): (Located>, Vec<'a, Located>>)| { + if loc_args.is_empty() { + loc_tag + } else { + // TODO FIME this region doesn't cover the tag's + // arguments; need to add them to the region! + let region = loc_tag.region; + let value = Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice()); + + Located { region, value } + } + } ) } @@ -868,7 +896,7 @@ pub fn case_branches<'a>( // 2. Parse the other branches. Their indentation levels must be == the first branch's. let (mut loc_first_pattern, state) = - space1_before(loc!(pattern(min_indent)), min_indent).parse(arena, state)?; + space1_before(loc_pattern(min_indent), min_indent).parse(arena, state)?; let original_indent = state.indent_col; let indented_more = original_indent + 1; let (spaces_before_arrow, state) = space0(min_indent).parse(arena, state)?; @@ -898,7 +926,7 @@ pub fn case_branches<'a>( let branch_parser = and!( then( - space1_around(loc!(pattern(min_indent)), min_indent), + space1_around(loc_pattern(min_indent), min_indent), move |_arena, state, loc_pattern| { if state.indent_col == original_indent { Ok((loc_pattern, state)) diff --git a/src/pretty_print_types.rs b/src/pretty_print_types.rs index 10d112de6a..7a0b0251e1 100644 --- a/src/pretty_print_types.rs +++ b/src/pretty_print_types.rs @@ -208,6 +208,13 @@ fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, paren EmptyTagUnion => buf.push_str(EMPTY_TAG_UNION), Func(args, ret) => write_fn(args, ret, subs, buf, parens), Record(fields, ext_var) => { + use crate::unify::gather_fields; + use crate::unify::RecordStructure; + + // If the `ext` has concrete fields (e.g. { foo : Int}{ bar : Bool }), merge them + let RecordStructure { fields, ext } = gather_fields(subs, fields, ext_var); + let ext_var = ext; + if fields.is_empty() { buf.push_str(EMPTY_RECORD) } else { diff --git a/src/unify.rs b/src/unify.rs index d6c59f5599..e093f8a9a6 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -16,9 +16,9 @@ struct Context { second_desc: Descriptor, } -struct RecordStructure { - fields: ImMap, - ext: Variable, +pub struct RecordStructure { + pub fields: ImMap, + pub ext: Variable, } struct TagUnionStructure { @@ -519,7 +519,7 @@ fn unify_flex( } } -fn gather_fields( +pub fn gather_fields( subs: &mut Subs, fields: ImMap, var: Variable, diff --git a/tests/test_format.rs b/tests/test_format.rs index c444eb31c8..6b6077e758 100644 --- a/tests/test_format.rs +++ b/tests/test_format.rs @@ -241,7 +241,7 @@ mod test_format { expr_formats_same(indoc!( r#" """ - + "" \""" ""\" """ @@ -294,6 +294,24 @@ mod test_format { )); } + #[test] + fn destructure_tag_closure() { + expr_formats_same(indoc!( + r#" + \Foo a -> Foo a + "# + )); + } + + #[test] + fn destructure_nested_tag_closure() { + expr_formats_same(indoc!( + r#" + \Foo (Bar a) -> Foo (Bar a) + "# + )); + } + // DEFS #[test] diff --git a/tests/test_infer.rs b/tests/test_infer.rs index 055958cec5..8d89219ec0 100644 --- a/tests/test_infer.rs +++ b/tests/test_infer.rs @@ -1005,7 +1005,7 @@ mod test_infer { { user & year: "foo" } "# ), - "{ year : Str }{ name : Str }", + "{ name : Str, year : Str }", ); } diff --git a/tests/test_parse.rs b/tests/test_parse.rs index fd567e4653..c6948a7e10 100644 --- a/tests/test_parse.rs +++ b/tests/test_parse.rs @@ -641,7 +641,7 @@ mod test_parse { assert_eq!(Ok(expected), actual); } - // VARIANT + // TAG #[test] fn basic_global_tag() { diff --git a/tests/test_uniqueness_infer.rs b/tests/test_uniqueness_infer.rs index 11031ac25f..1d82f83ef0 100644 --- a/tests/test_uniqueness_infer.rs +++ b/tests/test_uniqueness_infer.rs @@ -899,7 +899,7 @@ mod test_infer_uniq { { user & year: "foo" } "# ), - "Attr.Attr * { year : (Attr.Attr * Str) }{ name : (Attr.Attr * Str) }", + "Attr.Attr * { name : (Attr.Attr * Str), year : (Attr.Attr * Str) }", ); }