mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
factor out backtracking in def parsing
This commit is contained in:
parent
438a2e6f83
commit
aac75c6a25
1 changed files with 64 additions and 81 deletions
|
@ -500,38 +500,74 @@ fn equals_for_def<'a>() -> impl Parser<'a, ()> {
|
|||
/// * A type annotation
|
||||
/// * A type annotation followed on the next line by a pattern, an `=`, and an expression
|
||||
pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> {
|
||||
// we should fix this backtracking!
|
||||
attempt(
|
||||
Attempting::Def,
|
||||
map_with_arena!(
|
||||
either!(backtrackable(annotated_body(min_indent)), body(min_indent)),
|
||||
to_def
|
||||
),
|
||||
)
|
||||
let indented_more = min_indent + 1;
|
||||
|
||||
enum DefKind {
|
||||
DefColon,
|
||||
DefEqual,
|
||||
}
|
||||
|
||||
fn to_def<'a>(
|
||||
arena: &'a Bump,
|
||||
ann_body_or_body: Either<AnnotationOrAnnotatedBody<'a>, Body<'a>>,
|
||||
) -> Def<'a> {
|
||||
match ann_body_or_body {
|
||||
Either::First(((ann_pattern, ann_type), None)) => {
|
||||
annotation_or_alias(arena, &ann_pattern.value, ann_pattern.region, ann_type)
|
||||
}
|
||||
Either::First((
|
||||
(ann_pattern, ann_type),
|
||||
Some((opt_comment, (body_pattern, body_expr))),
|
||||
)) => Def::AnnotatedBody {
|
||||
ann_pattern: arena.alloc(ann_pattern),
|
||||
let def_colon_or_equals = one_of![
|
||||
map!(equals_for_def(), |_| DefKind::DefEqual),
|
||||
map!(ascii_char(b':'), |_| DefKind::DefColon)
|
||||
];
|
||||
|
||||
attempt(
|
||||
Attempting::Def,
|
||||
then(
|
||||
backtrackable(and!(pattern(min_indent), def_colon_or_equals)),
|
||||
move |arena, state, _progress, (loc_pattern, def_kind)| match def_kind {
|
||||
DefKind::DefColon => {
|
||||
// Spaces after the ':' (at a normal indentation level) and then the type.
|
||||
// The type itself must be indented more than the pattern and ':'
|
||||
let (_, ann_type, state) =
|
||||
space0_before(type_annotation::located(indented_more), min_indent)
|
||||
.parse(arena, state)?;
|
||||
|
||||
// see if there is a definition (assuming the preceding characters were a type
|
||||
// annotation
|
||||
let (_, opt_rest, state) = optional(and!(
|
||||
spaces_then_comment_or_newline(),
|
||||
body_at_indent(min_indent)
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
||||
let def = match opt_rest {
|
||||
None => annotation_or_alias(
|
||||
arena,
|
||||
&loc_pattern.value,
|
||||
loc_pattern.region,
|
||||
ann_type,
|
||||
),
|
||||
Some((opt_comment, (body_pattern, body_expr))) => Def::AnnotatedBody {
|
||||
ann_pattern: arena.alloc(loc_pattern),
|
||||
ann_type: arena.alloc(ann_type),
|
||||
comment: opt_comment,
|
||||
body_pattern: arena.alloc(body_pattern),
|
||||
body_expr: arena.alloc(body_expr),
|
||||
},
|
||||
Either::Second((body_pattern, body_expr)) => {
|
||||
Def::Body(arena.alloc(body_pattern), arena.alloc(body_expr))
|
||||
};
|
||||
|
||||
Ok((MadeProgress, def, state))
|
||||
}
|
||||
DefKind::DefEqual => {
|
||||
// Spaces after the '=' (at a normal indentation level) and then the expr.
|
||||
// The expr itself must be indented more than the pattern and '='
|
||||
let (_, body_expr, state) = space0_before(
|
||||
loc!(move |arena, state| { parse_expr(indented_more, arena, state) }),
|
||||
min_indent,
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Def::Body(arena.alloc(loc_pattern), arena.alloc(body_expr)),
|
||||
state,
|
||||
))
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// PARSER HELPERS
|
||||
|
@ -540,21 +576,6 @@ fn pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
|||
space0_after(loc_closure_param(min_indent), min_indent)
|
||||
}
|
||||
|
||||
fn annotation<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, (Located<Pattern<'a>>, Located<TypeAnnotation<'a>>)> {
|
||||
let indented_more = min_indent + 1;
|
||||
and!(
|
||||
pattern(min_indent),
|
||||
skip_first!(
|
||||
ascii_char(b':'),
|
||||
// Spaces after the ':' (at a normal indentation level) and then the type.
|
||||
// The type itself must be indented more than the pattern and ':'
|
||||
space0_before(type_annotation::located(indented_more), min_indent)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn spaces_then_comment_or_newline<'a>() -> impl Parser<'a, Option<&'a str>> {
|
||||
skip_first!(
|
||||
zero_or_more!(ascii_char(b' ')),
|
||||
|
@ -570,29 +591,6 @@ fn spaces_then_comment_or_newline<'a>() -> impl Parser<'a, Option<&'a str>> {
|
|||
|
||||
type Body<'a> = (Located<Pattern<'a>>, Located<Expr<'a>>);
|
||||
|
||||
fn body<'a>(min_indent: u16) -> impl Parser<'a, Body<'a>> {
|
||||
let indented_more = min_indent + 1;
|
||||
and!(
|
||||
// this backtrackable is required for the case
|
||||
//
|
||||
// i = 64
|
||||
//
|
||||
// i
|
||||
//
|
||||
// so the final i is not considered the start of a def
|
||||
backtrackable(pattern(min_indent)),
|
||||
skip_first!(
|
||||
equals_for_def(),
|
||||
// Spaces after the '=' (at a normal indentation level) and then the expr.
|
||||
// The expr itself must be indented more than the pattern and '='
|
||||
space0_before(
|
||||
loc!(move |arena, state| { parse_expr(indented_more, arena, state) }),
|
||||
min_indent,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn body_at_indent<'a>(indent_level: u16) -> impl Parser<'a, Body<'a>> {
|
||||
let indented_more = indent_level + 1;
|
||||
and!(
|
||||
|
@ -609,21 +607,6 @@ fn body_at_indent<'a>(indent_level: u16) -> impl Parser<'a, Body<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
type AnnotationOrAnnotatedBody<'a> = (
|
||||
(Located<Pattern<'a>>, Located<TypeAnnotation<'a>>),
|
||||
Option<(Option<&'a str>, Body<'a>)>,
|
||||
);
|
||||
|
||||
fn annotated_body<'a>(min_indent: u16) -> impl Parser<'a, AnnotationOrAnnotatedBody<'a>> {
|
||||
and!(
|
||||
annotation(min_indent),
|
||||
optional(and!(
|
||||
spaces_then_comment_or_newline(),
|
||||
body_at_indent(min_indent)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
fn annotation_or_alias<'a>(
|
||||
arena: &'a Bump,
|
||||
pattern: &Pattern<'a>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue