diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 5e3463604e..03006199d2 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -9,8 +9,9 @@ use crate::ident::{global_tag_or_ident, ident, lowercase_ident, Ident}; use crate::keyword; use crate::number_literal::number_literal; use crate::parser::{ - self, allocated, ascii_char, ascii_string, fail, not, not_followed_by, optional, sep_by1, then, - unexpected, unexpected_eof, Either, Fail, FailReason, ParseResult, Parser, State, + self, allocated, ascii_char, ascii_string, fail, newline_char, not, not_followed_by, optional, + sep_by1, then, unexpected, unexpected_eof, Either, Fail, FailReason, ParseResult, Parser, + State, }; use crate::type_annotation; use bumpalo::collections::string::String; @@ -552,7 +553,7 @@ fn spaces_then_comment_or_newline<'a>() -> impl Parser<'a, Option<&'a str>> { skip_first!( zero_or_more!(ascii_char(' ')), map!( - either!(ascii_char('\n'), line_comment()), + either!(newline_char(), line_comment()), |either_comment_or_newline| match either_comment_or_newline { Either::First(_) => None, Either::Second(comment) => Some(comment), @@ -1444,8 +1445,11 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located') + ascii_char(' '), + ascii_char('#'), + ascii_char('>') ), ), move |arena, state, (spaces, num_or_minus_char)| { diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 16e304c39f..4aa3e57073 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -432,8 +432,12 @@ fn line_too_long(attempting: Attempting, state: State<'_>) -> (Fail, State<'_>) (fail, state) } -/// A single ASCII char. +/// A single ASCII char that isn't a newline. +/// (For newlines, use newline_char(), which handles line numbers) pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, ()> { + // Make sure this really is not a newline! + debug_assert_ne!(expected, '\n'); + move |_arena, state: State<'a>| match state.bytes.first() { Some(&actual) if expected == actual => Ok(((), state.advance_without_indenting(1)?)), Some(_) => Err(unexpected(0, state, Attempting::Keyword)), @@ -441,6 +445,17 @@ pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, ()> { } } +/// A single '\n' character. +/// Use this instead of ascii_char('\n') because it properly handles +/// incrementing the line number. +pub fn newline_char<'a>() -> impl Parser<'a, ()> { + move |_arena, state: State<'a>| match state.bytes.first() { + Some(b'\n') => Ok(((), state.newline()?)), + Some(_) => Err(unexpected(0, state, Attempting::Keyword)), + _ => Err(unexpected_eof(0, Attempting::Keyword, state)), + } +} + /// One or more ASCII hex digits. (Useful when parsing unicode escape codes, /// which must consist entirely of ASCII hex digits.) pub fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str> {