Merge pull request #6648 from JackoCoolio/unmacro_parsers

Unmacro parser combinators
This commit is contained in:
Anton-4 2024-06-25 12:54:22 +02:00 committed by GitHub
commit 1b10772bcb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 774 additions and 914 deletions

View file

@ -1,5 +1,6 @@
use crate::ast::CommentOrNewline;
use crate::ast::Spaceable;
use crate::parser::succeed;
use crate::parser::Progress;
use crate::parser::SpaceProblem;
use crate::parser::{self, and, backtrackable, BadInputError, Parser, Progress::*};
@ -70,7 +71,7 @@ where
parser,
one_of![
backtrackable(space0_e(indent_after_problem)),
succeed!(&[] as &[_]),
succeed(&[] as &[_]),
],
),
),
@ -89,7 +90,7 @@ where
spaces(),
and(
parser,
one_of![backtrackable(spaces()), succeed!(&[] as &[_]),],
one_of![backtrackable(spaces()), succeed(&[] as &[_]),],
),
),
spaces_around_help,
@ -138,7 +139,7 @@ where
E: 'a + SpaceProblem,
{
parser::map_with_arena(
and!(spaces(), parser),
and(spaces(), parser),
|arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Loc<S>)| {
if space_list.is_empty() {
loc_expr
@ -161,7 +162,7 @@ where
E: 'a + SpaceProblem,
{
parser::map_with_arena(
and!(space0_e(indent_problem), parser),
and(space0_e(indent_problem), parser),
|arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Loc<S>)| {
if space_list.is_empty() {
loc_expr
@ -184,7 +185,7 @@ where
E: 'a + SpaceProblem,
{
parser::map_with_arena(
and!(parser, space0_e(indent_problem)),
and(parser, space0_e(indent_problem)),
|arena: &'a Bump, (loc_expr, space_list): (Loc<S>, &'a [CommentOrNewline<'a>])| {
if space_list.is_empty() {
loc_expr

View file

@ -14,9 +14,11 @@ use crate::ident::{
};
use crate::module::module_name_help;
use crate::parser::{
self, backtrackable, byte, byte_indent, increment_min_indent, line_min_indent, optional,
reset_min_indent, sep_by1, sep_by1_e, set_min_indent, specialize_err, specialize_err_ref, then,
two_bytes, EClosure, EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber,
self, and, backtrackable, between, byte, byte_indent, collection_inner,
collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first,
line_min_indent, loc, map, map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e,
set_min_indent, skip_first, skip_second, specialize_err, specialize_err_ref, then, two_bytes,
zero_or_more, EClosure, EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber,
EPattern, ERecord, EString, EType, EWhen, Either, ParseResult, Parser,
};
use crate::pattern::{closure_param, loc_implements_parser};
@ -48,9 +50,9 @@ pub fn test_parse_expr<'a>(
arena: &'a bumpalo::Bump,
state: State<'a>,
) -> Result<Loc<Expr<'a>>, EExpr<'a>> {
let parser = skip_second!(
let parser = skip_second(
space0_before_optional_after(loc_expr(true), EExpr::IndentStart, EExpr::IndentEnd),
expr_end()
expr_end(),
);
match parser.parse(arena, state, min_indent) {
@ -84,12 +86,12 @@ pub fn expr_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
fn loc_expr_in_parens_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EInParens<'a>> {
then(
loc!(collection_trailing_sep_e!(
loc(collection_trailing_sep_e(
byte(b'(', EInParens::Open),
specialize_err_ref(EInParens::Expr, loc_expr(false)),
byte(b',', EInParens::End),
byte(b')', EInParens::End),
Expr::SpaceBefore
Expr::SpaceBefore,
)),
move |arena, state, _, loc_elements| {
let elements = loc_elements.value;
@ -121,10 +123,10 @@ fn loc_expr_in_parens_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EInParens<'a>
}
fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
map_with_arena!(
loc!(and!(
map_with_arena(
loc(and(
specialize_err(EExpr::InParens, loc_expr_in_parens_help()),
record_field_access_chain()
record_field_access_chain(),
)),
move |arena: &'a Bump, value: Loc<(Loc<Expr<'a>>, Vec<'a, Suffix<'a>>)>| {
let Loc {
@ -143,27 +145,27 @@ fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>
}
Loc::at(region, value)
}
},
)
}
fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr<'a>> {
zero_or_more!(one_of!(
skip_first!(
zero_or_more(one_of!(
skip_first(
byte(b'.', EExpr::Access),
specialize_err(
|_, pos| EExpr::Access(pos),
one_of!(
map!(lowercase_ident(), |x| Suffix::Accessor(
map(lowercase_ident(), |x| Suffix::Accessor(
Accessor::RecordField(x)
)),
map!(integer_ident(), |x| Suffix::Accessor(Accessor::TupleIndex(
map(integer_ident(), |x| Suffix::Accessor(Accessor::TupleIndex(
x
))),
)
)
),
map!(byte(b'!', EExpr::Access), |_| Suffix::TaskAwaitBang),
map(byte(b'!', EExpr::Access), |_| Suffix::TaskAwaitBang),
))
}
@ -174,18 +176,18 @@ fn loc_term_or_underscore_or_conditional<'a>(
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of!(
loc_expr_in_parens_etc_help(),
loc!(specialize_err(EExpr::If, if_expr_help(options))),
loc!(specialize_err(EExpr::When, when::expr_help(options))),
loc!(specialize_err(EExpr::Str, string_like_literal_help())),
loc!(specialize_err(
loc(specialize_err(EExpr::If, if_expr_help(options))),
loc(specialize_err(EExpr::When, when::expr_help(options))),
loc(specialize_err(EExpr::Str, string_like_literal_help())),
loc(specialize_err(
EExpr::Number,
positive_number_literal_help()
)),
loc!(specialize_err(EExpr::Closure, closure_help(options))),
loc!(crash_kw()),
loc!(underscore_expression()),
loc!(record_literal_help()),
loc!(specialize_err(EExpr::List, list_literal_help())),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(crash_kw()),
loc(underscore_expression()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
ident_seq(),
)
}
@ -197,15 +199,15 @@ fn loc_term_or_underscore<'a>(
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of!(
loc_expr_in_parens_etc_help(),
loc!(specialize_err(EExpr::Str, string_like_literal_help())),
loc!(specialize_err(
loc(specialize_err(EExpr::Str, string_like_literal_help())),
loc(specialize_err(
EExpr::Number,
positive_number_literal_help()
)),
loc!(specialize_err(EExpr::Closure, closure_help(options))),
loc!(underscore_expression()),
loc!(record_literal_help()),
loc!(specialize_err(EExpr::List, list_literal_help())),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(underscore_expression()),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
ident_seq(),
)
}
@ -213,14 +215,14 @@ fn loc_term_or_underscore<'a>(
fn loc_term<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of!(
loc_expr_in_parens_etc_help(),
loc!(specialize_err(EExpr::Str, string_like_literal_help())),
loc!(specialize_err(
loc(specialize_err(EExpr::Str, string_like_literal_help())),
loc(specialize_err(
EExpr::Number,
positive_number_literal_help()
)),
loc!(specialize_err(EExpr::Closure, closure_help(options))),
loc!(record_literal_help()),
loc!(specialize_err(EExpr::List, list_literal_help())),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(record_literal_help()),
loc(specialize_err(EExpr::List, list_literal_help())),
ident_seq(),
)
}
@ -235,7 +237,7 @@ fn parse_ident_seq<'a>(
min_indent: u32,
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
let (_, loc_ident, state) =
loc!(assign_or_destructure_identifier()).parse(arena, state, min_indent)?;
loc(assign_or_destructure_identifier()).parse(arena, state, min_indent)?;
let expr = ident_to_expr(arena, loc_ident.value);
let (_p, suffixes, state) = record_field_access_chain()
.trace("record_field_access_chain")
@ -280,7 +282,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
let initial = state.clone();
let (_, (loc_op, loc_expr), state) =
and!(loc!(unary_negate()), loc_term(options)).parse(arena, state, min_indent)?;
and(loc(unary_negate()), loc_term(options)).parse(arena, state, min_indent)?;
let loc_expr = numeric_negate_expression(arena, initial, loc_op, loc_expr, &[]);
@ -290,10 +292,10 @@ fn loc_possibly_negative_or_negated_term<'a>(
one_of![
parse_unary_negate,
// this will parse negative numbers, which the unary negate thing up top doesn't (for now)
loc!(specialize_err(EExpr::Number, number_literal_help())),
loc!(map_with_arena!(
and!(
loc!(byte(b'!', EExpr::Start)),
loc(specialize_err(EExpr::Number, number_literal_help())),
loc(map_with_arena(
and(
loc(byte(b'!', EExpr::Start)),
space0_before_e(loc_term(options), EExpr::IndentStart)
),
|arena: &'a Bump, (loc_op, loc_expr): (Loc<_>, _)| {
@ -333,13 +335,13 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
one_of![
loc!(specialize_err(EExpr::If, if_expr_help(options))),
loc!(specialize_err(EExpr::When, when::expr_help(options))),
loc!(specialize_err(EExpr::Expect, expect_help(options))),
loc!(specialize_err(EExpr::Dbg, dbg_help(options))),
loc!(import_help(options)),
loc!(specialize_err(EExpr::Closure, closure_help(options))),
loc!(expr_operator_chain(options)),
loc(specialize_err(EExpr::If, if_expr_help(options))),
loc(specialize_err(EExpr::When, when::expr_help(options))),
loc(specialize_err(EExpr::Expect, expect_help(options))),
loc(specialize_err(EExpr::Dbg, dbg_help(options))),
loc(import_help(options)),
loc(specialize_err(EExpr::Closure, closure_help(options))),
loc(expr_operator_chain(options)),
fail_expr_start_e()
]
.trace("expr_start")
@ -633,7 +635,7 @@ pub fn parse_single_def<'a>(
let parse_expect_vanilla = crate::parser::keyword(crate::keyword::EXPECT, EExpect::Expect);
let parse_expect_fx = crate::parser::keyword(crate::keyword::EXPECT_FX, EExpect::Expect);
let parse_expect = either!(parse_expect_fx, parse_expect_vanilla);
let parse_expect = either(parse_expect_fx, parse_expect_vanilla);
match space0_after_e(crate::pattern::loc_pattern_help(), EPattern::IndentEnd).parse(
arena,
@ -956,12 +958,12 @@ pub fn parse_single_def<'a>(
fn import<'a>() -> impl Parser<'a, (Loc<ValueDef<'a>>, &'a [CommentOrNewline<'a>]), EImport<'a>> {
then(
and!(
loc!(skip_first!(
and(
loc(skip_first(
parser::keyword(keyword::IMPORT, EImport::Import),
increment_min_indent(one_of!(import_body(), import_ingested_file_body()))
increment_min_indent(one_of!(import_body(), import_ingested_file_body())),
)),
space0_e(EImport::EndNewline)
space0_e(EImport::EndNewline),
),
|_arena, state, progress, (import, spaces_after)| {
if !spaces_after.is_empty() || state.has_reached_end() {
@ -975,23 +977,23 @@ fn import<'a>() -> impl Parser<'a, (Loc<ValueDef<'a>>, &'a [CommentOrNewline<'a>
}
fn import_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
map!(
map(
record!(ModuleImport {
before_name: space0_e(EImport::IndentStart),
name: loc!(imported_module_name()),
name: loc(imported_module_name()),
params: optional(specialize_err(EImport::Params, import_params())),
alias: optional(import_as()),
exposed: optional(import_exposing())
}),
ValueDef::ModuleImport
ValueDef::ModuleImport,
)
}
fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<'a>> {
then(
and!(
and(
backtrackable(space0_e(EImportParams::Indent)),
specialize_err(EImportParams::Record, record_help())
specialize_err(EImportParams::Record, record_help()),
),
|arena, state, _, (before, record): (_, RecordHelp<'a>)| {
if let Some(update) = record.update {
@ -1021,7 +1023,7 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<
#[inline(always)]
fn imported_module_name<'a>() -> impl Parser<'a, ImportedModuleName<'a>, EImport<'a>> {
record!(ImportedModuleName {
package: optional(skip_second!(
package: optional(skip_second(
specialize_err(|_, pos| EImport::PackageShorthand(pos), lowercase_ident()),
byte(b'.', EImport::PackageShorthandDot)
)),
@ -1040,7 +1042,7 @@ fn import_as<'a>(
EImport::IndentAlias
),
item: then(
specialize_err(|_, pos| EImport::Alias(pos), loc!(unqualified_ident())),
specialize_err(|_, pos| EImport::Alias(pos), loc(unqualified_ident())),
|_arena, state, _progress, loc_ident| {
match loc_ident.value.chars().next() {
Some(first) if first.is_uppercase() => Ok((
@ -1073,9 +1075,9 @@ fn import_exposing<'a>() -> impl Parser<
EImport::IndentExposing,
EImport::ExposingListStart,
),
item: collection_trailing_sep_e!(
item: collection_trailing_sep_e(
byte(b'[', EImport::ExposingListStart),
loc!(import_exposed_name()),
loc(import_exposed_name()),
byte(b',', EImport::ExposingListEnd),
byte(b']', EImport::ExposingListEnd),
Spaced::SpaceBefore
@ -1086,25 +1088,25 @@ fn import_exposing<'a>() -> impl Parser<
#[inline(always)]
fn import_exposed_name<'a>(
) -> impl Parser<'a, crate::ast::Spaced<'a, crate::header::ExposedName<'a>>, EImport<'a>> {
map!(
map(
specialize_err(|_, pos| EImport::ExposedName(pos), unqualified_ident()),
|n| Spaced::Item(crate::header::ExposedName::new(n))
|n| Spaced::Item(crate::header::ExposedName::new(n)),
)
}
#[inline(always)]
fn import_ingested_file_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
map!(
map(
record!(IngestedFileImport {
before_path: space0_e(EImport::IndentStart),
path: loc!(specialize_err(
path: loc(specialize_err(
|_, pos| EImport::IngestedPath(pos),
string_literal::parse_str_literal()
)),
name: import_ingested_file_as(),
annotation: optional(import_ingested_file_annotation())
}),
ValueDef::IngestedFileImport
ValueDef::IngestedFileImport,
)
}
@ -1118,10 +1120,7 @@ fn import_ingested_file_as<'a>(
EImport::IndentAs,
EImport::IndentIngestedName
),
item: specialize_err(
|(), pos| EImport::IngestedName(pos),
loc!(lowercase_ident())
)
item: specialize_err(|(), pos| EImport::IngestedName(pos), loc(lowercase_ident()))
})
}
@ -1129,7 +1128,7 @@ fn import_ingested_file_as<'a>(
fn import_ingested_file_annotation<'a>() -> impl Parser<'a, IngestedFileAnnotation<'a>, EImport<'a>>
{
record!(IngestedFileAnnotation {
before_colon: skip_second!(
before_colon: skip_second(
backtrackable(space0_e(EImport::IndentColon)),
byte(b':', EImport::Colon)
),
@ -1535,7 +1534,7 @@ fn opaque_signature_with_space_before<'a>() -> impl Parser<
),
EExpr<'a>,
> {
and!(
and(
specialize_err(
EExpr::Type,
space0_before_e(
@ -1545,8 +1544,8 @@ fn opaque_signature_with_space_before<'a>() -> impl Parser<
),
optional(backtrackable(specialize_err(
EExpr::Type,
space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart,),
)))
space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart),
))),
)
}
@ -1717,30 +1716,28 @@ mod ability {
use super::*;
use crate::{
ast::{AbilityMember, Spaceable, Spaced},
parser::EAbility,
parser::{absolute_indented_seq, EAbility},
};
/// Parses a single ability demand line; see `parse_demand`.
fn parse_demand_help<'a>() -> impl Parser<'a, AbilityMember<'a>, EAbility<'a>> {
map!(
map(
// Require the type to be more indented than the name
absolute_indented_seq!(
specialize_err(|_, pos| EAbility::DemandName(pos), loc!(lowercase_ident())),
skip_first!(
and!(
absolute_indented_seq(
specialize_err(|_, pos| EAbility::DemandName(pos), loc(lowercase_ident())),
skip_first(
and(
// TODO: do we get anything from picking up spaces here?
space0_e(EAbility::DemandName),
byte(b':', EAbility::DemandColon)
byte(b':', EAbility::DemandColon),
),
specialize_err(EAbility::Type, type_annotation::located(true))
)
specialize_err(EAbility::Type, type_annotation::located(true)),
),
),
|(name, typ): (Loc<&'a str>, Loc<TypeAnnotation<'a>>)| {
AbilityMember {
name: name.map_owned(Spaced::Item),
typ,
}
}
|(name, typ): (Loc<&'a str>, Loc<TypeAnnotation<'a>>)| AbilityMember {
name: name.map_owned(Spaced::Item),
typ,
},
)
}
@ -2137,9 +2134,9 @@ fn parse_expr_end<'a>(
state: State<'a>,
initial_state: State<'a>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let parser = skip_first!(
let parser = skip_first(
crate::blankspace::check_indent(EExpr::IndentEnd),
loc_term_or_underscore(options)
loc_term_or_underscore(options),
);
match parser.parse(arena, state.clone(), min_indent) {
@ -2238,7 +2235,7 @@ fn parse_expr_end<'a>(
let before_op = state.clone();
// try an operator
let line_indent = state.line_indent();
match loc!(operator()).parse(arena, state.clone(), min_indent) {
match loc(operator()).parse(arena, state.clone(), min_indent) {
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((_, loc_op, state)) => {
expr_state.consume_spaces(arena);
@ -2578,14 +2575,14 @@ pub fn parse_top_level_defs<'a>(
fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
// closure_help_help(options)
map_with_arena!(
map_with_arena(
// After the first token, all other tokens must be indented past the start of the line
indented_seq_skip_first!(
indented_seq_skip_first(
// All closures start with a '\' - e.g. (\x -> x + 1)
byte_indent(b'\\', EClosure::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.
and!(
and(
// Parse the params
// Params are comma-separated
sep_by1_e(
@ -2597,22 +2594,22 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo
),
EClosure::Arg,
),
skip_first!(
skip_first(
// Parse the -> which separates params from body
two_bytes(b'-', b'>', EClosure::Arrow),
// Parse the body
space0_before_e(
specialize_err_ref(EClosure::Body, expr_start(options)),
EClosure::IndentBody
)
)
)
EClosure::IndentBody,
),
),
),
),
|arena: &'a Bump, (params, body)| {
let params: Vec<'a, Loc<Pattern<'a>>> = params;
let params: &'a [Loc<Pattern<'a>>] = params.into_bump_slice();
Expr::Closure(params, arena.alloc(body))
}
},
)
}
@ -2622,9 +2619,9 @@ mod when {
/// Parser for when expressions.
pub fn expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> {
map_with_arena!(
and!(
indented_seq_skip_first!(
map_with_arena(
and(
indented_seq_skip_first(
parser::keyword(keyword::WHEN, EWhen::When),
space0_around_e_no_after_indent_check(
specialize_err_ref(EWhen::Condition, expr_start(options)),
@ -2635,7 +2632,7 @@ mod when {
// ambiguity. The formatter will fix it up.
//
// We require that branches are indented relative to the line containing the `is`.
indented_seq_skip_first!(
indented_seq_skip_first(
parser::keyword(keyword::IS, EWhen::Is),
branches(options)
)
@ -2674,8 +2671,8 @@ mod when {
guard: loc_first_guard,
}));
let branch_parser = map!(
and!(
let branch_parser = map(
and(
then(
branch_alternatives(options, Some(pattern_indent_level)),
move |_arena, state, _, ((indent_column, loc_patterns), loc_guard)| {
@ -2687,7 +2684,7 @@ mod when {
}
},
),
branch_result(original_indent + 1)
branch_result(original_indent + 1),
),
|((patterns, guard), expr)| {
let patterns: Vec<'a, _> = patterns;
@ -2696,7 +2693,7 @@ mod when {
value: expr,
guard,
}
}
},
);
while !state.bytes().is_empty() {
@ -2728,11 +2725,11 @@ mod when {
check_for_arrow: false,
..options
};
and!(
and(
branch_alternatives_help(pattern_indent_level),
one_of![
map!(
skip_first!(
map(
skip_first(
parser::keyword(keyword::IF, EWhen::IfToken),
// TODO we should require space before the expression but not after
space0_around_ee(
@ -2747,7 +2744,7 @@ mod when {
Some
),
|_, s, _| Ok((NoProgress, None, s))
]
],
)
}
@ -2834,12 +2831,12 @@ mod when {
/// Parsing the righthandside of a branch in a when conditional.
fn branch_result<'a>(indent: u32) -> impl Parser<'a, Loc<Expr<'a>>, EWhen<'a>> {
move |arena, state, _min_indent| {
skip_first!(
skip_first(
two_bytes(b'-', b'>', EWhen::Arrow),
space0_before_e(
specialize_err_ref(EWhen::Branch, loc_expr(true)),
EWhen::IndentBranch,
)
),
)
.parse(arena, state, indent)
}
@ -2847,23 +2844,23 @@ mod when {
}
fn if_branch<'a>() -> impl Parser<'a, (Loc<Expr<'a>>, Loc<Expr<'a>>), EIf<'a>> {
skip_second!(
and!(
skip_second!(
skip_second(
and(
skip_second(
space0_around_ee(
specialize_err_ref(EIf::Condition, loc_expr(true)),
EIf::IndentCondition,
EIf::IndentThenToken,
),
parser::keyword(keyword::THEN, EIf::Then)
parser::keyword(keyword::THEN, EIf::Then),
),
space0_around_ee(
specialize_err_ref(EIf::ThenBranch, loc_expr(true)),
EIf::IndentThenBranch,
EIf::IndentElseToken,
)
),
),
parser::keyword(keyword::ELSE, EIf::Else)
parser::keyword(keyword::ELSE, EIf::Else),
)
}
@ -2956,9 +2953,9 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
// try to parse another `if`
// NOTE this drops spaces between the `else` and the `if`
let optional_if = and!(
let optional_if = and(
backtrackable(space0_e(EIf::IndentIf)),
parser::keyword(keyword::IF, EIf::If)
parser::keyword(keyword::IF, EIf::If),
);
match optional_if.parse(arena, state.clone(), min_indent) {
@ -3070,18 +3067,18 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
}
fn list_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EList<'a>> {
map_with_arena!(
collection_trailing_sep_e!(
map_with_arena(
collection_trailing_sep_e(
byte(b'[', EList::Open),
specialize_err_ref(EList::Expr, loc_expr(false)),
byte(b',', EList::End),
byte(b']', EList::End),
Expr::SpaceBefore
Expr::SpaceBefore,
),
|arena, elements: Collection<'a, _>| {
let elements = elements.ptrify_items(arena);
Expr::List(elements)
}
},
)
.trace("list_literal")
}
@ -3201,19 +3198,19 @@ impl<'a> Spaceable<'a> for RecordField<'a> {
pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
use RecordField::*;
map_with_arena!(
and!(
specialize_err(|_, pos| ERecord::Field(pos), loc!(lowercase_ident())),
and!(
map_with_arena(
and(
specialize_err(|_, pos| ERecord::Field(pos), loc(lowercase_ident())),
and(
spaces(),
optional(either!(
and!(byte(b':', ERecord::Colon), record_field_expr()),
and!(
optional(either(
and(byte(b':', ERecord::Colon), record_field_expr()),
and(
byte(b'?', ERecord::QuestionMark),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false)))
)
))
)
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))),
),
)),
),
),
|arena: &'a bumpalo::Bump, (loc_label, (spaces, opt_loc_val))| {
match opt_loc_val {
@ -3239,7 +3236,7 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
}
}
}
}
},
)
}
@ -3249,38 +3246,36 @@ enum RecordFieldExpr<'a> {
}
fn record_field_expr<'a>() -> impl Parser<'a, RecordFieldExpr<'a>, ERecord<'a>> {
map_with_arena!(
and!(
map_with_arena(
and(
spaces(),
either!(
and!(
either(
and(
two_bytes(b'<', b'-', ERecord::Arrow),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false)))
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))),
),
specialize_err_ref(ERecord::Expr, loc_expr(false))
)
specialize_err_ref(ERecord::Expr, loc_expr(false)),
),
),
|arena: &'a bumpalo::Bump, (spaces, either)| {
match either {
Either::First((_, loc_expr)) => RecordFieldExpr::Apply(spaces, loc_expr),
Either::Second(loc_expr) => RecordFieldExpr::Value({
if spaces.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_before(spaces, loc_expr.region)
}
}),
}
}
|arena: &'a bumpalo::Bump, (spaces, either)| match either {
Either::First((_, loc_expr)) => RecordFieldExpr::Apply(spaces, loc_expr),
Either::Second(loc_expr) => RecordFieldExpr::Value({
if spaces.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_before(spaces, loc_expr.region)
}
}),
},
)
}
fn record_updateable_identifier<'a>() -> impl Parser<'a, Expr<'a>, ERecord<'a>> {
specialize_err(
|_, pos| ERecord::Updateable(pos),
map_with_arena!(parse_ident, ident_to_expr),
map_with_arena(parse_ident, ident_to_expr),
)
}
@ -3290,37 +3285,37 @@ struct RecordHelp<'a> {
}
fn record_help<'a>() -> impl Parser<'a, RecordHelp<'a>, ERecord<'a>> {
between!(
between(
byte(b'{', ERecord::Open),
reset_min_indent(record!(RecordHelp {
// You can optionally have an identifier followed by an '&' to
// make this a record update, e.g. { Foo.user & username: "blah" }.
update: optional(backtrackable(skip_second!(
update: optional(backtrackable(skip_second(
spaces_around(
// We wrap the ident in an Expr here,
// so that we have a Spaceable value to work with,
// and then in canonicalization verify that it's an Expr::Var
// (and not e.g. an `Expr::Access`) and extract its string.
loc!(record_updateable_identifier()),
loc(record_updateable_identifier()),
),
byte(b'&', ERecord::Ampersand)
))),
fields: collection_inner!(
loc!(record_field()),
fields: collection_inner(
loc(record_field()),
byte(b',', ERecord::End),
RecordField::SpaceBefore
),
})),
byte(b'}', ERecord::End)
byte(b'}', ERecord::End),
)
}
fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
then(
and!(
and(
specialize_err(EExpr::Record, record_help()),
// there can be field access, e.g. `{ x : 4 }.x`
record_field_access_chain()
record_field_access_chain(),
),
move |arena, state, _, (record, accessors)| {
let expr_result = match record.update {
@ -3412,7 +3407,7 @@ fn apply_expr_access_chain<'a>(
}
fn string_like_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString<'a>> {
map_with_arena!(
map_with_arena(
crate::string_literal::parse_str_like_literal(),
|arena, lit| match lit {
StrLikeLiteral::Str(s) => Expr::Str(s),
@ -3420,12 +3415,12 @@ fn string_like_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString<'a>> {
// TODO: preserve the original escaping
Expr::SingleQuote(s.to_str_in(arena))
}
}
},
)
}
fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
map!(
map(
crate::number_literal::positive_number_literal(),
|literal| {
use crate::number_literal::NumLiteral::*;
@ -3443,12 +3438,12 @@ fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
is_negative,
},
}
}
},
)
}
fn number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, ENumber> {
map!(crate::number_literal::number_literal(), |literal| {
map(crate::number_literal::number_literal(), |literal| {
use crate::number_literal::NumLiteral::*;
match literal {

View file

@ -4,7 +4,10 @@ use crate::ast::{
use crate::blankspace::space0_e;
use crate::expr::merge_spaces;
use crate::ident::{lowercase_ident, UppercaseIdent};
use crate::parser::{byte, specialize_err, EPackageEntry, EPackageName, Parser};
use crate::parser::{
and, byte, loc, map_with_arena, skip_first, skip_second, specialize_err, EPackageEntry,
EPackageName, Parser,
};
use crate::parser::{optional, then};
use crate::string_literal;
use roc_module::symbol::{ModuleId, Symbol};
@ -366,29 +369,29 @@ pub struct PackageEntry<'a> {
}
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
map_with_arena!(
map_with_arena(
// You may optionally have a package shorthand,
// e.g. "uc" in `uc: roc/unicode 1.0.0`
//
// (Indirect dependencies don't have a shorthand.)
and!(
optional(and!(
skip_second!(
and!(
and(
optional(and(
skip_second(
and(
specialize_err(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()),
space0_e(EPackageEntry::IndentPackage)
space0_e(EPackageEntry::IndentPackage),
),
byte(b':', EPackageEntry::Colon)
byte(b':', EPackageEntry::Colon),
),
space0_e(EPackageEntry::IndentPackage)
space0_e(EPackageEntry::IndentPackage),
)),
and!(
optional(skip_first!(
and(
optional(skip_first(
crate::parser::keyword(crate::keyword::PLATFORM, EPackageEntry::Platform),
space0_e(EPackageEntry::IndentPackage)
space0_e(EPackageEntry::IndentPackage),
)),
loc!(specialize_err(EPackageEntry::BadPackage, package_name()))
)
loc(specialize_err(EPackageEntry::BadPackage, package_name())),
),
),
move |arena, (opt_shorthand, (platform_marker, package_or_path))| {
let entry = match opt_shorthand {
@ -411,15 +414,15 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
};
Spaced::Item(entry)
}
},
)
}
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> {
then(
loc!(specialize_err(
loc(specialize_err(
EPackageName::BadPath,
string_literal::parse_str_literal()
string_literal::parse_str_literal(),
)),
move |_arena, state, progress, text| match text.value {
StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), state)),

View file

@ -11,9 +11,10 @@ use crate::header::{
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
use crate::parser::Progress::{self, *};
use crate::parser::{
backtrackable, byte, increment_min_indent, optional, reset_min_indent, specialize_err,
two_bytes, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages, EParams,
EProvides, ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
and, backtrackable, byte, collection_trailing_sep_e, increment_min_indent, loc, map,
map_with_arena, optional, reset_min_indent, skip_first, skip_second, specialize_err, succeed,
two_bytes, zero_or_more, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages,
EParams, EProvides, ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
};
use crate::pattern::record_pattern_fields;
use crate::state::State;
@ -63,43 +64,43 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
record!(Module {
comments: space0_e(EHeader::IndentStart),
header: one_of![
map!(
skip_first!(
map(
skip_first(
keyword("module", EHeader::Start),
increment_min_indent(module_header())
),
Header::Module
),
map!(
skip_first!(
map(
skip_first(
keyword("interface", EHeader::Start),
increment_min_indent(interface_header())
),
Header::Module
),
map!(
skip_first!(
map(
skip_first(
keyword("app", EHeader::Start),
increment_min_indent(one_of![app_header(), old_app_header()])
),
Header::App
),
map!(
skip_first!(
map(
skip_first(
keyword("package", EHeader::Start),
increment_min_indent(one_of![package_header(), old_package_header()])
),
Header::Package
),
map!(
skip_first!(
map(
skip_first(
keyword("platform", EHeader::Start),
increment_min_indent(platform_header())
),
Header::Platform
),
map!(
skip_first!(
map(
skip_first(
keyword("hosted", EHeader::Start),
increment_min_indent(hosted_header())
),
@ -115,7 +116,7 @@ fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
after_keyword: space0_e(EHeader::IndentStart),
params: optional(specialize_err(EHeader::Params, module_params())),
exposes: specialize_err(EHeader::Exposes, exposes_list()),
interface_imports: succeed!(None)
interface_imports: succeed(None)
})
.trace("module_header")
}
@ -123,14 +124,15 @@ fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
fn module_params<'a>() -> impl Parser<'a, ModuleParams<'a>, EParams<'a>> {
record!(ModuleParams {
params: specialize_err(EParams::Pattern, record_pattern_fields()),
before_arrow: skip_second!(
before_arrow: skip_second(
space0_e(EParams::BeforeArrow),
loc!(two_bytes(b'-', b'>', EParams::Arrow))
loc(two_bytes(b'-', b'>', EParams::Arrow))
),
after_arrow: space0_e(EParams::AfterArrow),
})
}
// TODO does this need to be a macro?
macro_rules! merge_n_spaces {
($arena:expr, $($slice:expr),*) => {
{
@ -144,25 +146,25 @@ macro_rules! merge_n_spaces {
/// Parse old interface headers so we can format them into module headers
#[inline(always)]
fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
let after_keyword = map_with_arena!(
and!(
skip_second!(
let after_keyword = map_with_arena(
and(
skip_second(
space0_e(EHeader::IndentStart),
loc!(module_name_help(EHeader::ModuleName))
loc(module_name_help(EHeader::ModuleName)),
),
specialize_err(EHeader::Exposes, exposes_kw())
specialize_err(EHeader::Exposes, exposes_kw()),
),
|arena: &'a bumpalo::Bump,
(before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| {
merge_n_spaces!(arena, before_name, kw.before, kw.after)
}
},
);
record!(ModuleHeader {
after_keyword: after_keyword,
params: succeed!(None),
params: succeed(None),
exposes: specialize_err(EHeader::Exposes, exposes_list()).trace("exposes_list"),
interface_imports: map!(
interface_imports: map(
specialize_err(EHeader::Imports, imports()),
imports_none_if_empty
)
@ -183,7 +185,7 @@ fn imports_none_if_empty(value: ImportsKeywordItem<'_>) -> Option<ImportsKeyword
fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
record!(HostedHeader {
before_name: space0_e(EHeader::IndentStart),
name: loc!(module_name_help(EHeader::ModuleName)),
name: loc(module_name_help(EHeader::ModuleName)),
exposes: specialize_err(EHeader::Exposes, exposes_values_kw()),
imports: specialize_err(EHeader::Imports, imports()),
generates: specialize_err(EHeader::Generates, generates()),
@ -257,9 +259,9 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
before_provides: space0_e(EHeader::IndentStart),
provides: specialize_err(EHeader::Exposes, exposes_list()),
before_packages: space0_e(EHeader::IndentStart),
packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
old_imports: succeed!(None),
old_provides_to_new_package: succeed!(None),
packages: specialize_err(EHeader::Packages, loc(packages_collection())),
old_imports: succeed(None),
old_provides_to_new_package: succeed(None),
})
.trace("app_header")
}
@ -277,19 +279,19 @@ type OldAppPackages<'a> =
#[inline(always)]
fn old_app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
let old = record!(OldAppHeader {
before_name: skip_second!(
before_name: skip_second(
space0_e(EHeader::IndentStart),
loc!(crate::parser::specialize_err(
loc(crate::parser::specialize_err(
EHeader::AppName,
string_literal::parse_str_literal()
))
),
packages: optional(specialize_err(EHeader::Packages, loc!(packages()))),
packages: optional(specialize_err(EHeader::Packages, loc(packages()))),
imports: optional(specialize_err(EHeader::Imports, imports())),
provides: specialize_err(EHeader::Provides, provides_to()),
});
map_with_arena!(old, |arena: &'a bumpalo::Bump, old: OldAppHeader<'a>| {
map_with_arena(old, |arena: &'a bumpalo::Bump, old: OldAppHeader<'a>| {
let mut before_packages: &'a [CommentOrNewline] = &[];
let packages = match old.packages {
@ -394,7 +396,7 @@ fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
before_exposes: space0_e(EHeader::IndentStart),
exposes: specialize_err(EHeader::Exposes, exposes_module_collection()),
before_packages: space0_e(EHeader::IndentStart),
packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
packages: specialize_err(EHeader::Packages, loc(packages_collection())),
})
.trace("package_header")
}
@ -409,14 +411,14 @@ struct OldPackageHeader<'a> {
#[inline(always)]
fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
map_with_arena!(
map_with_arena(
record!(OldPackageHeader {
before_name: skip_second!(
before_name: skip_second(
space0_e(EHeader::IndentStart),
specialize_err(EHeader::PackageName, package_name())
),
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
packages: specialize_err(EHeader::Packages, loc!(packages())),
packages: specialize_err(EHeader::Packages, loc(packages())),
}),
|arena: &'a bumpalo::Bump, old: OldPackageHeader<'a>| {
let before_exposes = merge_n_spaces!(
@ -437,7 +439,7 @@ fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
before_packages,
packages: old.packages.map(|kw| kw.item),
}
}
},
)
.trace("old_package_header")
}
@ -446,7 +448,7 @@ fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
record!(PlatformHeader {
before_name: space0_e(EHeader::IndentStart),
name: loc!(specialize_err(EHeader::PlatformName, package_name())),
name: loc(specialize_err(EHeader::PlatformName, package_name())),
requires: specialize_err(EHeader::Requires, requires()),
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
packages: specialize_err(EHeader::Packages, packages()),
@ -460,9 +462,9 @@ fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
one_of![
specialize_err(
|_, pos| EProvides::Identifier(pos),
map!(lowercase_ident(), To::ExistingPackage)
map(lowercase_ident(), To::ExistingPackage)
),
specialize_err(EProvides::Package, map!(package_name(), To::NewPackage))
specialize_err(EProvides::Package, map(package_name(), To::NewPackage))
]
}
@ -475,7 +477,7 @@ fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> {
EProvides::IndentProvides,
EProvides::IndentListStart
),
entries: collection_trailing_sep_e!(
entries: collection_trailing_sep_e(
byte(b'[', EProvides::ListStart),
exposes_entry(EProvides::Identifier),
byte(b',', EProvides::ListEnd),
@ -489,7 +491,7 @@ fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> {
EProvides::IndentTo,
EProvides::IndentListStart
),
to: loc!(provides_to_package()),
to: loc(provides_to_package()),
})
.trace("provides_to")
}
@ -506,7 +508,7 @@ fn provides_exposed<'a>() -> impl Parser<
EProvides::IndentProvides,
EProvides::IndentListStart
),
item: collection_trailing_sep_e!(
item: collection_trailing_sep_e(
byte(b'[', EProvides::ListStart),
exposes_entry(EProvides::Identifier),
byte(b',', EProvides::ListEnd),
@ -519,25 +521,25 @@ fn provides_exposed<'a>() -> impl Parser<
#[inline(always)]
fn provides_types<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>, EProvides<'a>> {
skip_first!(
skip_first(
// We only support spaces here, not newlines, because this is not intended
// to be the design forever. Someday it will hopefully work like Elm,
// where platform authors can provide functions like Browser.sandbox which
// present an API based on ordinary-looking type variables.
zero_or_more!(byte(
zero_or_more(byte(
b' ',
// HACK: If this errors, EProvides::Provides is not an accurate reflection
// of what went wrong. However, this is both skipped and zero_or_more,
// so this error should never be visible to anyone in practice!
EProvides::Provides
EProvides::Provides,
)),
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', EProvides::ListStart),
provides_type_entry(EProvides::Identifier),
byte(b',', EProvides::ListEnd),
byte(b'}', EProvides::ListEnd),
Spaced::SpaceBefore
)
Spaced::SpaceBefore,
),
)
}
@ -549,9 +551,9 @@ where
F: Copy,
E: 'a,
{
loc!(map!(
specialize_err(|_, pos| to_expectation(pos), ident::uppercase()),
Spaced::Item
loc(map(
specialize_err(move |_, pos| to_expectation(pos), ident::uppercase()),
Spaced::Item,
))
}
@ -563,9 +565,9 @@ where
F: Copy,
E: 'a,
{
loc!(map!(
specialize_err(|_, pos| to_expectation(pos), unqualified_ident()),
|n| Spaced::Item(ExposedName::new(n))
loc(map(
specialize_err(move |_, pos| to_expectation(pos), unqualified_ident()),
|n| Spaced::Item(ExposedName::new(n)),
))
}
@ -586,7 +588,7 @@ fn requires<'a>(
#[inline(always)]
fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> {
record!(PlatformRequires {
rigids: skip_second!(requires_rigids(), space0_e(ERequires::ListStart)),
rigids: skip_second(requires_rigids(), space0_e(ERequires::ListStart)),
signature: requires_typed_ident()
})
}
@ -594,30 +596,30 @@ fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a
#[inline(always)]
fn requires_rigids<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>, ERequires<'a>> {
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', ERequires::ListStart),
specialize_err(
|_, pos| ERequires::Rigid(pos),
loc!(map!(ident::uppercase(), Spaced::Item))
loc(map(ident::uppercase(), Spaced::Item)),
),
byte(b',', ERequires::ListEnd),
byte(b'}', ERequires::ListEnd),
Spaced::SpaceBefore
Spaced::SpaceBefore,
)
}
#[inline(always)]
fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>, ERequires<'a>> {
skip_first!(
skip_first(
byte(b'{', ERequires::ListStart),
skip_second!(
skip_second(
reset_min_indent(space0_around_ee(
specialize_err(ERequires::TypedIdent, loc!(typed_ident()),),
specialize_err(ERequires::TypedIdent, loc(typed_ident())),
ERequires::ListStart,
ERequires::ListEnd
ERequires::ListEnd,
)),
byte(b'}', ERequires::ListStart)
)
byte(b'}', ERequires::ListStart),
),
)
}
@ -646,12 +648,12 @@ fn exposes_kw<'a>() -> impl Parser<'a, Spaces<'a, ExposesKeyword>, EExposes> {
#[inline(always)]
fn exposes_list<'a>() -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>, EExposes>
{
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'[', EExposes::ListStart),
exposes_entry(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore
Spaced::SpaceBefore,
)
}
@ -664,21 +666,22 @@ pub fn spaces_around_keyword<'a, K: Keyword, E>(
where
E: 'a + SpaceProblem,
{
map!(
and!(
skip_second!(
map(
and(
skip_second(
// parse any leading space before the keyword
backtrackable(space0_e(indent_problem1)),
crate::parser::keyword(K::KEYWORD, expectation)
// parse the keyword
crate::parser::keyword(K::KEYWORD, expectation),
),
space0_e(indent_problem2)
// parse the trailing space
space0_e(indent_problem2),
),
|(before, after)| {
Spaces {
before,
item: keyword_item,
after,
}
}
move |(before, after)| Spaces {
before,
item: keyword_item,
after,
},
)
}
@ -701,12 +704,12 @@ fn exposes_modules<'a>() -> impl Parser<
fn exposes_module_collection<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>, EExposes> {
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'[', EExposes::ListStart),
exposes_module(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore
Spaced::SpaceBefore,
)
}
@ -718,9 +721,9 @@ where
F: Copy,
E: 'a,
{
loc!(map!(
specialize_err(|_, pos| to_expectation(pos), module_name()),
Spaced::Item
loc(map(
specialize_err(move |_, pos| to_expectation(pos), module_name()),
Spaced::Item,
))
}
@ -749,12 +752,12 @@ fn packages_kw<'a>() -> impl Parser<'a, Spaces<'a, PackagesKeyword>, EPackages<'
#[inline(always)]
fn packages_collection<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>, EPackages<'a>> {
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', EPackages::ListStart),
specialize_err(EPackages::PackageEntry, loc!(package_entry())),
specialize_err(EPackages::PackageEntry, loc(package_entry())),
byte(b',', EPackages::ListEnd),
byte(b'}', EPackages::ListEnd),
Spaced::SpaceBefore
Spaced::SpaceBefore,
)
}
@ -785,7 +788,7 @@ fn generates_with<'a>() -> impl Parser<
EGeneratesWith::IndentWith,
EGeneratesWith::IndentListStart
),
item: collection_trailing_sep_e!(
item: collection_trailing_sep_e(
byte(b'[', EGeneratesWith::ListStart),
exposes_entry(EGeneratesWith::Identifier),
byte(b',', EGeneratesWith::ListEnd),
@ -808,9 +811,9 @@ fn imports<'a>() -> impl Parser<
EImports::IndentImports,
EImports::IndentListStart
),
item: collection_trailing_sep_e!(
item: collection_trailing_sep_e(
byte(b'[', EImports::ListStart),
loc!(imports_entry()),
loc(imports_entry()),
byte(b',', EImports::ListEnd),
byte(b']', EImports::ListEnd),
Spaced::SpaceBefore
@ -824,25 +827,25 @@ pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedId
// e.g.
//
// printLine : Str -> Effect {}
map!(
and!(
and!(
loc!(specialize_err(
map(
and(
and(
loc(specialize_err(
|_, pos| ETypedIdent::Identifier(pos),
lowercase_ident()
lowercase_ident(),
)),
space0_e(ETypedIdent::IndentHasType)
space0_e(ETypedIdent::IndentHasType),
),
skip_first!(
skip_first(
byte(b':', ETypedIdent::HasType),
space0_before_e(
specialize_err(
ETypedIdent::Type,
reset_min_indent(type_annotation::located(true))
reset_min_indent(type_annotation::located(true)),
),
ETypedIdent::IndentType,
)
)
),
),
),
|((ident, spaces_before_colon), ann)| {
Spaced::Item(TypedIdent {
@ -850,7 +853,7 @@ pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedId
spaces_before_colon,
ann,
})
}
},
)
}
@ -874,12 +877,24 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
Option<Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
);
let spaced_import = |((opt_shortname, module_name), opt_values): Temp<'a>| {
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
let entry = match opt_shortname {
Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values),
None => ImportsEntry::Module(module_name, exposed_values),
};
Spaced::Item(entry)
};
one_of!(
map!(
and!(
and!(
map(
and(
and(
// e.g. `pf.`
optional(backtrackable(skip_second!(
optional(backtrackable(skip_second(
shortname(),
byte(b'.', EImports::ShorthandDot)
))),
@ -887,9 +902,9 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
module_name_help(EImports::ModuleName)
),
// e.g. `.{ Task, after}`
optional(skip_first!(
optional(skip_first(
byte(b'.', EImports::ExposingDot),
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', EImports::SetStart),
exposes_entry(EImports::Identifier),
byte(b',', EImports::SetEnd),
@ -898,30 +913,18 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
)
))
),
|((opt_shortname, module_name), opt_values): Temp<'a>| {
let exposed_values = opt_values.unwrap_or_else(Collection::empty);
let entry = match opt_shortname {
Some(shortname) => {
ImportsEntry::Package(shortname, module_name, exposed_values)
}
None => ImportsEntry::Module(module_name, exposed_values),
};
Spaced::Item(entry)
}
spaced_import
)
.trace("normal_import"),
map!(
and!(
and!(
map(
and(
and(
// e.g. "filename"
// TODO: str literal allows for multiline strings. We probably don't want that for file names.
specialize_err(|_, pos| EImports::StrLiteral(pos), parse_str_literal()),
// e.g. as
and!(
and!(
and(
and(
space0_e(EImports::AsKeyword),
two_bytes(b'a', b's', EImports::AsKeyword)
),

File diff suppressed because it is too large Load diff

View file

@ -2,11 +2,12 @@ use crate::ast::{Collection, Implements, Pattern, PatternAs, Spaceable};
use crate::blankspace::{space0_e, spaces, spaces_before};
use crate::ident::{lowercase_ident, parse_ident, Accessor, Ident};
use crate::keyword;
use crate::parser::Progress::{self, *};
use crate::parser::{
self, backtrackable, byte, fail_when, optional, specialize_err, specialize_err_ref, then,
three_bytes, two_bytes, EPattern, PInParens, PList, PRecord, Parser,
self, backtrackable, byte, collection_trailing_sep_e, fail_when, loc, map, map_with_arena,
optional, skip_first, specialize_err, specialize_err_ref, then, three_bytes, two_bytes,
zero_or_more, EPattern, PInParens, PList, PRecord, Parser,
};
use crate::parser::{either, Progress::*};
use crate::state::State;
use crate::string_literal::StrLikeLiteral;
use bumpalo::collections::string::String;
@ -31,9 +32,9 @@ pub fn closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
// An ident is the most common param, e.g. \foo -> ...
loc_ident_pattern_help(true),
// Underscore is also common, e.g. \_ -> ...
loc!(underscore_pattern_help()),
loc(underscore_pattern_help()),
// You can destructure records in params, e.g. \{ x, y } -> ...
loc!(specialize_err(
loc(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
)),
@ -78,15 +79,15 @@ fn loc_pattern_help_help<'a>(
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
loc(underscore_pattern_help()),
loc_ident_pattern_help(can_have_arguments),
loc!(specialize_err(
loc(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
)),
loc!(specialize_err(EPattern::List, list_pattern_help())),
loc!(number_pattern_help()),
loc!(string_like_pattern_help()),
loc(specialize_err(EPattern::List, list_pattern_help())),
loc(number_pattern_help()),
loc(string_like_pattern_help()),
)
}
@ -100,7 +101,7 @@ fn pattern_as<'a>() -> impl Parser<'a, PatternAs<'a>, EPattern<'a>> {
let position = state.pos();
match loc!(lowercase_ident()).parse(arena, state, min_indent) {
match loc(lowercase_ident()).parse(arena, state, min_indent) {
Ok((_, identifier, state)) => Ok((
MadeProgress,
PatternAs {
@ -115,13 +116,13 @@ fn pattern_as<'a>() -> impl Parser<'a, PatternAs<'a>, EPattern<'a>> {
}
fn loc_tag_pattern_args_help<'a>() -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(false))
zero_or_more(loc_tag_pattern_arg(false))
}
/// Like `loc_tag_pattern_args_help`, but stops if a "implements" keyword is seen (indicating an ability).
fn loc_type_def_tag_pattern_args_help<'a>(
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(true))
zero_or_more(loc_tag_pattern_arg(true))
}
fn loc_tag_pattern_arg<'a>(
@ -190,12 +191,12 @@ pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPatt
fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
then(
loc!(collection_trailing_sep_e!(
loc(collection_trailing_sep_e(
byte(b'(', PInParens::Open),
specialize_err_ref(PInParens::Pattern, loc_pattern_help()),
byte(b',', PInParens::End),
byte(b')', PInParens::End),
Pattern::SpaceBefore
Pattern::SpaceBefore,
)),
move |_arena, state, _, loc_elements| {
let elements = loc_elements.value;
@ -223,7 +224,7 @@ fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInPare
fn number_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
specialize_err(
EPattern::NumLiteral,
map!(crate::number_literal::number_literal(), |literal| {
map(crate::number_literal::number_literal(), |literal| {
use crate::number_literal::NumLiteral::*;
match literal {
@ -246,7 +247,7 @@ fn number_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
fn string_like_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
specialize_err(
|_, pos| EPattern::Start(pos),
map_with_arena!(
map_with_arena(
crate::string_literal::parse_str_like_literal(),
|arena, lit| match lit {
StrLikeLiteral::Str(s) => Pattern::StrLiteral(s),
@ -254,21 +255,21 @@ fn string_like_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>>
// TODO: preserve the original escaping
Pattern::SingleQuote(s.to_str_in(arena))
}
}
},
),
)
}
fn list_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PList<'a>> {
map!(
collection_trailing_sep_e!(
map(
collection_trailing_sep_e(
byte(b'[', PList::Open),
list_element_pattern(),
byte(b',', PList::End),
byte(b']', PList::End),
Pattern::SpaceBefore
Pattern::SpaceBefore,
),
Pattern::List
Pattern::List,
)
}
@ -281,16 +282,13 @@ fn list_element_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
}
fn three_list_rest_pattern_error<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
fail_when(
PList::Rest,
loc!(three_bytes(b'.', b'.', b'.', PList::Rest)),
)
fail_when(PList::Rest, loc(three_bytes(b'.', b'.', b'.', PList::Rest)))
}
fn list_rest_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let (_, loc_word, state) =
loc!(two_bytes(b'.', b'.', PList::Open)).parse(arena, state, min_indent)?;
loc(two_bytes(b'.', b'.', PList::Open)).parse(arena, state, min_indent)?;
let no_as = Loc::at(loc_word.region, Pattern::ListRest(None));
@ -325,9 +323,8 @@ fn loc_ident_pattern_help<'a>(
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let original_state = state.clone();
let (_, loc_ident, state) =
specialize_err(|_, pos| EPattern::Start(pos), loc!(parse_ident))
.parse(arena, state, min_indent)?;
let (_, loc_ident, state) = specialize_err(|_, pos| EPattern::Start(pos), loc(parse_ident))
.parse(arena, state, min_indent)?;
match loc_ident.value {
Ident::Tag(tag) => {
@ -450,15 +447,15 @@ fn loc_ident_pattern_help<'a>(
}
fn underscore_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
map!(
skip_first!(
map(
skip_first(
byte(b'_', EPattern::Underscore),
optional(lowercase_ident_pattern())
optional(lowercase_ident_pattern()),
),
|output| match output {
Some(name) => Pattern::Underscore(name),
None => Pattern::Underscore(""),
}
},
)
}
@ -468,17 +465,17 @@ fn lowercase_ident_pattern<'a>() -> impl Parser<'a, &'a str, EPattern<'a>> {
#[inline(always)]
fn record_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
map!(record_pattern_fields(), Pattern::RecordDestructure)
map(record_pattern_fields(), Pattern::RecordDestructure)
}
pub fn record_pattern_fields<'a>() -> impl Parser<'a, Collection<'a, Loc<Pattern<'a>>>, PRecord<'a>>
{
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', PRecord::Open),
record_pattern_field(),
byte(b',', PRecord::End),
byte(b'}', PRecord::End),
Pattern::SpaceBefore
Pattern::SpaceBefore,
)
}
@ -489,9 +486,9 @@ fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>>
// You must have a field name, e.g. "email"
// using the initial pos is important for error reporting
let pos = state.pos();
let (progress, loc_label, state) = loc!(specialize_err(
let (progress, loc_label, state) = loc(specialize_err(
move |_, _| PRecord::Field(pos),
lowercase_ident()
lowercase_ident(),
))
.parse(arena, state, min_indent)?;
debug_assert_eq!(progress, MadeProgress);
@ -500,9 +497,9 @@ fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>>
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.)
let (_, opt_loc_val, state) = optional(either!(
let (_, opt_loc_val, state) = optional(either(
byte(b':', PRecord::Colon),
byte(b'?', PRecord::Optional)
byte(b'?', PRecord::Optional),
))
.parse(arena, state, min_indent)?;

View file

@ -2,8 +2,8 @@ use crate::ast::{EscapedChar, SingleQuoteLiteral, StrLiteral, StrSegment};
use crate::expr;
use crate::parser::Progress::{self, *};
use crate::parser::{
allocated, byte, loc, reset_min_indent, specialize_err_ref, then, BadInputError, ESingleQuote,
EString, Parser,
allocated, between, byte, loc, reset_min_indent, skip_second, specialize_err_ref, then,
BadInputError, ESingleQuote, EString, Parser,
};
use crate::state::State;
use bumpalo::collections::vec::Vec;
@ -73,7 +73,7 @@ pub enum StrLikeLiteral<'a> {
pub fn parse_str_literal<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
then(
loc!(parse_str_like_literal()),
loc(parse_str_like_literal()),
|_arena, state, progress, str_like| match str_like.value {
StrLikeLiteral::SingleQuote(_) => Err((
progress,
@ -374,12 +374,12 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
// Parse an arbitrary expression, then give a
// canonicalization error if that expression variant
// is not allowed inside a string interpolation.
let (_progress, loc_expr, new_state) = skip_second!(
let (_progress, loc_expr, new_state) = skip_second(
specialize_err_ref(
EString::Format,
loc(allocated(reset_min_indent(expr::expr_help())))
loc(allocated(reset_min_indent(expr::expr_help()))),
),
byte(b')', EString::FormatEnd)
byte(b')', EString::FormatEnd),
)
.parse(arena, state, min_indent)?;
@ -403,10 +403,10 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
// Parse the hex digits, surrounded by parens, then
// give a canonicalization error if the digits form
// an invalid unicode code point.
let (_progress, loc_digits, new_state) = between!(
let (_progress, loc_digits, new_state) = between(
byte(b'(', EString::CodePtOpen),
loc(ascii_hex_digits()),
byte(b')', EString::CodePtEnd)
byte(b')', EString::CodePtEnd),
)
.parse(arena, state, min_indent)?;
@ -483,12 +483,12 @@ pub fn parse_str_like_literal<'a>() -> impl Parser<'a, StrLikeLiteral<'a>, EStri
let original_byte_count = state.bytes().len();
// Parse an arbitrary expression, followed by ')'
let (_progress, loc_expr, new_state) = skip_second!(
let (_progress, loc_expr, new_state) = skip_second(
specialize_err_ref(
EString::Format,
loc(allocated(reset_min_indent(expr::expr_help())))
loc(allocated(reset_min_indent(expr::expr_help()))),
),
byte(b')', EString::FormatEnd)
byte(b')', EString::FormatEnd),
)
.parse(arena, state, min_indent)?;

View file

@ -9,12 +9,14 @@ use crate::expr::{record_field, FoundApplyValue};
use crate::ident::{lowercase_ident, lowercase_ident_keyword_e};
use crate::keyword;
use crate::parser::{
absolute_column_min_indent, increment_min_indent, then, ERecord, ETypeAbilityImpl,
absolute_column_min_indent, and, collection_trailing_sep_e, either, increment_min_indent,
indented_seq, loc, map, map_with_arena, skip_first, skip_second, succeed, then, zero_or_more,
ERecord, ETypeAbilityImpl,
};
use crate::parser::{
allocated, backtrackable, byte, fail, optional, specialize_err, specialize_err_ref, two_bytes,
word, EType, ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, Parser,
Progress::{self, *},
Progress::*,
};
use crate::state::State;
use bumpalo::collections::vec::Vec;
@ -38,12 +40,12 @@ fn tag_union_type<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeTagUnion<'a>> {
move |arena, state, min_indent| {
let (_, tags, state) = collection_trailing_sep_e!(
let (_, tags, state) = collection_trailing_sep_e(
byte(b'[', ETypeTagUnion::Open),
loc!(tag_type(false)),
loc(tag_type(false)),
byte(b',', ETypeTagUnion::End),
byte(b']', ETypeTagUnion::End),
Tag::SpaceBefore
Tag::SpaceBefore,
)
.parse(arena, state, min_indent)?;
@ -109,29 +111,29 @@ fn parse_type_alias_after_as<'a>() -> impl Parser<'a, TypeHeader<'a>, EType<'a>>
}
fn term<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
map_with_arena!(
and!(
map_with_arena(
and(
one_of!(
loc_wildcard(),
loc_inferred(),
specialize_err(EType::TInParens, loc_type_in_parens(stop_at_surface_has)),
loc!(specialize_err(
loc(specialize_err(
EType::TRecord,
record_type(stop_at_surface_has)
)),
loc!(specialize_err(
loc(specialize_err(
EType::TTagUnion,
tag_union_type(stop_at_surface_has)
)),
loc!(applied_type(stop_at_surface_has)),
loc!(parse_type_variable(stop_at_surface_has)),
loc(applied_type(stop_at_surface_has)),
loc(parse_type_variable(stop_at_surface_has)),
fail(EType::TStart),
),
// Inline alias notation, e.g. [Nil, Cons a (List a)] as List a
one_of![
map!(
and!(
skip_second!(
map(
and(
skip_second(
backtrackable(space0_e(EType::TIndentEnd)),
crate::parser::keyword(keyword::AS, EType::TEnd)
),
@ -139,8 +141,8 @@ fn term<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Loc<TypeAnnotation<'a>
),
Some
),
succeed!(None)
]
succeed(None)
],
),
|arena: &'a Bump,
(loc_ann, opt_as): (Loc<TypeAnnotation<'a>>, Option<(&'a [_], TypeHeader<'a>)>)| {
@ -156,14 +158,14 @@ fn term<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Loc<TypeAnnotation<'a>
None => loc_ann,
}
}
},
)
.trace("type_annotation:term")
}
/// The `*` type variable, e.g. in (List *) Wildcard,
fn loc_wildcard<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
map!(loc!(byte(b'*', EType::TWildcard)), |loc_val: Loc<()>| {
map(loc(byte(b'*', EType::TWildcard)), |loc_val: Loc<()>| {
loc_val.map(|_| TypeAnnotation::Wildcard)
})
}
@ -201,24 +203,24 @@ fn loc_inferred<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
fn loc_applied_arg<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
map_with_arena!(
and!(
map_with_arena(
and(
backtrackable(space0_e(EType::TIndentStart)),
one_of!(
loc_wildcard(),
loc_inferred(),
specialize_err(EType::TInParens, loc_type_in_parens(stop_at_surface_has)),
loc!(specialize_err(
loc(specialize_err(
EType::TRecord,
record_type(stop_at_surface_has)
)),
loc!(specialize_err(
loc(specialize_err(
EType::TTagUnion,
tag_union_type(stop_at_surface_has)
)),
loc!(specialize_err(EType::TApply, concrete_type())),
loc!(parse_type_variable(stop_at_surface_has))
)
loc(specialize_err(EType::TApply, concrete_type())),
loc(parse_type_variable(stop_at_surface_has))
),
),
|arena: &'a Bump, (spaces, argument): (&'a [_], Loc<TypeAnnotation<'a>>)| {
if spaces.is_empty() {
@ -227,7 +229,7 @@ fn loc_applied_arg<'a>(
let Loc { region, value } = argument;
arena.alloc(value).with_spaces_before(spaces, region)
}
}
},
)
}
@ -235,18 +237,18 @@ fn loc_type_in_parens<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, ETypeInParens<'a>> {
then(
loc!(and!(
collection_trailing_sep_e!(
loc(and(
collection_trailing_sep_e(
byte(b'(', ETypeInParens::Open),
specialize_err_ref(ETypeInParens::Type, expression(true, false)),
byte(b',', ETypeInParens::End),
byte(b')', ETypeInParens::End),
TypeAnnotation::SpaceBefore
TypeAnnotation::SpaceBefore,
),
optional(allocated(specialize_err_ref(
ETypeInParens::Type,
term(stop_at_surface_has)
)))
term(stop_at_surface_has),
))),
)),
|_arena, state, progress, item| {
let Loc {
@ -274,7 +276,7 @@ fn loc_type_in_parens<'a>(
fn tag_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>> {
move |arena, state: State<'a>, min_indent: u32| {
let (_, name, state) =
loc!(parse_tag_name(ETypeTagUnion::End)).parse(arena, state, min_indent)?;
loc(parse_tag_name(ETypeTagUnion::End)).parse(arena, state, min_indent)?;
let (_, args, state) =
specialize_err_ref(ETypeTagUnion::Type, loc_applied_args_e(stop_at_surface_has))
@ -313,9 +315,9 @@ fn record_type_field<'a>() -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'
// You must have a field name, e.g. "email"
// using the initial pos is important for error reporting
let pos = state.pos();
let (progress, loc_label, state) = loc!(specialize_err(
let (progress, loc_label, state) = loc(specialize_err(
move |_, _| ETypeRecord::Field(pos),
lowercase_ident_keyword_e()
lowercase_ident_keyword_e(),
))
.parse(arena, state, min_indent)?;
debug_assert_eq!(progress, MadeProgress);
@ -325,9 +327,9 @@ fn record_type_field<'a>() -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.)
let (_, opt_loc_val, state) = optional(either!(
let (_, opt_loc_val, state) = optional(either(
byte(b':', ETypeRecord::Colon),
byte(b'?', ETypeRecord::Optional)
byte(b'?', ETypeRecord::Optional),
))
.parse(arena, state, min_indent)?;
@ -375,9 +377,9 @@ fn record_type<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> {
record!(TypeAnnotation::Record {
fields: collection_trailing_sep_e!(
fields: collection_trailing_sep_e(
byte(b'{', ETypeRecord::Open),
loc!(record_type_field()),
loc(record_type_field()),
byte(b',', ETypeRecord::End),
byte(b'}', ETypeRecord::End),
AssignedField::SpaceBefore
@ -391,12 +393,12 @@ fn record_type<'a>(
}
fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
map!(
indented_seq!(
map(
indented_seq(
specialize_err(EType::TApply, concrete_type()),
// Optionally parse space-separated arguments for the constructor,
// e.g. `Str Float` in `Map Str Float`
loc_applied_args_e(stop_at_surface_has)
loc_applied_args_e(stop_at_surface_has),
),
|(ctor, args): (TypeAnnotation<'a>, Vec<'a, Loc<TypeAnnotation<'a>>>)| {
match &ctor {
@ -411,7 +413,7 @@ fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation
TypeAnnotation::Malformed(_) => ctor,
_ => unreachable!(),
}
}
},
)
.trace("type_annotation:applied_type")
}
@ -419,56 +421,56 @@ fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation
fn loc_applied_args_e<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
zero_or_more!(loc_applied_arg(stop_at_surface_has))
zero_or_more(loc_applied_arg(stop_at_surface_has))
}
// Hash & Eq & ...
fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
map!(
and!(
map(
and(
space0_before_optional_after(
specialize_err(EType::TApply, loc!(concrete_type())),
specialize_err(EType::TApply, loc(concrete_type())),
EType::TIndentStart,
EType::TIndentEnd,
),
zero_or_more!(skip_first!(
zero_or_more(skip_first(
byte(b'&', EType::TImplementsClause),
space0_before_optional_after(
specialize_err(EType::TApply, loc!(concrete_type())),
specialize_err(EType::TApply, loc(concrete_type())),
EType::TIndentStart,
EType::TIndentEnd,
)
))
),
)),
),
|(first_ability, mut other_abilities): (
Loc<TypeAnnotation<'a>>,
Vec<'a, Loc<TypeAnnotation<'a>>>
Vec<'a, Loc<TypeAnnotation<'a>>>,
)| {
other_abilities.insert(0, first_ability);
other_abilities
}
},
)
}
fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'a>> {
map!(
map(
// Suppose we are trying to parse "a implements Hash"
and!(
and(
space0_around_ee(
// Parse "a", with appropriate spaces
specialize_err(
|_, pos| EType::TBadTypeVariable(pos),
loc!(map!(lowercase_ident(), Spaced::Item)),
loc(map(lowercase_ident(), Spaced::Item)),
),
EType::TIndentStart,
EType::TIndentEnd
EType::TIndentEnd,
),
skip_first!(
skip_first(
// Parse "implements"; we don't care about this keyword
word(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
// Parse "Hash & ..."; this may be qualified from another module like "Hash.Hash"
absolute_column_min_indent(ability_chain())
)
absolute_column_min_indent(ability_chain()),
),
),
|(var, abilities): (Loc<Spaced<'a, &'a str>>, Vec<'a, Loc<TypeAnnotation<'a>>>)| {
let abilities_region = Region::span_across(
@ -481,7 +483,7 @@ fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'
abilities: abilities.into_bump_slice(),
};
Loc::at(region, implements_clause)
}
},
)
}
@ -490,18 +492,18 @@ fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'
fn implements_clause_chain<'a>(
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [Loc<ImplementsClause<'a>>]), EType<'a>> {
move |arena, state: State<'a>, min_indent: u32| {
let (_, (spaces_before, ()), state) = and!(
let (_, (spaces_before, ()), state) = and(
space0_e(EType::TIndentStart),
word(crate::keyword::WHERE, EType::TWhereBar)
word(crate::keyword::WHERE, EType::TWhereBar),
)
.parse(arena, state, min_indent)?;
// Parse the first clause (there must be one), then the rest
let (_, first_clause, state) = implements_clause().parse(arena, state, min_indent)?;
let (_, mut clauses, state) = zero_or_more!(skip_first!(
let (_, mut clauses, state) = zero_or_more(skip_first(
byte(b',', EType::TImplementsClause),
implements_clause()
implements_clause(),
))
.parse(arena, state, min_indent)?;
@ -518,36 +520,36 @@ fn implements_clause_chain<'a>(
/// Parse a implements-abilities clause, e.g. `implements [Eq, Hash]`.
pub fn implements_abilities<'a>() -> impl Parser<'a, Loc<ImplementsAbilities<'a>>, EType<'a>> {
increment_min_indent(skip_first!(
increment_min_indent(skip_first(
// Parse "implements"; we don't care about this keyword
word(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
// Parse "Hash"; this may be qualified from another module like "Hash.Hash"
space0_before_e(
loc!(map!(
collection_trailing_sep_e!(
loc(map(
collection_trailing_sep_e(
byte(b'[', EType::TStart),
loc!(parse_implements_ability()),
loc(parse_implements_ability()),
byte(b',', EType::TEnd),
byte(b']', EType::TEnd),
ImplementsAbility::SpaceBefore
ImplementsAbility::SpaceBefore,
),
ImplementsAbilities::Implements
ImplementsAbilities::Implements,
)),
EType::TIndentEnd,
)
),
))
}
fn parse_implements_ability<'a>() -> impl Parser<'a, ImplementsAbility<'a>, EType<'a>> {
increment_min_indent(record!(ImplementsAbility::ImplementsAbility {
ability: loc!(specialize_err(EType::TApply, concrete_type())),
ability: loc(specialize_err(EType::TApply, concrete_type())),
impls: optional(backtrackable(space0_before_e(
loc!(map!(
loc(map(
specialize_err(
EType::TAbilityImpl,
collection_trailing_sep_e!(
collection_trailing_sep_e(
byte(b'{', ETypeAbilityImpl::Open),
specialize_err(|e: ERecord<'_>, _| e.into(), loc!(ability_impl_field())),
specialize_err(|e: ERecord<'_>, _| e.into(), loc(ability_impl_field())),
byte(b',', ETypeAbilityImpl::End),
byte(b'}', ETypeAbilityImpl::End),
AssignedField::SpaceBefore
@ -577,8 +579,8 @@ fn expression<'a>(
let (p1, first, state) = space0_before_e(term(stop_at_surface_has), EType::TIndentStart)
.parse(arena, state, min_indent)?;
let result = and![
zero_or_more!(skip_first!(
let result = and(
zero_or_more(skip_first(
byte(b',', EType::TFunctionArgument),
one_of![
space0_around_ee(
@ -587,15 +589,15 @@ fn expression<'a>(
EType::TIndentEnd
),
fail(EType::TFunctionArgument)
]
],
))
.trace("type_annotation:expression:rest_args"),
skip_second!(
skip_second(
space0_e(EType::TIndentStart),
two_bytes(b'-', b'>', EType::TStart)
two_bytes(b'-', b'>', EType::TStart),
)
.trace("type_annotation:expression:arrow")
]
.trace("type_annotation:expression:arrow"),
)
.parse(arena, state.clone(), min_indent);
let (progress, annot, state) = match result {
@ -629,9 +631,9 @@ fn expression<'a>(
}
Err(err) => {
if !is_trailing_comma_valid {
let (_, comma, _) = optional(backtrackable(skip_first!(
let (_, comma, _) = optional(backtrackable(skip_first(
space0_e(EType::TIndentStart),
byte(b',', EType::TStart)
byte(b',', EType::TStart),
)))
.trace("check trailing comma")
.parse(arena, state.clone(), min_indent)?;