This commit is contained in:
Folkert 2021-03-11 16:40:01 +01:00
parent e214674016
commit b4b77add08

View file

@ -1,64 +1,15 @@
use crate::ast::CommentOrNewline::{self, *}; use crate::ast::CommentOrNewline::{self, *};
use crate::ast::{Attempting, Spaceable}; use crate::ast::{Attempting, Spaceable};
use crate::parser::{ use crate::parser::{
self, and, ascii_char, ascii_string, backtrackable, bad_input_to_syntax_error, optional, self, and, ascii_char, ascii_string, optional, parse_utf8, peek_utf8_char, then, unexpected,
parse_utf8, peek_utf8_char, then, unexpected, unexpected_eof, BadInputError, Col, Parser, unexpected_eof, BadInputError, Col, Parser,
Progress::{self, *}, Progress::{self, *},
Row, State, SyntaxError, Row, State, SyntaxError,
}; };
use bumpalo::collections::string::String; use bumpalo::collections::string::String;
use bumpalo::collections::vec::Vec; use bumpalo::collections::vec::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_region::all::{Located, Region}; use roc_region::all::Located;
/// Parses the given expression with 0 or more (spaces/comments/newlines) before and/or after it.
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
/// If any newlines or comments were found, the Expr will be wrapped in a SpaceBefore and/or
/// SpaceAfter as appropriate.
fn space0_around<'a, P, S>(
parser: P,
min_indent: u16,
) -> impl Parser<'a, Located<S>, SyntaxError<'a>>
where
S: Spaceable<'a>,
S: 'a,
S: Sized,
P: Parser<'a, Located<S>, SyntaxError<'a>>,
P: 'a,
{
parser::map_with_arena(
and(space0(min_indent), and(parser, space0(min_indent))),
move |arena: &'a Bump,
tuples: (
&'a [CommentOrNewline<'a>],
(Located<S>, &'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)
}
},
)
}
pub fn space0_around_ee<'a, P, S, E>( pub fn space0_around_ee<'a, P, S, E>(
parser: P, parser: P,
@ -114,76 +65,6 @@ where
) )
} }
/// Parses the given expression with 1 or more (spaces/comments/newlines) before it,
/// and also 1 or more spaces after it.
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
/// If any newlines or comments were found, the Expr will be wrapped in a SpaceBefore and/or
/// SpaceAfter as appropriate.
fn space1_around<'a, P, S>(
parser: P,
min_indent: u16,
) -> impl Parser<'a, Located<S>, SyntaxError<'a>>
where
S: Spaceable<'a>,
S: 'a,
P: Parser<'a, Located<S>, SyntaxError<'a>>,
P: 'a,
{
parser::map_with_arena(
and(space1(min_indent), and(parser, space1(min_indent))),
|arena, (spaces_before, (loc_expr, spaces_after))| {
if spaces_before.is_empty() {
if spaces_after.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_after(spaces_after, loc_expr.region)
}
} else if spaces_after.is_empty() {
arena
.alloc(loc_expr.value)
.with_spaces_before(spaces_before, loc_expr.region)
} else {
let loc_wrapped_expr = arena
.alloc(loc_expr.value)
.with_spaces_after(spaces_after, loc_expr.region);
arena
.alloc(loc_wrapped_expr.value)
.with_spaces_before(spaces_before, loc_wrapped_expr.region)
}
},
)
}
/// Parses the given expression with 0 or more (spaces/comments/newlines) before it.
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
/// The Expr will be wrapped in a SpaceBefore if there were any newlines or comments found.
fn space0_before<'a, P, S>(
parser: P,
min_indent: u16,
) -> impl Parser<'a, Located<S>, SyntaxError<'a>>
where
S: Spaceable<'a>,
S: 'a,
P: Parser<'a, Located<S>, SyntaxError<'a>>,
P: 'a,
{
parser::map_with_arena(
and!(space0(min_indent), parser),
|arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Located<S>)| {
if space_list.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_before(space_list, loc_expr.region)
}
},
)
}
pub fn space0_before_e<'a, P, S, E>( pub fn space0_before_e<'a, P, S, E>(
parser: P, parser: P,
min_indent: u16, min_indent: u16,
@ -238,92 +119,6 @@ where
) )
} }
/// Parses the given expression with 1 or more (spaces/comments/newlines) before it.
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
/// The Expr will be wrapped in a SpaceBefore if there were any newlines or comments found.
fn space1_before<'a, P, S>(
parser: P,
min_indent: u16,
) -> impl Parser<'a, Located<S>, SyntaxError<'a>>
where
S: Spaceable<'a>,
S: 'a,
P: Parser<'a, Located<S>, SyntaxError<'a>>,
P: 'a,
{
parser::map_with_arena(
and!(backtrackable(space1(min_indent)), parser),
|arena, (space_list, loc_expr)| {
if space_list.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_before(space_list, loc_expr.region)
}
},
)
}
/// Parses the given expression with 0 or more (spaces/comments/newlines) after it.
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
/// The Expr will be wrapped in a SpaceAfter if there were any newlines or comments found.
fn space0_after<'a, P, S>(
parser: P,
min_indent: u16,
) -> impl Parser<'a, Located<S>, SyntaxError<'a>>
where
S: Spaceable<'a>,
S: 'a,
P: Parser<'a, Located<S>, SyntaxError<'a>>,
P: 'a,
{
parser::map_with_arena(
and!(parser, space0(min_indent)),
|arena, (loc_expr, space_list)| {
if space_list.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_after(space_list, loc_expr.region)
}
},
)
}
/// Parses the given expression with 1 or more (spaces/comments/newlines) after it.
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
/// The Expr will be wrapped in a SpaceAfter if there were any newlines or comments found.
fn space1_after<'a, P, S>(
parser: P,
min_indent: u16,
) -> impl Parser<'a, Located<S>, SyntaxError<'a>>
where
S: Spaceable<'a>,
S: 'a,
P: Parser<'a, Located<S>, SyntaxError<'a>>,
P: 'a,
{
parser::map_with_arena(
and!(parser, space1(min_indent)),
|arena, (loc_expr, space_list)| {
if space_list.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_after(space_list, loc_expr.region)
}
},
)
}
/// Zero or more (spaces/comments/newlines).
fn space0<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> {
spaces(false, min_indent)
}
pub fn space0_e<'a, E>( pub fn space0_e<'a, E>(
min_indent: u16, min_indent: u16,
space_problem: fn(BadInputError, Row, Col) -> E, space_problem: fn(BadInputError, Row, Col) -> E,
@ -359,15 +154,6 @@ where
} }
} }
/// One or more (spaces/comments/newlines).
fn space1<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> {
// TODO try benchmarking a short-circuit for the typical case: see if there is
// exactly one space followed by char that isn't [' ', '\n', or '#'], and
// if so, return empty slice. The case where there's exactly 1 space should
// be by far the most common.
spaces(true, min_indent)
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum LineState { enum LineState {
Normal, Normal,
@ -512,20 +298,6 @@ pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser:
} }
} }
#[inline(always)]
fn spaces<'a>(
require_at_least_one: bool,
min_indent: u16,
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError<'a>> {
spaces_help(
require_at_least_one,
min_indent,
bad_input_to_syntax_error,
|_, _| SyntaxError::OutdentedTooFar,
|_, _| SyntaxError::Eof(Region::zero()),
)
}
#[inline(always)] #[inline(always)]
fn spaces_help<'a, E>( fn spaces_help<'a, E>(
require_at_least_one: bool, require_at_least_one: bool,