mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 04:08:19 +00:00
PNC for Patterns, stabilize formatting
This commit is contained in:
parent
bac165fd99
commit
3b0db07fa1
78 changed files with 789 additions and 332 deletions
|
@ -1745,6 +1745,12 @@ impl<'a> PatternAs<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum PatternApplyStyle {
|
||||
Whitespace,
|
||||
ParensAndCommas,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Pattern<'a> {
|
||||
// Identifier
|
||||
|
@ -1760,7 +1766,11 @@ pub enum Pattern<'a> {
|
|||
|
||||
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
|
||||
/// around the destructured names, e.g. { x ### x does stuff ###, y }
|
||||
|
@ -1841,8 +1851,8 @@ impl<'a> Pattern<'a> {
|
|||
false
|
||||
}
|
||||
}
|
||||
Apply(constructor_x, args_x) => {
|
||||
if let Apply(constructor_y, args_y) = other {
|
||||
Apply(constructor_x, args_x, _) => {
|
||||
if let Apply(constructor_y, args_y, _) = other {
|
||||
let equivalent_args = args_x
|
||||
.iter()
|
||||
.zip(args_y.iter())
|
||||
|
@ -2670,7 +2680,7 @@ impl<'a> Malformed for Pattern<'a> {
|
|||
Identifier{ .. } |
|
||||
Tag(_) |
|
||||
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()),
|
||||
RequiredField(_, pat) => pat.is_malformed(),
|
||||
OptionalField(_, expr) => expr.is_malformed(),
|
||||
|
|
|
@ -2,8 +2,8 @@ use crate::ast::{
|
|||
is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
|
||||
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
|
||||
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
|
||||
ModuleImportParams, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, TryTarget,
|
||||
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
ModuleImportParams, Pattern, PatternApplyStyle, Spaceable, Spaced, Spaces, SpacesBefore,
|
||||
TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
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>(
|
||||
|
@ -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
|
||||
/// pattern later
|
||||
fn loc_term_or_underscore<'a>(
|
||||
fn loc_term_or_closure<'a>(
|
||||
check_for_arrow: CheckForArrow,
|
||||
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
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(
|
||||
EExpr::Closure,
|
||||
closure_help(check_for_arrow)
|
||||
)),
|
||||
loc(crash_kw()),
|
||||
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(),
|
||||
loc_term(),
|
||||
)
|
||||
.trace("term_or_underscore")
|
||||
}
|
||||
|
||||
fn loc_term<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
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(
|
||||
EExpr::Closure,
|
||||
closure_help(check_for_arrow)
|
||||
)),
|
||||
loc(crash_kw()),
|
||||
loc(specialize_err(EExpr::Dbg, dbg_kw())),
|
||||
loc(try_kw()),
|
||||
loc(record_literal_help()),
|
||||
loc(specialize_err(EExpr::List, list_literal_help())),
|
||||
loc(apply_with_pnc()),
|
||||
ident_seq(),
|
||||
fn loc_term<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
map_with_arena(
|
||||
and(
|
||||
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(crash_kw()),
|
||||
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())),
|
||||
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| {
|
||||
let (_, ident, new_state) = ident_seq().parse(arena, state, min_indent)?;
|
||||
|
||||
let (_, args, newer_state) = specialize_err(
|
||||
EExpr::InParens,
|
||||
collection_trailing_sep_e(
|
||||
byte(b'(', EInParens::Open),
|
||||
specialize_err_ref(EInParens::Expr, loc_expr_block(false, true)),
|
||||
byte(b',', EInParens::End),
|
||||
byte(b')', EInParens::End),
|
||||
Expr::SpaceBefore,
|
||||
map_with_arena(
|
||||
specialize_err(
|
||||
EExpr::InParens,
|
||||
loc(collection_trailing_sep_e(
|
||||
byte(b'(', EInParens::Open),
|
||||
specialize_err_ref(EInParens::Expr, loc_expr_block(true)),
|
||||
byte(b',', EInParens::End),
|
||||
byte(b')', EInParens::End),
|
||||
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)?;
|
||||
|
||||
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,
|
||||
))
|
||||
.parse(arena, state, min_indent)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1743,7 +1739,8 @@ fn parse_negated_term<'a>(
|
|||
initial_state: State<'a>,
|
||||
loc_op: Loc<BinOp>,
|
||||
) -> 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 arg = numeric_negate_expression(
|
||||
|
@ -1793,7 +1790,7 @@ fn parse_expr_end<'a>(
|
|||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let parser = skip_first(
|
||||
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) {
|
||||
|
@ -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::Tag(value) => Pattern::Tag(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 value = expr_to_pattern_help(arena, &loc_val.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 });
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -3277,7 +3282,7 @@ fn starts_with_spaces_conservative(value: &Pattern<'_>) -> bool {
|
|||
| Pattern::ListRest(_)
|
||||
| Pattern::OpaqueRef(_) => false,
|
||||
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::RequiredField(_, _) | Pattern::OptionalField(_, _) => false,
|
||||
Pattern::SpaceBefore(_, _) => true,
|
||||
|
@ -3293,6 +3298,7 @@ fn header_to_pat<'a>(arena: &'a Bump, header: TypeHeader<'a>) -> Pattern<'a> {
|
|||
Pattern::Apply(
|
||||
arena.alloc(Loc::at(header.name.region, Pattern::Tag(header.name.value))),
|
||||
header.vars,
|
||||
PatternApplyStyle::Whitespace,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -3343,7 +3349,7 @@ fn pat_ends_with_spaces_conservative(pat: &Pattern<'_>) -> bool {
|
|||
| Pattern::ListRest(_)
|
||||
| Pattern::As(_, _)
|
||||
| Pattern::OpaqueRef(_) => false,
|
||||
Pattern::Apply(_, args) => args
|
||||
Pattern::Apply(_, args, _) => args
|
||||
.last()
|
||||
.map_or(false, |a| pat_ends_with_spaces_conservative(&a.value)),
|
||||
Pattern::RecordDestructure(_) => false,
|
||||
|
@ -3365,7 +3371,7 @@ pub fn join_alias_to_body<'a>(
|
|||
body_expr: &'a Loc<Expr<'a>>,
|
||||
) -> ValueDef<'a> {
|
||||
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 region_ann_pattern = Region::span_across(&loc_name.region, &vars_region);
|
||||
|
|
|
@ -879,9 +879,10 @@ impl<'a> Normalize<'a> for Pattern<'a> {
|
|||
Pattern::Identifier { ident } => Pattern::Identifier { ident },
|
||||
Pattern::Tag(a) => Pattern::Tag(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(b.normalize(arena)),
|
||||
c,
|
||||
),
|
||||
Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.normalize(arena)),
|
||||
Pattern::RequiredField(a, b) => {
|
||||
|
@ -1257,6 +1258,8 @@ impl<'a> Normalize<'a> for EPattern<'a> {
|
|||
EPattern::AccessorFunction(_) => EPattern::AccessorFunction(Position::zero()),
|
||||
EPattern::RecordUpdaterFunction(_) => EPattern::RecordUpdaterFunction(Position::zero()),
|
||||
EPattern::Str(e, _) => EPattern::Str(e.normalize(arena), Position::zero()),
|
||||
EPattern::ParenStart(_) => EPattern::ParenStart(Position::zero()),
|
||||
EPattern::ParenEnd(_) => EPattern::ParenEnd(Position::zero()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1066,6 +1066,9 @@ pub enum EPattern<'a> {
|
|||
AccessorFunction(Position),
|
||||
RecordUpdaterFunction(Position),
|
||||
Str(EString<'a>, Position),
|
||||
|
||||
ParenStart(Position),
|
||||
ParenEnd(Position),
|
||||
}
|
||||
|
||||
impl<'a> EPattern<'a> {
|
||||
|
@ -1090,7 +1093,9 @@ impl<'a> EPattern<'a> {
|
|||
| EPattern::IndentEnd(position)
|
||||
| EPattern::AsIndentStart(position)
|
||||
| EPattern::AccessorFunction(position)
|
||||
| EPattern::RecordUpdaterFunction(position) => Region::from_pos(*position),
|
||||
| EPattern::RecordUpdaterFunction(position)
|
||||
| EPattern::ParenStart(position)
|
||||
| EPattern::ParenEnd(position) => Region::from_pos(*position),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use crate::ast::{Collection, ExtractSpaces, Implements, Pattern, PatternAs, Spaceable};
|
||||
use crate::blankspace::{space0_e, spaces, spaces_before};
|
||||
use crate::ast::{
|
||||
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::keyword;
|
||||
use crate::parser::{
|
||||
self, backtrackable, byte, collection_trailing_sep_e, fail_when, loc, map, optional,
|
||||
skip_first, specialize_err, specialize_err_ref, then, three_bytes, two_bytes, zero_or_more,
|
||||
EPattern, PInParens, PList, PRecord, Parser,
|
||||
self, backtrackable, byte, collection_trailing_sep_e, fail_when, loc, map, map_with_arena,
|
||||
optional, skip_first, skip_second, specialize_err, specialize_err_ref, then, three_bytes,
|
||||
two_bytes, zero_or_more, EPattern, PInParens, PList, PRecord, Parser,
|
||||
};
|
||||
use crate::parser::{either, Progress::*};
|
||||
use crate::state::State;
|
||||
|
@ -85,15 +87,16 @@ fn loc_pattern_help_help<'a>(
|
|||
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
one_of!(
|
||||
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
|
||||
loc(string_like_pattern_help()),
|
||||
loc(number_pattern_help()),
|
||||
loc(underscore_pattern_help()),
|
||||
loc_ident_pattern_help(can_have_arguments),
|
||||
loc(specialize_err(
|
||||
EPattern::Record,
|
||||
crate::pattern::record_pattern_help()
|
||||
)),
|
||||
loc(specialize_err(EPattern::List, list_pattern_help())),
|
||||
loc(number_pattern_help()),
|
||||
loc(string_like_pattern_help()),
|
||||
// Move my pnc tag apply parser here
|
||||
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).
|
||||
fn loc_type_def_tag_pattern_args_help<'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))
|
||||
.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 {
|
||||
Ident::Tag(tag) => {
|
||||
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)`
|
||||
if can_have_arguments {
|
||||
let (_, loc_args, state) =
|
||||
loc_type_def_tag_pattern_args_help().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))
|
||||
}
|
||||
let (_, (args, style), state) = if can_have_arguments {
|
||||
one_of!(commas_and_paren_args_help, whitespace_args)
|
||||
.parse(arena, state, min_indent)?
|
||||
} 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))
|
||||
} 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) => {
|
||||
|
@ -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)`
|
||||
if can_have_arguments {
|
||||
let (_, loc_args, state) =
|
||||
loc_tag_pattern_args_help().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))
|
||||
}
|
||||
let (_, (args, style), state) = if can_have_arguments {
|
||||
one_of!(commas_and_paren_args_help, whitespace_args)
|
||||
.parse(arena, state, min_indent)?
|
||||
} 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))
|
||||
} 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 {
|
||||
|
@ -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:#?}"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::ast;
|
||||
use crate::ast::Defs;
|
||||
use crate::ast::Header;
|
||||
use crate::ast::Pattern;
|
||||
use crate::ast::SpacesBefore;
|
||||
use crate::header::parse_module_defs;
|
||||
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>> {
|
||||
let state = State::new(input.as_bytes());
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue