From 90d463873e97fd19d17dd4fa551aaa954821c92c Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 25 Nov 2019 22:19:33 -0500 Subject: [PATCH] Drop type_limit --- src/lib.rs | 7 -- src/parse/collection.rs | 36 ------ src/parse/mod.rs | 84 ++++++------- src/parse/module.rs | 20 ++- src/parse/parser.rs | 229 +++++++++++++++++++++++------------ src/parse/record.rs | 91 +++++--------- src/parse/type_annotation.rs | 16 ++- 7 files changed, 239 insertions(+), 244 deletions(-) delete mode 100644 src/parse/collection.rs diff --git a/src/lib.rs b/src/lib.rs index 458f48b8de..f266997b07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,3 @@ -// TODO reduce this limit by replacing more parser combinator functions with -// macros. It's the way these functions (but not the macros) interact that -// -// See https://bodil.lol/parser-combinators for more information; the parser -// is based on her design./ causes the need for higher limits. -#![type_length_limit = "3735510"] - pub mod can; pub mod collections; pub mod graph; diff --git a/src/parse/collection.rs b/src/parse/collection.rs deleted file mode 100644 index 97f1a27cae..0000000000 --- a/src/parse/collection.rs +++ /dev/null @@ -1,36 +0,0 @@ -use bumpalo::collections::Vec; -use parse::ast::Spaceable; -use parse::blankspace::space0_around; -use parse::parser::{sep_by0, skip_first, skip_second, Parser}; -use region::Located; - -/// Parse zero or more elements between two braces (e.g. square braces). -/// Elements can be optionally surrounded by spaces, and are separated by a -/// delimiter (e.g comma-separated). Braces and delimiters get discarded. -pub fn collection<'a, Elem, OpeningBrace, ClosingBrace, Delimiter, S>( - opening_brace: OpeningBrace, - elem: Elem, - delimiter: Delimiter, - closing_brace: ClosingBrace, - min_indent: u16, -) -> impl Parser<'a, Vec<'a, Located>> -where - OpeningBrace: Parser<'a, ()>, - Elem: Parser<'a, Located>, - Elem: 'a, - Delimiter: Parser<'a, ()>, - S: Spaceable<'a>, - S: 'a, - ClosingBrace: Parser<'a, ()>, -{ - // TODO allow trailing commas before the closing delimiter, *but* without - // losing any comments or newlines! This will require parsing them and then, - // if they are present, merging them into the final Spaceable. - skip_first( - opening_brace, - skip_second( - sep_by0(delimiter, space0_around(elem, min_indent)), - closing_brace, - ), - ) -} diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 2a9b9d644f..5ff134f7ec 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -2,13 +2,11 @@ pub mod parser; pub mod ast; pub mod blankspace; -pub mod collection; pub mod ident; pub mod keyword; pub mod module; pub mod number_literal; pub mod problems; -pub mod record; pub mod string_literal; pub mod type_annotation; @@ -21,14 +19,12 @@ use parse::blankspace::{ space0, space0_after, space0_around, space0_before, space1, space1_after, space1_around, space1_before, }; -use parse::collection::collection; use parse::ident::{ident, lowercase_ident, variant_or_ident, Ident}; use parse::number_literal::number_literal; use parse::parser::{ - allocated, between, char, not, not_followed_by, optional, skip_first, skip_second, string, - then, unexpected, unexpected_eof, Either, Fail, FailReason, ParseResult, Parser, State, + allocated, char, not, not_followed_by, optional, string, then, unexpected, unexpected_eof, + Either, Fail, FailReason, ParseResult, Parser, State, }; -use parse::record::record; use region::{Located, Region}; pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { @@ -128,7 +124,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!( - between( + between!( char('('), map_with_arena!( space0_around( @@ -142,7 +138,7 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located(min_indent: u16) -> impl Parser<'a, Located(min_indent: u16) -> impl Parser<'a, Located(min_indent: u16) -> impl Parser<'a, Def<'a>> { space0_after(loc_closure_param(min_indent), min_indent), either!( // Constant - skip_first( + skip_first!( equals_for_def(), // Spaces after the '=' (at a normal indentation level) and then the expr. // The expr itself must be indented more than the pattern and '=' space0_before( loc!(move |arena, state| parse_expr(indented_more, arena, state)), min_indent, - ), + ) ), // Annotation - skip_first( + skip_first!( char(':'), // Spaces after the ':' (at a normal indentation level) and then the type. // The type itself must be indented more than the pattern and ':' - space0_before(type_annotation::located(indented_more), indented_more), + space0_before(type_annotation::located(indented_more), indented_more) ) ) ), @@ -440,7 +436,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!( - skip_second( + 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 // may not correctly handle scenarios like this: @@ -456,17 +452,17 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> { // This seems likely enough to be broken that it's worth trying to reproduce // and then fix! (Or, if everything is somehow fine, delete this comment.) space0_after(type_annotation::located(min_indent), min_indent), - char(':'), + char(':') ), either!( // Custom type - skip_first( + skip_first!( // The `=` in `:=` (at this point we already consumed the `:`) char('='), one_or_more!(space0_before( type_annotation::located(min_indent), min_indent, - )), + )) ), // Alias space0_before(type_annotation::located(min_indent), min_indent) @@ -556,13 +552,13 @@ fn parse_def_expr<'a>( } fn loc_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located>> { - skip_first( + skip_first!( // If this is a reserved keyword ("if", "then", "case, "when"), then // it is not a function argument! not(reserved_keyword()), // Don't parse operators, because they have a higher precedence than function application. // If we encounter one, we're done parsing function args! - move |arena, state| loc_parse_function_arg(min_indent, arena, state), + move |arena, state| loc_parse_function_arg(min_indent, arena, state) ) } @@ -598,7 +594,7 @@ fn reserved_keyword<'a>() -> impl Parser<'a, ()> { fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { map_with_arena!( - skip_first( + skip_first!( // All closures start with a '\' - e.g. (\x -> x + 1) char('\\'), // Once we see the '\', we're committed to parsing this as a closure. @@ -615,7 +611,7 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { // the "->" but that does not seem worthwhile. one_or_more!(space1_after(loc_closure_param(min_indent), min_indent)) ), - skip_first( + skip_first!( // Parse the -> which separates params from body string("->"), // Parse the body @@ -625,9 +621,9 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { loc!(move |arena, state| parse_expr(min_indent, arena, state)), min_indent, ) - ), + ) ) - )), + )) ), |arena: &'a Bump, opt_contents| match opt_contents { None => Expr::MalformedClosure, @@ -654,10 +650,10 @@ fn parse_closure_param<'a>( loc!(record_destructure(min_indent)), // If you wrap it in parens, you can match any arbitrary pattern at all. // e.g. \User.UserId userId -> ... - between( + between!( char('('), space0_around(loc!(pattern(min_indent)), min_indent), - char(')'), + char(')') ), // The least common, but still allowed, e.g. \Foo -> ... loc!(map!(unqualified_variant(), |name| { @@ -697,12 +693,12 @@ fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>> { fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> { map!( - collection( + collection!( char('{'), loc!(ident_pattern()), char(','), char('}'), - min_indent, + min_indent ), Pattern::RecordDestructure ) @@ -722,12 +718,12 @@ pub fn case_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { case_with_indent(), attempt!( Attempting::CaseCondition, - skip_second( + skip_second!( space1_around( loc!(move |arena, state| parse_expr(min_indent, arena, state)), min_indent, ), - string(keyword::WHEN), + string(keyword::WHEN) ) ) ), @@ -773,13 +769,13 @@ pub fn case_branches<'a>( }; // Parse the first "->" and the expression after it. - let (loc_first_expr, mut state) = skip_first( + let (loc_first_expr, mut state) = skip_first!( string("->"), // The expr must be indented more than the pattern preceding it space0_before( loc!(move |arena, state| parse_expr(indented_more, arena, state)), indented_more, - ), + ) ) .parse(arena, state)?; @@ -799,12 +795,12 @@ pub fn case_branches<'a>( } }, ), - skip_first( + skip_first!( string("->"), space1_before( loc!(move |arena, state| parse_expr(min_indent, arena, state)), min_indent, - ), + ) ) ); @@ -830,27 +826,27 @@ pub fn case_branches<'a>( pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { map_with_arena!( and!( - skip_first( + skip_first!( string(keyword::IF), space1_around( loc!(move |arena, state| parse_expr(min_indent, arena, state)), min_indent, - ), + ) ), and!( - skip_first( + skip_first!( string(keyword::THEN), space1_around( loc!(move |arena, state| parse_expr(min_indent, arena, state)), min_indent, - ), + ) ), - skip_first( + skip_first!( string(keyword::ELSE), space1_before( loc!(move |arena, state| parse_expr(min_indent, arena, state)), min_indent, - ), + ) ) ) ), @@ -1086,12 +1082,12 @@ fn binop<'a>() -> impl Parser<'a, BinOp> { } pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { - let elems = collection( + let elems = collection!( char('['), loc!(expr(min_indent)), char(','), char(']'), - min_indent, + min_indent ); parser::attempt( @@ -1114,7 +1110,7 @@ pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { and!( attempt!( Attempting::Record, - loc!(record(loc!(expr(min_indent)), min_indent)) + loc!(record!(loc!(expr(min_indent)), min_indent)) ), optional(and!(space0(min_indent), equals_with_indent())) ), diff --git a/src/parse/module.rs b/src/parse/module.rs index 0c94e0eee7..a8c62e271b 100644 --- a/src/parse/module.rs +++ b/src/parse/module.rs @@ -5,13 +5,9 @@ use parse::ast::{ Module, }; use parse::blankspace::{space1, space1_around}; -use parse::collection::collection; use parse::ident::unqualified_ident; use parse::parse; -use parse::parser::{ - self, char, loc, optional, skip_first, skip_second, string, unexpected, unexpected_eof, Parser, - State, -}; +use parse::parser::{self, char, loc, optional, string, unexpected, unexpected_eof, Parser, State}; use region::Located; pub fn module<'a>() -> impl Parser<'a, Module<'a>> { @@ -36,7 +32,7 @@ pub fn app_module<'a>() -> impl Parser<'a, Module<'a>> { pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> { parser::map( and!( - skip_first(string("interface"), and!(space1(1), loc!(module_name()))), + skip_first!(string("interface"), and!(space1(1), loc!(module_name()))), and!(exposes(), imports()) ), |( @@ -151,8 +147,8 @@ fn exposes<'a>() -> impl Parser< ), > { and!( - and!(skip_second(space1(1), string("exposes")), space1(1)), - collection(char('['), loc!(exposes_entry()), char(','), char(']'), 1) + and!(skip_second!(space1(1), string("exposes")), space1(1)), + collection!(char('['), loc!(exposes_entry()), char(','), char(']'), 1) ) } @@ -165,8 +161,8 @@ fn imports<'a>() -> impl Parser< ), > { and!( - and!(skip_second(space1(1), string("imports")), space1(1)), - collection(char('['), loc!(imports_entry()), char(','), char(']'), 1) + and!(skip_second!(space1(1), string("imports")), space1(1)), + collection!(char('['), loc!(imports_entry()), char(','), char(']'), 1) ) } @@ -182,9 +178,9 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> { // e.g. `Task` module_name(), // e.g. `.{ Task, after}` - optional(skip_first( + optional(skip_first!( char('.'), - collection(char('{'), loc!(exposes_entry()), char(','), char('}'), 1) + collection!(char('{'), loc!(exposes_entry()), char(','), char('}'), 1) )) ), |arena, diff --git a/src/parse/parser.rs b/src/parse/parser.rs index ebac3e1de6..90e1c5e23c 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -394,21 +394,6 @@ pub fn string<'a>(keyword: &'static str) -> impl Parser<'a, ()> { } } -/// Parse everything between two braces (e.g. parentheses), skipping both braces -/// and keeping only whatever was parsed in between them. -pub fn between<'a, P, OpeningBrace, ClosingBrace, Val>( - opening_brace: OpeningBrace, - parser: P, - closing_brace: ClosingBrace, -) -> impl Parser<'a, Val> -where - OpeningBrace: Parser<'a, ()>, - P: Parser<'a, Val>, - ClosingBrace: Parser<'a, ()>, -{ - skip_first(opening_brace, skip_second(parser, closing_brace)) -} - /// Parse zero or more values separated by a delimiter (e.g. a comma) whose /// values are discarded pub fn sep_by0<'a, P, D, Val>(delimiter: D, parser: P) -> impl Parser<'a, Vec<'a, Val>> @@ -479,69 +464,6 @@ where } } -/// If the first one parses, ignore its output and move on to parse with the second one. -pub fn skip_first<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, B> -where - P1: Parser<'a, A>, - P2: Parser<'a, B>, -{ - move |arena: &'a Bump, state: State<'a>| { - let original_attempting = state.attempting; - - match p1.parse(arena, state) { - Ok((_, state)) => match p2.parse(arena, state) { - Ok((out2, state)) => Ok((out2, state)), - Err((fail, state)) => Err(( - Fail { - attempting: original_attempting, - ..fail - }, - state, - )), - }, - Err((fail, state)) => Err(( - Fail { - attempting: original_attempting, - ..fail - }, - state, - )), - } - } -} - -/// If the first one parses, parse the second one; if it also parses, use the -/// output from the first one. -pub fn skip_second<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, A> -where - P1: Parser<'a, A>, - P2: Parser<'a, B>, -{ - move |arena: &'a Bump, state: State<'a>| { - let original_attempting = state.attempting; - - match p1.parse(arena, state) { - Ok((out1, state)) => match p2.parse(arena, state) { - Ok((_, state)) => Ok((out1, state)), - Err((fail, state)) => Err(( - Fail { - attempting: original_attempting, - ..fail - }, - state, - )), - }, - Err((fail, state)) => Err(( - Fail { - attempting: original_attempting, - ..fail - }, - state, - )), - } - } -} - pub fn optional<'a, P, T>(parser: P) -> impl Parser<'a, Option> where P: Parser<'a, T>, @@ -585,6 +507,93 @@ macro_rules! loc { }; } +/// If the first one parses, ignore its output and move on to parse with the second one. +#[macro_export] +macro_rules! skip_first { + ($p1:expr, $p2:expr) => { + move |arena, state: $crate::parse::parser::State<'a>| { + use $crate::parse::parser::Fail; + + let original_attempting = state.attempting; + + match $p1.parse(arena, state) { + Ok((_, state)) => match $p2.parse(arena, state) { + Ok((out2, state)) => Ok((out2, state)), + Err((fail, state)) => Err(( + Fail { + attempting: original_attempting, + ..fail + }, + state, + )), + }, + Err((fail, state)) => Err(( + Fail { + attempting: original_attempting, + ..fail + }, + state, + )), + } + } + }; +} + +/// If the first one parses, parse the second one; if it also parses, use the +/// output from the first one. +#[macro_export] +macro_rules! skip_second { + ($p1:expr, $p2:expr) => { + move |arena, state: $crate::parse::parser::State<'a>| { + use $crate::parse::parser::Fail; + + let original_attempting = state.attempting; + + match $p1.parse(arena, state) { + Ok((out1, state)) => match $p2.parse(arena, state) { + Ok((_, state)) => Ok((out1, state)), + Err((fail, state)) => Err(( + Fail { + attempting: original_attempting, + ..fail + }, + state, + )), + }, + Err((fail, state)) => Err(( + Fail { + attempting: original_attempting, + ..fail + }, + state, + )), + } + } + }; +} + +/// Parse zero or more elements between two braces (e.g. square braces). +/// Elements can be optionally surrounded by spaces, and are separated by a +/// delimiter (e.g comma-separated). Braces and delimiters get discarded. +#[macro_export] +macro_rules! collection { + ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr) => { + // TODO allow trailing commas before the closing delimiter, *but* without + // losing any comments or newlines! This will require parsing them and then, + // if they are present, merging them into the final Spaceable. + skip_first!( + $opening_brace, + skip_second!( + $crate::parse::parser::sep_by0( + $delimiter, + $crate::parse::blankspace::space0_around($elem, $min_indent) + ), + $closing_brace + ) + ) + }; +} + #[macro_export] macro_rules! and { ($p1:expr, $p2:expr) => { @@ -780,6 +789,70 @@ macro_rules! either { }; } +/// Parse everything between two braces (e.g. parentheses), skipping both braces +/// and keeping only whatever was parsed in between them. +#[macro_export] +macro_rules! between { + ($opening_brace:expr, $parser:expr, $closing_brace:expr) => { + skip_first!($opening_brace, skip_second!($parser, $closing_brace)) + }; +} + +#[macro_export] +macro_rules! record_field { + ($val_parser:expr, $min_indent:expr) => { + move |arena: &'a bumpalo::Bump, + state: $crate::parse::parser::State<'a>| + -> $crate::parse::parser::ParseResult< + 'a, + $crate::parse::ast::AssignedField<'a, _>, + > { + use $crate::parse::ast::AssignedField::*; + use $crate::parse::blankspace::{space0, space0_before}; + use $crate::parse::ident::lowercase_ident; + + // You must have a field name, e.g. "email" + let (loc_label, state) = loc!(lowercase_ident()).parse(arena, state)?; + let (spaces, state) = space0($min_indent).parse(arena, state)?; + // Having a value is optional; both `{ email }` and `{ email: blah }` work. + // (This is true in both literals and types.) + let (opt_loc_val, state) = $crate::parse::parser::optional(skip_first!( + char(':'), + space0_before($val_parser, $min_indent) + )) + .parse(arena, state)?; + + let answer = match opt_loc_val { + Some(loc_val) => LabeledValue(loc_label, spaces, arena.alloc(loc_val)), + // If no value was provided, record it as a Var. + // Canonicalize will know what to do with a Var later. + None => { + if !spaces.is_empty() { + SpaceAfter(arena.alloc(LabelOnly(loc_label)), spaces) + } else { + LabelOnly(loc_label) + } + } + }; + + Ok((answer, state)) + } + }; +} + +#[macro_export] +macro_rules! record { + ($val_parser:expr, $min_indent:expr) => { + collection!( + char('{'), + loc!(record_field!($val_parser, $min_indent)), + char(','), + char('}'), + $min_indent + ) + }; +} + /// For some reason, some usages won't compile unless they use this instead of the macro version #[inline(always)] pub fn and<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, (A, B)> diff --git a/src/parse/record.rs b/src/parse/record.rs index 47e15676f6..be177b464d 100644 --- a/src/parse/record.rs +++ b/src/parse/record.rs @@ -3,65 +3,40 @@ use bumpalo::Bump; use parse::ast::AssignedField; use parse::ast::Spaceable; use parse::blankspace::{space0, space0_before}; -use parse::collection::collection; use parse::ident::lowercase_ident; -use parse::parser::{self, and, char, loc, optional, skip_first, Parser}; +use parse::parser::{self, and, char, loc, optional, Parser}; use region::Located; -/// Parse a record - generally one of these two: -/// -/// * Literal Value, e.g. { name: "foo", email: "blah@example.com" } -/// * Type Annotation, e.g. { name: String, email: String } -pub fn record<'a, P, S>( - val_parser: P, - min_indent: u16, -) -> impl Parser<'a, Vec<'a, Located>>> -where - P: Parser<'a, Located>, - P: 'a, - S: Spaceable<'a>, - S: 'a, -{ - collection( - char('{'), - loc(record_field(val_parser, min_indent)), - char(','), - char('}'), - min_indent, - ) -} +// Parse a record - generally one of these two: +// +// * Literal Value, e.g. { name: "foo", email: "blah@example.com" } +// * Type Annotation, e.g. { name: String, email: String } +// pub fn record<'a, P, S>( +// val_parser: P, +// min_indent: u16, +// ) -> impl Parser<'a, Vec<'a, Located>>> +// where +// P: Parser<'a, Located>, +// P: 'a, +// S: Spaceable<'a>, +// S: 'a, +// { +// collection!( +// char('{'), +// loc(record_field(val_parser, min_indent)), +// char(','), +// char('}'), +// min_indent +// ) +// } -fn record_field<'a, P, S>(val_parser: P, min_indent: u16) -> impl Parser<'a, AssignedField<'a, S>> -where - P: Parser<'a, Located>, - P: 'a, - S: Spaceable<'a>, - S: 'a, -{ - use parse::ast::AssignedField::*; - - parser::map_with_arena( - and( - // You must have a field name, e.g. "email" - loc!(lowercase_ident()), - and( - space0(min_indent), - // Having a value is optional; both `{ email }` and `{ email: blah }` work. - // (This is true in both literals and types.) - optional(skip_first(char(':'), space0_before(val_parser, min_indent))), - ), - ), - |arena: &'a Bump, (loc_label, (spaces, opt_loc_val))| match opt_loc_val { - Some(loc_val) => LabeledValue(loc_label, spaces, arena.alloc(loc_val)), - // If no value was provided, record it as a Var. - // Canonicalize will know what to do with a Var later. - None => { - if !spaces.is_empty() { - SpaceAfter(arena.alloc(LabelOnly(loc_label)), spaces) - } else { - LabelOnly(loc_label) - } - } - }, - ) -} +// fn record_field<'a, P, S>(val_parser: P, min_indent: u16) -> impl Parser<'a, AssignedField<'a, S>> +// where +// P: Parser<'a, Located>, +// P: 'a, +// S: Spaceable<'a>, +// S: 'a, +// { +// use parse::ast::AssignedField::*; +// panic!("TODO"); +// } diff --git a/src/parse/type_annotation.rs b/src/parse/type_annotation.rs index e55b95321d..1ccd323a44 100644 --- a/src/parse/type_annotation.rs +++ b/src/parse/type_annotation.rs @@ -5,10 +5,8 @@ use collections::arena_join; use parse::ast::{Attempting, TypeAnnotation}; use parse::blankspace::{space0_around, space1_before}; use parse::parser::{ - between, char, optional, skip_first, string, unexpected, unexpected_eof, ParseResult, Parser, - State, + char, optional, string, unexpected, unexpected_eof, ParseResult, Parser, State, }; -use parse::record::record; use region::Located; pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located>> { @@ -26,13 +24,13 @@ pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located(min_indent: u16) -> impl Parser<'a, Located>> { - between( + between!( char('('), space0_around( move |arena, state| located(min_indent).parse(arena, state), min_indent, ), - char(')'), + char(')') ) } @@ -42,14 +40,14 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> { map_with_arena!( and!( - record( + record!( move |arena, state| located(min_indent).parse(arena, state), - min_indent, + min_indent ), - optional(skip_first( + optional(skip_first!( // This could be a record fragment, e.g. `{ name: String }...r` string("..."), - move |arena, state| located(min_indent).parse(arena, state), + move |arena, state| located(min_indent).parse(arena, state) )) ), |arena: &'a Bump, (rec, opt_bound_var)| match opt_bound_var {