Drop type_limit

This commit is contained in:
Richard Feldman 2019-11-25 22:19:33 -05:00
parent 0d69d7e90f
commit 90d463873e
7 changed files with 239 additions and 244 deletions

View file

@ -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 can;
pub mod collections; pub mod collections;
pub mod graph; pub mod graph;

View file

@ -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<S>>>
where
OpeningBrace: Parser<'a, ()>,
Elem: Parser<'a, Located<S>>,
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,
),
)
}

View file

@ -2,13 +2,11 @@
pub mod parser; pub mod parser;
pub mod ast; pub mod ast;
pub mod blankspace; pub mod blankspace;
pub mod collection;
pub mod ident; pub mod ident;
pub mod keyword; pub mod keyword;
pub mod module; pub mod module;
pub mod number_literal; pub mod number_literal;
pub mod problems; pub mod problems;
pub mod record;
pub mod string_literal; pub mod string_literal;
pub mod type_annotation; pub mod type_annotation;
@ -21,14 +19,12 @@ use parse::blankspace::{
space0, space0_after, space0_around, space0_before, space1, space1_after, space1_around, space0, space0_after, space0_around, space0_before, space1, space1_after, space1_around,
space1_before, space1_before,
}; };
use parse::collection::collection;
use parse::ident::{ident, lowercase_ident, variant_or_ident, Ident}; use parse::ident::{ident, lowercase_ident, variant_or_ident, Ident};
use parse::number_literal::number_literal; use parse::number_literal::number_literal;
use parse::parser::{ use parse::parser::{
allocated, between, char, not, not_followed_by, optional, skip_first, skip_second, string, allocated, char, not, not_followed_by, optional, string, then, unexpected, unexpected_eof,
then, unexpected, unexpected_eof, Either, Fail, FailReason, ParseResult, Parser, State, Either, Fail, FailReason, ParseResult, Parser, State,
}; };
use parse::record::record;
use region::{Located, Region}; use region::{Located, Region};
pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { 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<Expr<'a>>> { pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> {
then( then(
loc!(and!( loc!(and!(
between( between!(
char('('), char('('),
map_with_arena!( map_with_arena!(
space0_around( space0_around(
@ -142,7 +138,7 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Ex
} }
} }
), ),
char(')'), char(')')
), ),
optional(either!( optional(either!(
// There may optionally be function args after the ')' // There may optionally be function args after the ')'
@ -158,7 +154,7 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Ex
// as if there were any args they'd have consumed it anyway // as if there were any args they'd have consumed it anyway
// e.g. in `((foo bar) baz.blah)` the `.blah` will be consumed by the `baz` parser // e.g. in `((foo bar) baz.blah)` the `.blah` will be consumed by the `baz` parser
either!( either!(
one_or_more!(skip_first(char('.'), lowercase_ident())), one_or_more!(skip_first!(char('.'), lowercase_ident())),
and!(space0(min_indent), equals_with_indent()) and!(space0(min_indent), equals_with_indent())
) )
)) ))
@ -364,10 +360,10 @@ pub fn loc_parenthetical_def<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
move |arena, state| { move |arena, state| {
let (loc_tuple, state) = loc!(and!( let (loc_tuple, state) = loc!(and!(
space0_after( space0_after(
between( between!(
char('('), char('('),
space0_around(loc!(pattern(min_indent)), min_indent), space0_around(loc!(pattern(min_indent)), min_indent),
char(')'), char(')')
), ),
min_indent, min_indent,
), ),
@ -414,21 +410,21 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> {
space0_after(loc_closure_param(min_indent), min_indent), space0_after(loc_closure_param(min_indent), min_indent),
either!( either!(
// Constant // Constant
skip_first( skip_first!(
equals_for_def(), equals_for_def(),
// Spaces after the '=' (at a normal indentation level) and then the expr. // Spaces after the '=' (at a normal indentation level) and then the expr.
// The expr itself must be indented more than the pattern and '=' // The expr itself must be indented more than the pattern and '='
space0_before( space0_before(
loc!(move |arena, state| parse_expr(indented_more, arena, state)), loc!(move |arena, state| parse_expr(indented_more, arena, state)),
min_indent, min_indent,
), )
), ),
// Annotation // Annotation
skip_first( skip_first!(
char(':'), char(':'),
// Spaces after the ':' (at a normal indentation level) and then the type. // Spaces after the ':' (at a normal indentation level) and then the type.
// The type itself must be indented more than the pattern and ':' // 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) // Type alias or custom type (uppercase ident followed by `:` or `:=` and type annotation)
map!( map!(
and!( and!(
skip_second( skip_second!(
// TODO FIXME this may need special logic to parse the first part of the type, // 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 // then parse the rest with increased indentation. The current implementation
// may not correctly handle scenarios like this: // 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 // 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.) // and then fix! (Or, if everything is somehow fine, delete this comment.)
space0_after(type_annotation::located(min_indent), min_indent), space0_after(type_annotation::located(min_indent), min_indent),
char(':'), char(':')
), ),
either!( either!(
// Custom type // Custom type
skip_first( skip_first!(
// The `=` in `:=` (at this point we already consumed the `:`) // The `=` in `:=` (at this point we already consumed the `:`)
char('='), char('='),
one_or_more!(space0_before( one_or_more!(space0_before(
type_annotation::located(min_indent), type_annotation::located(min_indent),
min_indent, min_indent,
)), ))
), ),
// Alias // Alias
space0_before(type_annotation::located(min_indent), min_indent) 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<Expr<'a>>> { fn loc_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> {
skip_first( skip_first!(
// If this is a reserved keyword ("if", "then", "case, "when"), then // If this is a reserved keyword ("if", "then", "case, "when"), then
// it is not a function argument! // it is not a function argument!
not(reserved_keyword()), not(reserved_keyword()),
// Don't parse operators, because they have a higher precedence than function application. // Don't parse operators, because they have a higher precedence than function application.
// If we encounter one, we're done parsing function args! // 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>> { fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
map_with_arena!( map_with_arena!(
skip_first( skip_first!(
// All closures start with a '\' - e.g. (\x -> x + 1) // All closures start with a '\' - e.g. (\x -> x + 1)
char('\\'), char('\\'),
// Once we see the '\', we're committed to parsing this as a closure. // 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. // the "->" but that does not seem worthwhile.
one_or_more!(space1_after(loc_closure_param(min_indent), min_indent)) one_or_more!(space1_after(loc_closure_param(min_indent), min_indent))
), ),
skip_first( skip_first!(
// Parse the -> which separates params from body // Parse the -> which separates params from body
string("->"), string("->"),
// Parse the body // 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)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
) )
), )
) )
)), ))
), ),
|arena: &'a Bump, opt_contents| match opt_contents { |arena: &'a Bump, opt_contents| match opt_contents {
None => Expr::MalformedClosure, None => Expr::MalformedClosure,
@ -654,10 +650,10 @@ fn parse_closure_param<'a>(
loc!(record_destructure(min_indent)), loc!(record_destructure(min_indent)),
// If you wrap it in parens, you can match any arbitrary pattern at all. // If you wrap it in parens, you can match any arbitrary pattern at all.
// e.g. \User.UserId userId -> ... // e.g. \User.UserId userId -> ...
between( between!(
char('('), char('('),
space0_around(loc!(pattern(min_indent)), min_indent), space0_around(loc!(pattern(min_indent)), min_indent),
char(')'), char(')')
), ),
// The least common, but still allowed, e.g. \Foo -> ... // The least common, but still allowed, e.g. \Foo -> ...
loc!(map!(unqualified_variant(), |name| { 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>> { fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
map!( map!(
collection( collection!(
char('{'), char('{'),
loc!(ident_pattern()), loc!(ident_pattern()),
char(','), char(','),
char('}'), char('}'),
min_indent, min_indent
), ),
Pattern::RecordDestructure Pattern::RecordDestructure
) )
@ -722,12 +718,12 @@ pub fn case_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
case_with_indent(), case_with_indent(),
attempt!( attempt!(
Attempting::CaseCondition, Attempting::CaseCondition,
skip_second( skip_second!(
space1_around( space1_around(
loc!(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, 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. // 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("->"), string("->"),
// The expr must be indented more than the pattern preceding it // The expr must be indented more than the pattern preceding it
space0_before( space0_before(
loc!(move |arena, state| parse_expr(indented_more, arena, state)), loc!(move |arena, state| parse_expr(indented_more, arena, state)),
indented_more, indented_more,
), )
) )
.parse(arena, state)?; .parse(arena, state)?;
@ -799,12 +795,12 @@ pub fn case_branches<'a>(
} }
}, },
), ),
skip_first( skip_first!(
string("->"), string("->"),
space1_before( space1_before(
loc!(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), )
) )
); );
@ -830,27 +826,27 @@ pub fn case_branches<'a>(
pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
map_with_arena!( map_with_arena!(
and!( and!(
skip_first( skip_first!(
string(keyword::IF), string(keyword::IF),
space1_around( space1_around(
loc!(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), )
), ),
and!( and!(
skip_first( skip_first!(
string(keyword::THEN), string(keyword::THEN),
space1_around( space1_around(
loc!(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, min_indent,
), )
), ),
skip_first( skip_first!(
string(keyword::ELSE), string(keyword::ELSE),
space1_before( space1_before(
loc!(move |arena, state| parse_expr(min_indent, arena, state)), loc!(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent, 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>> { pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
let elems = collection( let elems = collection!(
char('['), char('['),
loc!(expr(min_indent)), loc!(expr(min_indent)),
char(','), char(','),
char(']'), char(']'),
min_indent, min_indent
); );
parser::attempt( parser::attempt(
@ -1114,7 +1110,7 @@ pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
and!( and!(
attempt!( attempt!(
Attempting::Record, 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())) optional(and!(space0(min_indent), equals_with_indent()))
), ),

View file

@ -5,13 +5,9 @@ use parse::ast::{
Module, Module,
}; };
use parse::blankspace::{space1, space1_around}; use parse::blankspace::{space1, space1_around};
use parse::collection::collection;
use parse::ident::unqualified_ident; use parse::ident::unqualified_ident;
use parse::parse; use parse::parse;
use parse::parser::{ use parse::parser::{self, char, loc, optional, string, unexpected, unexpected_eof, Parser, State};
self, char, loc, optional, skip_first, skip_second, string, unexpected, unexpected_eof, Parser,
State,
};
use region::Located; use region::Located;
pub fn module<'a>() -> impl Parser<'a, Module<'a>> { 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>> { pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
parser::map( parser::map(
and!( and!(
skip_first(string("interface"), and!(space1(1), loc!(module_name()))), skip_first!(string("interface"), and!(space1(1), loc!(module_name()))),
and!(exposes(), imports()) and!(exposes(), imports())
), ),
|( |(
@ -151,8 +147,8 @@ fn exposes<'a>() -> impl Parser<
), ),
> { > {
and!( and!(
and!(skip_second(space1(1), string("exposes")), space1(1)), and!(skip_second!(space1(1), string("exposes")), space1(1)),
collection(char('['), loc!(exposes_entry()), char(','), char(']'), 1) collection!(char('['), loc!(exposes_entry()), char(','), char(']'), 1)
) )
} }
@ -165,8 +161,8 @@ fn imports<'a>() -> impl Parser<
), ),
> { > {
and!( and!(
and!(skip_second(space1(1), string("imports")), space1(1)), and!(skip_second!(space1(1), string("imports")), space1(1)),
collection(char('['), loc!(imports_entry()), char(','), char(']'), 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` // e.g. `Task`
module_name(), module_name(),
// e.g. `.{ Task, after}` // e.g. `.{ Task, after}`
optional(skip_first( optional(skip_first!(
char('.'), char('.'),
collection(char('{'), loc!(exposes_entry()), char(','), char('}'), 1) collection!(char('{'), loc!(exposes_entry()), char(','), char('}'), 1)
)) ))
), ),
|arena, |arena,

View file

@ -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 /// Parse zero or more values separated by a delimiter (e.g. a comma) whose
/// values are discarded /// values are discarded
pub fn sep_by0<'a, P, D, Val>(delimiter: D, parser: P) -> impl Parser<'a, Vec<'a, Val>> 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<T>> pub fn optional<'a, P, T>(parser: P) -> impl Parser<'a, Option<T>>
where where
P: Parser<'a, T>, 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_export]
macro_rules! and { macro_rules! and {
($p1:expr, $p2:expr) => { ($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 /// For some reason, some usages won't compile unless they use this instead of the macro version
#[inline(always)] #[inline(always)]
pub fn and<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, (A, B)> pub fn and<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, (A, B)>

View file

@ -3,65 +3,40 @@ use bumpalo::Bump;
use parse::ast::AssignedField; use parse::ast::AssignedField;
use parse::ast::Spaceable; use parse::ast::Spaceable;
use parse::blankspace::{space0, space0_before}; use parse::blankspace::{space0, space0_before};
use parse::collection::collection;
use parse::ident::lowercase_ident; 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; use region::Located;
/// Parse a record - generally one of these two: // Parse a record - generally one of these two:
/// //
/// * Literal Value, e.g. { name: "foo", email: "blah@example.com" } // * Literal Value, e.g. { name: "foo", email: "blah@example.com" }
/// * Type Annotation, e.g. { name: String, email: String } // * Type Annotation, e.g. { name: String, email: String }
pub fn record<'a, P, S>( // pub fn record<'a, P, S>(
val_parser: P, // val_parser: P,
min_indent: u16, // min_indent: u16,
) -> impl Parser<'a, Vec<'a, Located<AssignedField<'a, S>>>> // ) -> impl Parser<'a, Vec<'a, Located<AssignedField<'a, S>>>>
where // where
P: Parser<'a, Located<S>>, // P: Parser<'a, Located<S>>,
P: 'a, // P: 'a,
S: Spaceable<'a>, // S: Spaceable<'a>,
S: 'a, // S: 'a,
{ // {
collection( // collection!(
char('{'), // char('{'),
loc(record_field(val_parser, min_indent)), // loc(record_field(val_parser, min_indent)),
char(','), // char(','),
char('}'), // char('}'),
min_indent, // min_indent
) // )
} // }
fn record_field<'a, P, S>(val_parser: P, min_indent: u16) -> impl Parser<'a, AssignedField<'a, S>> // fn record_field<'a, P, S>(val_parser: P, min_indent: u16) -> impl Parser<'a, AssignedField<'a, S>>
where // where
P: Parser<'a, Located<S>>, // P: Parser<'a, Located<S>>,
P: 'a, // P: 'a,
S: Spaceable<'a>, // S: Spaceable<'a>,
S: 'a, // S: 'a,
{ // {
use parse::ast::AssignedField::*; // use parse::ast::AssignedField::*;
// panic!("TODO");
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)
}
}
},
)
}

View file

@ -5,10 +5,8 @@ use collections::arena_join;
use parse::ast::{Attempting, TypeAnnotation}; use parse::ast::{Attempting, TypeAnnotation};
use parse::blankspace::{space0_around, space1_before}; use parse::blankspace::{space0_around, space1_before};
use parse::parser::{ use parse::parser::{
between, char, optional, skip_first, string, unexpected, unexpected_eof, ParseResult, Parser, char, optional, string, unexpected, unexpected_eof, ParseResult, Parser, State,
State,
}; };
use parse::record::record;
use region::Located; use region::Located;
pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> { pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
@ -26,13 +24,13 @@ pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a
#[inline(always)] #[inline(always)]
fn loc_parenthetical_type<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> { fn loc_parenthetical_type<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
between( between!(
char('('), char('('),
space0_around( space0_around(
move |arena, state| located(min_indent).parse(arena, state), move |arena, state| located(min_indent).parse(arena, state),
min_indent, min_indent,
), ),
char(')'), char(')')
) )
} }
@ -42,14 +40,14 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> {
map_with_arena!( map_with_arena!(
and!( and!(
record( record!(
move |arena, state| located(min_indent).parse(arena, state), 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` // This could be a record fragment, e.g. `{ name: String }...r`
string("..."), 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 { |arena: &'a Bump, (rec, opt_bound_var)| match opt_bound_var {