diff --git a/compiler/parse/src/blankspace.rs b/compiler/parse/src/blankspace.rs index 10b256b9b8..541936c861 100644 --- a/compiler/parse/src/blankspace.rs +++ b/compiler/parse/src/blankspace.rs @@ -110,6 +110,60 @@ where ) } +pub fn space0_around_ee<'a, P, S, E>( + parser: P, + min_indent: u16, + space_problem: fn(BadInputError, Row, Col) -> E, + indent_before_problem: fn(Row, Col) -> E, + indent_after_problem: fn(Row, Col) -> E, +) -> impl Parser<'a, Located, E> +where + S: Spaceable<'a>, + S: 'a, + P: Parser<'a, Located, E>, + P: 'a, + E: 'a, +{ + parser::map_with_arena( + and( + space0_e(min_indent, space_problem, indent_before_problem), + and( + parser, + space0_e(min_indent, space_problem, indent_after_problem), + ), + ), + move |arena: &'a Bump, + tuples: ( + &'a [CommentOrNewline<'a>], + (Located, &'a [CommentOrNewline<'a>]), + )| { + let (spaces_before, (loc_val, spaces_after)) = tuples; + + if spaces_before.is_empty() { + if spaces_after.is_empty() { + loc_val + } else { + arena + .alloc(loc_val.value) + .with_spaces_after(spaces_after, loc_val.region) + } + } else if spaces_after.is_empty() { + arena + .alloc(loc_val.value) + .with_spaces_before(spaces_before, loc_val.region) + } else { + let wrapped_expr = arena + .alloc(loc_val.value) + .with_spaces_after(spaces_after, loc_val.region); + + arena + .alloc(wrapped_expr.value) + .with_spaces_before(spaces_before, wrapped_expr.region) + } + }, + ) +} + /// Parses the given expression with 1 or more (spaces/comments/newlines) before it, /// and also 1 or more spaces after it. /// Returns a Located where the location is around the Expr, ignoring the spaces. diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 8d187f10fb..9a7e593658 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -3,7 +3,8 @@ use crate::ast::{ }; use crate::blankspace::{ line_comment, space0, space0_after, space0_after_e, space0_around, space0_around_e, - space0_before, space0_before_e, space0_e, space1, space1_around, space1_before, spaces_exactly, + space0_around_ee, space0_before, space0_before_e, space0_e, space1, space1_around, + space1_before, spaces_exactly, }; use crate::ident::{global_tag_or_ident, ident, lowercase_ident, Ident}; use crate::keyword; @@ -11,8 +12,8 @@ use crate::number_literal::number_literal; use crate::parser::{ self, allocated, and_then_with_indent_level, ascii_char, ascii_string, attempt, backtrackable, fail, map, newline_char, not, not_followed_by, optional, sep_by1, specialize, specialize_ref, - then, unexpected, unexpected_eof, word1, word2, EExpr, Either, ParseResult, Parser, State, - SyntaxError, When, + then, unexpected, unexpected_eof, word1, word2, EExpr, ELambda, Either, ParseResult, Parser, + State, SyntaxError, When, }; use crate::pattern::loc_closure_param; use crate::type_annotation; @@ -596,7 +597,13 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>, SyntaxError<'a>> { // PARSER HELPERS fn pattern<'a>(min_indent: u16) -> impl Parser<'a, Located>, SyntaxError<'a>> { - space0_after(loc_closure_param(min_indent), min_indent) + space0_after( + specialize( + |e, _, _| SyntaxError::Pattern(e), + loc_closure_param(min_indent), + ), + min_indent, + ) } fn spaces_then_comment_or_newline<'a>() -> impl Parser<'a, Option<&'a str>, SyntaxError<'a>> { @@ -971,32 +978,44 @@ fn reserved_keyword<'a>() -> impl Parser<'a, (), SyntaxError<'a>> { } fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> { + specialize( + |e, r, c| SyntaxError::Expr(EExpr::Lambda(e, r, c)), + closure_help(min_indent), + ) +} + +fn closure_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, ELambda<'a>> { map_with_arena!( skip_first!( // All closures start with a '\' - e.g. (\x -> x + 1) - ascii_char(b'\\'), + word1(b'\\', ELambda::Start), // Once we see the '\', we're committed to parsing this as a closure. // It may turn out to be malformed, but it is definitely a closure. optional(and!( // Parse the params - attempt!( - Attempting::ClosureParams, - // Params are comma-separated - sep_by1( - ascii_char(b','), - space0_around(loc_closure_param(min_indent), min_indent) + // Params are comma-separated + sep_by1( + word1(b',', ELambda::Comma), + space0_around_ee( + specialize(ELambda::Pattern, loc_closure_param(min_indent)), + min_indent, + ELambda::Space, + ELambda::IndentArg, + ELambda::IndentArrow ) ), skip_first!( // Parse the -> which separates params from body - ascii_string("->"), + word2(b'-', b'>', ELambda::Arrow), // Parse the body - attempt!( - Attempting::ClosureBody, - space0_before( - loc!(move |arena, state| parse_expr(min_indent, arena, state)), - min_indent, - ) + space0_before_e( + specialize_ref( + ELambda::Syntax, + loc!(move |arena, state| parse_expr(min_indent, arena, state)) + ), + min_indent, + ELambda::Space, + ELambda::IndentBody ) ) )) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 71d2259bc1..2d1cf0fdb0 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -379,11 +379,28 @@ pub enum EExpr<'a> { When(When<'a>, Row, Col), + Lambda(ELambda<'a>, Row, Col), + // EInParens(PInParens<'a>, Row, Col), IndentStart(Row, Col), IndentEnd(Row, Col), } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ELambda<'a> { + Space(BadInputError, Row, Col), + Start(Row, Col), + Arrow(Row, Col), + Comma(Row, Col), + // TODO make EEXpr + Pattern(EPattern<'a>, Row, Col), + Syntax(&'a SyntaxError<'a>, Row, Col), + + IndentArrow(Row, Col), + IndentBody(Row, Col), + IndentArg(Row, Col), +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum When<'a> { Space(BadInputError, Row, Col), diff --git a/compiler/parse/src/pattern.rs b/compiler/parse/src/pattern.rs index b9b3f576e6..b664797cf5 100644 --- a/compiler/parse/src/pattern.rs +++ b/compiler/parse/src/pattern.rs @@ -26,11 +26,8 @@ pub enum PatternType { pub fn loc_closure_param<'a>( min_indent: u16, -) -> impl Parser<'a, Located>, SyntaxError<'a>> { - specialize( - |e, _, _| SyntaxError::Pattern(e), - move |arena, state| parse_closure_param(arena, state, min_indent), - ) +) -> impl Parser<'a, Located>, EPattern<'a>> { + move |arena, state| parse_closure_param(arena, state, min_indent) } fn parse_closure_param<'a>(