Un-macro add

This commit is contained in:
Jackson Wambolt 2024-04-15 19:44:47 -05:00
parent 64290a8cf6
commit 41d7d02e2a
No known key found for this signature in database
GPG key ID: 76F29A42FEE8811C
6 changed files with 89 additions and 138 deletions

View file

@ -138,7 +138,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 +161,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 +184,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

@ -10,7 +10,7 @@ use crate::blankspace::{
use crate::ident::{integer_ident, lowercase_ident, parse_ident, Accessor, Ident};
use crate::keyword;
use crate::parser::{
self, backtrackable, between, byte, byte_indent, increment_min_indent, indented_seq,
self, and, backtrackable, between, byte, byte_indent, increment_min_indent, indented_seq,
line_min_indent, optional, reset_min_indent, sep_by1, sep_by1_e, set_min_indent, skip_first,
skip_second, specialize_err, specialize_err_ref, then, two_bytes, EClosure, EExpect, EExpr,
EIf, EInParens, EList, ENumber, EPattern, ERecord, EString, EType, EWhen, Either, ParseResult,
@ -132,7 +132,7 @@ 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!(
loc!(and(
specialize_err(EExpr::InParens, loc_expr_in_parens_help()),
record_field_access_chain()
)),
@ -273,7 +273,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, &[]);
@ -282,7 +282,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
// 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!(
and(
loc!(byte(b'!', EExpr::Start)),
space0_before_e(loc_term(options), EExpr::IndentStart)
),
@ -1296,7 +1296,7 @@ fn opaque_signature_with_space_before<'a>() -> impl Parser<
),
EExpr<'a>,
> {
and!(
and(
specialize_err(
EExpr::Type,
space0_before_e(
@ -1306,8 +1306,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),
))),
)
}
@ -1488,7 +1488,7 @@ mod ability {
absolute_indented_seq(
specialize_err(|_, pos| EAbility::DemandName(pos), loc!(lowercase_ident())),
skip_first(
and!(
and(
// TODO: do we get anything from picking up spaces here?
space0_e(EAbility::DemandName),
byte(b':', EAbility::DemandColon)
@ -2368,7 +2368,7 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo
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(
@ -2406,7 +2406,7 @@ mod when {
/// Parser for when expressions.
pub fn expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> {
map_with_arena!(
and!(
and(
indented_seq(
parser::keyword(keyword::WHEN, EWhen::When),
space0_around_e_no_after_indent_check(
@ -2458,7 +2458,7 @@ mod when {
}));
let branch_parser = map!(
and!(
and(
then(
branch_alternatives(options, Some(pattern_indent_level)),
move |_arena, state, _, ((indent_column, loc_patterns), loc_guard)| {
@ -2511,7 +2511,7 @@ mod when {
check_for_arrow: false,
..options
};
and!(
and(
branch_alternatives_help(pattern_indent_level),
one_of![
map!(
@ -2530,7 +2530,7 @@ mod when {
Some
),
|_, s, _| Ok((NoProgress, None, s))
]
],
)
}
@ -2631,20 +2631,20 @@ mod when {
fn if_branch<'a>() -> impl Parser<'a, (Loc<Expr<'a>>, Loc<Expr<'a>>), EIf<'a>> {
skip_second(
and!(
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),
)
@ -2727,9 +2727,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) {
@ -2981,13 +2981,13 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> {
use RecordField::*;
map_with_arena!(
and!(
and(
specialize_err(|_, pos| ERecord::Field(pos), loc!(lowercase_ident())),
and!(
and(
spaces(),
optional(either!(
and!(byte(b':', ERecord::Colon), record_field_expr()),
and!(
and(byte(b':', ERecord::Colon), record_field_expr()),
and(
byte(b'?', ERecord::QuestionMark),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false)))
)
@ -3029,10 +3029,10 @@ enum RecordFieldExpr<'a> {
fn record_field_expr<'a>() -> impl Parser<'a, RecordFieldExpr<'a>, ERecord<'a>> {
map_with_arena!(
and!(
and(
spaces(),
either!(
and!(
and(
two_bytes(b'<', b'-', ERecord::Arrow),
spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false)))
),
@ -3096,10 +3096,10 @@ fn record_help<'a>() -> impl Parser<'a, RecordHelp<'a>, ERecord<'a>> {
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 {

View file

@ -4,7 +4,7 @@ use crate::ast::{
use crate::blankspace::space0_e;
use crate::expr::merge_spaces;
use crate::ident::{lowercase_ident, UppercaseIdent};
use crate::parser::{byte, skip_second, specialize_err, EPackageEntry, EPackageName, Parser};
use crate::parser::{and, byte, skip_second, specialize_err, EPackageEntry, EPackageName, Parser};
use crate::parser::{optional, then};
use crate::string_literal;
use roc_module::symbol::{ModuleId, Symbol};
@ -342,10 +342,10 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
// e.g. "uc" in `uc: roc/unicode 1.0.0`
//
// (Indirect dependencies don't have a shorthand.)
and!(
optional(and!(
and(
optional(and(
skip_second(
and!(
and(
specialize_err(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()),
space0_e(EPackageEntry::IndentPackage)
),

View file

@ -9,9 +9,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, skip_first, skip_second,
specialize_err, two_bytes, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages,
EProvides, ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
and, backtrackable, byte, increment_min_indent, optional, reset_min_indent, skip_first,
skip_second, specialize_err, two_bytes, EExposes, EGenerates, EGeneratesWith, EHeader,
EImports, EPackages, EProvides, ERequires, ETypedIdent, Parser, SourceError, SpaceProblem,
SyntaxError,
};
use crate::state::State;
use crate::string_literal::{self, parse_str_literal};
@ -413,7 +414,7 @@ where
E: 'a + SpaceProblem,
{
map!(
and!(
and(
skip_second(
// parse any leading space before the keyword
backtrackable(space0_e(indent_problem1)),
@ -560,8 +561,8 @@ fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<
//
// printLine : Str -> Effect {}
map!(
and!(
and!(
and(
and(
loc!(specialize_err(
|_, pos| ETypedIdent::Identifier(pos),
lowercase_ident()
@ -611,8 +612,8 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
one_of!(
map!(
and!(
and!(
and(
and(
// e.g. `pf.`
optional(backtrackable(skip_second(
shortname(),
@ -649,14 +650,14 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
)
.trace("normal_import"),
map!(
and!(
and!(
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

@ -1462,8 +1462,8 @@ where
macro_rules! collection_inner {
($elem:expr, $delimiter:expr, $space_before:expr) => {
map_with_arena!(
and!(
and!(
$crate::parser::and(
$crate::parser::and(
$crate::blankspace::spaces(),
$crate::parser::trailing_sep_by0(
$delimiter,
@ -1622,49 +1622,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
.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)),
}
};
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((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
@ -1715,7 +1713,7 @@ pub fn indented_seq<'a, O, E: 'a>(
}
}
/// 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.
pub fn absolute_indented_seq<'a, Output1, Output2, E: 'a>(
p1: impl Parser<'a, Output1, E>,
@ -2173,9 +2171,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),
)
};
}
@ -2545,54 +2543,6 @@ pub fn between<'a, Before, Inner, After, Err: 'a>(
skip_first(opening_brace, skip_second(inner, 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.

View file

@ -9,7 +9,7 @@ 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, skip_first, skip_second, then, ERecord,
absolute_column_min_indent, and, increment_min_indent, skip_first, skip_second, then, ERecord,
ETypeAbilityImpl,
};
use crate::parser::{
@ -117,7 +117,7 @@ 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!(
and(
one_of!(
loc_wildcard(),
loc_inferred(),
@ -137,7 +137,7 @@ fn term<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Loc<TypeAnnotation<'a>
// Inline alias notation, e.g. [Nil, Cons a (List a)] as List a
one_of![
map!(
and!(
and(
skip_second(
backtrackable(space0_e(EType::TIndentEnd)),
crate::parser::keyword(keyword::AS, EType::TEnd)
@ -209,7 +209,7 @@ fn loc_applied_arg<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
map_with_arena!(
and!(
and(
backtrackable(space0_e(EType::TIndentStart)),
one_of!(
loc_wildcard(),
@ -242,7 +242,7 @@ fn loc_type_in_parens<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, ETypeInParens<'a>> {
then(
loc!(and!(
loc!(and(
collection_trailing_sep_e!(
byte(b'(', ETypeInParens::Open),
specialize_err_ref(ETypeInParens::Type, expression(true, false)),
@ -399,7 +399,7 @@ fn record_type<'a>(
fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
map!(
and!(
and(
specialize_err(EType::TApply, concrete_type()),
// Optionally parse space-separated arguments for the constructor,
// e.g. `Str Float` in `Map Str Float`
@ -432,7 +432,7 @@ fn loc_applied_args_e<'a>(
// Hash & Eq & ...
fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
map!(
and!(
and(
space0_before_optional_after(
specialize_err(EType::TApply, loc!(concrete_type())),
EType::TIndentStart,
@ -460,7 +460,7 @@ fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, ETyp
fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'a>> {
map!(
// Suppose we are trying to parse "a implements Hash"
and!(
and(
space0_around_ee(
// Parse "a", with appropriate spaces
specialize_err(
@ -497,9 +497,9 @@ 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)?;
@ -584,7 +584,7 @@ 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![
let result = and(
zero_or_more!(skip_first(
byte(b',', EType::TFunctionArgument),
one_of![
@ -599,10 +599,10 @@ fn expression<'a>(
.trace("type_annotation:expression:rest_args"),
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 {