PNC for Patterns, stabilize formatting

This commit is contained in:
Anthony Bullard 2024-12-31 08:25:11 -06:00
parent bac165fd99
commit 3b0db07fa1
No known key found for this signature in database
78 changed files with 789 additions and 332 deletions

View file

@ -190,7 +190,7 @@ pub enum FormatProblem {
pub fn format_src(arena: &Bump, src: &str, flags: MigrationFlags) -> Result<String, FormatProblem> { pub fn format_src(arena: &Bump, src: &str, flags: MigrationFlags) -> Result<String, FormatProblem> {
let ast = arena.alloc(parse_all(arena, src).unwrap_or_else(|e| { let ast = arena.alloc(parse_all(arena, src).unwrap_or_else(|e| {
user_error!("Unexpected parse failure when parsing this formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, e) user_error!("Unexpected parse failure when parsing this formatting:\n\n{src}\n\nParse error was:\n\n{:#?}\n\n", e)
})); }));
let mut buf = Buf::new_in(arena, flags); let mut buf = Buf::new_in(arena, flags);
fmt_all(&mut buf, ast); fmt_all(&mut buf, ast);

View file

@ -8,7 +8,7 @@
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::{called_via::CalledVia, symbol::Symbol}; use roc_module::{called_via::CalledVia, symbol::Symbol};
use roc_parse::ast::{self, Collection}; use roc_parse::ast::{self, Collection, PatternApplyStyle};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use crate::{env::Env, pattern::Pattern, scope::Scope}; use crate::{env::Env, pattern::Pattern, scope::Scope};
@ -27,6 +27,7 @@ fn to_encoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
DERIVED_REGION, DERIVED_REGION,
ast::Pattern::Identifier { ident: payload }, ast::Pattern::Identifier { ident: payload },
)]), )]),
ast::PatternApplyStyle::Whitespace,
); );
// Encode.toEncoder payload // Encode.toEncoder payload
@ -132,6 +133,7 @@ fn hash<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
DERIVED_REGION, DERIVED_REGION,
ast::Pattern::Identifier { ident: payload }, ast::Pattern::Identifier { ident: payload },
)]), )]),
PatternApplyStyle::Whitespace,
); );
// Hash.hash hasher payload // Hash.hash hasher payload
@ -178,6 +180,7 @@ fn is_eq<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
DERIVED_REGION, DERIVED_REGION,
ast::Pattern::Identifier { ident: payload1 }, ast::Pattern::Identifier { ident: payload1 },
)]), )]),
PatternApplyStyle::Whitespace,
); );
// \@Opaq payload2 // \@Opaq payload2
let opaque2 = ast::Pattern::Apply( let opaque2 = ast::Pattern::Apply(
@ -186,6 +189,7 @@ fn is_eq<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
DERIVED_REGION, DERIVED_REGION,
ast::Pattern::Identifier { ident: payload2 }, ast::Pattern::Identifier { ident: payload2 },
)]), )]),
PatternApplyStyle::Whitespace,
); );
// Bool.isEq payload1 payload2 // Bool.isEq payload1 payload2
@ -232,6 +236,7 @@ fn to_inspector<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
DERIVED_REGION, DERIVED_REGION,
ast::Pattern::Identifier { ident: payload }, ast::Pattern::Identifier { ident: payload },
)]), )]),
PatternApplyStyle::Whitespace,
); );
// Inspect.toInspector payload // Inspect.toInspector payload

View file

@ -11,8 +11,9 @@ use roc_module::called_via::{BinOp, CalledVia};
use roc_module::ident::ModuleName; use roc_module::ident::ModuleName;
use roc_parse::ast::Expr::{self, *}; use roc_parse::ast::Expr::{self, *};
use roc_parse::ast::{ use roc_parse::ast::{
is_expr_suffixed, AssignedField, Collection, Defs, ModuleImportParams, Pattern, ResultTryKind, is_expr_suffixed, AssignedField, Collection, Defs, ModuleImportParams, Pattern,
StrLiteral, StrSegment, TryTarget, TypeAnnotation, ValueDef, WhenBranch, PatternApplyStyle, ResultTryKind, StrLiteral, StrSegment, TryTarget, TypeAnnotation, ValueDef,
WhenBranch,
}; };
use roc_problem::can::Problem; use roc_problem::can::Problem;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
@ -177,7 +178,11 @@ fn new_op_call_expr<'a>(
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Ok"))); env.arena.alloc(Loc::at(left.region, Pattern::Tag("Ok")));
branch_1_patts.push(Loc::at( branch_1_patts.push(Loc::at(
left.region, left.region,
Pattern::Apply(branch_1_tag, branch_1_patts_args.into_bump_slice()), Pattern::Apply(
branch_1_tag,
branch_1_patts_args.into_bump_slice(),
PatternApplyStyle::ParensAndCommas,
),
)); ));
let branch_one: &WhenBranch<'_> = env.arena.alloc(WhenBranch { let branch_one: &WhenBranch<'_> = env.arena.alloc(WhenBranch {
patterns: branch_1_patts.into_bump_slice(), patterns: branch_1_patts.into_bump_slice(),
@ -198,7 +203,11 @@ fn new_op_call_expr<'a>(
env.arena.alloc(Loc::at(left.region, Pattern::Tag("Err"))); env.arena.alloc(Loc::at(left.region, Pattern::Tag("Err")));
branch_2_patts.push(Loc::at( branch_2_patts.push(Loc::at(
right.region, right.region,
Pattern::Apply(branch_2_tag, branch_2_patts_args.into_bump_slice()), Pattern::Apply(
branch_2_tag,
branch_2_patts_args.into_bump_slice(),
PatternApplyStyle::ParensAndCommas,
),
)); ));
let branch_two: &WhenBranch<'_> = env.arena.alloc(WhenBranch { let branch_two: &WhenBranch<'_> = env.arena.alloc(WhenBranch {
patterns: branch_2_patts.into_bump_slice(), patterns: branch_2_patts.into_bump_slice(),
@ -1413,7 +1422,7 @@ fn desugar_pattern<'a>(env: &mut Env<'a>, scope: &mut Scope, pattern: Pattern<'a
| MalformedIdent(_, _) | MalformedIdent(_, _)
| QualifiedIdentifier { .. } => pattern, | QualifiedIdentifier { .. } => pattern,
Apply(tag, arg_patterns) => { Apply(tag, arg_patterns, style) => {
// Skip desugaring the tag, it should either be a Tag or OpaqueRef // Skip desugaring the tag, it should either be a Tag or OpaqueRef
let mut desugared_arg_patterns = Vec::with_capacity_in(arg_patterns.len(), env.arena); let mut desugared_arg_patterns = Vec::with_capacity_in(arg_patterns.len(), env.arena);
for arg_pattern in arg_patterns.iter() { for arg_pattern in arg_patterns.iter() {
@ -1423,7 +1432,7 @@ fn desugar_pattern<'a>(env: &mut Env<'a>, scope: &mut Scope, pattern: Pattern<'a
}); });
} }
Apply(tag, desugared_arg_patterns.into_bump_slice()) Apply(tag, desugared_arg_patterns.into_bump_slice(), style)
} }
RecordDestructure(field_patterns) => { RecordDestructure(field_patterns) => {
RecordDestructure(desugar_record_destructures(env, scope, field_patterns)) RecordDestructure(desugar_record_destructures(env, scope, field_patterns))

View file

@ -411,7 +411,7 @@ pub fn canonicalize_pattern<'a>(
))); )));
Pattern::UnsupportedPattern(region) Pattern::UnsupportedPattern(region)
} }
Apply(tag, patterns) => { Apply(tag, patterns, _) => {
let mut can_patterns = Vec::with_capacity(patterns.len()); let mut can_patterns = Vec::with_capacity(patterns.len());
for loc_pattern in *patterns { for loc_pattern in *patterns {
let can_pattern = canonicalize_pattern( let can_pattern = canonicalize_pattern(

View file

@ -78,6 +78,7 @@ Defs {
ident: "name", ident: "name",
}, },
], ],
Whitespace,
), ),
], ],
value: @125-154 Apply( value: @125-154 Apply(

View file

@ -13,14 +13,14 @@ use crate::pattern::{pattern_lift_spaces, pattern_lift_spaces_before};
use crate::spaces::{ use crate::spaces::{
fmt_comments_only, fmt_default_newline, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT, fmt_comments_only, fmt_default_newline, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT,
}; };
use crate::Buf; use crate::{Buf, MigrationFlags};
use bumpalo::Bump; use bumpalo::Bump;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_parse::ast::{ use roc_parse::ast::{
AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword, AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
ModuleImportParams, Pattern, Spaceable, Spaces, SpacesAfter, SpacesBefore, StrLiteral, ModuleImportParams, Pattern, PatternApplyStyle, Spaceable, Spaces, SpacesAfter, SpacesBefore,
TypeAnnotation, TypeDef, TypeHeader, ValueDef, StrLiteral, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
}; };
use roc_parse::expr::merge_spaces; use roc_parse::expr::merge_spaces;
use roc_parse::header::Keyword; use roc_parse::header::Keyword;
@ -564,6 +564,11 @@ impl<'a> Formattable for TypeHeader<'a> {
_newlines: Newlines, _newlines: Newlines,
indent: u16, indent: u16,
) { ) {
let old_flags = buf.flags;
buf.flags = MigrationFlags {
parens_and_commas: false,
..old_flags
};
pattern_fmt_apply( pattern_fmt_apply(
buf, buf,
Pattern::Tag(self.name.value), Pattern::Tag(self.name.value),
@ -571,7 +576,9 @@ impl<'a> Formattable for TypeHeader<'a> {
Parens::NotNeeded, Parens::NotNeeded,
indent, indent,
self.vars.iter().any(|v| v.is_multiline()), self.vars.iter().any(|v| v.is_multiline()),
PatternApplyStyle::Whitespace,
); );
buf.flags = old_flags;
} }
} }
@ -582,6 +589,7 @@ fn type_head_lift_spaces<'a, 'b: 'a>(
let pat = Pattern::Apply( let pat = Pattern::Apply(
arena.alloc(Loc::at(head.name.region, Pattern::Tag(head.name.value))), arena.alloc(Loc::at(head.name.region, Pattern::Tag(head.name.value))),
head.vars, head.vars,
PatternApplyStyle::Whitespace,
); );
pattern_lift_spaces(arena, &pat) pattern_lift_spaces(arena, &pat)
@ -903,7 +911,9 @@ impl<'a> Formattable for ValueDef<'a> {
fn ann_pattern_needs_parens(value: &Pattern<'_>) -> bool { fn ann_pattern_needs_parens(value: &Pattern<'_>) -> bool {
match value.extract_spaces().item { match value.extract_spaces().item {
Pattern::Tag(_) => true, Pattern::Tag(_) => true,
Pattern::Apply(func, _args) if matches!(func.extract_spaces().item, Pattern::Tag(..)) => { Pattern::Apply(func, _args, _style)
if matches!(func.extract_spaces().item, Pattern::Tag(..)) =>
{
true true
} }
_ => false, _ => false,

View file

@ -11,7 +11,7 @@ use crate::spaces::{
use crate::Buf; use crate::Buf;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_module::called_via::{self, BinOp, UnaryOp}; use roc_module::called_via::{self, BinOp, CalledVia, UnaryOp};
use roc_parse::ast::{ use roc_parse::ast::{
AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, Pattern, Spaceable, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, Pattern, Spaceable,
Spaces, SpacesAfter, SpacesBefore, TryTarget, WhenBranch, Spaces, SpacesAfter, SpacesBefore, TryTarget, WhenBranch,
@ -96,8 +96,9 @@ fn format_expr_only(
} }
Expr::Apply(loc_expr, loc_args, _) => { Expr::Apply(loc_expr, loc_args, _) => {
let apply_needs_parens = parens == Parens::InApply || parens == Parens::InApplyLastArg; let apply_needs_parens = parens == Parens::InApply || parens == Parens::InApplyLastArg;
if buf.flags().parens_and_commas {
if apply_needs_parens && !loc_args.is_empty() { fmt_apply(loc_expr, loc_args, indent, buf, false);
} else if apply_needs_parens && !loc_args.is_empty() {
fmt_parens(item, buf, indent); fmt_parens(item, buf, indent);
} else { } else {
fmt_apply(loc_expr, loc_args, indent, buf, false); fmt_apply(loc_expr, loc_args, indent, buf, false);
@ -716,7 +717,9 @@ fn fmt_apply(
let is_first_arg = i == 0; let is_first_arg = i == 0;
let arg = expr_lift_spaces( let arg = expr_lift_spaces(
if is_last_arg { if use_commas_and_parens {
Parens::NotNeeded
} else if is_last_arg {
Parens::InApplyLastArg Parens::InApplyLastArg
} else { } else {
Parens::InApply Parens::InApply
@ -1038,7 +1041,7 @@ pub fn expr_lift_spaces<'a, 'b: 'a>(
) -> Spaces<'a, Expr<'a>> { ) -> Spaces<'a, Expr<'a>> {
match expr { match expr {
Expr::Apply(func, args, called_via) => { Expr::Apply(func, args, called_via) => {
if args.is_empty() { if args.is_empty() && !matches!(called_via, CalledVia::ParensAndCommas) {
return expr_lift_spaces(Parens::NotNeeded, arena, &func.value); return expr_lift_spaces(Parens::NotNeeded, arena, &func.value);
} }
@ -1082,7 +1085,9 @@ pub fn expr_lift_spaces<'a, 'b: 'a>(
} }
}; };
if parens == Parens::InApply || parens == Parens::InApplyLastArg { if (parens == Parens::InApply || parens == Parens::InApplyLastArg)
&& !matches!(called_via, CalledVia::ParensAndCommas)
{
res = Spaces { res = Spaces {
before: &[], before: &[],
item: Expr::ParensAround(arena.alloc(lower(arena, res))), item: Expr::ParensAround(arena.alloc(lower(arena, res))),

View file

@ -7,7 +7,8 @@ use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT};
use crate::Buf; use crate::Buf;
use bumpalo::Bump; use bumpalo::Bump;
use roc_parse::ast::{ use roc_parse::ast::{
Base, CommentOrNewline, Pattern, PatternAs, Spaceable, Spaces, SpacesAfter, SpacesBefore, Base, CommentOrNewline, Pattern, PatternApplyStyle, PatternAs, Spaceable, Spaces, SpacesAfter,
SpacesBefore,
}; };
use roc_parse::expr::merge_spaces; use roc_parse::expr::merge_spaces;
use roc_region::all::Loc; use roc_region::all::Loc;
@ -72,7 +73,7 @@ impl<'a> Formattable for Pattern<'a> {
} }
}, },
Pattern::StrLiteral(literal) => is_str_multiline(literal), Pattern::StrLiteral(literal) => is_str_multiline(literal),
Pattern::Apply(pat, args) => { Pattern::Apply(pat, args, _) => {
pat.is_multiline() || args.iter().any(|a| a.is_multiline()) pat.is_multiline() || args.iter().any(|a| a.is_multiline())
} }
@ -162,7 +163,22 @@ fn fmt_pattern_only(
buf.indent(indent); buf.indent(indent);
buf.push_str(name); buf.push_str(name);
} }
Pattern::Apply(loc_pattern, loc_arg_patterns) => { Pattern::Apply(
loc_pattern,
loc_arg_patterns,
style @ PatternApplyStyle::ParensAndCommas,
) => {
pattern_fmt_apply(
buf,
loc_pattern.value,
loc_arg_patterns,
Parens::NotNeeded,
indent,
is_multiline,
*style,
);
}
Pattern::Apply(loc_pattern, loc_arg_patterns, style) => {
pattern_fmt_apply( pattern_fmt_apply(
buf, buf,
loc_pattern.value, loc_pattern.value,
@ -170,6 +186,7 @@ fn fmt_pattern_only(
parens, parens,
indent, indent,
is_multiline, is_multiline,
*style,
); );
} }
Pattern::RecordDestructure(loc_patterns) => { Pattern::RecordDestructure(loc_patterns) => {
@ -445,11 +462,14 @@ pub fn pattern_fmt_apply(
parens: Parens, parens: Parens,
indent: u16, indent: u16,
is_multiline: bool, is_multiline: bool,
style: PatternApplyStyle,
) { ) {
let use_commas_and_parens =
matches!(style, PatternApplyStyle::ParensAndCommas) || buf.flags().parens_and_commas;
buf.indent(indent); buf.indent(indent);
// Sometimes, an Apply pattern needs parens around it. // Sometimes, an Apply pattern needs parens around it.
// In particular when an Apply's argument is itself an Apply (> 0) arguments // In particular when an Apply's argument is itself an Apply (> 0) arguments
let parens = !args.is_empty() && parens == Parens::InApply; let parens = !args.is_empty() && parens == Parens::InApply && !use_commas_and_parens;
let indent_more = if is_multiline { let indent_more = if is_multiline {
indent + INDENT indent + INDENT
@ -473,14 +493,27 @@ pub fn pattern_fmt_apply(
fmt_pattern_only(&func.item, buf, Parens::InApply, indent, is_multiline); fmt_pattern_only(&func.item, buf, Parens::InApply, indent, is_multiline);
if use_commas_and_parens {
buf.push('(');
}
let mut last_after = func.after; let mut last_after = func.after;
let mut add_newlines = is_multiline; let mut add_newlines = is_multiline;
for loc_arg in args.iter() { for (i, loc_arg) in args.iter().enumerate() {
buf.spaces(1); let is_last_arg = i == args.len() - 1;
let is_first_arg = i == 0;
let parens = Parens::InApply; if !(is_first_arg && use_commas_and_parens) {
buf.spaces(1);
}
let parens = if use_commas_and_parens {
Parens::NotNeeded
} else {
Parens::InApply
};
let arg = pattern_lift_spaces(buf.text.bump(), &loc_arg.value); let arg = pattern_lift_spaces(buf.text.bump(), &loc_arg.value);
let mut was_multiline = arg.item.is_multiline(); let mut was_multiline = arg.item.is_multiline();
@ -527,6 +560,9 @@ pub fn pattern_fmt_apply(
buf.push_str("(implements)"); buf.push_str("(implements)");
} else { } else {
fmt_pattern_only(&arg.item, buf, parens, indent_more, arg.item.is_multiline()); fmt_pattern_only(&arg.item, buf, parens, indent_more, arg.item.is_multiline());
if use_commas_and_parens && (!is_last_arg || add_newlines) {
buf.push(',');
}
} }
last_after = arg.after; last_after = arg.after;
@ -542,7 +578,7 @@ pub fn pattern_fmt_apply(
} }
} }
if parens { if parens || use_commas_and_parens {
buf.push(')'); buf.push(')');
} }
} }
@ -597,7 +633,7 @@ fn pattern_prec(pat: Pattern<'_>) -> Prec {
| Pattern::Tuple(..) | Pattern::Tuple(..)
| Pattern::List(..) | Pattern::List(..)
| Pattern::ListRest(_) => Prec::Term, | Pattern::ListRest(_) => Prec::Term,
Pattern::Apply(_, _) | Pattern::As(_, _) => Prec::Apply, Pattern::Apply(_, _, _) | Pattern::As(_, _) => Prec::Apply,
Pattern::SpaceBefore(inner, _) | Pattern::SpaceAfter(inner, _) => pattern_prec(*inner), Pattern::SpaceBefore(inner, _) | Pattern::SpaceAfter(inner, _) => pattern_prec(*inner),
Pattern::Malformed(_) | Pattern::MalformedIdent(..) => Prec::Term, Pattern::Malformed(_) | Pattern::MalformedIdent(..) => Prec::Term,
} }
@ -617,7 +653,7 @@ pub fn pattern_lift_spaces<'a, 'b: 'a>(
pat: &Pattern<'b>, pat: &Pattern<'b>,
) -> Spaces<'a, Pattern<'a>> { ) -> Spaces<'a, Pattern<'a>> {
match pat { match pat {
Pattern::Apply(func, args) => { Pattern::Apply(func, args, style) => {
let func_lifted = pattern_lift_spaces(arena, &func.value); let func_lifted = pattern_lift_spaces(arena, &func.value);
let args = arena.alloc_slice_copy(args); let args = arena.alloc_slice_copy(args);
@ -652,7 +688,7 @@ pub fn pattern_lift_spaces<'a, 'b: 'a>(
}; };
Spaces { Spaces {
before, before,
item: Pattern::Apply(arena.alloc(func), args), item: Pattern::Apply(arena.alloc(func), args, *style),
after, after,
} }
} }
@ -712,7 +748,7 @@ fn handle_multiline_str_spaces<'a>(pat: &Pattern<'_>, before: &mut &'a [CommentO
fn starts_with_block_str(item: &Pattern<'_>) -> bool { fn starts_with_block_str(item: &Pattern<'_>) -> bool {
match item { match item {
Pattern::As(inner, _) | Pattern::Apply(inner, _) => starts_with_block_str(&inner.value), Pattern::As(inner, _) | Pattern::Apply(inner, _, _) => starts_with_block_str(&inner.value),
Pattern::SpaceBefore(inner, _) | Pattern::SpaceAfter(inner, _) => { Pattern::SpaceBefore(inner, _) | Pattern::SpaceAfter(inner, _) => {
starts_with_block_str(inner) starts_with_block_str(inner)
} }

View file

@ -1745,6 +1745,12 @@ impl<'a> PatternAs<'a> {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PatternApplyStyle {
Whitespace,
ParensAndCommas,
}
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Pattern<'a> { pub enum Pattern<'a> {
// Identifier // Identifier
@ -1760,7 +1766,11 @@ pub enum Pattern<'a> {
OpaqueRef(&'a str), OpaqueRef(&'a str),
Apply(&'a Loc<Pattern<'a>>, &'a [Loc<Pattern<'a>>]), Apply(
&'a Loc<Pattern<'a>>,
&'a [Loc<Pattern<'a>>],
PatternApplyStyle,
),
/// This is Located<Pattern> rather than Located<str> so we can record comments /// This is Located<Pattern> rather than Located<str> so we can record comments
/// around the destructured names, e.g. { x ### x does stuff ###, y } /// around the destructured names, e.g. { x ### x does stuff ###, y }
@ -1841,8 +1851,8 @@ impl<'a> Pattern<'a> {
false false
} }
} }
Apply(constructor_x, args_x) => { Apply(constructor_x, args_x, _) => {
if let Apply(constructor_y, args_y) = other { if let Apply(constructor_y, args_y, _) = other {
let equivalent_args = args_x let equivalent_args = args_x
.iter() .iter()
.zip(args_y.iter()) .zip(args_y.iter())
@ -2670,7 +2680,7 @@ impl<'a> Malformed for Pattern<'a> {
Identifier{ .. } | Identifier{ .. } |
Tag(_) | Tag(_) |
OpaqueRef(_) => false, OpaqueRef(_) => false,
Apply(func, args) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()), Apply(func, args, _) => func.is_malformed() || args.iter().any(|arg| arg.is_malformed()),
RecordDestructure(items) => items.iter().any(|item| item.is_malformed()), RecordDestructure(items) => items.iter().any(|item| item.is_malformed()),
RequiredField(_, pat) => pat.is_malformed(), RequiredField(_, pat) => pat.is_malformed(),
OptionalField(_, expr) => expr.is_malformed(), OptionalField(_, expr) => expr.is_malformed(),

View file

@ -2,8 +2,8 @@ use crate::ast::{
is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
ModuleImportParams, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, TryTarget, ModuleImportParams, Pattern, PatternApplyStyle, Spaceable, Spaced, Spaces, SpacesBefore,
TypeAnnotation, TypeDef, TypeHeader, ValueDef, TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
}; };
use crate::blankspace::{ use crate::blankspace::{
loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e,
@ -181,7 +181,7 @@ fn loc_term_or_underscore_or_conditional<'a>(
} }
} }
loc_term_or_underscore(check_for_arrow).parse(arena, state, min_indent) loc_term_or_closure(check_for_arrow).parse(arena, state, min_indent)
} }
} }
fn loc_conditional<'a>( fn loc_conditional<'a>(
@ -198,84 +198,80 @@ fn loc_conditional<'a>(
/// In some contexts we want to parse the `_` as an expression, so it can then be turned into a /// In some contexts we want to parse the `_` as an expression, so it can then be turned into a
/// pattern later /// pattern later
fn loc_term_or_underscore<'a>( fn loc_term_or_closure<'a>(
check_for_arrow: CheckForArrow, check_for_arrow: CheckForArrow,
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> { ) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of!( one_of!(
loc_expr_in_parens_etc_help(),
loc(specialize_err(EExpr::Str, string_like_literal_help())),
loc(specialize_err(
EExpr::Number,
positive_number_literal_help()
)),
loc(specialize_err( loc(specialize_err(
EExpr::Closure, EExpr::Closure,
closure_help(check_for_arrow) closure_help(check_for_arrow)
)), )),
loc(crash_kw()), loc_term(),
loc(specialize_err(EExpr::Dbg, dbg_kw())),
loc(try_kw()),
loc(underscore_expression()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
loc(apply_with_pnc()),
ident_seq(),
) )
.trace("term_or_underscore") .trace("term_or_underscore")
} }
fn loc_term<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> { fn loc_term<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of!( map_with_arena(
loc_expr_in_parens_etc_help(), and(
loc(specialize_err(EExpr::Str, string_like_literal_help())), one_of!(
loc(specialize_err( loc_expr_in_parens_etc_help(),
EExpr::Number, loc(specialize_err(EExpr::Str, string_like_literal_help())),
positive_number_literal_help() loc(specialize_err(
)), EExpr::Number,
loc(specialize_err( positive_number_literal_help()
EExpr::Closure, )),
closure_help(check_for_arrow) loc(crash_kw()),
)), loc(specialize_err(EExpr::Dbg, dbg_kw())),
loc(crash_kw()), loc(try_kw()),
loc(specialize_err(EExpr::Dbg, dbg_kw())), loc(underscore_expression()),
loc(try_kw()), loc(record_literal_help()),
loc(record_literal_help()), loc(specialize_err(EExpr::List, list_literal_help())),
loc(specialize_err(EExpr::List, list_literal_help())), ident_seq(),
loc(apply_with_pnc()), ),
ident_seq(), optional(pnc_args()),
),
|arena, (expr, maybe_arg_loc)| {
if let Some(arg_loc) = maybe_arg_loc {
let e = Expr::Apply(arena.alloc(expr), arg_loc.value, CalledVia::ParensAndCommas);
Loc {
value: e,
region: Region::span_across(&expr.region, &arg_loc.region),
}
} else {
expr
}
},
) )
.trace("term")
} }
fn apply_with_pnc<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> { fn pnc_args<'a>() -> impl Parser<'a, Loc<&'a [&'a Loc<Expr<'a>>]>, EExpr<'a>> {
|arena: &'a Bump, state: State<'a>, min_indent: u32| { |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let (_, ident, new_state) = ident_seq().parse(arena, state, min_indent)?; map_with_arena(
specialize_err(
let (_, args, newer_state) = specialize_err( EExpr::InParens,
EExpr::InParens, loc(collection_trailing_sep_e(
collection_trailing_sep_e( byte(b'(', EInParens::Open),
byte(b'(', EInParens::Open), specialize_err_ref(EInParens::Expr, loc_expr_block(true)),
specialize_err_ref(EInParens::Expr, loc_expr_block(false, true)), byte(b',', EInParens::End),
byte(b',', EInParens::End), byte(b')', EInParens::End),
byte(b')', EInParens::End), Expr::SpaceBefore,
Expr::SpaceBefore, )),
), ),
|arena, arg_loc| {
let mut args_vec = Vec::new_in(arena);
for arg in arg_loc.value.items.iter() {
let a: &Loc<Expr<'a>> = arena.alloc(arg);
args_vec.push(a);
}
Loc {
value: args_vec.into_bump_slice(),
region: arg_loc.region,
}
},
) )
.parse(arena, new_state, min_indent)?; .parse(arena, state, min_indent)
let arg_slice: &[&Loc<Expr<'a>>] = {
let mut args_vec = Vec::new_in(arena);
for arg in args.items.iter() {
let a: &Loc<Expr<'a>> = arena.alloc(arg);
args_vec.push(a);
}
args_vec.into_bump_slice()
};
Ok((
Progress::MadeProgress,
Expr::Apply(arena.alloc(ident), arg_slice, CalledVia::ParensAndCommas),
newer_state,
))
} }
} }
@ -1743,7 +1739,8 @@ fn parse_negated_term<'a>(
initial_state: State<'a>, initial_state: State<'a>,
loc_op: Loc<BinOp>, loc_op: Loc<BinOp>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let (_, negated_expr, state) = loc_term(check_for_arrow).parse(arena, state, min_indent)?; let (_, negated_expr, state) =
loc_term_or_closure(check_for_arrow).parse(arena, state, min_indent)?;
let new_end = state.pos(); let new_end = state.pos();
let arg = numeric_negate_expression( let arg = numeric_negate_expression(
@ -1793,7 +1790,7 @@ fn parse_expr_end<'a>(
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let parser = skip_first( let parser = skip_first(
crate::blankspace::check_indent(EExpr::IndentEnd), crate::blankspace::check_indent(EExpr::IndentEnd),
loc_term_or_underscore(check_for_arrow), loc_term_or_closure(check_for_arrow),
); );
match parser.parse(arena, state.clone(), call_min_indent) { match parser.parse(arena, state.clone(), call_min_indent) {
@ -2089,7 +2086,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
Expr::Underscore(opt_name) => Pattern::Underscore(opt_name), Expr::Underscore(opt_name) => Pattern::Underscore(opt_name),
Expr::Tag(value) => Pattern::Tag(value), Expr::Tag(value) => Pattern::Tag(value),
Expr::OpaqueRef(value) => Pattern::OpaqueRef(value), Expr::OpaqueRef(value) => Pattern::OpaqueRef(value),
Expr::Apply(loc_val, loc_args, _) => { Expr::Apply(loc_val, loc_args, called_via) => {
let region = loc_val.region; let region = loc_val.region;
let value = expr_to_pattern_help(arena, &loc_val.value)?; let value = expr_to_pattern_help(arena, &loc_val.value)?;
let val_pattern = arena.alloc(Loc { region, value }); let val_pattern = arena.alloc(Loc { region, value });
@ -2103,7 +2100,15 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
arg_patterns.push(Loc { region, value }); arg_patterns.push(Loc { region, value });
} }
let pattern = Pattern::Apply(val_pattern, arg_patterns.into_bump_slice()); let pattern = Pattern::Apply(
val_pattern,
arg_patterns.into_bump_slice(),
if matches!(called_via, CalledVia::ParensAndCommas) {
PatternApplyStyle::ParensAndCommas
} else {
PatternApplyStyle::Whitespace
},
);
pattern pattern
} }
@ -3277,7 +3282,7 @@ fn starts_with_spaces_conservative(value: &Pattern<'_>) -> bool {
| Pattern::ListRest(_) | Pattern::ListRest(_)
| Pattern::OpaqueRef(_) => false, | Pattern::OpaqueRef(_) => false,
Pattern::As(left, _) => starts_with_spaces_conservative(&left.value), Pattern::As(left, _) => starts_with_spaces_conservative(&left.value),
Pattern::Apply(left, _) => starts_with_spaces_conservative(&left.value), Pattern::Apply(left, _, _) => starts_with_spaces_conservative(&left.value),
Pattern::RecordDestructure(_) => false, Pattern::RecordDestructure(_) => false,
Pattern::RequiredField(_, _) | Pattern::OptionalField(_, _) => false, Pattern::RequiredField(_, _) | Pattern::OptionalField(_, _) => false,
Pattern::SpaceBefore(_, _) => true, Pattern::SpaceBefore(_, _) => true,
@ -3293,6 +3298,7 @@ fn header_to_pat<'a>(arena: &'a Bump, header: TypeHeader<'a>) -> Pattern<'a> {
Pattern::Apply( Pattern::Apply(
arena.alloc(Loc::at(header.name.region, Pattern::Tag(header.name.value))), arena.alloc(Loc::at(header.name.region, Pattern::Tag(header.name.value))),
header.vars, header.vars,
PatternApplyStyle::Whitespace,
) )
} }
} }
@ -3343,7 +3349,7 @@ fn pat_ends_with_spaces_conservative(pat: &Pattern<'_>) -> bool {
| Pattern::ListRest(_) | Pattern::ListRest(_)
| Pattern::As(_, _) | Pattern::As(_, _)
| Pattern::OpaqueRef(_) => false, | Pattern::OpaqueRef(_) => false,
Pattern::Apply(_, args) => args Pattern::Apply(_, args, _) => args
.last() .last()
.map_or(false, |a| pat_ends_with_spaces_conservative(&a.value)), .map_or(false, |a| pat_ends_with_spaces_conservative(&a.value)),
Pattern::RecordDestructure(_) => false, Pattern::RecordDestructure(_) => false,
@ -3365,7 +3371,7 @@ pub fn join_alias_to_body<'a>(
body_expr: &'a Loc<Expr<'a>>, body_expr: &'a Loc<Expr<'a>>,
) -> ValueDef<'a> { ) -> ValueDef<'a> {
let loc_name = arena.alloc(header.name.map(|x| Pattern::Tag(x))); let loc_name = arena.alloc(header.name.map(|x| Pattern::Tag(x)));
let ann_pattern = Pattern::Apply(loc_name, header.vars); let ann_pattern = Pattern::Apply(loc_name, header.vars, PatternApplyStyle::Whitespace);
let vars_region = Region::across_all(header.vars.iter().map(|v| &v.region)); let vars_region = Region::across_all(header.vars.iter().map(|v| &v.region));
let region_ann_pattern = Region::span_across(&loc_name.region, &vars_region); let region_ann_pattern = Region::span_across(&loc_name.region, &vars_region);

View file

@ -879,9 +879,10 @@ impl<'a> Normalize<'a> for Pattern<'a> {
Pattern::Identifier { ident } => Pattern::Identifier { ident }, Pattern::Identifier { ident } => Pattern::Identifier { ident },
Pattern::Tag(a) => Pattern::Tag(a), Pattern::Tag(a) => Pattern::Tag(a),
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a), Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
Pattern::Apply(a, b) => Pattern::Apply( Pattern::Apply(a, b, c) => Pattern::Apply(
arena.alloc(a.normalize(arena)), arena.alloc(a.normalize(arena)),
arena.alloc(b.normalize(arena)), arena.alloc(b.normalize(arena)),
c,
), ),
Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.normalize(arena)), Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.normalize(arena)),
Pattern::RequiredField(a, b) => { Pattern::RequiredField(a, b) => {
@ -1257,6 +1258,8 @@ impl<'a> Normalize<'a> for EPattern<'a> {
EPattern::AccessorFunction(_) => EPattern::AccessorFunction(Position::zero()), EPattern::AccessorFunction(_) => EPattern::AccessorFunction(Position::zero()),
EPattern::RecordUpdaterFunction(_) => EPattern::RecordUpdaterFunction(Position::zero()), EPattern::RecordUpdaterFunction(_) => EPattern::RecordUpdaterFunction(Position::zero()),
EPattern::Str(e, _) => EPattern::Str(e.normalize(arena), Position::zero()), EPattern::Str(e, _) => EPattern::Str(e.normalize(arena), Position::zero()),
EPattern::ParenStart(_) => EPattern::ParenStart(Position::zero()),
EPattern::ParenEnd(_) => EPattern::ParenEnd(Position::zero()),
} }
} }
} }

View file

@ -1066,6 +1066,9 @@ pub enum EPattern<'a> {
AccessorFunction(Position), AccessorFunction(Position),
RecordUpdaterFunction(Position), RecordUpdaterFunction(Position),
Str(EString<'a>, Position), Str(EString<'a>, Position),
ParenStart(Position),
ParenEnd(Position),
} }
impl<'a> EPattern<'a> { impl<'a> EPattern<'a> {
@ -1090,7 +1093,9 @@ impl<'a> EPattern<'a> {
| EPattern::IndentEnd(position) | EPattern::IndentEnd(position)
| EPattern::AsIndentStart(position) | EPattern::AsIndentStart(position)
| EPattern::AccessorFunction(position) | EPattern::AccessorFunction(position)
| EPattern::RecordUpdaterFunction(position) => Region::from_pos(*position), | EPattern::RecordUpdaterFunction(position)
| EPattern::ParenStart(position)
| EPattern::ParenEnd(position) => Region::from_pos(*position),
} }
} }
} }

View file

@ -1,11 +1,13 @@
use crate::ast::{Collection, ExtractSpaces, Implements, Pattern, PatternAs, Spaceable}; use crate::ast::{
use crate::blankspace::{space0_e, spaces, spaces_before}; Collection, ExtractSpaces, Implements, Pattern, PatternApplyStyle, PatternAs, Spaceable,
};
use crate::blankspace::{space0_before_optional_after, space0_e, spaces, spaces_before};
use crate::ident::{lowercase_ident, parse_ident, Accessor, Ident}; use crate::ident::{lowercase_ident, parse_ident, Accessor, Ident};
use crate::keyword; use crate::keyword;
use crate::parser::{ use crate::parser::{
self, backtrackable, byte, collection_trailing_sep_e, fail_when, loc, map, optional, self, backtrackable, byte, collection_trailing_sep_e, fail_when, loc, map, map_with_arena,
skip_first, specialize_err, specialize_err_ref, then, three_bytes, two_bytes, zero_or_more, optional, skip_first, skip_second, specialize_err, specialize_err_ref, then, three_bytes,
EPattern, PInParens, PList, PRecord, Parser, two_bytes, zero_or_more, EPattern, PInParens, PList, PRecord, Parser,
}; };
use crate::parser::{either, Progress::*}; use crate::parser::{either, Progress::*};
use crate::state::State; use crate::state::State;
@ -85,15 +87,16 @@ fn loc_pattern_help_help<'a>(
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> { ) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!( one_of!(
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()), specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
loc(string_like_pattern_help()),
loc(number_pattern_help()),
loc(underscore_pattern_help()), loc(underscore_pattern_help()),
loc_ident_pattern_help(can_have_arguments),
loc(specialize_err( loc(specialize_err(
EPattern::Record, EPattern::Record,
crate::pattern::record_pattern_help() crate::pattern::record_pattern_help()
)), )),
loc(specialize_err(EPattern::List, list_pattern_help())), loc(specialize_err(EPattern::List, list_pattern_help())),
loc(number_pattern_help()), // Move my pnc tag apply parser here
loc(string_like_pattern_help()), loc_ident_pattern_help(can_have_arguments),
) )
} }
@ -121,10 +124,6 @@ fn pattern_as<'a>() -> impl Parser<'a, PatternAs<'a>, EPattern<'a>> {
} }
} }
fn loc_tag_pattern_args_help<'a>() -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more(loc_tag_pattern_arg(false))
}
/// Like `loc_tag_pattern_args_help`, but stops if a "implements" keyword is seen (indicating an ability). /// Like `loc_tag_pattern_args_help`, but stops if a "implements" keyword is seen (indicating an ability).
fn loc_type_def_tag_pattern_args_help<'a>( fn loc_type_def_tag_pattern_args_help<'a>(
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> { ) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
@ -339,6 +338,37 @@ fn loc_ident_pattern_help<'a>(
let (_, loc_ident, state) = specialize_err(|_, pos| EPattern::Start(pos), loc(parse_ident)) let (_, loc_ident, state) = specialize_err(|_, pos| EPattern::Start(pos), loc(parse_ident))
.parse(arena, state, min_indent)?; .parse(arena, state, min_indent)?;
let commas_and_paren_args_help = map_with_arena(
collection_trailing_sep_e(
byte(b'(', EPattern::ParenStart),
loc_tag_pattern_arg(false),
byte(b',', EPattern::NotAPattern),
byte(b')', EPattern::ParenEnd),
Pattern::SpaceBefore,
),
|arena, args| {
let mut args_vec = Vec::new_in(arena);
for arg in args.iter() {
let a: &Loc<Pattern<'a>> = arena.alloc(arg);
args_vec.push(*a);
}
(
args_vec.into_bump_slice(),
PatternApplyStyle::ParensAndCommas,
)
},
);
let whitespace_args =
map_with_arena(loc_type_def_tag_pattern_args_help(), |arena, args| {
let mut args_vec = Vec::new_in(arena);
for arg in args.iter() {
let a: &Loc<Pattern<'a>> = arena.alloc(arg);
args_vec.push(*a);
}
(args_vec.into_bump_slice(), PatternApplyStyle::Whitespace)
});
match loc_ident.value { match loc_ident.value {
Ident::Tag(tag) => { Ident::Tag(tag) => {
let loc_tag = Loc { let loc_tag = Loc {
@ -347,24 +377,29 @@ fn loc_ident_pattern_help<'a>(
}; };
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)` // Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
if can_have_arguments { let (_, (args, style), state) = if can_have_arguments {
let (_, loc_args, state) = one_of!(commas_and_paren_args_help, whitespace_args)
loc_type_def_tag_pattern_args_help().parse(arena, state, min_indent)?; .parse(arena, state, min_indent)?
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, Loc { region, value }, state))
}
} else { } else {
match commas_and_paren_args_help.parse(arena, state.clone(), min_indent) {
Ok((_, res, new_state)) => (MadeProgress, res, new_state),
Err((NoProgress, _)) => return Ok((MadeProgress, loc_tag, state)),
Err((MadeProgress, e)) => return Err((MadeProgress, e)),
}
};
let loc_args: &[Loc<Pattern<'_>>] = { args };
if loc_args.is_empty() {
Ok((MadeProgress, loc_tag, state)) 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, style);
Ok((MadeProgress, Loc { region, value }, state))
} }
} }
Ident::OpaqueRef(name) => { Ident::OpaqueRef(name) => {
@ -374,24 +409,25 @@ fn loc_ident_pattern_help<'a>(
}; };
// Make sure `@Foo Bar 1` is parsed as `@Foo (Bar) 1`, and not `@Foo (Bar 1)` // Make sure `@Foo Bar 1` is parsed as `@Foo (Bar) 1`, and not `@Foo (Bar 1)`
if can_have_arguments { let (_, (args, style), state) = if can_have_arguments {
let (_, loc_args, state) = one_of!(commas_and_paren_args_help, whitespace_args)
loc_tag_pattern_args_help().parse(arena, state, min_indent)?; .parse(arena, state, min_indent)?
if loc_args.is_empty() {
Ok((MadeProgress, loc_pat, 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_pat), loc_args.into_bump_slice());
Ok((MadeProgress, Loc { region, value }, state))
}
} else { } else {
commas_and_paren_args_help.parse(arena, state, min_indent)?
};
let loc_args: &[Loc<Pattern<'_>>] = { args };
if loc_args.is_empty() {
Ok((MadeProgress, loc_pat, state)) Ok((MadeProgress, loc_pat, 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_pat), loc_args, style);
Ok((MadeProgress, Loc { region, value }, state))
} }
} }
Ident::Access { Ident::Access {
@ -589,3 +625,90 @@ fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>>
} }
} }
} }
pub fn pattern_end<'a>() -> impl Parser<'a, (), EPattern<'a>> {
|_arena, state: State<'a>, _min_indent: u32| {
if state.has_reached_end() {
Ok((NoProgress, (), state))
} else {
Err((NoProgress, EPattern::End(state.pos())))
}
}
}
pub fn test_parse_pattern<'a>(
min_indent: u32,
arena: &'a bumpalo::Bump,
state: State<'a>,
) -> Result<Loc<Pattern<'a>>, EPattern<'a>> {
let parser = skip_second(
space0_before_optional_after(
loc_pattern_help(),
EPattern::IndentStart,
EPattern::IndentEnd,
),
pattern_end(),
);
match parser.parse(arena, state, min_indent) {
Ok((_, expression, _)) => Ok(expression),
Err((_, fail)) => Err(fail),
}
}
#[cfg(test)]
mod test_parse_pattern {
use super::*;
use pretty_assertions::assert_eq;
use roc_region::all::Position;
fn new_region(start: u32, end: u32) -> Region {
Region::new(Position::new(start), Position::new(end))
}
#[test]
fn test_parse_ident_tag() {
let arena = Bump::new();
let state = State::new("Ok a".as_ref());
let min_indent = 0;
let (_, res, _) = loc_pattern_help().parse(&arena, state, min_indent).unwrap();
let expected_tag = Loc {
value: Pattern::Tag("Ok"),
region: new_region(0, 2),
};
let expected_args = [Loc {
value: Pattern::Identifier { ident: "a" },
region: new_region(3, 4),
}];
let expected = Loc {
value: Pattern::Apply(&expected_tag, &expected_args, PatternApplyStyle::Whitespace),
region: new_region(0, 4),
};
assert_eq!(format!("{res:#?}"), format!("{expected:#?}"));
}
#[test]
fn test_parse_ident_tag_pnc() {
let arena = Bump::new();
let state = State::new("Ok(a)".as_ref());
let min_indent = 0;
let (_, res, _) = loc_pattern_help().parse(&arena, state, min_indent).unwrap();
let expected_tag = Loc {
value: Pattern::Tag("Ok"),
region: new_region(0, 2),
};
let expected_args = [Loc {
value: Pattern::Identifier { ident: "a" },
region: new_region(3, 4),
}];
let expected = Loc {
value: Pattern::Apply(
&expected_tag,
&expected_args,
PatternApplyStyle::ParensAndCommas,
),
region: new_region(0, 4),
};
assert_eq!(format!("{res:#?}"), format!("{expected:#?}"));
}
}

View file

@ -1,6 +1,7 @@
use crate::ast; use crate::ast;
use crate::ast::Defs; use crate::ast::Defs;
use crate::ast::Header; use crate::ast::Header;
use crate::ast::Pattern;
use crate::ast::SpacesBefore; use crate::ast::SpacesBefore;
use crate::header::parse_module_defs; use crate::header::parse_module_defs;
use crate::parser::SourceError; use crate::parser::SourceError;
@ -32,6 +33,18 @@ pub fn parse_loc_with<'a>(
} }
} }
pub fn parse_pattern_with<'a>(
arena: &'a Bump,
input: &'a str,
) -> Result<Loc<Pattern<'a>>, SourceError<'a, SyntaxError<'a>>> {
let state = State::new(input.as_bytes());
match crate::pattern::test_parse_pattern(0, arena, state.clone()) {
Ok(loc_patt) => Ok(loc_patt),
Err(fail) => Err(SyntaxError::Pattern(fail).into_source_error(&state)),
}
}
pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Defs<'a>, SyntaxError<'a>> { pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Defs<'a>, SyntaxError<'a>> {
let state = State::new(input.as_bytes()); let state = State::new(input.as_bytes());

View file

@ -10,6 +10,8 @@
//! //!
//! Note that `--release` is important, as this tool is very slow in debug mode. //! Note that `--release` is important, as this tool is very slow in debug mode.
use std::io::Read;
use test_syntax::{ use test_syntax::{
minimize::{print_minimizations, Options}, minimize::{print_minimizations, Options},
test_helpers::InputKind, test_helpers::InputKind,
@ -18,7 +20,7 @@ use test_syntax::{
fn main() { fn main() {
let args = std::env::args().collect::<Vec<String>>(); let args = std::env::args().collect::<Vec<String>>();
if args.len() < 3 || args.len() > 5 { if args.len() < 3 || args.len() > 5 {
eprintln!("Usage: {} [--minimize-full-error] [--minimize-initial-parse-error] [expr|full|moduledefs|header] <input>", args[0]); eprintln!("Usage: {} [--minimize-full-error] [--minimize-initial-parse-error] [expr|full|moduledefs|header|pattern] <input>", args[0]);
std::process::exit(1); std::process::exit(1);
} }
@ -46,13 +48,21 @@ fn main() {
"full" => InputKind::Full, "full" => InputKind::Full,
"moduledefs" => InputKind::ModuleDefs, "moduledefs" => InputKind::ModuleDefs,
"header" => InputKind::Header, "header" => InputKind::Header,
"pattern" => InputKind::Pattern,
_ => { _ => {
eprintln!("Invalid input kind: {}", args[index]); eprintln!("Invalid input kind: {}", args[index]);
std::process::exit(1); std::process::exit(1);
} }
}; };
let input = &args[index + 1];
let mut buf = String::new();
let text = if input == "-" {
std::io::stdin().read_to_string(&mut buf).unwrap();
buf.to_string()
} else {
std::fs::read_to_string(input).unwrap()
};
let text = std::fs::read_to_string(&args[index + 1]).unwrap();
let found_error = print_minimizations(&text, options); let found_error = print_minimizations(&text, options);
std::process::exit(if found_error { 0 } else { 1 }); std::process::exit(if found_error { 0 } else { 1 });
} }

View file

@ -9,13 +9,13 @@ use roc_error_macros::set_panic_not_exit;
use roc_fmt::{annotation::Formattable, header::fmt_header, MigrationFlags}; use roc_fmt::{annotation::Formattable, header::fmt_header, MigrationFlags};
use roc_module::ident::QualifiedModuleName; use roc_module::ident::QualifiedModuleName;
use roc_module::symbol::{IdentIds, Interns, ModuleIds, PackageModuleIds, Symbol}; use roc_module::symbol::{IdentIds, Interns, ModuleIds, PackageModuleIds, Symbol};
use roc_parse::ast::RecursiveValueDefIter;
use roc_parse::ast::ValueDef; use roc_parse::ast::ValueDef;
use roc_parse::ast::{Pattern, RecursiveValueDefIter};
use roc_parse::header::parse_module_defs; use roc_parse::header::parse_module_defs;
use roc_parse::parser::Parser; use roc_parse::parser::Parser;
use roc_parse::parser::SyntaxError; use roc_parse::parser::SyntaxError;
use roc_parse::state::State; use roc_parse::state::State;
use roc_parse::test_helpers::parse_loc_with; use roc_parse::test_helpers::{parse_loc_with, parse_pattern_with};
use roc_parse::{ast::Malformed, normalize::Normalize}; use roc_parse::{ast::Malformed, normalize::Normalize};
use roc_parse::{ use roc_parse::{
ast::{Defs, Expr, FullAst, Header, SpacesBefore}, ast::{Defs, Expr, FullAst, Header, SpacesBefore},
@ -45,6 +45,9 @@ pub enum Input<'a> {
/// Both the header and the module defs /// Both the header and the module defs
Full(&'a str), Full(&'a str),
/// A single pattern
Pattern(&'a str),
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -53,6 +56,7 @@ pub enum InputKind {
ModuleDefs, ModuleDefs,
Expr, Expr,
Full, Full,
Pattern,
} }
impl InputKind { impl InputKind {
@ -62,6 +66,7 @@ impl InputKind {
InputKind::ModuleDefs => Input::ModuleDefs(text), InputKind::ModuleDefs => Input::ModuleDefs(text),
InputKind::Expr => Input::Expr(text), InputKind::Expr => Input::Expr(text),
InputKind::Full => Input::Full(text), InputKind::Full => Input::Full(text),
InputKind::Pattern => Input::Pattern(text),
} }
} }
} }
@ -73,6 +78,7 @@ pub enum InputOwned {
ModuleDefs(String), ModuleDefs(String),
Expr(String), Expr(String),
Full(String), Full(String),
Pattern(String),
} }
impl InputOwned { impl InputOwned {
@ -82,6 +88,7 @@ impl InputOwned {
InputOwned::ModuleDefs(s) => Input::ModuleDefs(s), InputOwned::ModuleDefs(s) => Input::ModuleDefs(s),
InputOwned::Expr(s) => Input::Expr(s), InputOwned::Expr(s) => Input::Expr(s),
InputOwned::Full(s) => Input::Full(s), InputOwned::Full(s) => Input::Full(s),
InputOwned::Pattern(s) => Input::Pattern(s),
} }
} }
} }
@ -96,6 +103,8 @@ pub enum Output<'a> {
Expr(Loc<Expr<'a>>), Expr(Loc<Expr<'a>>),
Full(FullAst<'a>), Full(FullAst<'a>),
Pattern(Loc<Pattern<'a>>),
} }
impl<'a> Output<'a> { impl<'a> Output<'a> {
@ -127,6 +136,10 @@ impl<'a> Output<'a> {
buf.fmt_end_of_file(); buf.fmt_end_of_file();
InputOwned::Full(buf.as_str().to_string()) InputOwned::Full(buf.as_str().to_string())
} }
Output::Pattern(patt) => {
patt.format(&mut buf, 0);
InputOwned::Pattern(buf.as_str().to_string())
}
} }
} }
@ -136,6 +149,7 @@ impl<'a> Output<'a> {
Output::ModuleDefs(defs) => format!("{defs:#?}\n"), Output::ModuleDefs(defs) => format!("{defs:#?}\n"),
Output::Expr(expr) => format!("{expr:#?}\n"), Output::Expr(expr) => format!("{expr:#?}\n"),
Output::Full { .. } => format!("{self:#?}\n"), Output::Full { .. } => format!("{self:#?}\n"),
Output::Pattern(patt) => format!("{patt:#?}\n"),
} }
} }
@ -150,6 +164,7 @@ impl<'a> Output<'a> {
Output::Full(_) => { Output::Full(_) => {
// TODO: canonicalize full ast // TODO: canonicalize full ast
} }
Output::Pattern(_) => {}
Output::Expr(loc_expr) => { Output::Expr(loc_expr) => {
let mut var_store = VarStore::default(); let mut var_store = VarStore::default();
let mut imported: Vec<(QualifiedModuleName, Region)> = vec![]; let mut imported: Vec<(QualifiedModuleName, Region)> = vec![];
@ -248,6 +263,7 @@ impl<'a> Malformed for Output<'a> {
Output::ModuleDefs(defs) => defs.is_malformed(), Output::ModuleDefs(defs) => defs.is_malformed(),
Output::Expr(expr) => expr.is_malformed(), Output::Expr(expr) => expr.is_malformed(),
Output::Full(full) => full.is_malformed(), Output::Full(full) => full.is_malformed(),
Output::Pattern(patt) => patt.is_malformed(),
} }
} }
} }
@ -259,6 +275,7 @@ impl<'a> Normalize<'a> for Output<'a> {
Output::ModuleDefs(defs) => Output::ModuleDefs(defs.normalize(arena)), Output::ModuleDefs(defs) => Output::ModuleDefs(defs.normalize(arena)),
Output::Expr(expr) => Output::Expr(expr.normalize(arena)), Output::Expr(expr) => Output::Expr(expr.normalize(arena)),
Output::Full(full) => Output::Full(full.normalize(arena)), Output::Full(full) => Output::Full(full.normalize(arena)),
Output::Pattern(patt) => Output::Pattern(patt.normalize(arena)),
} }
} }
} }
@ -270,6 +287,7 @@ impl<'a> Input<'a> {
Input::ModuleDefs(s) => s, Input::ModuleDefs(s) => s,
Input::Expr(s) => s, Input::Expr(s) => s,
Input::Full(s) => s, Input::Full(s) => s,
Input::Pattern(s) => s,
} }
} }
@ -308,6 +326,11 @@ impl<'a> Input<'a> {
Ok(Output::Full(FullAst { header, defs })) Ok(Output::Full(FullAst { header, defs }))
} }
Input::Pattern(input) => {
let patt = parse_pattern_with(arena, input).map_err(|e| e.problem)?;
Ok(Output::Pattern(patt))
}
} }
} }
@ -325,9 +348,11 @@ impl<'a> Input<'a> {
let arena = Bump::new(); let arena = Bump::new();
let actual = self.parse_in(&arena).unwrap_or_else(|err| { let actual = self.parse_in(&arena).unwrap_or_else(|err| {
panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:?}\n\n", self.as_str(), err); panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:#?}\n\n", self.as_str(), err);
}); });
println!("Actual {actual:#?}");
let output = actual.format(); let output = actual.format();
handle_formatted_output(output.as_ref()); handle_formatted_output(output.as_ref());

View file

@ -21,6 +21,7 @@
"E", "E",
), ),
[], [],
Whitespace,
), ),
ann_type: @2-3 Apply( ann_type: @2-3 Apply(
"", "",

View file

@ -26,6 +26,7 @@
ident: "x", ident: "x",
}, },
], ],
Whitespace,
), ),
ann_type: @11-25 TagUnion { ann_type: @11-25 TagUnion {
ext: None, ext: None,
@ -54,6 +55,7 @@
ident: "x", ident: "x",
}, },
], ],
Whitespace,
), ),
body_expr: @37-46 Apply( body_expr: @37-46 Apply(
@37-43 Tag( @37-43 Tag(

View file

@ -0,0 +1,16 @@
@0-4 SpaceAfter(
Apply(
@0-2 Tag(
"Ok",
),
[
@3-4 Identifier {
ident: "a",
},
],
ParensAndCommas,
),
[
Newline,
],
)

View file

@ -0,0 +1,16 @@
@0-4 SpaceAfter(
Apply(
@0-2 Tag(
"Ok",
),
[
@3-4 Identifier {
ident: "a",
},
],
Whitespace,
),
[
Newline,
],
)

View file

@ -27,6 +27,7 @@
ident: "inner", ident: "inner",
}, },
], ],
Whitespace,
), ),
PatternAs { PatternAs {
spaces_before: [], spaces_before: [],

View file

@ -38,6 +38,7 @@
}, },
), ),
], ],
Whitespace,
), ),
], ],
@17-18 SpaceBefore( @17-18 SpaceBefore(

View file

@ -20,6 +20,7 @@
], ],
), ),
], ],
ParensAndCommas,
), ),
], ],
@11-13 Num( @11-13 Num(

View file

@ -16,6 +16,7 @@
], ],
), ),
], ],
Whitespace,
), ),
], ],
@8-10 UnaryOp( @8-10 UnaryOp(

View file

@ -19,6 +19,7 @@
], ],
), ),
], ],
Whitespace,
), ),
], ],
@10-12 Var { @10-12 Var {

View file

@ -16,7 +16,7 @@
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Apply( @0-10 Apply(
@0-1 NumLiteral( @0-1 NumLiteral(
"1", "1",
), ),
@ -35,6 +35,7 @@
], ],
), ),
], ],
ParensAndCommas,
), ),
@11-13 BoundVariable( @11-13 BoundVariable(
"gi", "gi",

View file

@ -1,5 +1,5 @@
1 1(
(0 # 0( #
0) 0,),)
f : f f : f
t t

View file

@ -17,37 +17,44 @@
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Apply( @0-12 Apply(
@0-1 NumLiteral( @0-12 Apply(
"1", @0-1 NumLiteral(
), "1",
[ ),
@2-11 SpaceAfter( [
Apply( @2-11 SpaceAfter(
@3-4 NumLiteral( Apply(
"0", @3-4 NumLiteral(
"0",
),
[
@7-8 SpaceBefore(
NumLiteral(
"0",
),
[
LineComment(
"",
),
],
),
],
ParensAndCommas,
), ),
[ [
@7-8 SpaceBefore( Newline,
NumLiteral(
"0",
),
[
LineComment(
"",
),
],
),
], ],
), ),
[ ],
Newline, ParensAndCommas,
], ),
), [
@12-13 Identifier { @12-13 Identifier {
ident: "f", ident: "f",
}, },
], ],
Whitespace,
), ),
@14-15 BoundVariable( @14-15 BoundVariable(
"f", "f",

View file

@ -16,39 +16,46 @@
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Apply( @0-15 Apply(
@0-1 NumLiteral( @0-15 Apply(
"1", @0-1 NumLiteral(
), "1",
[ ),
@2-13 SpaceAfter( [
Apply( @2-13 SpaceAfter(
@2-4 Identifier { Apply(
ident: "ts", @2-4 Identifier {
}, ident: "ts",
[ },
@5-9 SpaceAfter( [
NumLiteral( @5-9 SpaceAfter(
"0", NumLiteral(
), "0",
[
Newline,
Newline,
LineComment(
"",
), ),
], [
), Newline,
Newline,
LineComment(
"",
),
],
),
],
ParensAndCommas,
),
[
Newline,
], ],
), ),
[ ],
Newline, ParensAndCommas,
], ),
), [
@15-16 Identifier { @15-16 Identifier {
ident: "f", ident: "f",
}, },
], ],
Whitespace,
), ),
@17-20 BoundVariable( @17-20 BoundVariable(
"i7f", "i7f",

View file

@ -34,6 +34,7 @@
ident: "f", ident: "f",
}, },
], ],
Whitespace,
), ),
@14-15 BoundVariable( @14-15 BoundVariable(
"f", "f",

View file

@ -26,6 +26,7 @@
ident: "str", ident: "str",
}, },
], ],
Whitespace,
), ),
@12-36 Apply( @12-36 Apply(
@12-17 Tag( @12-17 Tag(

View file

@ -35,6 +35,7 @@
ident: "e", ident: "e",
}, },
], ],
Whitespace,
), ),
@8-14 Apply( @8-14 Apply(
@9-10 SpaceAfter( @9-10 SpaceAfter(
@ -52,6 +53,7 @@
ident: "p", ident: "p",
}, },
], ],
Whitespace,
), ),
], ],
), ),

View file

@ -1,11 +1,11 @@
@0-22 SpaceAfter( @0-23 SpaceAfter(
Defs( Defs(
Defs { Defs {
tags: [ tags: [
EitherIndex(0), EitherIndex(0),
], ],
regions: [ regions: [
@0-20, @0-21,
], ],
space_before: [ space_before: [
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
@ -19,23 +19,23 @@
header: TypeHeader { header: TypeHeader {
name: @0-1 "C", name: @0-1 "C",
vars: [ vars: [
@2-3 SpaceAfter( @2-3 NumLiteral(
NumLiteral( "4",
"4",
),
[
LineComment(
"",
),
],
), ),
], ],
}, },
loc_implements: @6-16 Implements, loc_implements: @7-17 SpaceBefore(
Implements,
[
LineComment(
"",
),
],
),
members: [ members: [
AbilityMember { AbilityMember {
name: @17-18 "e", name: @18-19 "e",
typ: @19-20 BoundVariable( typ: @20-21 BoundVariable(
"m", "m",
), ),
}, },
@ -44,7 +44,7 @@
], ],
value_defs: [], value_defs: [],
}, },
@21-22 SpaceBefore( @22-23 SpaceBefore(
Tag( Tag(
"C", "C",
), ),

View file

@ -0,0 +1,3 @@
C 4 #
implements e:m
C

View file

@ -33,6 +33,7 @@
], ],
), ),
], ],
Whitespace,
), ),
@15-16 BoundVariable( @15-16 BoundVariable(
"s", "s",

View file

@ -1,6 +1,6 @@
1 1(
(0 # 0 #
f) f,)
(0 # (0 #
f) : f f) : f
e e

View file

@ -17,28 +17,34 @@
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Apply( @0-9 Apply(
@0-1 NumLiteral( @0-9 Apply(
"1", @0-1 NumLiteral(
), "1",
[ ),
@2-8 Apply( [
@3-4 SpaceAfter( @2-8 Apply(
NumLiteral( @3-4 SpaceAfter(
"0", NumLiteral(
"0",
),
[
LineComment(
"",
),
],
), ),
[ [
LineComment( @7-8 Identifier {
"", ident: "f",
), },
], ],
Whitespace,
), ),
[ ],
@7-8 Identifier { ParensAndCommas,
ident: "f", ),
}, [
],
),
@10-16 Apply( @10-16 Apply(
@11-12 SpaceAfter( @11-12 SpaceAfter(
NumLiteral( NumLiteral(
@ -55,8 +61,10 @@
ident: "f", ident: "f",
}, },
], ],
Whitespace,
), ),
], ],
Whitespace,
), ),
@18-19 BoundVariable( @18-19 BoundVariable(
"f", "f",

View file

@ -32,6 +32,7 @@
], ],
), ),
], ],
Whitespace,
), ),
[ [
Newline, Newline,

View file

@ -34,6 +34,7 @@
), ),
), ),
], ],
Whitespace,
), ),
@12-13 Apply( @12-13 Apply(
"", "",

View file

@ -42,12 +42,14 @@
), ),
), ),
], ],
Whitespace,
), ),
[ [
@12-13 Identifier { @12-13 Identifier {
ident: "f", ident: "f",
}, },
], ],
Whitespace,
), ),
@14-15 Apply( @14-15 Apply(
"", "",

View file

@ -33,8 +33,10 @@
"0", "0",
), ),
], ],
Whitespace,
), ),
], ],
Whitespace,
), ),
@15-16 BoundVariable( @15-16 BoundVariable(
"f", "f",

View file

@ -36,6 +36,7 @@
"2", "2",
), ),
], ],
Whitespace,
), ),
@12-13 Apply( @12-13 Apply(
"", "",

View file

@ -1,7 +1,6 @@
-( -(
0 0(
( 1
1 d,
d )
)
) )

View file

@ -5,44 +5,42 @@
"0", "0",
), ),
[ [
@4-7 ParensAround( @4-7 Defs(
Defs( Defs {
Defs { tags: [
tags: [ EitherIndex(2147483648),
EitherIndex(2147483648), ],
], regions: [
regions: [ @4-5,
@4-5, ],
], space_before: [
space_before: [ Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 }, ],
], space_after: [
space_after: [ Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 }, ],
], spaces: [],
spaces: [], type_defs: [],
type_defs: [], value_defs: [
value_defs: [ Stmt(
Stmt( @4-5 Num(
@4-5 Num( "1",
"1",
),
), ),
], ),
],
},
@6-7 SpaceBefore(
Var {
module_name: "",
ident: "d",
}, },
@6-7 SpaceBefore( [
Var { Newline,
module_name: "", ],
ident: "d",
},
[
Newline,
],
),
), ),
), ),
], ],
Space, ParensAndCommas,
), ),
), ),
@0-1 Negate, @0-1 Negate,

View file

@ -32,6 +32,7 @@
], ],
), ),
], ],
Whitespace,
), ),
], ],
@14-15 Var { @14-15 Var {

View file

@ -25,6 +25,7 @@
ident: "x", ident: "x",
}, },
], ],
Whitespace,
), ),
@7-8 BoundVariable( @7-8 BoundVariable(
"i", "i",

View file

@ -23,6 +23,7 @@
], ],
), ),
], ],
Whitespace,
), ),
[ [
Newline, Newline,

View file

@ -26,6 +26,7 @@
ident: "it", ident: "it",
}, },
], ],
Whitespace,
), ),
@12-22 Apply( @12-22 Apply(
@12-14 Var { @12-14 Var {

View file

@ -20,6 +20,7 @@
ident: "m", ident: "m",
}, },
], ],
Whitespace,
), ),
[ [
Newline, Newline,

View file

@ -28,6 +28,7 @@
ident: "a", ident: "a",
}, },
], ],
Whitespace,
), ),
], ],
}, },

View file

@ -1,4 +1,3 @@
1 1(i, p) #
(i, p # : f
) : f
n n

View file

@ -16,29 +16,26 @@
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Apply( @0-8 Apply(
@0-1 NumLiteral( @0-1 NumLiteral(
"1", "1",
), ),
[ [
@1-8 Tuple( @2-3 Identifier {
ident: "i",
},
@4-5 SpaceAfter(
Identifier {
ident: "p",
},
[ [
@2-3 Identifier { LineComment(
ident: "i", "",
},
@4-5 SpaceAfter(
Identifier {
ident: "p",
},
[
LineComment(
"",
),
],
), ),
], ],
), ),
], ],
ParensAndCommas,
), ),
@9-10 BoundVariable( @9-10 BoundVariable(
"f", "f",

View file

@ -37,6 +37,7 @@
], ],
), ),
], ],
Whitespace,
), ),
@7-8 BoundVariable( @7-8 BoundVariable(
"s", "s",

View file

@ -23,6 +23,7 @@
}, },
), ),
], ],
Whitespace,
), ),
[ [
Newline, Newline,

View file

@ -43,11 +43,13 @@
ident: "ry", ident: "ry",
}, },
], ],
Whitespace,
), ),
@47-48 Underscore( @47-48 Underscore(
"", "",
), ),
], ],
Whitespace,
), ),
[ [
Newline, Newline,

View file

@ -32,6 +32,7 @@
), ),
), ),
], ],
Whitespace,
), ),
], ],
}, },

View file

@ -37,6 +37,7 @@
ident: "a", ident: "a",
}, },
], ],
Whitespace,
), ),
@10-11 BoundVariable( @10-11 BoundVariable(
"t", "t",

View file

@ -45,6 +45,7 @@
"K", "K",
), ),
], ],
Whitespace,
), ),
[ [
LineComment( LineComment(

View file

@ -29,6 +29,7 @@
ident: "code", ident: "code",
}, },
], ],
Whitespace,
), ),
@30-33 Var { @30-33 Var {
module_name: "", module_name: "",

View file

@ -29,6 +29,7 @@
ident: "code", ident: "code",
}, },
], ],
Whitespace,
), ),
@29-32 Var { @29-32 Var {
module_name: "", module_name: "",

View file

@ -1,2 +1,2 @@
1 (0 0) f : f 1(0(0)) f : f
i i

View file

@ -17,30 +17,37 @@
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Annotation( Annotation(
@0-1 Apply( @0-10 Apply(
@0-1 NumLiteral( @0-10 Apply(
"1", @0-1 NumLiteral(
"1",
),
[
@2-9 Apply(
@3-4 NumLiteral(
"0",
),
[
@5-6 SpaceAfter(
NumLiteral(
"0",
),
[
Newline,
],
),
],
ParensAndCommas,
),
],
ParensAndCommas,
), ),
[ [
@2-9 Apply(
@3-4 NumLiteral(
"0",
),
[
@5-6 SpaceAfter(
NumLiteral(
"0",
),
[
Newline,
],
),
],
),
@10-11 Identifier { @10-11 Identifier {
ident: "f", ident: "f",
}, },
], ],
Whitespace,
), ),
@12-13 BoundVariable( @12-13 BoundVariable(
"f", "f",

View file

@ -28,6 +28,7 @@
), ),
), ),
], ],
Whitespace,
), ),
@8-15 Apply( @8-15 Apply(
@8-9 Var { @8-9 Var {

View file

@ -50,6 +50,7 @@
"", "",
), ),
], ],
Whitespace,
), ),
@11-19 Apply( @11-19 Apply(
@11-15 Tag( @11-15 Tag(
@ -79,6 +80,7 @@
ident: "y", ident: "y",
}, },
], ],
Whitespace,
), ),
@31-39 Apply( @31-39 Apply(
@31-35 Tag( @31-35 Tag(
@ -108,6 +110,7 @@
"", "",
), ),
], ],
Whitespace,
), ),
@51-59 Apply( @51-59 Apply(
@51-55 Tag( @51-55 Tag(
@ -161,6 +164,7 @@
"", "",
), ),
], ],
Whitespace,
), ),
@90-98 Apply( @90-98 Apply(
@90-94 Tag( @90-94 Tag(
@ -174,8 +178,10 @@
ident: "y", ident: "y",
}, },
], ],
Whitespace,
), ),
], ],
Whitespace,
), ),
@102-128 Apply( @102-128 Apply(
@102-106 Tag( @102-106 Tag(

View file

@ -23,6 +23,7 @@
[], [],
), ),
], ],
Whitespace,
), ),
[ [
Newline, Newline,

View file

@ -37,6 +37,14 @@ mod test_fmt {
) )
} }
fn pattern_formats_same(input: &str) {
Input::Pattern(input.trim()).check_invariants(
check_formatting(input.trim()),
true,
Some(false),
);
}
fn fmt_module_and_defs<'a>( fn fmt_module_and_defs<'a>(
arena: &Bump, arena: &Bump,
src: &str, src: &str,
@ -3497,6 +3505,33 @@ mod test_fmt {
)); ));
} }
#[test]
fn zero_arg_application_with_parens() {
expr_formats_same(indoc!(
r"
a()
"
));
}
#[test]
fn try_then_application_with_parens() {
expr_formats_same(indoc!(
r"
try something!(arg)
"
));
}
#[test]
fn dbg_then_application_with_parens() {
expr_formats_same(indoc!(
r"
dbg something!(arg)
"
));
}
#[test] #[test]
fn single_line_application_with_parens() { fn single_line_application_with_parens() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
@ -3885,6 +3920,40 @@ mod test_fmt {
)); ));
} }
#[test]
fn multi_line_when_condition_2_pnc() {
expr_formats_same(indoc!(
r"
when
# this is quite complicated
complexFunction(a, b, c)
# Watch out
is
Complex(x, y) ->
simplify(x, y)
Simple(z) ->
z
"
));
}
#[test]
fn anthony_testing() {
expr_formats_same(indoc!(
r"
when alter (Ok value) is
Ok newValue ->
bucket = listGetUnsafe buckets bucketIndex
newData = List.set data (Num.toU64 bucket.dataIndex) (key, newValue)
@Dict { buckets, data: newData, maxBucketCapacity, maxLoadFactor, shifts }
Err Missing ->
removeBucket (@Dict { buckets, data, maxBucketCapacity, maxLoadFactor, shifts }) bucketIndex
"
));
}
#[test] #[test]
fn multi_line_when_condition_3() { fn multi_line_when_condition_3() {
expr_formats_to( expr_formats_to(
@ -6238,4 +6307,24 @@ mod test_fmt {
// " // "
// )); // ));
// } // }
#[test]
fn pattern_tag_apply_with_whitespace_single_arg() {
pattern_formats_same(indoc!("Ok a"));
}
#[test]
fn pattern_tag_apply_with_pnc_single_arg() {
pattern_formats_same(indoc!("Ok(a)"));
}
#[test]
fn pattern_tag_apply_with_whitespace_multi_arg() {
pattern_formats_same(indoc!("Ok a b"));
}
#[test]
fn pattern_tag_apply_with_pnc_multi_arg() {
pattern_formats_same(indoc!("Ok(a, b)"));
}
} }

View file

@ -33,6 +33,9 @@ mod test_snapshots {
(full => $input:expr) => { (full => $input:expr) => {
Input::Full($input) Input::Full($input)
}; };
(pattern => $input:expr) => {
Input::Pattern($input)
};
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -324,6 +327,8 @@ mod test_snapshots {
pass/apply_record_parens_newline_field.expr, pass/apply_record_parens_newline_field.expr,
pass/apply_tag.expr, pass/apply_tag.expr,
pass/apply_tag_pnc.expr, pass/apply_tag_pnc.expr,
pass/apply_tag_single_arg_pnc.pattern,
pass/apply_tag_single_arg_whitespace.pattern,
pass/apply_three_args.expr, pass/apply_three_args.expr,
pass/apply_tuple_ext_parens_ty.expr, pass/apply_tuple_ext_parens_ty.expr,
pass/apply_two_args.expr, pass/apply_two_args.expr,
@ -450,7 +455,7 @@ mod test_snapshots {
pass/if_in_record_field_opt_pat.expr, pass/if_in_record_field_opt_pat.expr,
pass/if_newline_then_negate_else_recordupdater.expr, pass/if_newline_then_negate_else_recordupdater.expr,
pass/if_then_weird_indent.expr, pass/if_then_weird_indent.expr,
pass/implements_after_parens_comment.expr, pass/implements_after_comment_with_newline.expr,
pass/implements_annotation_comment.expr, pass/implements_annotation_comment.expr,
pass/implements_in_pat_after_comment.expr, pass/implements_in_pat_after_comment.expr,
pass/implements_newline_in_fn_ty.expr, pass/implements_newline_in_fn_ty.expr,
@ -859,8 +864,6 @@ mod test_snapshots {
Err(err) => Err(format!("{err:?}")), Err(err) => Err(format!("{err:?}")),
}; };
println!("{:?}", result);
if expect == TestExpectation::Pass { if expect == TestExpectation::Pass {
let tokens = roc_parse::highlight::highlight(&source); let tokens = roc_parse::highlight::highlight(&source);
for token in tokens { for token in tokens {

View file

@ -767,7 +767,7 @@ impl IterTokens for Loc<Pattern<'_>> {
Pattern::Identifier { .. } => onetoken(Token::Variable, region, arena), Pattern::Identifier { .. } => onetoken(Token::Variable, region, arena),
Pattern::Tag(_) => onetoken(Token::Tag, region, arena), Pattern::Tag(_) => onetoken(Token::Tag, region, arena),
Pattern::OpaqueRef(_) => onetoken(Token::Type, region, arena), Pattern::OpaqueRef(_) => onetoken(Token::Type, region, arena),
Pattern::Apply(p1, p2) => (p1.iter_tokens(arena).into_iter()) Pattern::Apply(p1, p2, _) => (p1.iter_tokens(arena).into_iter())
.chain(p2.iter_tokens(arena)) .chain(p2.iter_tokens(arena))
.collect_in(arena), .collect_in(arena),
Pattern::RecordDestructure(ps) => ps.iter_tokens(arena), Pattern::RecordDestructure(ps) => ps.iter_tokens(arena),

View file

@ -2281,7 +2281,9 @@ fn to_pattern_report<'a>(
| EPattern::IndentEnd(pos) | EPattern::IndentEnd(pos)
| EPattern::AsIndentStart(pos) | EPattern::AsIndentStart(pos)
| EPattern::AccessorFunction(pos) | EPattern::AccessorFunction(pos)
| EPattern::RecordUpdaterFunction(pos) => to_unhandled_parse_error_report( | EPattern::RecordUpdaterFunction(pos)
| EPattern::ParenStart(pos)
| EPattern::ParenEnd(pos) => to_unhandled_parse_error_report(
alloc, alloc,
lines, lines,
filename, filename,