Relax indentation parsing inside parens, lists, and records

This commit is contained in:
Joshua Warner 2024-12-15 08:36:24 -08:00
parent 1b4f5bbb52
commit 96c5dcb651
No known key found for this signature in database
GPG key ID: 89AD497003F93FDD
7 changed files with 146 additions and 29 deletions

View file

@ -51,7 +51,11 @@ pub fn test_parse_expr<'a>(
state: State<'a>,
) -> Result<Loc<Expr<'a>>, EExpr<'a>> {
let parser = skip_second(
space0_before_optional_after(loc_expr_block(true), EExpr::IndentStart, EExpr::IndentEnd),
space0_before_optional_after(
loc_expr_block(true, false),
EExpr::IndentStart,
EExpr::IndentEnd,
),
expr_end(),
);
@ -78,7 +82,7 @@ pub struct ExprParseOptions {
pub fn expr_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
move |arena, state: State<'a>, min_indent: u32| {
loc_expr(true)
loc_expr(true, false)
.parse(arena, state, min_indent)
.map(|(a, b, c)| (a, b.value, c))
}
@ -88,11 +92,7 @@ fn loc_expr_in_parens_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EInParens<'a>
then(
loc(collection_trailing_sep_e(
byte(b'(', EInParens::Open),
specialize_err_ref(
EInParens::Expr,
// space0_before_e(
loc_expr_block(false),
),
specialize_err_ref(EInParens::Expr, loc_expr_block(false, true)),
byte(b',', EInParens::End),
byte(b')', EInParens::End),
Expr::SpaceBefore,
@ -383,21 +383,27 @@ fn unary_not<'a>() -> impl Parser<'a, (), EExpr<'a>> {
}
/// Entry point for parsing an expression.
fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
fn expr_start<'a>(
options: ExprParseOptions,
allow_any_indent: bool,
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of![
loc(specialize_err(EExpr::If, if_expr_help(options))),
loc(specialize_err(EExpr::When, when::when_expr_help(options))),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(expr_operator_chain(options)),
loc(expr_operator_chain(options, allow_any_indent)),
fail_expr_start_e()
]
.trace("expr_start")
}
/// Parse a chain of expressions separated by operators. Also handles function application.
fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
fn expr_operator_chain<'a>(
options: ExprParseOptions,
allow_any_indent: bool,
) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
(move |arena, state: State<'a>, min_indent: u32| {
parse_expr_operator_chain(arena, state, min_indent, options)
parse_expr_operator_chain(arena, state, min_indent, options, allow_any_indent)
})
.trace("expr_operator_chain")
}
@ -407,6 +413,7 @@ fn parse_expr_operator_chain<'a>(
state: State<'a>,
min_indent: u32,
options: ExprParseOptions,
allow_any_indent: bool,
) -> Result<(Progress, Expr<'a>, State<'a>), (Progress, EExpr<'a>)> {
let line_indent = state.line_indent();
@ -432,7 +439,7 @@ fn parse_expr_operator_chain<'a>(
let mut state = state;
let call_min_indent = line_indent + 1;
let call_min_indent = if allow_any_indent { 0 } else { line_indent + 1 };
loop {
let allow_negate = state.pos() > end;
@ -1675,7 +1682,7 @@ fn parse_stmt_backpassing<'a>(
match expr_to_pattern_help(arena, &call.value) {
Ok(good) => {
let (_, mut ann_type, state) =
expr_start(options).parse(arena, state, call_min_indent)?;
expr_start(options, false).parse(arena, state, call_min_indent)?;
// put the spaces from after the operator in front of the call
if !spaces_after_operator.is_empty() {
@ -1753,7 +1760,7 @@ fn parse_stmt_multi_backpassing<'a>(
match two_bytes(b'<', b'-', EExpr::BackpassArrow).parse(arena, state.clone(), min_indent) {
Err((_, fail)) => Err((MadeProgress, fail)),
Ok((_, _, state)) => {
let parse_body = space0_before_e(expr_start(options), EExpr::IndentEnd);
let parse_body = space0_before_e(expr_start(options, false), EExpr::IndentEnd);
let (_, loc_body, state) = parse_body.parse(arena, state, line_indent + 1)?;
@ -1790,6 +1797,7 @@ fn parse_stmt_assignment<'a>(
|a, _| a.clone(),
spaces_after_operator,
!spaces_after_operator.value.is_empty(),
false,
)?;
let alias =
@ -2065,6 +2073,7 @@ fn parse_ability_def<'a>(
pub fn loc_expr_block<'a>(
accept_multi_backpassing: bool,
allow_any_indent: bool,
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
space0_after_e(
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
@ -2085,6 +2094,7 @@ pub fn loc_expr_block<'a>(
|a, _| a.clone(),
loc_first_space,
true,
allow_any_indent,
)
},
EExpr::IndentEnd,
@ -2092,12 +2102,18 @@ pub fn loc_expr_block<'a>(
.trace("loc_expr_block")
}
pub fn loc_expr<'a>(accept_multi_backpassing: bool) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
pub fn loc_expr<'a>(
accept_multi_backpassing: bool,
allow_any_indent: bool,
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
space0_before_e(
expr_start(ExprParseOptions {
accept_multi_backpassing,
check_for_arrow: true,
}),
expr_start(
ExprParseOptions {
accept_multi_backpassing,
check_for_arrow: true,
},
allow_any_indent,
),
EExpr::IndentEnd,
)
}
@ -2383,7 +2399,7 @@ mod when {
indented_seq_skip_first(
parser::keyword(keyword::WHEN, EWhen::When),
space0_around_e_no_after_indent_check(
specialize_err_ref(EWhen::Condition, expr_start(options)),
specialize_err_ref(EWhen::Condition, expr_start(options, true)),
EWhen::IndentCondition,
)
),
@ -2494,7 +2510,7 @@ mod when {
space0_around_ee(
specialize_err_ref(
EWhen::IfGuard,
increment_min_indent(expr_start(options))
increment_min_indent(expr_start(options, true))
),
EWhen::IndentIfGuard,
EWhen::IndentArrow,
@ -2620,7 +2636,7 @@ fn if_branch<'a>() -> impl Parser<'a, ((Loc<Expr<'a>>, u32), Loc<Expr<'a>>), EIf
and(
and(
space0_around_ee(
specialize_err_ref(EIf::Condition, loc_expr(true)),
specialize_err_ref(EIf::Condition, loc_expr(true, false)),
EIf::IndentCondition,
EIf::IndentThenToken,
)
@ -2796,6 +2812,7 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
EIf::ElseBranch,
loc_first_space,
allow_defs,
false,
)?;
let expr = Expr::If {
@ -2868,6 +2885,7 @@ where
wrap_error,
loc_first_space,
allow_defs,
false,
)
}
@ -2884,6 +2902,7 @@ fn parse_block_inner<'a, E>(
wrap_error: fn(&'a EExpr<'a>, Position) -> E,
first_space: Loc<&'a [CommentOrNewline<'a>]>,
allow_defs: bool,
allow_any_indent: bool,
) -> ParseResult<'a, Loc<Expr<'a>>, E>
where
E: 'a + SpaceProblem,
@ -2922,7 +2941,8 @@ where
Ok((MadeProgress, loc_expr, state))
} else {
let (p2, loc_expr, state) =
specialize_err_ref(wrap_error, expr_start(options)).parse(arena, state, min_indent)?;
specialize_err_ref(wrap_error, expr_start(options, allow_any_indent))
.parse(arena, state, min_indent)?;
let loc_expr = if first_space.value.is_empty() {
loc_expr
@ -3547,7 +3567,7 @@ fn list_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EList<'a>> {
map_with_arena(
collection_trailing_sep_e(
byte(b'[', EList::Open),
specialize_err_ref(EList::Expr, loc_expr(false)),
specialize_err_ref(EList::Expr, loc_expr(false, true)),
byte(b',', EList::End),
byte(b']', EList::End),
Expr::SpaceBefore,
@ -3640,7 +3660,7 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
and(byte(b':', ERecord::Colon), record_field_expr()),
and(
byte(b'?', ERecord::QuestionMark),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false, true))),
),
)),
),
@ -3657,7 +3677,7 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
spaces(),
skip_first(
byte(b':', ERecord::Colon),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false, false))),
),
),
),
@ -3698,7 +3718,10 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
fn record_field_expr<'a>() -> impl Parser<'a, Loc<Expr<'a>>, ERecord<'a>> {
map_with_arena(
and(spaces(), specialize_err_ref(ERecord::Expr, loc_expr(false))),
and(
spaces(),
specialize_err_ref(ERecord::Expr, loc_expr(false, false)),
),
|arena: &'a bumpalo::Bump, (spaces, loc_expr)| {
if spaces.is_empty() {
loc_expr