mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-29 09:23:46 +00:00
Refactor parser methods to not return State as part of ParseError
As previously discovered with #4464, it's easy to accidentally mis-use the State value returned on the Err path. There were mixed assumptions about what that State represents: (1) the State where the error occurred, or (2) the State at the beginning of the thing we were just parsing. I fixed this up to always mean (2) - at which point we don't actually need to return the State at all - so it's impossible for further discrepency to creep in. I also took the liberty to refactor a few more methods to be purely combinator-based, rather than calling `parse` directly.
This commit is contained in:
parent
3cd57e078e
commit
2d9aba2242
17 changed files with 374 additions and 456 deletions
|
@ -3,8 +3,8 @@ use crate::blankspace::{space0_before_e, space0_e};
|
|||
use crate::ident::{lowercase_ident, parse_ident, Ident};
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
backtrackable, optional, specialize, specialize_ref, then, word1, word2, word3, EPattern,
|
||||
PInParens, PList, PRecord, ParseResult, Parser,
|
||||
backtrackable, fail_when, optional, specialize, specialize_ref, then, word1, word2, word3,
|
||||
EPattern, PInParens, PList, PRecord, Parser,
|
||||
};
|
||||
use crate::state::State;
|
||||
use bumpalo::collections::string::String;
|
||||
|
@ -24,15 +24,7 @@ pub enum PatternType {
|
|||
WhenBranch,
|
||||
}
|
||||
|
||||
pub fn loc_closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
parse_closure_param
|
||||
}
|
||||
|
||||
fn parse_closure_param<'a>(
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
min_indent: u32,
|
||||
) -> ParseResult<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
pub fn closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
one_of!(
|
||||
// An ident is the most common param, e.g. \foo -> ...
|
||||
loc_ident_pattern_help(true),
|
||||
|
@ -47,7 +39,6 @@ fn parse_closure_param<'a>(
|
|||
// e.g. \User.UserId userId -> ...
|
||||
specialize(EPattern::PInParens, loc_pattern_in_parens_help())
|
||||
)
|
||||
.parse(arena, state, min_indent)
|
||||
}
|
||||
|
||||
pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
|
@ -88,16 +79,12 @@ fn loc_tag_pattern_arg<'a>(
|
|||
min_indent,
|
||||
)?;
|
||||
|
||||
let (_, loc_pat, state) = loc_parse_tag_pattern_arg(min_indent, arena, state)?;
|
||||
let (_, loc_pat, state) = loc_parse_tag_pattern_arg().parse(arena, state, min_indent)?;
|
||||
|
||||
let Loc { region, value } = loc_pat;
|
||||
|
||||
if stop_on_has_kw && matches!(value, Pattern::Identifier("has")) {
|
||||
Err((
|
||||
NoProgress,
|
||||
EPattern::End(original_state.pos()),
|
||||
original_state,
|
||||
))
|
||||
Err((NoProgress, EPattern::End(original_state.pos())))
|
||||
} else {
|
||||
Ok((
|
||||
MadeProgress,
|
||||
|
@ -119,17 +106,13 @@ pub fn loc_has_parser<'a>() -> impl Parser<'a, Loc<Has<'a>>, EPattern<'a>> {
|
|||
if matches!(pattern.value, Pattern::Identifier("has")) {
|
||||
Ok((progress, Loc::at(pattern.region, Has::Has), state))
|
||||
} else {
|
||||
Err((progress, EPattern::End(state.pos()), state))
|
||||
Err((progress, EPattern::End(state.pos())))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_parse_tag_pattern_arg<'a>(
|
||||
min_indent: u32,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
fn loc_parse_tag_pattern_arg<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
|
||||
one_of!(
|
||||
specialize(EPattern::PInParens, loc_pattern_in_parens_help()),
|
||||
loc!(underscore_pattern_help()),
|
||||
|
@ -143,7 +126,6 @@ fn loc_parse_tag_pattern_arg<'a>(
|
|||
loc!(single_quote_pattern_help()),
|
||||
loc!(number_pattern_help())
|
||||
)
|
||||
.parse(arena, state, min_indent)
|
||||
}
|
||||
|
||||
fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
|
||||
|
@ -168,7 +150,7 @@ fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInPare
|
|||
state,
|
||||
))
|
||||
} else if elements.is_empty() {
|
||||
Err((NoProgress, PInParens::Empty(state.pos()), state))
|
||||
Err((NoProgress, PInParens::Empty(state.pos())))
|
||||
} else {
|
||||
// TODO: don't discard comments before/after
|
||||
// (stored in the Collection)
|
||||
|
@ -221,8 +203,8 @@ fn single_quote_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>>
|
|||
}
|
||||
|
||||
fn list_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PList<'a>> {
|
||||
move |arena, state, min_indent| {
|
||||
let (_, pats, state) = collection_trailing_sep_e!(
|
||||
map!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'[', PList::Open),
|
||||
list_element_pattern(),
|
||||
word1(b',', PList::End),
|
||||
|
@ -230,13 +212,9 @@ fn list_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PList<'a>> {
|
|||
PList::Open,
|
||||
PList::IndentEnd,
|
||||
Pattern::SpaceBefore
|
||||
)
|
||||
.parse(arena, state, min_indent)?;
|
||||
|
||||
let result = Pattern::List(pats);
|
||||
|
||||
Ok((MadeProgress, result, state))
|
||||
}
|
||||
),
|
||||
Pattern::List
|
||||
)
|
||||
}
|
||||
|
||||
fn list_element_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
|
||||
|
@ -248,12 +226,7 @@ fn list_element_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
|
|||
}
|
||||
|
||||
fn three_list_rest_pattern_error<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
|
||||
then(
|
||||
loc!(word3(b'.', b'.', b'.', PList::Rest)),
|
||||
|_arena, state, _progress, word| {
|
||||
Err((MadeProgress, PList::Rest(word.region.start()), state))
|
||||
},
|
||||
)
|
||||
fail_when(PList::Rest, loc!(word3(b'.', b'.', b'.', PList::Rest)))
|
||||
}
|
||||
|
||||
fn list_rest_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
|
||||
|
@ -330,11 +303,7 @@ fn loc_ident_pattern_help<'a>(
|
|||
// Plain identifiers (e.g. `foo`) are allowed in patterns, but
|
||||
// more complex ones (e.g. `Foo.bar` or `foo.bar.baz`) are not.
|
||||
if crate::keyword::KEYWORDS.contains(&parts[0]) {
|
||||
Err((
|
||||
NoProgress,
|
||||
EPattern::End(original_state.pos()),
|
||||
original_state,
|
||||
))
|
||||
Err((NoProgress, EPattern::End(original_state.pos())))
|
||||
} else if module_name.is_empty() && parts.len() == 1 {
|
||||
Ok((
|
||||
MadeProgress,
|
||||
|
@ -387,34 +356,26 @@ fn loc_ident_pattern_help<'a>(
|
|||
}
|
||||
|
||||
fn underscore_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
|
||||
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
|
||||
let (_, _, next_state) =
|
||||
word1(b'_', EPattern::Underscore).parse(arena, state, min_indent)?;
|
||||
|
||||
let (_, output, final_state) =
|
||||
optional(lowercase_ident_pattern).parse(arena, next_state, min_indent)?;
|
||||
|
||||
match output {
|
||||
Some(name) => Ok((MadeProgress, Pattern::Underscore(name), final_state)),
|
||||
None => Ok((MadeProgress, Pattern::Underscore(""), final_state)),
|
||||
map!(
|
||||
skip_first!(
|
||||
word1(b'_', EPattern::Underscore),
|
||||
optional(lowercase_ident_pattern())
|
||||
),
|
||||
|output| match output {
|
||||
Some(name) => Pattern::Underscore(name),
|
||||
None => Pattern::Underscore(""),
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn lowercase_ident_pattern<'a>(
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
min_indent: u32,
|
||||
) -> ParseResult<'a, &'a str, EPattern<'a>> {
|
||||
let pos = state.pos();
|
||||
|
||||
specialize(move |_, _| EPattern::End(pos), lowercase_ident()).parse(arena, state, min_indent)
|
||||
fn lowercase_ident_pattern<'a>() -> impl Parser<'a, &'a str, EPattern<'a>> {
|
||||
specialize(move |_, pos| EPattern::End(pos), lowercase_ident())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn record_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
|
||||
move |arena, state, min_indent| {
|
||||
let (_, fields, state) = collection_trailing_sep_e!(
|
||||
map!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'{', PRecord::Open),
|
||||
record_pattern_field(),
|
||||
word1(b',', PRecord::End),
|
||||
|
@ -422,13 +383,9 @@ fn record_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
|
|||
PRecord::Open,
|
||||
PRecord::IndentEnd,
|
||||
Pattern::SpaceBefore
|
||||
)
|
||||
.parse(arena, state, min_indent)?;
|
||||
|
||||
let result = Pattern::RecordDestructure(fields);
|
||||
|
||||
Ok((MadeProgress, result, state))
|
||||
}
|
||||
),
|
||||
Pattern::RecordDestructure
|
||||
)
|
||||
}
|
||||
|
||||
fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue