diff --git a/src/parse/blankspace.rs b/src/parse/blankspace.rs index 26a6c4428c..bd7be84f62 100644 --- a/src/parse/blankspace.rs +++ b/src/parse/blankspace.rs @@ -3,9 +3,23 @@ use bumpalo::collections::vec::Vec; use bumpalo::Bump; use parse::ast::CommentOrNewline::{self, *}; use parse::ast::Spaceable; -use parse::parser::{and, map_with_arena, unexpected, unexpected_eof, Parser, State}; +use parse::parser::{map_with_arena, unexpected, unexpected_eof, Parser, State}; use region::Located; +/// For some reason, some functions need to use this instead of using the and! macro directly. +#[inline(always)] +pub fn and<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, (A, B)> +where + P1: Parser<'a, A>, + P2: Parser<'a, B>, + P1: 'a, + P2: 'a, + A: 'a, + B: 'a, +{ + and!(p1, p2) +} + /// Parses the given expression with 0 or more (spaces/comments/newlines) before and/or after it. /// Returns a Located where the location is around the Expr, ignoring the spaces. /// If any newlines or comments were found, the Expr will be wrapped in a SpaceBefore and/or @@ -20,7 +34,7 @@ where { map_with_arena( and(space0(min_indent), and(parser, space0(min_indent))), - |arena, (spaces_before, (loc_val, spaces_after))| { + move |arena, (spaces_before, (loc_val, spaces_after))| { if spaces_before.is_empty() { if spaces_after.is_empty() { loc_val @@ -97,7 +111,7 @@ where P: 'a, { map_with_arena( - and(space0(min_indent), parser), + and!(space0(min_indent), parser), |arena, (space_list, loc_expr)| { if space_list.is_empty() { loc_expr @@ -121,7 +135,7 @@ where P: 'a, { map_with_arena( - and(space1(min_indent), parser), + and!(space1(min_indent), parser), |arena, (space_list, loc_expr)| { if space_list.is_empty() { loc_expr @@ -145,7 +159,7 @@ where P: 'a, { map_with_arena( - and(parser, space0(min_indent)), + and!(parser, space0(min_indent)), |arena, (loc_expr, space_list)| { if space_list.is_empty() { loc_expr @@ -169,7 +183,7 @@ where P: 'a, { map_with_arena( - and(parser, space1(min_indent)), + and!(parser, space1(min_indent)), |arena, (loc_expr, space_list)| { if space_list.is_empty() { loc_expr diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 959286e2c0..c9a9f70b98 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,3 +1,5 @@ +#[macro_use] +pub mod parser; pub mod ast; pub mod blankspace; pub mod collection; @@ -5,8 +7,6 @@ pub mod ident; pub mod keyword; pub mod module; pub mod number_literal; -#[macro_use] -pub mod parser; pub mod problems; pub mod record; pub mod string_literal; @@ -28,10 +28,10 @@ use parse::collection::collection; use parse::ident::{ident, unqualified_ident, variant_or_ident, Ident}; use parse::number_literal::number_literal; use parse::parser::{ - allocated, and, attempt, between, char, either, map, map_with_arena, not, not_followed_by, - one_of10, one_of17, one_of2, one_of3, one_of5, one_of6, one_or_more, optional, skip_first, - skip_second, string, then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason, - ParseResult, Parser, State, + allocated, attempt, between, char, either, map, map_with_arena, not, not_followed_by, one_of10, + one_of17, one_of2, one_of3, one_of5, one_of6, one_or_more, optional, skip_first, skip_second, + string, then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason, ParseResult, + Parser, State, }; use parse::record::record; use region::{Located, Region}; @@ -42,14 +42,14 @@ pub fn module<'a>() -> impl Parser<'a, Module<'a>> { #[inline(always)] fn interface_module<'a>() -> impl Parser<'a, Module<'a>> { - map(and(interface_header(), module_defs()), |(header, defs)| { + map(and!(interface_header(), module_defs()), |(header, defs)| { Module::Interface { header, defs } }) } #[inline(always)] fn app_module<'a>() -> impl Parser<'a, Module<'a>> { - map(and(app_header(), module_defs()), |(header, defs)| { + map(and!(app_header(), module_defs()), |(header, defs)| { Module::App { header, defs } }) } @@ -57,9 +57,9 @@ fn app_module<'a>() -> impl Parser<'a, Module<'a>> { #[inline(always)] fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> { map( - and( - skip_first(string("interface"), and(space1(1), loc!(ident()))), - and(mod_header_list("exposes"), mod_header_list("imports")), + and!( + skip_first(string("interface"), and!(space1(1), loc!(ident()))), + and!(mod_header_list("exposes"), mod_header_list("imports")) ), |( (after_interface, loc_name_ident), @@ -117,9 +117,9 @@ fn mod_header_list<'a>( Vec<'a, Located>>, ), > { - and( - and(skip_second(space1(1), string(kw)), space1(1)), - collection(char('['), loc!(mod_header_entry()), char(','), char(']'), 1), + and!( + and!(skip_second(space1(1), string(kw)), space1(1)), + collection(char('['), loc!(mod_header_entry()), char(','), char(']'), 1) ) } @@ -128,7 +128,7 @@ fn mod_header_entry<'a>() -> impl Parser<'a, HeaderEntry<'a>> { one_of2( map(unqualified_ident(), |ident| HeaderEntry::Val(ident)), map( - and(unqualified_variant(), optional(string("..."))), + and!(unqualified_variant(), optional(string("..."))), |(ident, opt_ellipsis)| match opt_ellipsis { None => HeaderEntry::TypeOnly(ident), Some(()) => HeaderEntry::TypeAndVariants(ident), @@ -170,18 +170,18 @@ fn loc_parse_expr_body_without_operators<'a>( pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { one_of2( map_with_arena( - and( + and!( loc!(char('!')), - loc!(move |arena, state| parse_expr(min_indent, arena, state)), + loc!(move |arena, state| parse_expr(min_indent, arena, state)) ), |arena, (loc_op, loc_expr)| { Expr::UnaryOp(arena.alloc(loc_expr), loc_op.map(|_| UnaryOp::Not)) }, ), map_with_arena( - and( + and!( loc!(char('-')), - loc!(move |arena, state| parse_expr(min_indent, arena, state)), + loc!(move |arena, state| parse_expr(min_indent, arena, state)) ), |arena, (loc_op, loc_expr)| { Expr::UnaryOp(arena.alloc(loc_expr), loc_op.map(|_| UnaryOp::Negate)) @@ -192,7 +192,7 @@ pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Expr<'a>> { let expr_parser = map_with_arena( - and( + and!( // First parse the body without operators, then try to parse possible operators after. move |arena, state| loc_parse_expr_body_without_operators(min_indent, arena, state), // Parse the operator, with optional spaces before it. @@ -200,15 +200,15 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe // Since spaces can only wrap an Expr, not an BinOp, we have to first // parse the spaces and then attach them retroactively to the expression // preceding the operator (the one we parsed before considering operators). - optional(and( - and(space0(min_indent), loc!(binop())), + optional(and!( + and!(space0(min_indent), loc!(binop())), // The spaces *after* the operator can be attached directly to // the expression following the operator. space0_before( loc!(move |arena, state| parse_expr(min_indent, arena, state)), min_indent, - ), - )), + ) + )) ), |arena, (loc_expr1, opt_operator)| match opt_operator { Some(((spaces_before_op, loc_op), loc_expr2)) => { @@ -233,7 +233,7 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located>> { then( - loc!(and( + loc!(and!( between( char('('), space0_around( @@ -257,9 +257,9 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located(arena: &'a Bump, expr: &Expr<'a>) -> Result, /// It would be too weird to parse; imagine `(UserId userId) : ...` above `(UserId userId) = ...` pub fn loc_parenthetical_def<'a>(min_indent: u16) -> impl Parser<'a, Located>> { move |arena, state| { - let (loc_tuple, state) = loc!(and( + let (loc_tuple, state) = loc!(and!( space0_after( between( char('('), @@ -437,7 +437,7 @@ pub fn loc_parenthetical_def<'a>(min_indent: u16) -> impl Parser<'a, Located(min_indent: u16) -> impl Parser<'a, Def<'a>> { one_of2( // Constant or annotation map_with_arena( - and( + and!( // A pattern followed by '=' or ':' space0_after(loc_closure_param(min_indent), min_indent), either( @@ -496,7 +496,7 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> { // The type itself must be indented more than the pattern and ':' space0_before(type_annotation::located(indented_more), indented_more), ), - ), + ) ), |arena, (loc_pattern, expr_or_ann)| match expr_or_ann { Either::First(loc_expr) => Def::Body(loc_pattern, arena.alloc(loc_expr)), @@ -505,7 +505,7 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> { ), // Type alias or custom type (uppercase ident followed by `:` or `:=` and type annotation) map( - and( + and!( skip_second( // TODO FIXME this may need special logic to parse the first part of the type, // then parse the rest with increased indentation. The current implementation @@ -536,7 +536,7 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> { ), // Alias space0_before(type_annotation::located(min_indent), min_indent), - ), + ) ), |(loc_type_name, rest)| match rest { Either::First(loc_ann) => Def::CustomType(loc_type_name, loc_ann), @@ -573,7 +573,7 @@ fn parse_def_expr<'a>( then( attempt( Attempting::Def, - and( + and!( // Parse the body of the first def. It doesn't need any spaces // around it parsed, because both the subsquent defs and the // final body will have space1_before on them. @@ -581,7 +581,7 @@ fn parse_def_expr<'a>( // It should be indented more than the original, and it will // end when outdented again. loc!(move |arena, state| parse_expr(indented_more, arena, state)), - and( + and!( // Optionally parse additional defs. zero_or_more(allocated(space1_before( loc!(def(original_indent)), @@ -592,8 +592,8 @@ fn parse_def_expr<'a>( space1_before( loc!(move |arena, state| parse_expr(original_indent, arena, state)), original_indent, - ), - ), + ) + ) ), ), move |arena, state, (loc_first_body, (mut defs, loc_ret))| { @@ -671,7 +671,7 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { char('\\'), // 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. - optional(and( + optional(and!( // Parse the params attempt( Attempting::ClosureParams, @@ -694,7 +694,7 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { min_indent, ), ), - ), + ) )), ), |arena, opt_contents| match opt_contents { @@ -786,7 +786,7 @@ fn ident_pattern<'a>() -> impl Parser<'a, Pattern<'a>> { pub fn case_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { then( - and( + and!( case_with_indent(), attempt( Attempting::CaseCondition, @@ -797,7 +797,7 @@ pub fn case_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { ), string(keyword::WHEN), ), - ), + ) ), move |arena, state, (case_indent, loc_condition)| { if case_indent < min_indent { @@ -854,7 +854,7 @@ pub fn case_branches<'a>( // Record this as the first branch, then optionally parse additional branches. branches.push(arena.alloc((loc_first_pattern, loc_first_expr))); - let branch_parser = and( + let branch_parser = and!( then( space1_around(loc!(pattern(min_indent)), min_indent), move |_arena, state, loc_pattern| { @@ -873,7 +873,7 @@ pub fn case_branches<'a>( loc!(move |arena, state| parse_expr(min_indent, arena, state)), min_indent, ), - ), + ) ); loop { @@ -897,7 +897,7 @@ pub fn case_branches<'a>( pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { map_with_arena( - and( + and!( skip_first( string(keyword::IF), space1_around( @@ -905,7 +905,7 @@ pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { min_indent, ), ), - and( + and!( skip_first( string(keyword::THEN), space1_around( @@ -919,8 +919,8 @@ pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { loc!(move |arena, state| parse_expr(min_indent, arena, state)), min_indent, ), - ), - ), + ) + ) ), |arena, (condition, (then_branch, else_branch))| { Expr::If(arena.alloc((condition, then_branch, else_branch))) @@ -946,7 +946,7 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located(min_indent: u16) -> impl Parser<'a, Vec<'a, Located(min_indent: u16) -> impl Parser<'a, Expr<'a>> { then( - and( + and!( loc!(ident()), optional(either( // There may optionally be function args after this ident @@ -1017,8 +1017,8 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { // If there aren't any args, there may be a '=' or ':' after it. // (It's a syntax error to write e.g. `foo bar =` - so if there // were any args, there is definitely no need to parse '=' or ':'!) - and(space0(min_indent), either(equals_with_indent(), char(':'))), - )), + and!(space0(min_indent), either(equals_with_indent(), char(':'))), + )) ), move |arena, state, (loc_ident, opt_extras)| { // This appears to be a var, keyword, or function application. @@ -1179,12 +1179,12 @@ pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { // Parser<'a, Vec<'a, Located>>> pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { then( - and( + and!( attempt( Attempting::Record, loc!(record(loc!(expr(min_indent)), min_indent)), ), - optional(and(space0(min_indent), equals_with_indent())), + optional(and!(space0(min_indent), equals_with_indent())) ), move |arena, state, (loc_assigned_fields, opt_def)| match opt_def { None => { diff --git a/src/parse/parser.rs b/src/parse/parser.rs index c0877a792a..356d31b158 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -574,39 +574,6 @@ where } } -#[inline(always)] -fn and_impl<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, (A, B)> -where - P1: Parser<'a, A>, - P2: Parser<'a, B>, -{ - move |arena: &'a Bump, state: State<'a>| { - // We have to clone this because if the first parser passes and then - // the second one fails, we need to revert back to the original state. - let original_state = state.clone(); - - match p1.parse(arena, state) { - Ok((out1, state)) => match p2.parse(arena, state) { - Ok((out2, state)) => Ok(((out1, out2), state)), - Err((fail, _)) => Err(( - Fail { - attempting: original_state.attempting, - ..fail - }, - original_state, - )), - }, - Err((fail, state)) => Err(( - Fail { - attempting: original_state.attempting, - ..fail - }, - state, - )), - } - } -} - #[inline(always)] pub fn either_impl<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, Either> where @@ -1270,18 +1237,6 @@ where BoxedParser::new(map_impl(parser, transform)) } -pub fn and<'a, P1, P2, A, B>(p1: P1, p2: P2) -> BoxedParser<'a, (A, B)> -where - P1: Parser<'a, A>, - P2: Parser<'a, B>, - P1: 'a, - P2: 'a, - A: 'a, - B: 'a, -{ - BoxedParser::new(and_impl(p1, p2)) -} - #[inline(always)] fn map_with_arena_impl<'a, P, F, Before, After>(parser: P, transform: F) -> impl Parser<'a, After> where @@ -1347,7 +1302,7 @@ where #[macro_export] macro_rules! loc { ($parser:expr) => { - move |arena, state: State<'a>| { + move |arena, state: $crate::parse::parser::State<'a>| { use $crate::region::{Located, Region}; let start_col = state.column; @@ -1371,3 +1326,36 @@ macro_rules! loc { } }; } + +#[macro_export] +macro_rules! and { + ($p1:expr, $p2:expr) => { + move |arena: &'a bumpalo::Bump, state: $crate::parse::parser::State<'a>| { + use $crate::parse::parser::Fail; + + // We have to clone this because if the first parser passes and then + // the second one fails, we need to revert back to the original state. + let original_state = state.clone(); + + match $p1.parse(arena, state) { + Ok((out1, state)) => match $p2.parse(arena, state) { + Ok((out2, state)) => Ok(((out1, out2), state)), + Err((fail, _)) => Err(( + Fail { + attempting: original_state.attempting, + ..fail + }, + original_state, + )), + }, + Err((fail, state)) => Err(( + Fail { + attempting: original_state.attempting, + ..fail + }, + state, + )), + } + } + }; +} diff --git a/src/parse/record.rs b/src/parse/record.rs index 4bd472005a..825327810d 100644 --- a/src/parse/record.rs +++ b/src/parse/record.rs @@ -4,7 +4,7 @@ use parse::ast::Spaceable; use parse::blankspace::{space0, space0_before}; use parse::collection::collection; use parse::ident::unqualified_ident; -use parse::parser::{and, char, map_with_arena, optional, skip_first, Parser, State}; +use parse::parser::{char, map_with_arena, optional, skip_first, Parser}; use region::Located; /// Parse a record - generally one of these two: @@ -39,6 +39,20 @@ where loc!(parser) } +/// For some reason, record_field() needs to use this instead of using the and! macro directly. +#[inline(always)] +pub fn and<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, (A, B)> +where + P1: Parser<'a, A>, + P2: Parser<'a, B>, + P1: 'a, + P2: 'a, + A: 'a, + B: 'a, +{ + and!(p1, p2) +} + fn record_field<'a, P, S>(val_parser: P, min_indent: u16) -> impl Parser<'a, AssignedField<'a, S>> where P: Parser<'a, Located>, diff --git a/src/parse/type_annotation.rs b/src/parse/type_annotation.rs index b344ffa1b2..dea648a29f 100644 --- a/src/parse/type_annotation.rs +++ b/src/parse/type_annotation.rs @@ -5,7 +5,7 @@ use collections::arena_join; use parse::ast::{Attempting, TypeAnnotation}; use parse::blankspace::{space0_around, space1_before}; use parse::parser::{ - and, between, char, map, map_with_arena, one_of5, optional, skip_first, string, unexpected, + between, char, map, map_with_arena, one_of5, optional, skip_first, string, unexpected, unexpected_eof, zero_or_more, ParseResult, Parser, State, }; use parse::record::record; @@ -41,7 +41,7 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> { use parse::type_annotation::TypeAnnotation::*; map_with_arena( - and( + and!( record( move |arena, state| located(min_indent).parse(arena, state), min_indent, @@ -50,7 +50,7 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> { // This could be a record fragment, e.g. `{ name: String }...r` string("..."), move |arena, state| located(min_indent).parse(arena, state), - )), + )) ), |arena, (rec, opt_bound_var)| match opt_bound_var { None => Record(rec), @@ -61,14 +61,14 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> { fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> { map( - and( + and!( parse_concrete_type, // Optionally parse space-separated arguments for the constructor, // e.g. `Str Float` in `Map Str Float` zero_or_more(space1_before( move |arena, state| located(min_indent).parse(arena, state), min_indent, - )), + )) ), |(ctor, args)| { match &ctor {