From 2d9aba22427a91e6928b363a7af9592e7b8d9178 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Tue, 15 Nov 2022 21:25:51 -0500 Subject: [PATCH] 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. --- crates/cli/src/format.rs | 4 +- crates/compiler/load_internal/src/file.rs | 6 +- crates/compiler/parse/src/blankspace.rs | 6 +- crates/compiler/parse/src/expr.rs | 218 ++++++------- crates/compiler/parse/src/header.rs | 4 +- crates/compiler/parse/src/ident.rs | 54 ++-- crates/compiler/parse/src/module.rs | 19 +- crates/compiler/parse/src/number_literal.rs | 8 +- crates/compiler/parse/src/parser.rs | 302 ++++++++++--------- crates/compiler/parse/src/pattern.rs | 105 ++----- crates/compiler/parse/src/state.rs | 4 +- crates/compiler/parse/src/string_literal.rs | 41 +-- crates/compiler/parse/src/type_annotation.rs | 31 +- crates/compiler/parse/tests/test_parse.rs | 2 +- crates/repl_cli/src/repl_state.rs | 12 +- crates/reporting/src/error/parse.rs | 2 +- crates/reporting/tests/test_reporting.rs | 12 +- 17 files changed, 374 insertions(+), 456 deletions(-) diff --git a/crates/cli/src/format.rs b/crates/cli/src/format.rs index 7d4877b43b..d8b0d3947d 100644 --- a/crates/cli/src/format.rs +++ b/crates/cli/src/format.rs @@ -157,9 +157,7 @@ fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result, SyntaxError<' let (module, state) = module::parse_header(arena, State::new(src.as_bytes())) .map_err(|e| SyntaxError::Header(e.problem))?; - let (_, defs, _) = module_defs() - .parse(arena, state, 0) - .map_err(|(_, e, _)| e)?; + let (_, defs, _) = module_defs().parse(arena, state, 0).map_err(|(_, e)| e)?; Ok(Ast { module, defs }) } diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 55821edc15..b6ad939c11 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -4860,11 +4860,11 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result, Loadi let parse_start = Instant::now(); let source = header.parse_state.original_bytes(); let parse_state = header.parse_state; - let parsed_defs = match module_defs().parse(arena, parse_state, 0) { + let parsed_defs = match module_defs().parse(arena, parse_state.clone(), 0) { Ok((_, success, _state)) => success, - Err((_, fail, state)) => { + Err((_, fail)) => { return Err(LoadingProblem::ParsingFailed( - fail.into_file_error(header.module_path, &state), + fail.into_file_error(header.module_path, &parse_state), )); } }; diff --git a/crates/compiler/parse/src/blankspace.rs b/crates/compiler/parse/src/blankspace.rs index 4177843693..f59fb507d2 100644 --- a/crates/compiler/parse/src/blankspace.rs +++ b/crates/compiler/parse/src/blankspace.rs @@ -159,7 +159,7 @@ where if state.column() >= min_indent { Ok((NoProgress, (), state)) } else { - Err((NoProgress, indent_problem(state.pos()), state)) + Err((NoProgress, indent_problem(state.pos()))) } } } @@ -184,7 +184,6 @@ where FastSpaceState::HasTab(position) => Err(( MadeProgress, E::space_problem(BadInputError::HasTab, position), - state, )), FastSpaceState::Good { newlines, @@ -194,7 +193,7 @@ where if consumed == 0 { Ok((NoProgress, &[] as &[_], state)) } else if column < min_indent { - Err((MadeProgress, indent_problem(state.pos()), state)) + Err((MadeProgress, indent_problem(state.pos()))) } else { let comments_and_newlines = Vec::with_capacity_in(newlines, arena); let spaces = eat_spaces(state, comments_and_newlines); @@ -218,7 +217,6 @@ where FastSpaceState::HasTab(position) => Err(( MadeProgress, E::space_problem(BadInputError::HasTab, position), - state, )), FastSpaceState::Good { newlines, diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index af719f90aa..20094f8c2a 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -14,7 +14,7 @@ use crate::parser::{ word1_indent, word2, EClosure, EExpect, EExpr, EIf, EInParens, EList, ENumber, EPattern, ERecord, EString, ETuple, EType, EWhen, Either, ParseResult, Parser, }; -use crate::pattern::{loc_closure_param, loc_has_parser}; +use crate::pattern::{closure_param, loc_has_parser}; use crate::state::State; use crate::type_annotation; use bumpalo::collections::Vec; @@ -30,7 +30,7 @@ fn expr_end<'a>() -> impl Parser<'a, (), EExpr<'a>> { if state.has_reached_end() { Ok((NoProgress, (), state)) } else { - Err((NoProgress, EExpr::BadExprEnd(state.pos()), state)) + Err((NoProgress, EExpr::BadExprEnd(state.pos()))) } } } @@ -44,7 +44,7 @@ pub fn test_parse_expr<'a>( match parser.parse(arena, state, min_indent) { Ok((_, expression, _)) => Ok(expression), - Err((_, fail, _)) => Err(fail), + Err((_, fail)) => Err(fail), } } @@ -102,7 +102,7 @@ fn loc_expr_in_parens_help<'a>() -> impl Parser<'a, Loc>, EInParens<'a> state, )) } else if elements.is_empty() { - Err((NoProgress, EInParens::Empty(state.pos()), state)) + Err((NoProgress, EInParens::Empty(state.pos()))) } else { // TODO: don't discard comments before/after // (stored in the Collection) @@ -157,7 +157,11 @@ fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc>, EExpr<'a> } fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, &'a str>, EExpr<'a>> { - |arena, state, min_indent| match record_field_access().parse(arena, state, min_indent) { + |arena, state: State<'a>, min_indent| match record_field_access().parse( + arena, + state.clone(), + min_indent, + ) { Ok((_, initial, state)) => { let mut accesses = Vec::with_capacity_in(1, arena); @@ -165,18 +169,18 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, &'a str>, EExpr<'a let mut loop_state = state; loop { - match record_field_access().parse(arena, loop_state, min_indent) { + match record_field_access().parse(arena, loop_state.clone(), min_indent) { Ok((_, next, state)) => { accesses.push(next); loop_state = state; } - Err((MadeProgress, fail, state)) => return Err((MadeProgress, fail, state)), - Err((NoProgress, _, state)) => return Ok((MadeProgress, accesses, state)), + Err((MadeProgress, fail)) => return Err((MadeProgress, fail)), + Err((NoProgress, _)) => return Ok((MadeProgress, accesses, loop_state)), } } } - Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)), - Err((NoProgress, _, state)) => Err((NoProgress, EExpr::Access(state.pos()), state)), + Err((MadeProgress, fail)) => Err((MadeProgress, fail)), + Err((NoProgress, _)) => Err((NoProgress, EExpr::Access(state.pos()))), } } @@ -292,7 +296,7 @@ fn loc_possibly_negative_or_negated_term<'a>( } fn fail_expr_start_e<'a, T: 'a>() -> impl Parser<'a, T, EExpr<'a>> { - |_arena, state: State<'a>, _min_indent: u32| Err((NoProgress, EExpr::Start(state.pos()), state)) + |_arena, state: State<'a>, _min_indent: u32| Err((NoProgress, EExpr::Start(state.pos()))) } fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> { @@ -313,7 +317,7 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> { Ok((MadeProgress, (), state)) } else { // this is not a negated expression - Err((NoProgress, EExpr::UnaryNot(state.pos()), state)) + Err((NoProgress, EExpr::UnaryNot(state.pos()))) } } } @@ -338,8 +342,8 @@ fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a let initial_state = state.clone(); let end = state.pos(); - match space0_e(EExpr::IndentEnd).parse(arena, state, min_indent) { - Err((_, _, state)) => Ok((MadeProgress, expr.value, state)), + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => Ok((MadeProgress, expr.value, state)), Ok((_, spaces_before_op, state)) => { let expr_state = ExprState { operators: Vec::new_in(arena), @@ -570,14 +574,14 @@ pub fn parse_single_def<'a>( let spaces_before_current_start = state.pos(); let state = match space0_e(EExpr::IndentStart).parse(arena, state, min_indent) { - Err((MadeProgress, _, s)) => { - return Err((MadeProgress, EExpr::DefMissingFinalExpr(s.pos()), s)); + Err((MadeProgress, _)) => { + return Err((MadeProgress, EExpr::DefMissingFinalExpr(initial.pos()))); } Ok((_, spaces, state)) => { spaces_before_current = spaces; state } - Err((NoProgress, _, state)) => state, + Err((NoProgress, _)) => initial.clone(), }; let start = state.pos(); @@ -591,9 +595,9 @@ pub fn parse_single_def<'a>( state.clone(), min_indent, ) { - Err((NoProgress, _, _)) => { + Err((NoProgress, _)) => { match parse_expect.parse(arena, state, min_indent) { - Err((_, _, _)) => { + Err((_, _)) => { // a hacky way to get expression-based error messages. TODO fix this Ok((NoProgress, None, initial)) } @@ -645,7 +649,7 @@ pub fn parse_single_def<'a>( } } } - Err((MadeProgress, _, _)) => { + Err((MadeProgress, _)) => { // a hacky way to get expression-based error messages. TODO fix this Ok((NoProgress, None, initial)) } @@ -1014,7 +1018,7 @@ fn parse_defs_end<'a>( next_state } Ok((progress, None, s)) => return Ok((progress, defs, s)), - Err((progress, err, s)) => return Err((progress, err, s)), + Err((progress, err)) => return Err((progress, err)), }; } } @@ -1038,12 +1042,11 @@ fn parse_defs_expr<'a>( // this is no def, because there is no `=` or `:`; parse as an expr let parse_final_expr = space0_before_e(loc_expr(), EExpr::IndentEnd); - match parse_final_expr.parse(arena, state, min_indent) { - Err((_, fail, state)) => { + match parse_final_expr.parse(arena, state.clone(), min_indent) { + Err((_, fail)) => { return Err(( MadeProgress, EExpr::DefMissingFinalExpr2(arena.alloc(fail), state.pos()), - state, )); } Ok((_, loc_ret, state)) => { @@ -1104,7 +1107,7 @@ fn finish_parsing_alias_or_opaque<'a>( let (expr, arguments) = expr_state .validate_is_type_def(arena, loc_op, kind) - .map_err(|fail| (MadeProgress, fail, state.clone()))?; + .map_err(|fail| (MadeProgress, fail))?; let mut defs = Defs::default(); @@ -1180,8 +1183,8 @@ fn finish_parsing_alias_or_opaque<'a>( ), ); - match parser.parse(arena, state, min_indent) { - Err((_, fail, state)) => return Err((MadeProgress, fail, state)), + match parser.parse(arena, state.clone(), min_indent) { + Err((_, fail)) => return Err((MadeProgress, fail)), Ok((_, mut ann_type, state)) => { // put the spaces from after the operator in front of the call if !spaces_after_operator.is_empty() { @@ -1209,7 +1212,7 @@ fn finish_parsing_alias_or_opaque<'a>( }; let fail = EExpr::BadOperator(op, loc_op.region.start()); - return Err((MadeProgress, fail, state)); + return Err((MadeProgress, fail)); } } } @@ -1261,12 +1264,10 @@ mod ability { indent: IndentLevel, ) -> impl Parser<'a, (u32, AbilityMember<'a>), EAbility<'a>> { move |arena, state: State<'a>, min_indent: u32| { - let initial = state.clone(); - // Put no restrictions on the indent after the spaces; we'll check it manually. match space0_e(EAbility::DemandName).parse(arena, state, 0) { - Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)), - Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)), + Err((MadeProgress, fail)) => Err((NoProgress, fail)), + Err((NoProgress, fail)) => Err((NoProgress, fail)), Ok((_progress, spaces, state)) => { match indent { @@ -1275,7 +1276,6 @@ mod ability { Err(( MadeProgress, EAbility::DemandAlignment(indent_difference, state.pos()), - initial, )) } IndentLevel::Exact(wanted) if state.column() < wanted => { @@ -1286,7 +1286,6 @@ mod ability { // expression NoProgress, EAbility::DemandAlignment(indent_difference, state.pos()), - initial, )) } IndentLevel::Exact(wanted) if state.column() > wanted => { @@ -1304,7 +1303,6 @@ mod ability { Err(( progress, EAbility::DemandAlignment(indent_difference, state.pos()), - initial, )) } _ => { @@ -1312,14 +1310,12 @@ mod ability { let parser = parse_demand_help(); - match parser.parse(arena, state, min_indent) { - Err((MadeProgress, fail, state)) => { - Err((MadeProgress, fail, state)) - } - Err((NoProgress, fail, _)) => { + match parser.parse(arena, state.clone(), min_indent) { + Err((MadeProgress, fail)) => Err((MadeProgress, fail)), + Err((NoProgress, fail)) => { // We made progress relative to the entire ability definition, // so this is an error. - Err((MadeProgress, fail, initial)) + Err((MadeProgress, fail)) } Ok((_, mut demand, state)) => { @@ -1355,12 +1351,11 @@ fn finish_parsing_ability_def_help<'a>( // Parse the first demand. This will determine the indentation level all the // other demands must observe. + let start = state.pos(); let (_, (demand_indent_level, first_demand), mut state) = ability::parse_demand(ability::IndentLevel::PendingMin(min_indent_for_demand)) .parse(arena, state, min_indent_for_demand) - .map_err(|(progress, err, state)| { - (progress, EExpr::Ability(err, state.pos()), state) - })?; + .map_err(|(progress, err)| (progress, EExpr::Ability(err, start)))?; demands.push(first_demand); let demand_indent = ability::IndentLevel::Exact(demand_indent_level); @@ -1372,15 +1367,10 @@ fn finish_parsing_ability_def_help<'a>( state = next_state; demands.push(demand); } - Err((MadeProgress, problem, old_state)) => { - return Err(( - MadeProgress, - EExpr::Ability(problem, old_state.pos()), - old_state, - )); + Err((MadeProgress, problem)) => { + return Err((MadeProgress, EExpr::Ability(problem, state.pos()))); } - Err((NoProgress, _, old_state)) => { - state = old_state; + Err((NoProgress, _)) => { break; } } @@ -1431,10 +1421,11 @@ fn parse_expr_operator<'a>( let initial_state = state.clone(); - let (spaces, state) = match space0_e(EExpr::IndentEnd).parse(arena, state, min_indent) { - Err((_, _, state)) => (&[] as &[_], state), - Ok((_, spaces, state)) => (spaces, state), - }; + let (spaces, state) = + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => (&[] as &[_], state), + Ok((_, spaces, state)) => (spaces, state), + }; expr_state.arguments.push(arena.alloc(arg)); expr_state.spaces_after = spaces; @@ -1448,7 +1439,7 @@ fn parse_expr_operator<'a>( let call = expr_state .validate_assignment_or_backpassing(arena, loc_op, EExpr::ElmStyleFunction) - .map_err(|fail| (MadeProgress, fail, state.clone()))?; + .map_err(|fail| (MadeProgress, fail))?; let (value_def, def_region, state) = { match expr_to_pattern_help(arena, &call.value) { @@ -1475,7 +1466,7 @@ fn parse_expr_operator<'a>( // this `=` likely occurred inline; treat it as an invalid operator let fail = EExpr::BadOperator(arena.alloc("="), loc_op.region.start()); - return Err((MadeProgress, fail, state)); + return Err((MadeProgress, fail)); } } }; @@ -1493,7 +1484,7 @@ fn parse_expr_operator<'a>( .validate_assignment_or_backpassing(arena, loc_op, |_, pos| { EExpr::BadOperator("<-", pos) }) - .map_err(|fail| (MadeProgress, fail, state.clone()))?; + .map_err(|fail| (MadeProgress, fail))?; let (loc_pattern, loc_body, state) = { match expr_to_pattern_help(arena, &call.value) { @@ -1514,7 +1505,7 @@ fn parse_expr_operator<'a>( // this `=` likely occurred inline; treat it as an invalid operator let fail = EExpr::BadOperator("=", loc_op.region.start()); - return Err((MadeProgress, fail, state)); + return Err((MadeProgress, fail)); } } }; @@ -1545,8 +1536,12 @@ fn parse_expr_operator<'a>( _ => unreachable!(), }, ), - _ => match loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent) { - Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)), + _ => match loc_possibly_negative_or_negated_term(options).parse( + arena, + state.clone(), + min_indent, + ) { + Err((MadeProgress, f)) => Err((MadeProgress, f)), Ok((_, mut new_expr, state)) => { let new_end = state.pos(); @@ -1559,8 +1554,8 @@ fn parse_expr_operator<'a>( .with_spaces_before(spaces_after_operator, new_expr.region); } - match space0_e(EExpr::IndentEnd).parse(arena, state, min_indent) { - Err((_, _, state)) => { + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => { let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); let call = to_call(arena, args, expr_state.expr); @@ -1588,8 +1583,8 @@ fn parse_expr_operator<'a>( } } } - Err((NoProgress, expr, e)) => { - todo!("{:?} {:?}", expr, e) + Err((NoProgress, expr)) => { + todo!("{:?} {:?}", expr, state) } }, } @@ -1609,7 +1604,7 @@ fn parse_expr_end<'a>( ); match parser.parse(arena, state.clone(), min_indent) { - Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)), + Err((MadeProgress, f)) => Err((MadeProgress, f)), Ok(( _, has @ Loc { @@ -1638,11 +1633,7 @@ fn parse_expr_end<'a>( Err(_) => { let start = argument.region.start(); let err = &*arena.alloc(EPattern::Start(start)); - return Err(( - MadeProgress, - EExpr::Pattern(err, argument.region.start()), - state, - )); + return Err((MadeProgress, EExpr::Pattern(err, argument.region.start()))); } } } @@ -1679,8 +1670,8 @@ fn parse_expr_end<'a>( } let initial_state = state.clone(); - match space0_e(EExpr::IndentEnd).parse(arena, state, min_indent) { - Err((_, _, state)) => { + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => { expr_state.arguments.push(arena.alloc(arg)); expr_state.end = new_end; expr_state.spaces_after = &[]; @@ -1697,11 +1688,11 @@ fn parse_expr_end<'a>( } } } - Err((NoProgress, _, _)) => { + Err((NoProgress, _)) => { let before_op = state.clone(); // try an operator - match loc!(operator()).parse(arena, state, min_indent) { - Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)), + match loc!(operator()).parse(arena, state.clone(), min_indent) { + Err((MadeProgress, f)) => Err((MadeProgress, f)), Ok((_, loc_op, state)) => { expr_state.consume_spaces(arena); let initial_state = before_op; @@ -1715,7 +1706,8 @@ fn parse_expr_end<'a>( initial_state, ) } - Err((NoProgress, _, mut state)) => { + Err((NoProgress, _)) => { + let mut state = state; // try multi-backpassing if options.accept_multi_backpassing && state.bytes().starts_with(b",") { state = state.advance(1); @@ -1743,10 +1735,12 @@ fn parse_expr_end<'a>( patterns.insert(0, loc_pattern); - match word2(b'<', b'-', EExpr::BackpassArrow) - .parse(arena, state, min_indent) - { - Err((_, fail, state)) => Err((MadeProgress, fail, state)), + match word2(b'<', b'-', EExpr::BackpassArrow).parse( + arena, + state.clone(), + min_indent, + ) { + Err((_, fail)) => Err((MadeProgress, fail)), Ok((_, _, state)) => { let parse_body = space0_before_e( increment_min_indent(loc_expr()), @@ -1771,7 +1765,7 @@ fn parse_expr_end<'a>( } } } else if options.check_for_arrow && state.bytes().starts_with(b"->") { - Err((MadeProgress, EExpr::BadOperator("->", state.pos()), state)) + Err((MadeProgress, EExpr::BadOperator("->", state.pos()))) } else { let expr = parse_expr_final(expr_state, arena); @@ -1998,7 +1992,7 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo sep_by1_e( word1(b',', EClosure::Comma), space0_around_ee( - specialize(EClosure::Pattern, loc_closure_param()), + specialize(EClosure::Pattern, closure_param()), EClosure::IndentArg, EClosure::IndentArrow, ), @@ -2090,11 +2084,7 @@ mod when { Ok((MadeProgress, (loc_patterns, loc_guard), state)) } else { let indent = pattern_indent_level - indent_column; - Err(( - MadeProgress, - EWhen::PatternAlignment(indent, state.pos()), - state, - )) + Err((MadeProgress, EWhen::PatternAlignment(indent, state.pos()))) } }, ), @@ -2111,18 +2101,16 @@ mod when { ); while !state.bytes().is_empty() { - match branch_parser.parse(arena, state, min_indent) { + match branch_parser.parse(arena, state.clone(), min_indent) { Ok((_, next_output, next_state)) => { state = next_state; branches.push(arena.alloc(next_output)); } - Err((MadeProgress, problem, old_state)) => { - return Err((MadeProgress, problem, old_state)); + Err((MadeProgress, problem)) => { + return Err((MadeProgress, problem)); } - Err((NoProgress, _, old_state)) => { - state = old_state; - + Err((NoProgress, _)) => { break; } } @@ -2193,25 +2181,19 @@ mod when { pattern_indent_level: Option, ) -> impl Parser<'a, (u32, Vec<'a, Loc>>), EWhen<'a>> { move |arena, state: State<'a>, min_indent: u32| { - let initial = state.clone(); - // put no restrictions on the indent after the spaces; we'll check it manually match space0_e(EWhen::IndentPattern).parse(arena, state, 0) { - Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)), - Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)), + Err((MadeProgress, fail)) => Err((NoProgress, fail)), + Err((NoProgress, fail)) => Err((NoProgress, fail)), Ok((_progress, spaces, state)) => { match pattern_indent_level { Some(wanted) if state.column() > wanted => { // this branch is indented too much - Err((NoProgress, EWhen::IndentPattern(state.pos()), initial)) + Err((NoProgress, EWhen::IndentPattern(state.pos()))) } Some(wanted) if state.column() < wanted => { let indent = wanted - state.column(); - Err(( - NoProgress, - EWhen::PatternAlignment(indent, state.pos()), - initial, - )) + Err((NoProgress, EWhen::PatternAlignment(indent, state.pos()))) } _ => { let pattern_indent = @@ -2223,13 +2205,11 @@ mod when { let parser = sep_by1(word1(b'|', EWhen::Bar), branch_single_alternative()); - match parser.parse(arena, state, pattern_indent) { - Err((MadeProgress, fail, state)) => { - Err((MadeProgress, fail, state)) - } - Err((NoProgress, fail, _)) => { + match parser.parse(arena, state.clone(), pattern_indent) { + Err((MadeProgress, fail)) => Err((MadeProgress, fail)), + Err((NoProgress, fail)) => { // roll back space parsing if the pattern made no progress - Err((NoProgress, fail, initial)) + Err((NoProgress, fail)) } Ok((_, mut loc_patterns, state)) => { @@ -2303,7 +2283,7 @@ fn expect_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpe EExpect::IndentCondition, ) .parse(arena, state, start_column + 1) - .map_err(|(_, f, s)| (MadeProgress, f, s))?; + .map_err(|(_, f)| (MadeProgress, f))?; let parse_cont = specialize_ref( EExpect::Continuation, @@ -2340,8 +2320,8 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< parser::keyword_e(keyword::IF, EIf::If) ); - match optional_if.parse(arena, state, min_indent) { - Err((_, _, state)) => break state, + match optional_if.parse(arena, state.clone(), min_indent) { + Err((_, _)) => break state, Ok((_, _, state)) => { loop_state = state; continue; @@ -2354,7 +2334,7 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< EIf::IndentElseBranch, ) .parse(arena, state_final_else, min_indent) - .map_err(|(_, f, s)| (MadeProgress, f, s))?; + .map_err(|(_, f)| (MadeProgress, f))?; let expr = Expr::If(branches.into_bump_slice(), arena.alloc(else_branch)); @@ -2711,12 +2691,12 @@ where macro_rules! bad_made_progress { ($op:expr) => {{ - Err((MadeProgress, to_error($op, state.pos()), state)) + Err((MadeProgress, to_error($op, state.pos()))) }}; } match chomped { - "" => Err((NoProgress, to_expectation(state.pos()), state)), + "" => Err((NoProgress, to_expectation(state.pos()))), "+" => good!(BinOp::Plus, 1), "-" => good!(BinOp::Minus, 1), "*" => good!(BinOp::Star, 1), @@ -2727,7 +2707,7 @@ where "<" => good!(BinOp::LessThan, 1), "." => { // a `.` makes no progress, so it does not interfere with `.foo` access(or) - Err((NoProgress, to_error(".", state.pos()), state)) + Err((NoProgress, to_error(".", state.pos()))) } "=" => good!(BinOp::Assignment, 1), ":=" => good!(BinOp::IsOpaqueType, 2), @@ -2742,7 +2722,7 @@ where "//" => good!(BinOp::DoubleSlash, 2), "->" => { // makes no progress, so it does not interfere with `_ if isGood -> ...` - Err((NoProgress, to_error("->", state.pos()), state)) + Err((NoProgress, to_error("->", state.pos()))) } "<-" => good!(BinOp::Backpassing, 2), _ => bad_made_progress!(chomped), diff --git a/crates/compiler/parse/src/header.rs b/crates/compiler/parse/src/header.rs index 8c09f2daf1..7a3280b1af 100644 --- a/crates/compiler/parse/src/header.rs +++ b/crates/compiler/parse/src/header.rs @@ -312,8 +312,8 @@ pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> .parse(arena, state, min_indent) .and_then(|(progress, text, next_state)| match text { StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), next_state)), - StrLiteral::Line(_) => Err((progress, EPackageName::Escapes(pos), next_state)), - StrLiteral::Block(_) => Err((progress, EPackageName::Multiline(pos), next_state)), + StrLiteral::Line(_) => Err((progress, EPackageName::Escapes(pos))), + StrLiteral::Block(_) => Err((progress, EPackageName::Multiline(pos))), }) } } diff --git a/crates/compiler/parse/src/ident.rs b/crates/compiler/parse/src/ident.rs index 349cac5e9b..4f369f0bad 100644 --- a/crates/compiler/parse/src/ident.rs +++ b/crates/compiler/parse/src/ident.rs @@ -88,10 +88,10 @@ impl<'a> Ident<'a> { /// * A named pattern match, e.g. "foo" in `foo =` or `foo ->` or `\foo ->` pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { move |_, state: State<'a>, _min_indent: u32| match chomp_lowercase_part(state.bytes()) { - Err(progress) => Err((progress, (), state)), + Err(progress) => Err((progress, ())), Ok(ident) => { if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) { - Err((NoProgress, (), state)) + Err((NoProgress, ())) } else { let width = ident.len(); Ok((MadeProgress, ident, state.advance(width))) @@ -113,7 +113,7 @@ pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> { /// * A tag pub fn uppercase<'a>() -> impl Parser<'a, UppercaseIdent<'a>, ()> { move |_, state: State<'a>, _min_indent: u32| match chomp_uppercase_part(state.bytes()) { - Err(progress) => Err((progress, (), state)), + Err(progress) => Err((progress, ())), Ok(ident) => { let width = ident.len(); Ok((MadeProgress, ident.into(), state.advance(width))) @@ -128,7 +128,7 @@ pub fn uppercase<'a>() -> impl Parser<'a, UppercaseIdent<'a>, ()> { /// * A tag pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { move |_, state: State<'a>, _min_indent: u32| match chomp_uppercase_part(state.bytes()) { - Err(progress) => Err((progress, (), state)), + Err(progress) => Err((progress, ())), Ok(ident) => { let width = ident.len(); Ok((MadeProgress, ident, state.advance(width))) @@ -138,10 +138,10 @@ pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, ()> { pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str, ()> { move |_, state: State<'a>, _min_indent: u32| match chomp_anycase_part(state.bytes()) { - Err(progress) => Err((progress, (), state)), + Err(progress) => Err((progress, ())), Ok(ident) => { if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) { - Err((MadeProgress, (), state)) + Err((MadeProgress, ())) } else { let width = ident.len(); Ok((MadeProgress, ident, state.advance(width))) @@ -163,27 +163,32 @@ pub fn parse_ident<'a>( ) -> ParseResult<'a, Ident<'a>, EExpr<'a>> { let initial = state.clone(); - match parse_ident_help(arena, state) { - Ok((progress, ident, state)) => { + match chomp_identifier_chain(arena, state.bytes(), state.pos()) { + Ok((width, ident)) => { + let state = advance_state!(state, width as usize)?; if let Ident::Access { module_name, parts } = ident { if module_name.is_empty() { if let Some(first) = parts.first() { for keyword in crate::keyword::KEYWORDS.iter() { if first == keyword { - return Err((NoProgress, EExpr::Start(initial.pos()), initial)); + return Err((NoProgress, EExpr::Start(initial.pos()))); } } } } } - Ok((progress, ident, state)) + Ok((MadeProgress, ident, state)) } - Err((NoProgress, _, state)) => Err((NoProgress, EExpr::Start(state.pos()), state)), - Err((MadeProgress, fail, state)) => match fail { - BadIdent::Start(pos) => Err((NoProgress, EExpr::Start(pos), state)), - BadIdent::Space(e, pos) => Err((NoProgress, EExpr::Space(e, pos), state)), - _ => malformed_identifier(initial.bytes(), fail, state), + Err((0, _)) => Err((NoProgress, EExpr::Start(state.pos()))), + Err((width, fail)) => match fail { + BadIdent::Start(pos) => Err((NoProgress, EExpr::Start(pos))), + BadIdent::Space(e, pos) => Err((NoProgress, EExpr::Space(e, pos))), + _ => malformed_identifier( + initial.bytes(), + fail, + advance_state!(state, width as usize)?, + ), }, } } @@ -504,7 +509,7 @@ fn chomp_module_chain(buffer: &[u8]) -> Result { pub fn concrete_type<'a>() -> impl Parser<'a, (&'a str, &'a str), ()> { move |_, state: State<'a>, _min_indent: u32| match chomp_concrete_type(state.bytes()) { - Err(progress) => Err((progress, (), state)), + Err(progress) => Err((progress, ())), Ok((module_name, type_name, width)) => { Ok((MadeProgress, (module_name, type_name), state.advance(width))) } @@ -574,20 +579,3 @@ fn chomp_access_chain<'a>(buffer: &'a [u8], parts: &mut Vec<'a, &'a str>) -> Res Ok(chomped as u32) } } - -fn parse_ident_help<'a>( - arena: &'a Bump, - mut state: State<'a>, -) -> ParseResult<'a, Ident<'a>, BadIdent> { - match chomp_identifier_chain(arena, state.bytes(), state.pos()) { - Ok((width, ident)) => { - state = advance_state!(state, width as usize)?; - Ok((MadeProgress, ident, state)) - } - Err((0, fail)) => Err((NoProgress, fail, state)), - Err((width, fail)) => { - state = advance_state!(state, width as usize)?; - Err((MadeProgress, fail, state)) - } - } -} diff --git a/crates/compiler/parse/src/module.rs b/crates/compiler/parse/src/module.rs index bee66552cd..5ca959cf00 100644 --- a/crates/compiler/parse/src/module.rs +++ b/crates/compiler/parse/src/module.rs @@ -7,9 +7,9 @@ use crate::header::{ use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent}; use crate::parser::Progress::{self, *}; use crate::parser::{ - backtrackable, increment_min_indent, optional, reset_min_indent, specialize, specialize_region, - word1, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages, EProvides, - ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError, + backtrackable, increment_min_indent, optional, reset_min_indent, specialize, word1, EExposes, + EGenerates, EGeneratesWith, EHeader, EImports, EPackages, EProvides, ERequires, ETypedIdent, + Parser, SourceError, SpaceProblem, SyntaxError, }; use crate::state::State; use crate::string_literal; @@ -21,7 +21,7 @@ fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> { if state.has_reached_end() { Ok((NoProgress, (), state)) } else { - Err((NoProgress, SyntaxError::NotEndOfFile(state.pos()), state)) + Err((NoProgress, SyntaxError::NotEndOfFile(state.pos()))) } } } @@ -29,10 +29,7 @@ fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> { #[inline(always)] pub fn module_defs<'a>() -> impl Parser<'a, Defs<'a>, SyntaxError<'a>> { skip_second!( - specialize_region( - |e, r| SyntaxError::Expr(e, r.start()), - crate::expr::toplevel_defs(), - ), + specialize(SyntaxError::Expr, crate::expr::toplevel_defs(),), end_of_file() ) } @@ -42,9 +39,9 @@ pub fn parse_header<'a>( state: State<'a>, ) -> Result<(Module<'a>, State<'a>), SourceError<'a, EHeader<'a>>> { let min_indent = 0; - match header().parse(arena, state, min_indent) { + match header().parse(arena, state.clone(), min_indent) { Ok((_, module, state)) => Ok((module, state)), - Err((_, fail, state)) => Err(SourceError::new(fail, &state)), + Err((_, fail)) => Err(SourceError::new(fail, &state)), } } @@ -234,7 +231,7 @@ fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> { Ok((MadeProgress, ModuleName::new(name), state)) } - Err(progress) => Err((progress, (), state)), + Err(progress) => Err((progress, ())), } } diff --git a/crates/compiler/parse/src/number_literal.rs b/crates/compiler/parse/src/number_literal.rs index 66bf3c4ab4..71d27ba4e2 100644 --- a/crates/compiler/parse/src/number_literal.rs +++ b/crates/compiler/parse/src/number_literal.rs @@ -20,7 +20,7 @@ pub fn positive_number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber> } _ => { // this is not a number at all - Err((Progress::NoProgress, ENumber::End, state)) + Err((Progress::NoProgress, ENumber::End)) } } } @@ -38,7 +38,7 @@ pub fn number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber> { } _ => { // this is not a number at all - Err((Progress::NoProgress, ENumber::End, state)) + Err((Progress::NoProgress, ENumber::End)) } } } @@ -89,12 +89,12 @@ fn chomp_number_dec<'a>( if is_negative && chomped == 0 { // we're probably actually looking at unary negation here - return Err((Progress::NoProgress, ENumber::End, state)); + return Err((Progress::NoProgress, ENumber::End)); } if !bytes.first().copied().unwrap_or_default().is_ascii_digit() { // we're probably actually looking at unary negation here - return Err((Progress::NoProgress, ENumber::End, state)); + return Err((Progress::NoProgress, ENumber::End)); } let string = diff --git a/crates/compiler/parse/src/parser.rs b/crates/compiler/parse/src/parser.rs index 6ce086b915..cf30c6a37b 100644 --- a/crates/compiler/parse/src/parser.rs +++ b/crates/compiler/parse/src/parser.rs @@ -10,8 +10,7 @@ pub enum Either { Second(Second), } -pub type ParseResult<'a, Output, Error> = - Result<(Progress, Output, State<'a>), (Progress, Error, State<'a>)>; +pub type ParseResult<'a, Output, Error> = Result<(Progress, Output, State<'a>), (Progress, Error)>; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Progress { @@ -851,7 +850,7 @@ where let (progress, value, state) = match &res { Ok((progress, result, state)) => (progress, Ok(result), state), - Err((progress, error, state)) => (progress, Err(error), state), + Err((progress, error)) => (progress, Err(error), state), }; println!( @@ -924,7 +923,7 @@ where let width = keyword.len(); if !state.bytes().starts_with(keyword.as_bytes()) { - return Err((NoProgress, if_error(state.pos()), state)); + return Err((NoProgress, if_error(state.pos()))); } // the next character should not be an identifier character @@ -938,7 +937,7 @@ where state = state.advance(width); Ok((MadeProgress, (), state)) } - Some(_) => Err((NoProgress, if_error(state.pos()), state)), + Some(_) => Err((NoProgress, if_error(state.pos()))), } } } @@ -955,6 +954,8 @@ where Error: 'a, { move |arena, state: State<'a>, min_indent: u32| { + let original_state = state.clone(); + let start_bytes_len = state.bytes().len(); match parser.parse(arena, state, min_indent) { @@ -968,10 +969,10 @@ where buf.push(first_output); loop { - match delimiter.parse(arena, state, min_indent) { + match delimiter.parse(arena, state.clone(), min_indent) { Ok((_, (), next_state)) => { // If the delimiter passed, check the element parser. - match parser.parse(arena, next_state, min_indent) { + match parser.parse(arena, next_state.clone(), min_indent) { Ok((element_progress, next_output, next_state)) => { // in practice, we want elements to make progress debug_assert_eq!(element_progress, MadeProgress); @@ -979,28 +980,28 @@ where state = next_state; buf.push(next_output); } - Err((_, fail, state)) => { + Err((_, fail)) => { // If the delimiter parsed, but the following // element did not, that's a fatal error. let progress = Progress::from_lengths( start_bytes_len, - state.bytes().len(), + next_state.bytes().len(), ); - return Err((progress, fail, state)); + return Err((progress, fail)); } } } - Err((delim_progress, fail, old_state)) => match delim_progress { - MadeProgress => return Err((MadeProgress, fail, old_state)), - NoProgress => return Ok((NoProgress, buf, old_state)), + Err((delim_progress, fail)) => match delim_progress { + MadeProgress => return Err((MadeProgress, fail)), + NoProgress => return Ok((NoProgress, buf, state)), }, } } } - Err((element_progress, fail, new_state)) => match element_progress { - MadeProgress => Err((MadeProgress, fail, new_state)), - NoProgress => Ok((NoProgress, Vec::new_in(arena), new_state)), + Err((element_progress, fail)) => match element_progress { + MadeProgress => Err((MadeProgress, fail)), + NoProgress => Ok((NoProgress, Vec::new_in(arena), original_state)), }, } } @@ -1018,6 +1019,7 @@ where Error: 'a, { move |arena, state: State<'a>, min_indent: u32| { + let original_state = state.clone(); let start_bytes_len = state.bytes().len(); match parser.parse(arena, state, min_indent) { @@ -1030,10 +1032,10 @@ where buf.push(first_output); loop { - match delimiter.parse(arena, state, min_indent) { + match delimiter.parse(arena, state.clone(), min_indent) { Ok((_, (), next_state)) => { // If the delimiter passed, check the element parser. - match parser.parse(arena, next_state, min_indent) { + match parser.parse(arena, next_state.clone(), min_indent) { Ok((element_progress, next_output, next_state)) => { // in practice, we want elements to make progress debug_assert_eq!(element_progress, MadeProgress); @@ -1041,27 +1043,27 @@ where state = next_state; buf.push(next_output); } - Err((_, _fail, old_state)) => { + Err((_, _fail)) => { // If the delimiter parsed, but the following // element did not, that means we saw a trailing comma let progress = Progress::from_lengths( start_bytes_len, - old_state.bytes().len(), + next_state.bytes().len(), ); - return Ok((progress, buf, old_state)); + return Ok((progress, buf, next_state)); } } } - Err((delim_progress, fail, old_state)) => match delim_progress { - MadeProgress => return Err((MadeProgress, fail, old_state)), - NoProgress => return Ok((NoProgress, buf, old_state)), + Err((delim_progress, fail)) => match delim_progress { + MadeProgress => return Err((MadeProgress, fail)), + NoProgress => return Ok((NoProgress, buf, state)), }, } } } - Err((element_progress, fail, new_state)) => match element_progress { - MadeProgress => Err((MadeProgress, fail, new_state)), - NoProgress => Ok((NoProgress, Vec::new_in(arena), new_state)), + Err((element_progress, fail)) => match element_progress { + MadeProgress => Err((MadeProgress, fail)), + NoProgress => Ok((NoProgress, Vec::new_in(arena), original_state)), }, } } @@ -1090,6 +1092,7 @@ where buf.push(first_output); loop { + let old_state = state.clone(); match delimiter.parse(arena, state, min_indent) { Ok((_, (), next_state)) => { // If the delimiter passed, check the element parser. @@ -1098,16 +1101,16 @@ where state = next_state; buf.push(next_output); } - Err((_, fail, state)) => { - return Err((MadeProgress, fail, state)); + Err((_, fail)) => { + return Err((MadeProgress, fail)); } } } - Err((delim_progress, fail, old_state)) => { + Err((delim_progress, fail)) => { match delim_progress { MadeProgress => { // fail if the delimiter made progress - return Err((MadeProgress, fail, old_state)); + return Err((MadeProgress, fail)); } NoProgress => { let progress = Progress::from_lengths( @@ -1121,7 +1124,7 @@ where } } } - Err((fail_progress, fail, new_state)) => Err((fail_progress, fail, new_state)), + Err((fail_progress, fail)) => Err((fail_progress, fail)), } } } @@ -1140,6 +1143,7 @@ where Error: 'a, { move |arena, state: State<'a>, min_indent: u32| { + let original_state = state.clone(); let start_bytes_len = state.bytes().len(); match parser.parse(arena, state, min_indent) { @@ -1151,27 +1155,28 @@ where buf.push(first_output); loop { + let old_state = state.clone(); match delimiter.parse(arena, state, min_indent) { Ok((_, (), next_state)) => { // If the delimiter passed, check the element parser. - match parser.parse(arena, next_state, min_indent) { + match parser.parse(arena, next_state.clone(), min_indent) { Ok((_, next_output, next_state)) => { state = next_state; buf.push(next_output); } - Err((MadeProgress, fail, state)) => { - return Err((MadeProgress, fail, state)); + Err((MadeProgress, fail)) => { + return Err((MadeProgress, fail)); } - Err((NoProgress, _fail, state)) => { - return Err((NoProgress, to_element_error(state.pos()), state)); + Err((NoProgress, _fail)) => { + return Err((NoProgress, to_element_error(next_state.pos()))); } } } - Err((delim_progress, fail, old_state)) => { + Err((delim_progress, fail)) => { match delim_progress { MadeProgress => { // fail if the delimiter made progress - return Err((MadeProgress, fail, old_state)); + return Err((MadeProgress, fail)); } NoProgress => { let progress = Progress::from_lengths( @@ -1186,10 +1191,8 @@ where } } - Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)), - Err((NoProgress, _fail, state)) => { - Err((NoProgress, to_element_error(state.pos()), state)) - } + Err((MadeProgress, fail)) => Err((MadeProgress, fail)), + Err((NoProgress, _fail)) => Err((NoProgress, to_element_error(original_state.pos()))), } } } @@ -1201,7 +1204,7 @@ pub fn fail_when_progress( state: State<'_>, ) -> ParseResult<'_, T, E> { match progress { - MadeProgress => Err((MadeProgress, fail, state)), + MadeProgress => Err((MadeProgress, fail)), NoProgress => Ok((NoProgress, value, state)), } } @@ -1218,7 +1221,7 @@ where match parser.parse(arena, state, min_indent) { Ok((progress, out1, state)) => Ok((progress, Some(out1), state)), - Err((_, _, _)) => { + Err((_, _)) => { // NOTE this will backtrack // TODO can we get rid of some of the potential backtracking? Ok((NoProgress, None, original_state)) @@ -1257,16 +1260,14 @@ macro_rules! loc { #[macro_export] macro_rules! skip_first { ($p1:expr, $p2:expr) => { - move |arena, state: $crate::state::State<'a>, min_indent: u32| { - let original_state = state.clone(); - - match $p1.parse(arena, state, min_indent) { - Ok((p1, _, state)) => match $p2.parse(arena, state, min_indent) { - Ok((p2, out2, state)) => Ok((p1.or(p2), out2, state)), - Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)), - }, - Err((progress, fail, _)) => Err((progress, fail, original_state)), - } + move |arena, state: $crate::state::State<'a>, min_indent: u32| match $p1 + .parse(arena, state, min_indent) + { + Ok((p1, _, state)) => match $p2.parse(arena, state, min_indent) { + Ok((p2, out2, state)) => Ok((p1.or(p2), out2, state)), + Err((p2, fail)) => Err((p1.or(p2), fail)), + }, + Err((progress, fail)) => Err((progress, fail)), } }; } @@ -1276,16 +1277,14 @@ macro_rules! skip_first { #[macro_export] macro_rules! skip_second { ($p1:expr, $p2:expr) => { - move |arena, state: $crate::state::State<'a>, min_indent: u32| { - let original_state = state.clone(); - - match $p1.parse(arena, state, min_indent) { - Ok((p1, out1, state)) => match $p2.parse(arena, state, min_indent) { - Ok((p2, _, state)) => Ok((p1.or(p2), out1, state)), - Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)), - }, - Err((progress, fail, _)) => Err((progress, fail, original_state)), - } + move |arena, state: $crate::state::State<'a>, min_indent: u32| match $p1 + .parse(arena, state, min_indent) + { + Ok((p1, out1, state)) => match $p2.parse(arena, state, min_indent) { + Ok((p2, _, state)) => Ok((p1.or(p2), out1, state)), + Err((p2, fail)) => Err((p1.or(p2), fail)), + }, + Err((progress, fail)) => Err((progress, fail)), } }; } @@ -1381,6 +1380,23 @@ macro_rules! succeed { }; } +pub fn fail_when<'a, T, T2, E, F, P>(f: F, p: P) -> impl Parser<'a, T, E> +where + T: 'a, + T2: 'a, + E: 'a, + F: Fn(Position) -> E, + P: Parser<'a, T2, E>, +{ + move |arena: &'a bumpalo::Bump, state: State<'a>, min_indent: u32| { + let original_state = state.clone(); + match p.parse(arena, state, min_indent) { + Ok((_, _, _)) => Err((MadeProgress, f(original_state.pos()))), + Err((progress, err)) => Err((progress, err)), + } + } +} + pub fn fail<'a, T, E, F>(f: F) -> impl Parser<'a, T, E> where T: 'a, @@ -1388,25 +1404,21 @@ where F: Fn(Position) -> E, { move |_arena: &'a bumpalo::Bump, state: State<'a>, _min_indent: u32| { - Err((NoProgress, f(state.pos()), state)) + Err((NoProgress, f(state.pos()))) } } #[macro_export] macro_rules! and { ($p1:expr, $p2:expr) => { - move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| { - // We have to clone this because if the first parser passes and then - // the second one fails, we need to revert back to the original state. - let original_state = state.clone(); - - match $p1.parse(arena, state, min_indent) { - Ok((p1, out1, state)) => match $p2.parse(arena, state, min_indent) { - Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)), - Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)), - }, - Err((progress, fail, state)) => Err((progress, fail, state)), - } + move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| match $p1 + .parse(arena, state, min_indent) + { + Ok((p1, out1, state)) => match $p2.parse(arena, state, min_indent) { + Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)), + Err((p2, fail)) => Err((p1.or(p2), fail)), + }, + Err((progress, fail)) => Err((progress, fail)), } }; } @@ -1428,16 +1440,12 @@ macro_rules! indented_seq { let p1_indent = start_indent; let p2_indent = p1_indent + 1; - // We have to clone this because if the first parser passes and then - // the second one fails, we need to revert back to the original state. - let original_state = state.clone(); - match $p1.parse(arena, state, p1_indent) { Ok((p1, (), state)) => match $p2.parse(arena, state, p2_indent) { Ok((p2, out2, state)) => Ok((p1.or(p2), out2, state)), - Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)), + Err((p2, fail)) => Err((p1.or(p2), fail)), }, - Err((progress, fail, state)) => Err((progress, fail, state)), + Err((progress, fail)) => Err((progress, fail)), } } }; @@ -1454,16 +1462,12 @@ macro_rules! absolute_indented_seq { let p1_indent = start_indent; let p2_indent = p1_indent + 1; - // We have to clone this because if the first parser passes and then - // the second one fails, we need to revert back to the original state. - let original_state = state.clone(); - match $p1.parse(arena, state, p1_indent) { Ok((p1, out1, state)) => match $p2.parse(arena, state, p2_indent) { Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)), - Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)), + Err((p2, fail)) => Err((p1.or(p2), fail)), }, - Err((progress, fail, state)) => Err((progress, fail, state)), + Err((progress, fail)) => Err((progress, fail)), } } }; @@ -1477,8 +1481,8 @@ macro_rules! one_of { match $p1.parse(arena, state, min_indent) { valid @ Ok(_) => valid, - Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)), - Err((NoProgress, _, _)) => $p2.parse(arena, original_state, min_indent), + Err((MadeProgress, fail)) => Err((MadeProgress, fail)), + Err((NoProgress, _)) => $p2.parse(arena, original_state, min_indent), } } }; @@ -1494,12 +1498,14 @@ macro_rules! one_of { #[macro_export] macro_rules! maybe { ($p1:expr) => { - move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| match $p1 - .parse(arena, state, min_indent) - { - Ok((progress, value, state)) => Ok((progress, Some(value), state)), - Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)), - Err((NoProgress, _, state)) => Ok((NoProgress, None, state)), + move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| { + let original_state = state.clone(); + + match $p1.parse(arena, state, min_indent) { + Ok((progress, value, state)) => Ok((progress, Some(value), state)), + Err((MadeProgress, fail)) => Err((MadeProgress, fail)), + Err((NoProgress, _)) => Ok((NoProgress, None, original_state)), + } } }; } @@ -1508,11 +1514,12 @@ macro_rules! maybe { macro_rules! one_of_with_error { ($toerror:expr; $p1:expr) => { move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| { + let original_state = state.clone(); match $p1.parse(arena, state, min_indent) { valid @ Ok(_) => valid, - Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state )), - Err((NoProgress, _, state)) => Err((MadeProgress, $toerror(state.pos()), state)), + Err((MadeProgress, fail)) => Err((MadeProgress, fail)), + Err((NoProgress, _)) => Err((MadeProgress, $toerror(original_state.pos()))), } } }; @@ -1569,24 +1576,11 @@ where P: Parser<'a, T, X>, Y: 'a, { - move |a, s, min_indent| match parser.parse(a, s, min_indent) { - Ok(t) => Ok(t), - Err((p, error, s)) => Err((p, map_error(error, s.pos()), s)), - } -} - -/// Like `specialize`, except the error function receives a Region representing the begin/end of the error -pub fn specialize_region<'a, F, P, T, X, Y>(map_error: F, parser: P) -> impl Parser<'a, T, Y> -where - F: Fn(X, Region) -> Y, - P: Parser<'a, T, X>, - Y: 'a, -{ - move |a, s: State<'a>, min_indent: u32| { - let start = s.pos(); - match parser.parse(a, s, min_indent) { + move |a, state: State<'a>, min_indent| { + let original_state = state.clone(); + match parser.parse(a, state, min_indent) { Ok(t) => Ok(t), - Err((p, error, s)) => Err((p, map_error(error, Region::new(start, s.pos())), s)), + Err((p, error)) => Err((p, map_error(error, original_state.pos()))), } } } @@ -1598,9 +1592,12 @@ where Y: 'a, X: 'a, { - move |a, s, min_indent| match parser.parse(a, s, min_indent) { - Ok(t) => Ok(t), - Err((p, error, s)) => Err((p, map_error(a.alloc(error), s.pos()), s)), + move |a, state: State<'a>, min_indent| { + let original_state = state.clone(); + match parser.parse(a, state, min_indent) { + Ok(t) => Ok(t), + Err((p, error)) => Err((p, map_error(a.alloc(error), original_state.pos()))), + } } } @@ -1616,7 +1613,7 @@ where let state = state.advance(1); Ok((MadeProgress, (), state)) } - _ => Err((NoProgress, to_error(state.pos()), state)), + _ => Err((NoProgress, to_error(state.pos()))), } } @@ -1629,7 +1626,7 @@ where move |_arena: &'a Bump, state: State<'a>, min_indent: u32| { if min_indent > state.column() { - return Err((NoProgress, to_error(state.pos()), state)); + return Err((NoProgress, to_error(state.pos()))); } match state.bytes().first() { @@ -1637,7 +1634,7 @@ where let state = state.advance(1); Ok((MadeProgress, (), state)) } - _ => Err((NoProgress, to_error(state.pos()), state)), + _ => Err((NoProgress, to_error(state.pos()))), } } } @@ -1657,7 +1654,7 @@ where let state = state.advance(2); Ok((MadeProgress, (), state)) } else { - Err((NoProgress, to_error(state.pos()), state)) + Err((NoProgress, to_error(state.pos()))) } } } @@ -1683,7 +1680,7 @@ where let state = state.advance(3); Ok((MadeProgress, (), state)) } else { - Err((NoProgress, to_error(state.pos()), state)) + Err((NoProgress, to_error(state.pos()))) } } } @@ -1728,6 +1725,8 @@ macro_rules! zero_or_more { move |arena, state: State<'a>, min_indent: u32| { use bumpalo::collections::Vec; + let original_state = state.clone(); + let start_bytes_len = state.bytes().len(); match $parser.parse(arena, state, min_indent) { @@ -1738,16 +1737,17 @@ macro_rules! zero_or_more { buf.push(first_output); loop { + let old_state = state.clone(); match $parser.parse(arena, state, min_indent) { Ok((_, next_output, next_state)) => { state = next_state; buf.push(next_output); } - Err((fail_progress, fail, old_state)) => { + Err((fail_progress, fail)) => { match fail_progress { MadeProgress => { // made progress on an element and then failed; that's an error - return Err((MadeProgress, fail, old_state)); + return Err((MadeProgress, fail)); } NoProgress => { // the next element failed with no progress @@ -1760,16 +1760,16 @@ macro_rules! zero_or_more { } } } - Err((fail_progress, fail, new_state)) => { + Err((fail_progress, fail)) => { match fail_progress { MadeProgress => { // made progress on an element and then failed; that's an error - Err((MadeProgress, fail, new_state)) + Err((MadeProgress, fail)) } NoProgress => { // the first element failed (with no progress), but that's OK // because we only need to parse 0 elements - Ok((NoProgress, Vec::new_in(arena), new_state)) + Ok((NoProgress, Vec::new_in(arena), original_state)) } } } @@ -1792,15 +1792,16 @@ macro_rules! one_or_more { buf.push(first_output); loop { + let old_state = state.clone(); match $parser.parse(arena, state, min_indent) { Ok((_, next_output, next_state)) => { state = next_state; buf.push(next_output); } - Err((NoProgress, _, old_state)) => { + Err((NoProgress, _)) => { return Ok((MadeProgress, buf, old_state)); } - Err((MadeProgress, fail, old_state)) => { + Err((MadeProgress, fail)) => { return Err((MadeProgress, fail, old_state)); } } @@ -1826,19 +1827,22 @@ macro_rules! debug { #[macro_export] macro_rules! either { ($p1:expr, $p2:expr) => { - move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| match $p1 - .parse(arena, state, min_indent) - { - Ok((progress, output, state)) => { - Ok((progress, $crate::parser::Either::First(output), state)) - } - Err((NoProgress, _, state)) => match $p2.parse(arena, state, min_indent) { + move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| { + let original_state = state.clone(); + match $p1.parse(arena, state, min_indent) { Ok((progress, output, state)) => { - Ok((progress, $crate::parser::Either::Second(output), state)) + Ok((progress, $crate::parser::Either::First(output), state)) } - Err((progress, fail, state)) => Err((progress, fail, state)), - }, - Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)), + Err((NoProgress, _)) => { + match $p2.parse(arena, original_state.clone(), min_indent) { + Ok((progress, output, state)) => { + Ok((progress, $crate::parser::Either::Second(output), state)) + } + Err((progress, fail)) => Err((progress, fail)), + } + } + Err((MadeProgress, fail)) => Err((MadeProgress, fail)), + } } }; } @@ -1900,12 +1904,10 @@ where P: Parser<'a, Val, Error>, Error: 'a, { - move |arena: &'a Bump, state: State<'a>, min_indent: u32| { - let old_state = state.clone(); - - match parser.parse(arena, state, min_indent) { - Ok((_, a, s1)) => Ok((NoProgress, a, s1)), - Err((_, f, _)) => Err((NoProgress, f, old_state)), - } + move |arena: &'a Bump, state: State<'a>, min_indent: u32| match parser + .parse(arena, state, min_indent) + { + Ok((_, a, s1)) => Ok((NoProgress, a, s1)), + Err((_, f)) => Err((NoProgress, f)), } } diff --git a/crates/compiler/parse/src/pattern.rs b/crates/compiler/parse/src/pattern.rs index dc55c3dafc..bf9db278f7 100644 --- a/crates/compiler/parse/src/pattern.rs +++ b/crates/compiler/parse/src/pattern.rs @@ -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>, EPattern<'a>> { - parse_closure_param -} - -fn parse_closure_param<'a>( - arena: &'a Bump, - state: State<'a>, - min_indent: u32, -) -> ParseResult<'a, Loc>, EPattern<'a>> { +pub fn closure_param<'a>() -> impl Parser<'a, Loc>, 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>, 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>, 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>, EPattern<'a>> { +fn loc_parse_tag_pattern_arg<'a>() -> impl Parser<'a, Loc>, 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>, PInParens<'a>> { @@ -168,7 +150,7 @@ fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc>, 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>, PList<'a>> { @@ -248,12 +226,7 @@ fn list_element_pattern<'a>() -> impl Parser<'a, Loc>, PList<'a>> { } fn three_list_rest_pattern_error<'a>() -> impl Parser<'a, Loc>, 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>, 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>, PRecord<'a>> { diff --git a/crates/compiler/parse/src/state.rs b/crates/compiler/parse/src/state.rs index 0b86c801bf..d264bf3a20 100644 --- a/crates/compiler/parse/src/state.rs +++ b/crates/compiler/parse/src/state.rs @@ -58,9 +58,9 @@ impl<'a> State<'a> { &self, indent: u32, e: impl Fn(Position) -> E, - ) -> Result)> { + ) -> Result { if self.column() < indent { - Err((Progress::NoProgress, e(self.pos()), self.clone())) + Err((Progress::NoProgress, e(self.pos()))) } else { Ok(std::cmp::max(indent, self.line_indent())) } diff --git a/crates/compiler/parse/src/string_literal.rs b/crates/compiler/parse/src/string_literal.rs index dedff7ebc6..8cc1863717 100644 --- a/crates/compiler/parse/src/string_literal.rs +++ b/crates/compiler/parse/src/string_literal.rs @@ -19,7 +19,7 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> { buf.push(byte as char); } else if buf.is_empty() { // We didn't find any hex digits! - return Err((NoProgress, EString::CodePtEnd(state.pos()), state)); + return Err((NoProgress, EString::CodePtEnd(state.pos()))); } else { state.advance_mut(buf.len()); @@ -27,7 +27,7 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> { } } - Err((NoProgress, EString::CodePtEnd(state.pos()), state)) + Err((NoProgress, EString::CodePtEnd(state.pos()))) } } @@ -36,7 +36,7 @@ pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> { if state.consume_mut("\'") { // we will be parsing a single-quote-string } else { - return Err((NoProgress, EString::Open(state.pos()), state)); + return Err((NoProgress, EString::Open(state.pos()))); } // Handle back slaches in byte literal @@ -64,18 +64,18 @@ pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> { return Ok((MadeProgress, &*arena.alloc_str(&test.to_string()), state)); } // invalid error, backslah escaping something we do not recognize - return Err((NoProgress, EString::CodePtEnd(state.pos()), state)); + return Err((NoProgress, EString::CodePtEnd(state.pos()))); } None => { // no close quote found - return Err((NoProgress, EString::CodePtEnd(state.pos()), state)); + return Err((NoProgress, EString::CodePtEnd(state.pos()))); } } } Some(_) => { // do nothing for other characters, handled below } - None => return Err((NoProgress, EString::CodePtEnd(state.pos()), state)), + None => return Err((NoProgress, EString::CodePtEnd(state.pos()))), } let mut bytes = state.bytes().iter(); @@ -90,7 +90,7 @@ pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> { } Some(_) => end_index += 1, None => { - return Err((NoProgress, EString::Open(state.pos()), state)); + return Err((NoProgress, EString::Open(state.pos()))); } } } @@ -99,12 +99,12 @@ pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> { // no progress was made // this case is a double single quote, ex: '' // not supporting empty single quotes - return Err((NoProgress, EString::Open(state.pos()), state)); + return Err((NoProgress, EString::Open(state.pos()))); } if end_index > (std::mem::size_of::() + 1) { // bad case: too big to fit into u32 - return Err((NoProgress, EString::Open(state.pos()), state)); + return Err((NoProgress, EString::Open(state.pos()))); } // happy case -> we have some bytes that will fit into a u32 @@ -116,13 +116,13 @@ pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> { Ok(string) => Ok((MadeProgress, string, state)), Err(_) => { // invalid UTF-8 - return Err((NoProgress, EString::CodePtEnd(state.pos()), state)); + return Err((NoProgress, EString::CodePtEnd(state.pos()))); } } } } -fn consume_indent(mut state: State, mut indent: u32) -> Result { +fn consume_indent(mut state: State, mut indent: u32) -> Result { while indent > 0 { match state.bytes().first() { Some(b' ') => { @@ -136,7 +136,6 @@ fn consume_indent(mut state: State, mut indent: u32) -> Result Result( - state: State<'a>, - string_bytes: &'a [u8], -) -> Result<&'a str, (Progress, EString<'a>, State<'a>)> { +fn utf8<'a>(state: State<'a>, string_bytes: &'a [u8]) -> Result<&'a str, (Progress, EString<'a>)> { std::str::from_utf8(string_bytes).map_err(|_| { // Note Based on where this `utf8` function is used, the fact that we know the whole string // in the parser is valid utf8, and barring bugs in the parser itself @@ -156,7 +152,6 @@ fn utf8<'a>( ( MadeProgress, EString::Space(BadInputError::BadUtf8, state.pos()), - state, ) }) } @@ -186,7 +181,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> { // we will be parsing a single-line string is_multiline = false; } else { - return Err((NoProgress, EString::Open(state.pos()), state)); + return Err((NoProgress, EString::Open(state.pos()))); } let mut bytes = state.bytes().iter(); @@ -227,7 +222,6 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> { return Err(( MadeProgress, EString::Space(BadInputError::BadUtf8, state.pos()), - state, )); } } @@ -336,11 +330,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> { // all remaining chars. This will mask all other errors, but // it should make it easiest to debug; the file will be a giant // error starting from where the open quote appeared. - return Err(( - MadeProgress, - EString::EndlessSingle(start_state.pos()), - start_state, - )); + return Err((MadeProgress, EString::EndlessSingle(start_state.pos()))); } } b'\\' => { @@ -432,7 +422,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> { // Invalid escape! A backslash must be followed // by either an open paren or else one of the // escapable characters (\n, \t, \", \\, etc) - return Err((MadeProgress, EString::UnknownEscape(state.pos()), state)); + return Err((MadeProgress, EString::UnknownEscape(state.pos()))); } } } @@ -450,7 +440,6 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> { } else { EString::EndlessSingle(start_state.pos()) }, - start_state, )) } } diff --git a/crates/compiler/parse/src/type_annotation.rs b/crates/compiler/parse/src/type_annotation.rs index c293969183..4574935919 100644 --- a/crates/compiler/parse/src/type_annotation.rs +++ b/crates/compiler/parse/src/type_annotation.rs @@ -104,7 +104,7 @@ fn parse_type_alias_after_as<'a>() -> impl Parser<'a, TypeHeader<'a>, EType<'a>> match res { Ok(header) => Ok((progress, header, state)), - Err(err) => Err((progress, EType::TInlineAlias(err, state.pos()), state)), + Err(err) => Err((progress, EType::TInlineAlias(err, state.pos()))), } }, ) @@ -242,11 +242,13 @@ where F: Fn(Position) -> E, E: 'a, { - move |arena, state: State<'a>, min_indent: u32| match crate::ident::tag_name() - .parse(arena, state, min_indent) - { + move |arena, state: State<'a>, min_indent: u32| match crate::ident::tag_name().parse( + arena, + state.clone(), + min_indent, + ) { Ok(good) => Ok(good), - Err((progress, _, state)) => Err((progress, to_problem(state.pos()), state)), + Err((progress, _)) => Err((progress, to_problem(state.pos()))), } } @@ -645,14 +647,15 @@ fn concrete_type<'a>() -> impl Parser<'a, TypeAnnotation<'a>, ETypeApply> { move |arena: &'a Bump, state: State<'a>, min_indent: u32| { let initial_bytes = state.bytes(); - match crate::ident::concrete_type().parse(arena, state, min_indent) { + match crate::ident::concrete_type().parse(arena, state.clone(), min_indent) { Ok((_, (module_name, type_name), state)) => { let answer = TypeAnnotation::Apply(module_name, type_name, &[]); Ok((MadeProgress, answer, state)) } - Err((NoProgress, _, state)) => Err((NoProgress, ETypeApply::End(state.pos()), state)), - Err((MadeProgress, _, mut state)) => { + Err((NoProgress, _)) => Err((NoProgress, ETypeApply::End(state.pos()))), + Err((MadeProgress, _)) => { + let mut state = state.clone(); // we made some progress, but ultimately failed. // that means a malformed type name let chomped = crate::ident::chomp_malformed(state.bytes()); @@ -671,18 +674,20 @@ fn concrete_type<'a>() -> impl Parser<'a, TypeAnnotation<'a>, ETypeApply> { fn parse_type_variable<'a>( stop_at_surface_has: bool, ) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> { - move |arena, state: State<'a>, min_indent: u32| match crate::ident::lowercase_ident() - .parse(arena, state, min_indent) - { + move |arena, state: State<'a>, min_indent: u32| match crate::ident::lowercase_ident().parse( + arena, + state.clone(), + min_indent, + ) { Ok((_, name, state)) => { if name == "has" && stop_at_surface_has { - Err((NoProgress, EType::TEnd(state.pos()), state)) + Err((NoProgress, EType::TEnd(state.pos()))) } else { let answer = TypeAnnotation::BoundVariable(name); Ok((MadeProgress, answer, state)) } } - Err((progress, _, state)) => Err((progress, EType::TBadTypeVariable(state.pos()), state)), + Err((progress, _)) => Err((progress, EType::TBadTypeVariable(state.pos()))), } } diff --git a/crates/compiler/parse/tests/test_parse.rs b/crates/compiler/parse/tests/test_parse.rs index 82d35a635f..4c7c8a45b1 100644 --- a/crates/compiler/parse/tests/test_parse.rs +++ b/crates/compiler/parse/tests/test_parse.rs @@ -846,7 +846,7 @@ mod test_parse { Ok((_, _, _state)) => { // dbg!(_state); } - Err((_, _fail, _state)) => { + Err((_, _fail)) => { // dbg!(_fail, _state); panic!("Failed to parse!"); } diff --git a/crates/repl_cli/src/repl_state.rs b/crates/repl_cli/src/repl_state.rs index 0e2fc6f718..c05a5bf340 100644 --- a/crates/repl_cli/src/repl_state.rs +++ b/crates/repl_cli/src/repl_state.rs @@ -321,12 +321,12 @@ fn parse_src<'a>(arena: &'a Bump, line: &'a str) -> ParseOutcome<'a> { match roc_parse::expr::loc_expr().parse(arena, State::new(src_bytes), 0) { Ok((_, loc_expr, _)) => ParseOutcome::Expr(loc_expr.value), // Special case some syntax errors to allow for multi-line inputs - Err((_, EExpr::Closure(EClosure::Body(_, _), _), _)) - | Err((_, EExpr::When(EWhen::Pattern(EPattern::Start(_), _), _), _)) - | Err((_, EExpr::Start(_), _)) - | Err((_, EExpr::IndentStart(_), _)) => ParseOutcome::Incomplete, - Err((_, EExpr::DefMissingFinalExpr(_), _)) - | Err((_, EExpr::DefMissingFinalExpr2(_, _), _)) => { + Err((_, EExpr::Closure(EClosure::Body(_, _), _))) + | Err((_, EExpr::When(EWhen::Pattern(EPattern::Start(_), _), _))) + | Err((_, EExpr::Start(_))) + | Err((_, EExpr::IndentStart(_))) => ParseOutcome::Incomplete, + Err((_, EExpr::DefMissingFinalExpr(_))) + | Err((_, EExpr::DefMissingFinalExpr2(_, _))) => { // This indicates that we had an attempted def; re-parse it as a single-line def. match parse_single_def( ExprParseOptions { diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index 862cf903f2..e9ed73dd68 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -1547,7 +1547,7 @@ fn to_unexpected_arrow_report<'a>( ), alloc.concat([ alloc.reflow(r"It makes sense to see arrows around here, "), - alloc.reflow(r"so I suspect it is something earlier."), + alloc.reflow(r"so I suspect it is something earlier. "), alloc.reflow( r"Maybe this pattern is indented a bit farther from the previous patterns?", ), diff --git a/crates/reporting/tests/test_reporting.rs b/crates/reporting/tests/test_reporting.rs index 56d0e73105..11837be69a 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -5011,12 +5011,13 @@ mod test_reporting { I am parsing a `when` expression right now, but this arrow is confusing me: + 5│ 5 -> 2 6│ _ -> 2 ^^ It makes sense to see arrows around here, so I suspect it is something - earlier.Maybe this pattern is indented a bit farther from the previous - patterns? + earlier. Maybe this pattern is indented a bit farther from the + previous patterns? Note: Here is an example of a valid `when` expression for reference. @@ -5047,12 +5048,13 @@ mod test_reporting { I am parsing a `when` expression right now, but this arrow is confusing me: + 5│ 5 -> Num.neg 6│ 2 -> 2 ^^ It makes sense to see arrows around here, so I suspect it is something - earlier.Maybe this pattern is indented a bit farther from the previous - patterns? + earlier. Maybe this pattern is indented a bit farther from the + previous patterns? Note: Here is an example of a valid `when` expression for reference. @@ -5295,6 +5297,7 @@ mod test_reporting { This multiline string is not sufficiently indented: + 4│ """ 5│ testing ^ @@ -8069,6 +8072,7 @@ All branches in an `if` must have the same type! I was partway through parsing an ability definition, but I got stuck here: + 4│ MEq has 5│ eq b c : a, a -> U64 | a has MEq ^