simplify defs parsing

This commit is contained in:
Kiryl Dziamura 2024-07-01 19:44:32 +02:00
parent de9ed734c5
commit c4b63aa80a
No known key found for this signature in database
GPG key ID: FB539501A4561ACF
2 changed files with 113 additions and 38 deletions

View file

@ -532,6 +532,19 @@ pub fn split_loc_exprs_around<'a>(
(before, after)
}
/// Checks if the bang suffix is applied only at the top level of expression
pub fn is_top_level_suffixed(expr: &Expr) -> bool {
// TODO: should we check BinOps with pizza where the last expression is TaskAwaitBang?
match expr {
Expr::TaskAwaitBang(..) => true,
Expr::Apply(a, _, _) => is_top_level_suffixed(&a.value),
Expr::SpaceBefore(a, _) => is_top_level_suffixed(a),
Expr::SpaceAfter(a, _) => is_top_level_suffixed(a),
_ => false,
}
}
/// Check if the bang suffix is applied recursevely in expression
pub fn is_expr_suffixed(expr: &Expr) -> bool {
match expr {
// expression without arguments, `read!`
@ -1177,7 +1190,7 @@ impl<'a> Defs<'a> {
})
}
pub fn last_value_suffixed(&mut self) -> Option<&'a Loc<Expr<'a>>> {
pub fn pop_last_value(&mut self) -> Option<&'a Loc<Expr<'a>>> {
let last_value_suffix = self
.tags
.iter()
@ -1192,12 +1205,8 @@ impl<'a> Defs<'a> {
..
},
loc_expr,
) if collection.is_empty() && is_expr_suffixed(&loc_expr.value) => {
Some((tag_index, loc_expr))
}
ValueDef::Stmt(loc_expr) if is_expr_suffixed(&loc_expr.value) => {
Some((tag_index, loc_expr))
}
) if collection.is_empty() => Some((tag_index, loc_expr)),
ValueDef::Stmt(loc_expr) => Some((tag_index, loc_expr)),
_ => None,
},
});

View file

@ -1,9 +1,9 @@
use crate::ast::{
is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
ModuleImportParams, Pattern, RecordBuilderField, Spaceable, Spaced, Spaces, TypeAnnotation,
TypeDef, TypeHeader, ValueDef,
is_expr_suffixed, is_top_level_suffixed, AssignedField, Collection, CommentOrNewline, Defs,
Expr, ExtractSpaces, Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword,
ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport,
ModuleImport, ModuleImportParams, Pattern, RecordBuilderField, Spaceable, Spaced, Spaces,
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
};
use crate::blankspace::{
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
@ -372,14 +372,31 @@ fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a
end,
};
parse_expr_end(
match parse_expr_end(
new_min_indent,
options,
expr_state,
arena,
state,
initial_state,
)
) {
Err(err) => Err(err),
Ok((progress, expr, new_state)) => {
// We need to check if we have just parsed a suffixed statement,
// if so, this is a defs node.
if is_top_level_suffixed(&expr) {
let def_region = Region::new(end, new_state.pos());
let value_def = ValueDef::Stmt(arena.alloc(Loc::at(def_region, expr)));
let mut defs = Defs::default();
defs.push_value_def(value_def, def_region, &[], &[]);
return parse_defs_expr(options, min_indent, defs, arena, new_state);
} else {
Ok((progress, expr, new_state))
}
}
}
}
}
})
@ -1134,18 +1151,60 @@ pub fn parse_single_def_assignment<'a>(
parse_def_expr.parse(arena, initial_state, min_indent)?;
let region = Region::span_across(&def_loc_pattern.region, &first_loc_expr.region);
let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), arena.alloc(first_loc_expr));
Ok((
progress_after_first,
Some(SingleDef {
type_or_value: Either::Second(value_def),
// If the expression is actually a suffixed statement, then we need to continue
// to parse the rest of the expression
if is_top_level_suffixed(&first_loc_expr.value) {
let mut defs = Defs::default();
// Take the suffixed value and make it a e.g. Body(`{}=`, Apply(Var(...)))
// we will keep the pattern `def_loc_pattern` for the new Defs
defs.push_value_def(
ValueDef::Stmt(arena.alloc(first_loc_expr)),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state_after_first_expression,
))
spaces_before_current,
&[],
);
// Try to parse the rest of the expression as multiple defs, which may contain sub-assignments
match parse_defs_expr(
options,
min_indent,
defs,
arena,
state_after_first_expression,
) {
Ok((progress_after_rest_of_def, expr, state_after_rest_of_def)) => {
let final_loc_expr = arena.alloc(Loc::at(region, expr));
let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), final_loc_expr);
Ok((
progress_after_rest_of_def,
Some(SingleDef {
type_or_value: Either::Second(value_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state_after_rest_of_def,
))
}
Err((progress, err)) => Err((progress, err)),
}
} else {
let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), arena.alloc(first_loc_expr));
Ok((
progress_after_first,
Some(SingleDef {
type_or_value: Either::Second(value_def),
region,
spaces_before: spaces_before_current,
spaces_after: &[],
}),
state_after_first_expression,
))
}
}
/// e.g. Things that can be on their own line in a def, e.g. `expect`, `expect-fx`, or `dbg`
@ -1378,22 +1437,29 @@ fn parse_defs_expr<'a>(
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
match parse_defs_end(options, min_indent, defs, arena, state) {
Err(bad) => Err(bad),
Ok((_, mut def_state, state)) => {
Ok((_, def_state, state)) => {
// this is no def, because there is no `=` or `:`; parse as an expr
let parse_final_expr = space0_before_e(expr_start(options), EExpr::IndentEnd);
match parse_final_expr.parse(arena, state.clone(), min_indent) {
match space0_before_e(expr_start(options), EExpr::IndentEnd).parse(
arena,
state.clone(),
min_indent,
) {
Err((_, fail)) => {
// If the last def was a suffixed statement, assume this was
// intentional by the application author instead of giving
// an error.
match def_state.last_value_suffixed() {
Some(loc_ret) => Ok((
MadeProgress,
Expr::Defs(arena.alloc(def_state), arena.alloc(loc_ret)),
state,
)),
_ => Err((
let mut def_state = def_state;
match def_state.pop_last_value() {
Some(loc_ret) => {
// If the poped value was the only item in defs - just return it as an expression
if def_state.is_empty() {
Ok((MadeProgress, loc_ret.value, state))
} else {
Ok((
MadeProgress,
Expr::Defs(arena.alloc(def_state), arena.alloc(loc_ret)),
state,
))
}
}
None => Err((
MadeProgress,
EExpr::DefMissingFinalExpr2(arena.alloc(fail), state.pos()),
)),