diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index ebfbf67101..dda6227ddc 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -1,7 +1,7 @@ use crate::ast::{AssignedField, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation}; use crate::blankspace::{ line_comment, space0, space0_after_e, space0_around_ee, space0_before, space0_before_e, - space0_e, space1, space1_before, spaces_exactly_e, + space0_e, space1, spaces_exactly_e, }; use crate::ident::{ident, lowercase_ident, Ident}; use crate::keyword; @@ -776,26 +776,30 @@ fn equals_for_def_help<'a>() -> impl Parser<'a, (), EExpr<'a>> { } } -fn parse_defs<'a>( - min_indent: u16, -) -> impl Parser<'a, Vec<'a, &'a Located>>, SyntaxError<'a>> { - specialize(|e, _, _| SyntaxError::Expr(e), parse_defs_help(min_indent)) -} - fn parse_defs_help<'a>( min_indent: u16, ) -> impl Parser<'a, Vec<'a, &'a Located>>, EExpr<'a>> { - let parse_def = move |a, s| { - space0_before_e( - loc!(def_help(min_indent)), - min_indent, - EExpr::Space, - EExpr::IndentStart, + let parse_def = move |arena, state| { + let (_, (spaces, def), state) = and!( + backtrackable(space0_e(min_indent, EExpr::Space, EExpr::IndentStart)), + loc!(def_help(min_indent)) ) - .parse(a, s) + .parse(arena, state)?; + + let result = if spaces.is_empty() { + &*arena.alloc(def) + } else { + &*arena.alloc( + arena + .alloc(def.value) + .with_spaces_before(spaces, def.region), + ) + }; + + Ok((MadeProgress, result, state)) }; - zero_or_more!(allocated(parse_def)) + zero_or_more!(parse_def) } /// A definition, consisting of one of these: @@ -1035,44 +1039,19 @@ fn parse_def_expr_help<'a>( loc_first_pattern: Located>, spaces_after_equals: &'a [CommentOrNewline<'a>], ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { - let result = parse_def_expr( - min_indent, - def_start_col, - equals_sign_indent, - arena, - state, - loc_first_pattern, - spaces_after_equals, - ); - - match result { - Ok(good) => Ok(good), - Err((progress, fail, state)) => { - let row = state.line; - let col = state.column; - Err((progress, EExpr::Def(arena.alloc(fail), row, col), state)) - } - } -} - -fn parse_def_expr<'a>( - min_indent: u16, - def_start_col: u16, - equals_sign_indent: u16, - arena: &'a Bump, - state: State<'a>, - loc_first_pattern: Located>, - spaces_after_equals: &'a [CommentOrNewline<'a>], -) -> ParseResult<'a, Expr<'a>, SyntaxError<'a>> { if def_start_col < min_indent { - Err((NoProgress, SyntaxError::OutdentedTooFar, state)) - // `<` because '=' should be same indent (or greater) as the entire def-expr + Err(( + NoProgress, + EExpr::IndentDefBody(state.line, state.column), + state, + )) } else if equals_sign_indent < def_start_col { - let msg = format!( - r"TODO the = in this declaration seems outdented. equals_sign_indent was {} and def_start_col was {}", - equals_sign_indent, def_start_col - ); - Err((NoProgress, SyntaxError::NotYetImplemented(msg), state)) + // `<` because '=' should be same indent or greater + Err(( + NoProgress, + EExpr::IndentDefBody(state.line, state.column), + state, + )) } else { // Indented more beyond the original indent of the entire def-expr. let indented_more = def_start_col + 1; @@ -1085,18 +1064,20 @@ fn parse_def_expr<'a>( // // It should be indented more than the original, and it will // end when outdented again. - loc!(move |arena, state| parse_expr(indented_more, arena, state)), + loc!(move |arena, state| parse_expr_help(indented_more, arena, state)), and!( // Optionally parse additional defs. - parse_defs(def_start_col), + debug!(parse_defs_help(def_start_col)), // Parse the final expression that will be returned. // It should be indented the same amount as the original. - space1_before( + debug!(space0_before_e( loc!(move |arena, state: State<'a>| { - parse_expr(def_start_col, arena, state) + parse_expr_help(def_start_col, arena, state) }), def_start_col, - ) + EExpr::Space, + EExpr::IndentStart, + )) ) ), move |arena, state, progress, (loc_first_body, (mut defs, loc_ret))| { diff --git a/compiler/reporting/src/error/parse.rs b/compiler/reporting/src/error/parse.rs index 13cc54c0c5..0cb157a5c1 100644 --- a/compiler/reporting/src/error/parse.rs +++ b/compiler/reporting/src/error/parse.rs @@ -276,6 +276,28 @@ fn to_expr_report<'a>( } } + EExpr::Colon(row, col) => { + let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col); + let region = Region::from_row_col(*row, *col); + + let doc = alloc.stack(vec![ + alloc.reflow(r"I am in the middle of parsing a definition, but I got stuck here:"), + alloc.region_with_subregion(surroundings, region), + alloc.concat(vec![ + alloc.reflow("Looks like you are trying to define a function. "), + alloc.reflow("In roc, functions are always written as a lambda, like "), + alloc.parser_suggestion("increment = \\n -> n + 1"), + alloc.reflow("."), + ]), + ]); + + Report { + filename, + doc, + title: "ARGUMENTS BEFORE EQUALS".to_string(), + } + } + _ => todo!("unhandled parse error: {:?}", parse_problem), } } diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index fd8469ee98..d24c680159 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -4103,6 +4103,7 @@ mod test_reporting { I am partway through parsing a definition, but I got stuck here: + 1│ main = 2│ 5 ** 3 ^