mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
Drop type_limit
This commit is contained in:
parent
0d69d7e90f
commit
90d463873e
7 changed files with 239 additions and 244 deletions
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
|
@ -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<Expr<'a>>> {
|
||||
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<Ex
|
|||
}
|
||||
}
|
||||
),
|
||||
char(')'),
|
||||
char(')')
|
||||
),
|
||||
optional(either!(
|
||||
// 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
|
||||
// e.g. in `((foo bar) baz.blah)` the `.blah` will be consumed by the `baz` parser
|
||||
either!(
|
||||
one_or_more!(skip_first(char('.'), lowercase_ident())),
|
||||
one_or_more!(skip_first!(char('.'), lowercase_ident())),
|
||||
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| {
|
||||
let (loc_tuple, state) = loc!(and!(
|
||||
space0_after(
|
||||
between(
|
||||
between!(
|
||||
char('('),
|
||||
space0_around(loc!(pattern(min_indent)), min_indent),
|
||||
char(')'),
|
||||
char(')')
|
||||
),
|
||||
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),
|
||||
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<Expr<'a>>> {
|
||||
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()))
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<T>>
|
||||
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)>
|
||||
|
|
|
@ -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<AssignedField<'a, S>>>>
|
||||
where
|
||||
P: Parser<'a, Located<S>>,
|
||||
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<AssignedField<'a, S>>>>
|
||||
// where
|
||||
// P: Parser<'a, Located<S>>,
|
||||
// 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<S>>,
|
||||
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<S>>,
|
||||
// P: 'a,
|
||||
// S: Spaceable<'a>,
|
||||
// S: 'a,
|
||||
// {
|
||||
// use parse::ast::AssignedField::*;
|
||||
// panic!("TODO");
|
||||
// }
|
||||
|
|
|
@ -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<TypeAnnotation<'a>>> {
|
||||
|
@ -26,13 +24,13 @@ pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a
|
|||
|
||||
#[inline(always)]
|
||||
fn loc_parenthetical_type<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue