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, 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,19 +3246,18 @@ 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 {
),
|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() {
@ -3272,15 +3268,14 @@ fn record_field_expr<'a>() -> impl Parser<'a, RecordFieldExpr<'a>, ERecord<'a>>
.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 {
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)
),

View file

@ -1390,8 +1390,7 @@ where
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, word};
/// # use roc_parse::loc;
/// # use crate::roc_parse::parser::{Parser, Progress, word, loc};
/// # use roc_region::all::{Loc, Position};
/// # use bumpalo::Bump;
/// # #[derive(Debug, PartialEq)]
@ -1400,7 +1399,7 @@ where
/// # }
/// # let arena = Bump::new();
/// # fn foo<'a>(arena: &'a Bump) {
/// let parser = loc!(word("hello", Problem::NotFound));
/// let parser = loc(word("hello", Problem::NotFound));
///
/// let (progress, output, state) = parser.parse(&arena, State::new("hello, world".as_bytes()), 0).unwrap();
/// assert_eq!(progress, Progress::MadeProgress);
@ -1409,15 +1408,13 @@ where
/// # }
/// # foo(&arena);
/// ```
#[macro_export]
macro_rules! loc {
($parser:expr) => {
move |arena, state: $crate::state::State<'a>, min_indent: u32| {
use roc_region::all::{Loc, Region};
pub fn loc<'a, Output, E: 'a>(
parser: impl Parser<'a, Output, E>,
) -> impl Parser<'a, Loc<Output>, E> {
move |arena, state: crate::state::State<'a>, min_indent: u32| {
let start = state.pos();
match $parser.parse(arena, state, min_indent) {
match parser.parse(arena, state, min_indent) {
Ok((progress, value, state)) => {
let end = state.pos();
let region = Region::new(start, end);
@ -1427,7 +1424,6 @@ macro_rules! loc {
Err(err) => Err(err),
}
}
};
}
/// If the first one parses, ignore its output and move on to parse with the second one.
@ -1436,13 +1432,12 @@ macro_rules! loc {
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, word};
/// # use crate::roc_parse::parser::{Parser, Progress, word, skip_first};
/// # use roc_parse::ident::lowercase_ident;
/// # use roc_parse::skip_first;
/// # use bumpalo::Bump;
/// # let arena = Bump::new();
/// # fn foo<'a>(arena: &'a Bump) {
/// let parser = skip_first!(
/// let parser = skip_first(
/// word("hello, ", |_| ()),
/// lowercase_ident()
/// );
@ -1454,19 +1449,21 @@ macro_rules! loc {
/// # }
/// # foo(&arena);
/// ```
#[macro_export]
macro_rules! skip_first {
($p1:expr, $p2:expr) => {
move |arena, state: $crate::state::State<'a>, min_indent: u32| match $p1
pub fn skip_first<'a, P1, First, P2, Second, E>(p1: P1, p2: P2) -> impl Parser<'a, Second, E>
where
P1: Parser<'a, First, E>,
P2: Parser<'a, Second, E>,
E: 'a,
{
move |arena, state: crate::state::State<'a>, min_indent: u32| match p1
.parse(arena, state, min_indent)
{
Ok((p1, _, state)) => match $p2.parse(arena, state, min_indent) {
Ok((p1, _, state)) => match p2.parse(arena, state, min_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), out2, state)),
Err((p2, fail)) => Err((p1.or(p2), fail)),
},
Err((progress, fail)) => Err((progress, fail)),
}
};
}
/// If the first one parses, parse the second one; if it also parses, use the
@ -1476,13 +1473,12 @@ macro_rules! skip_first {
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, word};
/// # use crate::roc_parse::parser::{Parser, Progress, word, skip_second};
/// # use roc_parse::ident::lowercase_ident;
/// # use roc_parse::skip_second;
/// # use bumpalo::Bump;
/// # let arena = Bump::new();
/// # fn foo<'a>(arena: &'a Bump) {
/// let parser = skip_second!(
/// let parser = skip_second(
/// lowercase_ident(),
/// word(", world", |_| ())
/// );
@ -1494,75 +1490,84 @@ macro_rules! skip_first {
/// # }
/// # foo(&arena);
/// ```
#[macro_export]
macro_rules! skip_second {
($p1:expr, $p2:expr) => {
move |arena, state: $crate::state::State<'a>, min_indent: u32| match $p1
pub fn skip_second<'a, P1, First, P2, Second, E>(p1: P1, p2: P2) -> impl Parser<'a, First, E>
where
E: 'a,
P1: Parser<'a, First, E>,
P2: Parser<'a, Second, E>,
{
move |arena, state: crate::state::State<'a>, min_indent: u32| match p1
.parse(arena, state, min_indent)
{
Ok((p1, out1, state)) => match $p2.parse(arena, state, min_indent) {
Ok((p1, out1, state)) => match p2.parse(arena, state, min_indent) {
Ok((p2, _, state)) => Ok((p1.or(p2), out1, state)),
Err((p2, fail)) => Err((p1.or(p2), fail)),
},
Err((progress, fail)) => Err((progress, fail)),
}
};
}
#[macro_export]
macro_rules! collection_inner {
($elem:expr, $delimiter:expr, $space_before:expr) => {
map_with_arena!(
and!(
and!(
$crate::blankspace::spaces(),
$crate::parser::trailing_sep_by0(
$delimiter,
$crate::blankspace::spaces_before_optional_after($elem,)
)
pub fn collection_inner<'a, Elem: 'a + crate::ast::Spaceable<'a> + Clone, E: 'a + SpaceProblem>(
elem: impl Parser<'a, Loc<Elem>, E> + 'a,
delimiter: impl Parser<'a, (), E>,
space_before: impl Fn(&'a Elem, &'a [crate::ast::CommentOrNewline<'a>]) -> Elem,
) -> impl Parser<'a, crate::ast::Collection<'a, Loc<Elem>>, E> {
map_with_arena(
and(
and(
crate::blankspace::spaces(),
trailing_sep_by0(
delimiter,
crate::blankspace::spaces_before_optional_after(elem),
),
$crate::blankspace::spaces()
),
|arena: &'a bumpalo::Bump,
((spaces, mut parsed_elems), mut final_comments): (
crate::blankspace::spaces(),
),
#[allow(clippy::type_complexity)]
move |arena: &'a bumpalo::Bump,
out: (
(
&'a [$crate::ast::CommentOrNewline<'a>],
bumpalo::collections::vec::Vec<'a, Loc<_>>
&'a [crate::ast::CommentOrNewline<'a>],
bumpalo::collections::Vec<'a, Loc<Elem>>,
),
&'a [$crate::ast::CommentOrNewline<'a>]
&'a [crate::ast::CommentOrNewline<'a>],
)| {
let ((spaces, mut parsed_elems), mut final_comments) = out;
if !spaces.is_empty() {
if let Some(first) = parsed_elems.first_mut() {
first.value = $space_before(arena.alloc(first.value), spaces)
first.value = space_before(arena.alloc(first.value.clone()), spaces);
} else {
debug_assert!(final_comments.is_empty());
final_comments = spaces;
}
}
$crate::ast::Collection::with_items_and_comments(
crate::ast::Collection::with_items_and_comments(
arena,
parsed_elems.into_bump_slice(),
final_comments,
)
}
},
)
};
}
#[macro_export]
macro_rules! collection_trailing_sep_e {
($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $space_before:expr) => {
between!(
$opening_brace,
$crate::parser::reset_min_indent($crate::collection_inner!(
$elem,
$delimiter,
$space_before
)),
$closing_brace
pub fn collection_trailing_sep_e<
'a,
Elem: 'a + crate::ast::Spaceable<'a> + Clone,
E: 'a + SpaceProblem,
>(
opening_brace: impl Parser<'a, (), E>,
elem: impl Parser<'a, Loc<Elem>, E> + 'a,
delimiter: impl Parser<'a, (), E>,
closing_brace: impl Parser<'a, (), E>,
space_before: impl Fn(&'a Elem, &'a [crate::ast::CommentOrNewline<'a>]) -> Elem,
) -> impl Parser<'a, crate::ast::Collection<'a, Loc<Elem>>, E> {
between(
opening_brace,
reset_min_indent(collection_inner(elem, delimiter, space_before)),
closing_brace,
)
};
}
/// Creates a parser that always succeeds with the given argument as output.
@ -1571,12 +1576,11 @@ macro_rules! collection_trailing_sep_e {
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, Progress::NoProgress};
/// # use roc_parse::succeed;
/// # use crate::roc_parse::parser::{Parser, Progress, succeed};
/// # use bumpalo::Bump;
/// # let arena = Bump::new();
/// # fn foo<'a>(arena: &'a Bump) {
/// let parser = succeed!("different");
/// let parser = succeed("different");
///
/// let (progress, output, state) = Parser::<&'a str,()>::parse(&parser, &arena, State::new("hello, world".as_bytes()), 0).unwrap();
/// assert_eq!(progress, Progress::NoProgress);
@ -1585,13 +1589,10 @@ macro_rules! collection_trailing_sep_e {
/// # }
/// # foo(&arena);
/// ```
#[macro_export]
macro_rules! succeed {
($value:expr) => {
move |_arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
Ok((NoProgress, $value, state))
pub fn succeed<'a, T: Clone, E: 'a>(value: T) -> impl Parser<'a, T, E> {
move |_arena: &'a bumpalo::Bump, state: crate::state::State<'a>, _min_indent: u32| {
Ok((NoProgress, value.clone(), state))
}
};
}
/// Creates a parser that always fails.
@ -1673,49 +1674,47 @@ where
/// Runs two parsers in succession. If both parsers succeed, the output is a tuple of both outputs.
/// Both parsers must have the same error type.
///
/// # Examples
/// # Example
///
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, word, byte};
/// # use crate::roc_parse::parser::{Parser, Progress, and, word};
/// # use roc_region::all::Position;
/// # use roc_parse::and;
/// # use bumpalo::Bump;
/// # #[derive(Debug, PartialEq)]
/// # enum Problem {
/// # NotFound(Position),
/// # }
/// # let arena = Bump::new();
/// # fn foo<'a>(arena: &'a Bump) {
/// let parser = and!(
/// word("hello", Problem::NotFound),
/// byte(b',', Problem::NotFound)
/// );
/// let parser1 = word("hello", Problem::NotFound);
/// let parser2 = word(", ", Problem::NotFound);
/// let parser = and(parser1, parser2);
///
/// // Success case
/// let (progress, output, state) = parser.parse(&arena, State::new("hello, world".as_bytes()), 0).unwrap();
/// assert_eq!(progress, Progress::MadeProgress);
/// assert_eq!(output, ((), ()));
/// assert_eq!(state.pos().offset, 6);
/// assert_eq!(output, ((),()));
/// assert_eq!(state.pos(), Position::new(7));
///
/// let (progress, err) = parser.parse(&arena, State::new("hello! world".as_bytes()), 0).unwrap_err();
/// // Error case
/// let (progress, err) = parser.parse(&arena, State::new("hello!! world".as_bytes()), 0).unwrap_err();
/// assert_eq!(progress, Progress::MadeProgress);
/// assert_eq!(err, Problem::NotFound(Position::new(5)));
/// # }
/// # foo(&arena);
/// ```
#[macro_export]
macro_rules! and {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| match $p1
pub fn and<'a, Output1, Output2, E: 'a>(
p1: impl Parser<'a, Output1, E>,
p2: impl Parser<'a, Output2, E>,
) -> impl Parser<'a, (Output1, Output2), E> {
move |arena: &'a bumpalo::Bump, state: crate::state::State<'a>, min_indent: u32| match p1
.parse(arena, state, min_indent)
{
Ok((p1, out1, state)) => match $p2.parse(arena, state, min_indent) {
Ok((p1, out1, state)) => match p2.parse(arena, state, min_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)),
Err((p2, fail)) => Err((p1.or(p2), fail)),
},
Err((progress, fail)) => Err((progress, fail)),
}
};
}
/// Take as input something that looks like a struct literal where values are parsers
@ -1737,12 +1736,14 @@ macro_rules! record {
};
}
/// Similar to [`and!`], but we modify the `min_indent` of the second parser to be
/// 1 greater than the `line_indent()` at the start of the first parser.
#[macro_export]
macro_rules! indented_seq_skip_first {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
/// Similar to [`skip_first`], but we modify the `min_indent` of the second
/// parser (`parser`) to be 1 greater than the `line_indent()` at the start of
/// the first parser (`before`).
pub fn indented_seq_skip_first<'a, O, E: 'a>(
before: impl Parser<'a, (), E>,
parser: impl Parser<'a, O, E>,
) -> impl Parser<'a, O, E> {
move |arena: &'a bumpalo::Bump, state: crate::state::State<'a>, _min_indent: u32| {
let start_indent = state.line_indent();
// TODO: we should account for min_indent here, but this doesn't currently work
@ -1754,23 +1755,23 @@ macro_rules! indented_seq_skip_first {
let p1_indent = start_indent;
let p2_indent = p1_indent + 1;
match $p1.parse(arena, state, p1_indent) {
Ok((p1, (), state)) => match $p2.parse(arena, state, p2_indent) {
match before.parse(arena, state, p1_indent) {
Ok((p1, (), state)) => match parser.parse(arena, state, p2_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), out2, state)),
Err((p2, fail)) => Err((p1.or(p2), fail)),
},
Err((progress, fail)) => Err((progress, fail)),
}
}
};
}
/// Similar to [`and!`], but we modify the min_indent of the second parser to be
/// Similar to `and`, but we modify the min_indent of the second parser to be
/// 1 greater than the line_indent() at the start of the first parser.
#[macro_export]
macro_rules! indented_seq {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
pub fn indented_seq<'a, Output1, Output2, E: 'a>(
p1: impl Parser<'a, Output1, E>,
p2: impl Parser<'a, Output2, E>,
) -> impl Parser<'a, (Output1, Output2), E> {
move |arena: &'a bumpalo::Bump, state: crate::state::State<'a>, _min_indent: u32| {
let start_indent = state.line_indent();
// TODO: we should account for min_indent here, but this doesn't currently work
@ -1782,36 +1783,36 @@ macro_rules! indented_seq {
let p1_indent = start_indent;
let p2_indent = p1_indent + 1;
match $p1.parse(arena, state, p1_indent) {
Ok((p1, out1, state)) => match $p2.parse(arena, state, p2_indent) {
match p1.parse(arena, state, p1_indent) {
Ok((p1, out1, state)) => match p2.parse(arena, state, p2_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)),
Err((p2, fail)) => Err((p1.or(p2), fail)),
},
Err((progress, fail)) => Err((progress, fail)),
}
}
};
}
/// Similar to [`and!`], but we modify the `min_indent` of the second parser to be
/// Similar to [`and`], but we modify the `min_indent` of the second parser to be
/// 1 greater than the `column()` at the start of the first parser.
macro_rules! absolute_indented_seq {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
pub fn absolute_indented_seq<'a, Output1, Output2, E: 'a>(
p1: impl Parser<'a, Output1, E>,
p2: impl Parser<'a, Output2, E>,
) -> impl Parser<'a, (Output1, Output2), E> {
move |arena: &'a bumpalo::Bump, state: crate::state::State<'a>, _min_indent: u32| {
let start_indent = state.column();
let p1_indent = start_indent;
let p2_indent = p1_indent + 1;
match $p1.parse(arena, state, p1_indent) {
Ok((p1, out1, state)) => match $p2.parse(arena, state, p2_indent) {
match p1.parse(arena, state, p1_indent) {
Ok((p1, out1, state)) => match p2.parse(arena, state, p2_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)),
Err((p2, fail)) => Err((p1.or(p2), fail)),
},
Err((progress, fail)) => Err((progress, fail)),
}
}
};
}
/// Returns the result of the first parser that makes progress, even if it failed.
@ -2250,9 +2251,9 @@ where
#[macro_export]
macro_rules! byte_check_indent {
($byte_to_match:expr, $problem:expr, $min_indent:expr, $indent_problem:expr) => {
and!(
$crate::parser::and(
byte($byte_to_match, $problem),
$crate::parser::check_indent($min_indent, $indent_problem)
$crate::parser::check_indent($min_indent, $indent_problem),
)
};
}
@ -2264,16 +2265,15 @@ macro_rules! byte_check_indent {
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, word};
/// # use crate::roc_parse::parser::{Parser, Progress, word, map};
/// # use roc_region::all::Position;
/// # use roc_parse::map;
/// # use bumpalo::Bump;
/// # #[derive(Debug, PartialEq)]
/// # enum Problem {
/// # NotFound(Position),
/// # }
/// # let arena = Bump::new();
/// let parser = map!(
/// let parser = map(
/// word("hello", Problem::NotFound),
/// |_output| "new output!"
/// );
@ -2289,78 +2289,28 @@ macro_rules! byte_check_indent {
/// assert_eq!(progress, Progress::NoProgress);
/// assert_eq!(err, Problem::NotFound(Position::zero()));
/// ```
#[macro_export]
macro_rules! map {
($parser:expr, $transform:expr) => {
pub fn map<'a, Output, MappedOutput, E: 'a>(
parser: impl Parser<'a, Output, E>,
transform: impl Fn(Output) -> MappedOutput,
) -> impl Parser<'a, MappedOutput, E> {
move |arena, state, min_indent| {
#[allow(clippy::redundant_closure_call)]
$parser
parser
.parse(arena, state, min_indent)
.map(|(progress, output, next_state)| (progress, $transform(output), next_state))
.map(|(progress, output, next_state)| (progress, transform(output), next_state))
}
};
}
/// Maps/transforms the `Ok` result of parsing using the given function.
/// Similar to [`map!`], but the transform function also takes a bump allocator.
///
/// # Example
///
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, word};
/// # use roc_region::all::Position;
/// # use roc_parse::map_with_arena;
/// # use bumpalo::Bump;
/// # #[derive(Debug, PartialEq)]
/// # enum Problem {
/// # NotFound(Position),
/// # }
/// # let arena = Bump::new();
/// let parser = map_with_arena!(
/// word("hello", Problem::NotFound),
/// |_arena, _output| "new output!"
/// );
///
/// // Success case
/// let (progress, output, state) = parser.parse(&arena, State::new("hello, world".as_bytes()), 0).unwrap();
/// assert_eq!(progress, Progress::MadeProgress);
/// assert_eq!(output, "new output!");
/// assert_eq!(state.pos(), Position::new(5));
///
/// // Error case
/// let (progress, err) = parser.parse(&arena, State::new("bye, world".as_bytes()), 0).unwrap_err();
/// assert_eq!(progress, Progress::NoProgress);
/// assert_eq!(err, Problem::NotFound(Position::zero()));
/// ```
#[macro_export]
macro_rules! map_with_arena {
($parser:expr, $transform:expr) => {
move |arena, state, min_indent| {
#[allow(clippy::redundant_closure_call)]
$parser
.parse(arena, state, min_indent)
.map(|(progress, output, next_state)| {
(progress, $transform(arena, output), next_state)
})
}
};
}
/// Applies the parser as many times as possible.
/// This parser will only fail if the given parser makes partial progress.
#[macro_export]
macro_rules! zero_or_more {
($parser:expr) => {
pub fn zero_or_more<'a, Output, E: 'a>(
parser: impl Parser<'a, Output, E>,
) -> impl Parser<'a, bumpalo::collections::Vec<'a, Output>, E> {
move |arena, state: State<'a>, min_indent: u32| {
use bumpalo::collections::Vec;
let original_state = state.clone();
let start_bytes_len = state.bytes().len();
match $parser.parse(arena, state, min_indent) {
match parser.parse(arena, state, min_indent) {
Ok((_, first_output, next_state)) => {
let mut state = next_state;
let mut buf = Vec::with_capacity_in(1, arena);
@ -2369,7 +2319,7 @@ macro_rules! zero_or_more {
loop {
let old_state = state.clone();
match $parser.parse(arena, state, min_indent) {
match parser.parse(arena, state, min_indent) {
Ok((_, next_output, next_state)) => {
state = next_state;
buf.push(next_output);
@ -2383,7 +2333,10 @@ macro_rules! zero_or_more {
NoProgress => {
// the next element failed with no progress
// report whether we made progress before
let progress = Progress::from_lengths(start_bytes_len, old_state.bytes().len());
let progress = Progress::from_lengths(
start_bytes_len,
old_state.bytes().len(),
);
return Ok((progress, buf, old_state));
}
}
@ -2406,7 +2359,6 @@ macro_rules! zero_or_more {
}
}
}
};
}
/// Creates a parser that matches one or more times.
@ -2417,9 +2369,8 @@ macro_rules! zero_or_more {
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, Progress::{MadeProgress, NoProgress}, word};
/// # use crate::roc_parse::parser::{Parser, Progress, word, one_or_more};
/// # use roc_region::all::Position;
/// # use roc_parse::one_or_more;
/// # use bumpalo::Bump;
/// # #[derive(Debug, PartialEq)]
/// # enum Problem {
@ -2427,7 +2378,7 @@ macro_rules! zero_or_more {
/// # }
/// # let arena = Bump::new();
/// # fn foo<'a>(arena: &'a Bump) {
/// let parser = one_or_more!(
/// let parser = one_or_more(
/// word("hello, ", Problem::NotFound),
/// Problem::NotFound
/// );
@ -2445,13 +2396,15 @@ macro_rules! zero_or_more {
/// # }
/// # foo(&arena);
/// ```
#[macro_export]
macro_rules! one_or_more {
($parser:expr, $to_error:expr) => {
move |arena, state: State<'a>, min_indent: u32| {
use bumpalo::collections::Vec;
match $parser.parse(arena, state.clone(), min_indent) {
pub fn one_or_more<'a, Output, E: 'a>(
parser: impl Parser<'a, Output, E>,
to_error: impl Fn(Position) -> E,
) -> impl Parser<'a, bumpalo::collections::Vec<'a, Output>, E> {
move |arena, state: State<'a>, min_indent: u32| match parser.parse(
arena,
state.clone(),
min_indent,
) {
Ok((_, first_output, next_state)) => {
let mut state = next_state;
let mut buf = Vec::with_capacity_in(1, arena);
@ -2460,7 +2413,7 @@ macro_rules! one_or_more {
loop {
let old_state = state.clone();
match $parser.parse(arena, state, min_indent) {
match parser.parse(arena, state, min_indent) {
Ok((_, next_output, next_state)) => {
state = next_state;
buf.push(next_output);
@ -2474,10 +2427,8 @@ macro_rules! one_or_more {
}
}
}
Err((progress, _)) => Err((progress, $to_error(state.pos()))),
Err((progress, _)) => Err((progress, to_error(state.pos()))),
}
}
};
}
/// Creates a parser that debug prints the result of parsing.
@ -2527,9 +2478,8 @@ macro_rules! debug {
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, Progress::{MadeProgress, NoProgress}, word, Either};
/// # use crate::roc_parse::parser::{Parser, Progress, word, either, Either};
/// # use roc_region::all::Position;
/// # use roc_parse::either;
/// # use bumpalo::Bump;
/// # #[derive(Debug, PartialEq)]
/// # enum Problem {
@ -2537,7 +2487,7 @@ macro_rules! debug {
/// # }
/// # let arena = Bump::new();
/// # fn foo<'a>(arena: &'a Bump) {
/// let parser = either!(
/// let parser = either(
/// word("hello", Problem::NotFound),
/// word("bye", Problem::NotFound)
/// );
@ -2560,27 +2510,25 @@ macro_rules! debug {
/// # }
/// # foo(&arena);
/// ```
#[macro_export]
macro_rules! either {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| {
pub fn either<'a, OutputLeft, OutputRight, E: 'a>(
p1: impl Parser<'a, OutputLeft, E>,
p2: impl Parser<'a, OutputRight, E>,
) -> impl Parser<'a, Either<OutputLeft, OutputRight>, E> {
move |arena: &'a bumpalo::Bump, state: crate::state::State<'a>, min_indent: u32| {
let original_state = state.clone();
match $p1.parse(arena, state, min_indent) {
match p1.parse(arena, state, min_indent) {
Ok((progress, output, state)) => {
Ok((progress, $crate::parser::Either::First(output), state))
Ok((progress, crate::parser::Either::First(output), state))
}
Err((NoProgress, _)) => {
match $p2.parse(arena, original_state.clone(), min_indent) {
Err((NoProgress, _)) => match p2.parse(arena, original_state.clone(), min_indent) {
Ok((progress, output, state)) => {
Ok((progress, $crate::parser::Either::Second(output), state))
Ok((progress, crate::parser::Either::Second(output), state))
}
Err((progress, fail)) => Err((progress, fail)),
}
}
},
Err((MadeProgress, fail)) => Err((MadeProgress, fail)),
}
}
};
}
/// Given three parsers, parse them all but ignore the output of the first and last one.
@ -2592,9 +2540,8 @@ macro_rules! either {
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, word, byte};
/// # use crate::roc_parse::parser::{Parser, Progress, word, byte, between};
/// # use roc_region::all::Position;
/// # use roc_parse::{between, skip_first, skip_second};
/// # use bumpalo::Bump;
/// # #[derive(Debug, PartialEq)]
/// # enum Problem {
@ -2602,7 +2549,7 @@ macro_rules! either {
/// # }
/// # let arena = Bump::new();
/// # fn foo<'a>(arena: &'a Bump) {
/// let parser = between!(
/// let parser = between(
/// byte(b'(', Problem::NotFound),
/// word("hello", Problem::NotFound),
/// byte(b')', Problem::NotFound)
@ -2614,104 +2561,16 @@ macro_rules! either {
/// # }
/// # foo(&arena);
/// ```
#[macro_export]
macro_rules! between {
($opening_brace:expr, $parser:expr, $closing_brace:expr) => {
skip_first!($opening_brace, skip_second!($parser, $closing_brace))
};
}
/// Runs two parsers in succession. If both parsers succeed, the output is a tuple of both outputs.
/// Both parsers must have the same error type.
///
/// This is a function version of the [`and!`] macro.
/// For some reason, some usages won't compile unless they use this instead of the macro version.
///
/// # Example
///
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, and, word};
/// # use roc_region::all::Position;
/// # use bumpalo::Bump;
/// # #[derive(Debug, PartialEq)]
/// # enum Problem {
/// # NotFound(Position),
/// # }
/// # let arena = Bump::new();
/// let parser1 = word("hello", Problem::NotFound);
/// let parser2 = word(", ", Problem::NotFound);
/// let parser = and(parser1, parser2);
///
/// // Success case
/// let (progress, output, state) = parser.parse(&arena, State::new("hello, world".as_bytes()), 0).unwrap();
/// assert_eq!(progress, Progress::MadeProgress);
/// assert_eq!(output, ((),()));
/// assert_eq!(state.pos(), Position::new(7));
///
/// // Error case
/// let (progress, err) = parser.parse(&arena, State::new("hello!! world".as_bytes()), 0).unwrap_err();
/// assert_eq!(progress, Progress::MadeProgress);
/// assert_eq!(err, Problem::NotFound(Position::new(5)));
/// ```
#[inline(always)]
pub fn and<'a, P1, P2, A, B, E>(p1: P1, p2: P2) -> impl Parser<'a, (A, B), E>
where
P1: Parser<'a, A, E>,
P2: Parser<'a, B, E>,
P1: 'a,
P2: 'a,
A: 'a,
B: 'a,
E: 'a,
{
and!(p1, p2)
}
/// Adds location info. This is a function version the [`loc!`] macro.
///
/// For some reason, some usages won't compile unless they use this instead of the macro version.
/// This is likely because the lifetime `'a` is not defined at the call site.
///
/// # Examples
/// ```
/// # #![forbid(unused_imports)]
/// # use roc_parse::state::State;
/// # use crate::roc_parse::parser::{Parser, Progress, word, loc};
/// # use roc_region::all::{Loc, Position};
/// # use bumpalo::Bump;
/// # #[derive(Debug, PartialEq)]
/// # enum Problem {
/// # NotFound(Position),
/// # }
/// # let arena = Bump::new();
/// # fn foo<'a>(arena: &'a Bump) {
/// let parser = loc(word("hello", Problem::NotFound));
///
/// let (progress, output, state) = parser.parse(&arena, State::new("hello, world".as_bytes()), 0).unwrap();
/// assert_eq!(progress, Progress::MadeProgress);
/// assert_eq!(output, Loc::new(0, 5, ()));
/// assert_eq!(state.pos().offset, 5);
/// # }
/// # foo(&arena);
/// ```
#[inline(always)]
pub fn loc<'a, P, Val, Error>(parser: P) -> impl Parser<'a, Loc<Val>, Error>
where
P: Parser<'a, Val, Error>,
Error: 'a,
{
loc!(parser)
pub fn between<'a, Before, Inner, After, Err: 'a>(
opening_brace: impl Parser<'a, Before, Err>,
inner: impl Parser<'a, Inner, Err>,
closing_brace: impl Parser<'a, After, Err>,
) -> impl Parser<'a, Inner, Err> {
skip_first(opening_brace, skip_second(inner, closing_brace))
}
/// Maps/transforms the `Ok` result of parsing using the given function.
/// Similar to [`map!`], but the transform function also takes a bump allocator.
///
/// Function version of the [`map_with_arena!`] macro.
///
/// For some reason, some usages won't compile unless they use this instead of the macro version.
/// This is likely because the lifetime `'a` is not defined at the call site.
/// Similar to [`map`], but the transform function also takes a bump allocator.
///
/// # Example
///
@ -2749,14 +2608,14 @@ pub fn map_with_arena<'a, P, F, Before, After, E>(
) -> impl Parser<'a, After, E>
where
P: Parser<'a, Before, E>,
P: 'a,
F: Fn(&'a Bump, Before) -> After,
F: 'a,
Before: 'a,
After: 'a,
E: 'a,
{
map_with_arena!(parser, transform)
move |arena, state, min_indent| {
parser
.parse(arena, state, min_indent)
.map(|(progress, output, next_state)| (progress, transform(arena, output), next_state))
}
}
/// Creates a new parser that does not progress but still forwards the output of

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,8 +323,7 @@ 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))
let (_, loc_ident, state) = specialize_err(|_, pos| EPattern::Start(pos), loc(parse_ident))
.parse(arena, state, min_indent)?;
match loc_ident.value {
@ -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)?;