Refactor Parser trait to pass min_indent

This removes the need to explicitly pass thru min_indent when using the parser combinators.

My ultimate goal here is to evolve the current parser closer toward a purely combinator-based parser,
at which point we can more easily transition smoothly to a formal(ish) grammar, or expand the meanings of combinators
to include things like:
* Incremental (re)parsing
* Unified parsing and formatting code
* Better error recovery
* Using the main parser directly for syntax highlighting
This commit is contained in:
Joshua Warner 2022-10-25 21:03:28 -07:00
parent ae1a9e4dd6
commit 07be8ec000
No known key found for this signature in database
GPG key ID: 89AD497003F93FDD
18 changed files with 704 additions and 844 deletions

View file

@ -10,7 +10,6 @@ use roc_region::all::Position;
pub fn space0_around_ee<'a, P, S, E>(
parser: P,
min_indent: u32,
indent_before_problem: fn(Position) -> E,
indent_after_problem: fn(Position) -> E,
) -> impl Parser<'a, Loc<S>, E>
@ -23,8 +22,8 @@ where
{
parser::map_with_arena(
and(
space0_e(min_indent, indent_before_problem),
and(parser, space0_e(min_indent, indent_after_problem)),
space0_e(indent_before_problem),
and(parser, space0_e(indent_after_problem)),
),
spaces_around_help,
)
@ -32,7 +31,6 @@ where
pub fn space0_before_optional_after<'a, P, S, E>(
parser: P,
min_indent: u32,
indent_before_problem: fn(Position) -> E,
indent_after_problem: fn(Position) -> E,
) -> impl Parser<'a, Loc<S>, E>
@ -45,11 +43,11 @@ where
{
parser::map_with_arena(
and(
space0_e(min_indent, indent_before_problem),
space0_e(indent_before_problem),
and(
parser,
one_of![
backtrackable(space0_e(min_indent, indent_after_problem)),
backtrackable(space0_e(indent_after_problem)),
succeed!(&[] as &[_]),
],
),
@ -96,7 +94,6 @@ where
pub fn space0_before_e<'a, P, S, E>(
parser: P,
min_indent: u32,
indent_problem: fn(Position) -> E,
) -> impl Parser<'a, Loc<S>, E>
where
@ -107,7 +104,7 @@ where
E: 'a + SpaceProblem,
{
parser::map_with_arena(
and!(space0_e(min_indent, indent_problem), parser),
and!(space0_e(indent_problem), parser),
|arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Loc<S>)| {
if space_list.is_empty() {
loc_expr
@ -122,7 +119,6 @@ where
pub fn space0_after_e<'a, P, S, E>(
parser: P,
min_indent: u32,
indent_problem: fn(Position) -> E,
) -> impl Parser<'a, Loc<S>, E>
where
@ -133,7 +129,7 @@ where
E: 'a + SpaceProblem,
{
parser::map_with_arena(
and!(parser, space0_e(min_indent, indent_problem)),
and!(parser, space0_e(indent_problem)),
|arena: &'a Bump, (loc_expr, space_list): (Loc<S>, &'a [CommentOrNewline<'a>])| {
if space_list.is_empty() {
loc_expr
@ -146,14 +142,11 @@ where
)
}
pub fn check_indent<'a, E>(
min_indent: u32,
indent_problem: fn(Position) -> E,
) -> impl Parser<'a, (), E>
pub fn check_indent<'a, E>(indent_problem: fn(Position) -> E) -> impl Parser<'a, (), E>
where
E: 'a,
{
move |_, state: State<'a>| {
move |_, state: State<'a>, min_indent: u32| {
if state.column() >= min_indent {
Ok((NoProgress, (), state))
} else {
@ -163,24 +156,22 @@ where
}
pub fn space0_e<'a, E>(
min_indent: u32,
indent_problem: fn(Position) -> E,
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
where
E: 'a + SpaceProblem,
{
spaces_help_help(min_indent, indent_problem)
spaces_help_help(indent_problem)
}
#[inline(always)]
fn spaces_help_help<'a, E>(
min_indent: u32,
indent_problem: fn(Position) -> E,
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
where
E: 'a + SpaceProblem,
{
move |arena, state: State<'a>| match fast_eat_spaces(&state) {
move |arena, state: State<'a>, min_indent: u32| match fast_eat_spaces(&state) {
FastSpaceState::HasTab(position) => Err((
MadeProgress,
E::space_problem(BadInputError::HasTab, position),

File diff suppressed because it is too large Load diff

View file

@ -270,24 +270,23 @@ pub struct PackageEntry<'a> {
}
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
move |arena, state| {
move |arena, state, min_indent| {
// You may optionally have a package shorthand,
// e.g. "uc" in `uc: roc/unicode 1.0.0`
//
// (Indirect dependencies don't have a shorthand.)
let min_indent = 1;
let (_, opt_shorthand, state) = maybe!(and!(
skip_second!(
specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()),
word1(b':', EPackageEntry::Colon)
),
space0_e(min_indent, EPackageEntry::IndentPackage)
space0_e(EPackageEntry::IndentPackage)
))
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
let (_, package_or_path, state) =
loc!(specialize(EPackageEntry::BadPackage, package_name())).parse(arena, state)?;
loc!(specialize(EPackageEntry::BadPackage, package_name()))
.parse(arena, state, min_indent)?;
let entry = match opt_shorthand {
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
@ -307,10 +306,10 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
}
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> {
move |arena, state: State<'a>| {
move |arena, state: State<'a>, min_indent: u32| {
let pos = state.pos();
specialize(EPackageName::BadPath, string_literal::parse())
.parse(arena, state)
.parse(arena, state, min_indent)
.and_then(|(progress, text, next_state)| match text {
StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), next_state)),
StrLiteral::Line(_) => Err((progress, EPackageName::Escapes(pos), next_state)),

View file

@ -84,7 +84,7 @@ impl<'a> Ident<'a> {
/// * A record field, e.g. "email" in `.email` or in `email:`
/// * A named pattern match, e.g. "foo" in `foo =` or `foo ->` or `\foo ->`
pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
move |_, state: State<'a>| match chomp_lowercase_part(state.bytes()) {
move |_, state: State<'a>, _min_indent: u32| match chomp_lowercase_part(state.bytes()) {
Err(progress) => Err((progress, (), state)),
Ok(ident) => {
if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) {
@ -98,7 +98,9 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
}
pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
move |arena, state: State<'a>| uppercase_ident().parse(arena, state)
move |arena, state: State<'a>, min_indent: u32| {
uppercase_ident().parse(arena, state, min_indent)
}
}
/// This could be:
@ -107,7 +109,7 @@ pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
/// * A type name
/// * A tag
pub fn uppercase<'a>() -> impl Parser<'a, UppercaseIdent<'a>, ()> {
move |_, state: State<'a>| match chomp_uppercase_part(state.bytes()) {
move |_, state: State<'a>, _min_indent: u32| match chomp_uppercase_part(state.bytes()) {
Err(progress) => Err((progress, (), state)),
Ok(ident) => {
let width = ident.len();
@ -122,7 +124,7 @@ pub fn uppercase<'a>() -> impl Parser<'a, UppercaseIdent<'a>, ()> {
/// * A type name
/// * A tag
pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
move |_, state: State<'a>| match chomp_uppercase_part(state.bytes()) {
move |_, state: State<'a>, _min_indent: u32| match chomp_uppercase_part(state.bytes()) {
Err(progress) => Err((progress, (), state)),
Ok(ident) => {
let width = ident.len();
@ -132,7 +134,10 @@ pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
}
pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str, ()> {
move |_, state: State<'a>| match chomp_part(|c| c.is_alphabetic(), state.bytes()) {
move |_, state: State<'a>, _min_indent: u32| match chomp_part(
|c| c.is_alphabetic(),
state.bytes(),
) {
Err(progress) => Err((progress, (), state)),
Ok(ident) => {
if crate::keyword::KEYWORDS.iter().any(|kw| &ident == kw) {
@ -151,7 +156,11 @@ macro_rules! advance_state {
};
}
pub fn parse_ident<'a>(arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Ident<'a>, EExpr<'a>> {
pub fn parse_ident<'a>(
arena: &'a Bump,
state: State<'a>,
_min_indent: u32,
) -> ParseResult<'a, Ident<'a>, EExpr<'a>> {
let initial = state.clone();
match parse_ident_help(arena, state) {
@ -456,7 +465,7 @@ fn chomp_module_chain(buffer: &[u8]) -> Result<u32, Progress> {
}
pub fn concrete_type<'a>() -> impl Parser<'a, (&'a str, &'a str), ()> {
move |_, state: State<'a>| match chomp_concrete_type(state.bytes()) {
move |_, state: State<'a>, _min_indent: u32| match chomp_concrete_type(state.bytes()) {
Err(progress) => Err((progress, (), state)),
Ok((module_name, type_name, width)) => {
Ok((MadeProgress, (module_name, type_name), state.advance(width)))

View file

@ -7,9 +7,9 @@ use crate::header::{
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
use crate::parser::Progress::{self, *};
use crate::parser::{
backtrackable, optional, specialize, specialize_region, word1, EExposes, EGenerates,
EGeneratesWith, EHeader, EImports, EPackages, EProvides, ERequires, ETypedIdent, Parser,
SourceError, SpaceProblem, SyntaxError,
backtrackable, increment_min_indent, optional, reset_min_indent, specialize, specialize_region,
word1, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages, EProvides,
ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
};
use crate::state::State;
use crate::string_literal;
@ -17,7 +17,7 @@ use crate::type_annotation;
use roc_region::all::{Loc, Position};
fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|_arena, state: State<'a>| {
|_arena, state: State<'a>, _min_indent: u32| {
if state.has_reached_end() {
Ok((NoProgress, (), state))
} else {
@ -28,11 +28,10 @@ fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
#[inline(always)]
pub fn module_defs<'a>() -> impl Parser<'a, Defs<'a>, SyntaxError<'a>> {
let min_indent = 0;
skip_second!(
specialize_region(
|e, r| SyntaxError::Expr(e, r.start()),
crate::expr::toplevel_defs(min_indent),
crate::expr::toplevel_defs(),
),
end_of_file()
)
@ -42,7 +41,8 @@ pub fn parse_header<'a>(
arena: &'a bumpalo::Bump,
state: State<'a>,
) -> Result<(Module<'a>, State<'a>), SourceError<'a, EHeader<'a>>> {
match header().parse(arena, state) {
let min_indent = 0;
match header().parse(arena, state, min_indent) {
Ok((_, module, state)) => Ok((module, state)),
Err((_, fail, state)) => Err(SourceError::new(fail, &state)),
}
@ -55,10 +55,13 @@ fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
map!(
and!(
space0_e(0, EHeader::IndentStart),
space0_e(EHeader::IndentStart),
one_of![
map!(
skip_first!(keyword_e("interface", EHeader::Start), interface_header()),
skip_first!(
keyword_e("interface", EHeader::Start),
increment_min_indent(interface_header())
),
|mut header: InterfaceHeader<'a>| -> Clos<'a> {
Box::new(|spaces| {
header.before_header = spaces;
@ -67,7 +70,10 @@ fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
}
),
map!(
skip_first!(keyword_e("app", EHeader::Start), app_header()),
skip_first!(
keyword_e("app", EHeader::Start),
increment_min_indent(app_header())
),
|mut header: AppHeader<'a>| -> Clos<'a> {
Box::new(|spaces| {
header.before_header = spaces;
@ -76,7 +82,10 @@ fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
}
),
map!(
skip_first!(keyword_e("platform", EHeader::Start), platform_header()),
skip_first!(
keyword_e("platform", EHeader::Start),
increment_min_indent(platform_header())
),
|mut header: PlatformHeader<'a>| -> Clos<'a> {
Box::new(|spaces| {
header.before_header = spaces;
@ -85,7 +94,10 @@ fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
}
),
map!(
skip_first!(keyword_e("hosted", EHeader::Start), hosted_header()),
skip_first!(
keyword_e("hosted", EHeader::Start),
increment_min_indent(hosted_header())
),
|mut header: HostedHeader<'a>| -> Clos<'a> {
Box::new(|spaces| {
header.before_header = spaces;
@ -101,17 +113,16 @@ fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
#[inline(always)]
fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> {
|arena, state| {
let min_indent = 1;
|arena, state, min_indent: u32| {
let (_, after_interface_keyword, state) =
space0_e(min_indent, EHeader::IndentStart).parse(arena, state)?;
let (_, name, state) = loc!(module_name_help(EHeader::ModuleName)).parse(arena, state)?;
space0_e(EHeader::IndentStart).parse(arena, state, min_indent)?;
let (_, name, state) =
loc!(module_name_help(EHeader::ModuleName)).parse(arena, state, min_indent)?;
let (_, ((before_exposes, after_exposes), exposes), state) =
specialize(EHeader::Exposes, exposes_values()).parse(arena, state)?;
specialize(EHeader::Exposes, exposes_values()).parse(arena, state, min_indent)?;
let (_, ((before_imports, after_imports), imports), state) =
specialize(EHeader::Imports, imports()).parse(arena, state)?;
specialize(EHeader::Imports, imports()).parse(arena, state, min_indent)?;
let header = InterfaceHeader {
name,
@ -131,21 +142,20 @@ fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> {
#[inline(always)]
fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
|arena, state| {
let min_indent = 1;
|arena, state, min_indent: u32| {
let (_, after_hosted_keyword, state) =
space0_e(min_indent, EHeader::IndentStart).parse(arena, state)?;
let (_, name, state) = loc!(module_name_help(EHeader::ModuleName)).parse(arena, state)?;
space0_e(EHeader::IndentStart).parse(arena, state, min_indent)?;
let (_, name, state) =
loc!(module_name_help(EHeader::ModuleName)).parse(arena, state, min_indent)?;
let (_, ((before_exposes, after_exposes), exposes), state) =
specialize(EHeader::Exposes, exposes_values()).parse(arena, state)?;
specialize(EHeader::Exposes, exposes_values()).parse(arena, state, min_indent)?;
let (_, ((before_imports, after_imports), imports), state) =
specialize(EHeader::Imports, imports()).parse(arena, state)?;
specialize(EHeader::Imports, imports()).parse(arena, state, min_indent)?;
let (_, ((before_generates, after_generates), generates), state) =
specialize(EHeader::Generates, generates()).parse(arena, state)?;
specialize(EHeader::Generates, generates()).parse(arena, state, min_indent)?;
let (_, ((before_with, after_with), generates_with), state) =
specialize(EHeader::GeneratesWith, generates_with()).parse(arena, state)?;
specialize(EHeader::GeneratesWith, generates_with()).parse(arena, state, min_indent)?;
let header = HostedHeader {
name,
@ -217,7 +227,7 @@ fn chomp_module_name(buffer: &[u8]) -> Result<&str, Progress> {
#[inline(always)]
fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> {
|_, mut state: State<'a>| match chomp_module_name(state.bytes()) {
|_, mut state: State<'a>, _min_indent: u32| match chomp_module_name(state.bytes()) {
Ok(name) => {
let width = name.len();
state = state.advance(width);
@ -230,23 +240,21 @@ fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> {
#[inline(always)]
fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|arena, state| {
let min_indent = 1;
|arena, state, min_indent: u32| {
let (_, after_app_keyword, state) =
space0_e(min_indent, EHeader::IndentStart).parse(arena, state)?;
space0_e(EHeader::IndentStart).parse(arena, state, min_indent)?;
let (_, name, state) = loc!(crate::parser::specialize(
EHeader::AppName,
string_literal::parse()
))
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
let (_, opt_pkgs, state) =
maybe!(specialize(EHeader::Packages, packages())).parse(arena, state)?;
maybe!(specialize(EHeader::Packages, packages())).parse(arena, state, min_indent)?;
let (_, opt_imports, state) =
maybe!(specialize(EHeader::Imports, imports())).parse(arena, state)?;
maybe!(specialize(EHeader::Imports, imports())).parse(arena, state, min_indent)?;
let (_, provides, state) =
specialize(EHeader::Provides, provides_to()).parse(arena, state)?;
specialize(EHeader::Provides, provides_to()).parse(arena, state, min_indent)?;
let (before_packages, after_packages, packages) = match opt_pkgs {
Some(pkgs) => {
@ -297,27 +305,26 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
#[inline(always)]
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|arena, state| {
let min_indent = 1;
|arena, state, min_indent: u32| {
let (_, after_platform_keyword, state) =
space0_e(min_indent, EHeader::IndentStart).parse(arena, state)?;
let (_, name, state) =
loc!(specialize(EHeader::PlatformName, package_name())).parse(arena, state)?;
space0_e(EHeader::IndentStart).parse(arena, state, min_indent)?;
let (_, name, state) = loc!(specialize(EHeader::PlatformName, package_name()))
.parse(arena, state, min_indent)?;
let (_, ((before_requires, after_requires), requires), state) =
specialize(EHeader::Requires, requires()).parse(arena, state)?;
specialize(EHeader::Requires, requires()).parse(arena, state, min_indent)?;
let (_, ((before_exposes, after_exposes), exposes), state) =
specialize(EHeader::Exposes, exposes_modules()).parse(arena, state)?;
specialize(EHeader::Exposes, exposes_modules()).parse(arena, state, min_indent)?;
let (_, packages, state) = specialize(EHeader::Packages, packages()).parse(arena, state)?;
let (_, packages, state) =
specialize(EHeader::Packages, packages()).parse(arena, state, min_indent)?;
let (_, ((before_imports, after_imports), imports), state) =
specialize(EHeader::Imports, imports()).parse(arena, state)?;
specialize(EHeader::Imports, imports()).parse(arena, state, min_indent)?;
let (_, ((before_provides, after_provides), (provides, _provides_type)), state) =
specialize(EHeader::Provides, provides_without_to()).parse(arena, state)?;
specialize(EHeader::Provides, provides_without_to()).parse(arena, state, min_indent)?;
let header = PlatformHeader {
name,
@ -368,14 +375,11 @@ fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
#[inline(always)]
fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> {
let min_indent = 1;
map!(
and!(
provides_without_to(),
and!(
spaces_around_keyword(
min_indent,
"to",
EProvides::To,
EProvides::IndentTo,
@ -413,10 +417,8 @@ fn provides_without_to<'a>() -> impl Parser<
),
EProvides<'a>,
> {
let min_indent = 1;
and!(
spaces_around_keyword(
min_indent,
"provides",
EProvides::Provides,
EProvides::IndentProvides,
@ -428,7 +430,6 @@ fn provides_without_to<'a>() -> impl Parser<
exposes_entry(EProvides::Identifier),
word1(b',', EProvides::ListEnd),
word1(b']', EProvides::ListEnd),
min_indent,
EProvides::Open,
EProvides::IndentListEnd,
Spaced::SpaceBefore
@ -442,8 +443,6 @@ fn provides_without_to<'a>() -> impl Parser<
#[inline(always)]
fn provides_types<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>, EProvides<'a>> {
let min_indent = 1;
skip_first!(
// We only support spaces here, not newlines, because this is not intended
// to be the design forever. Someday it will hopefully work like Elm,
@ -461,7 +460,6 @@ fn provides_types<'a>(
provides_type_entry(EProvides::Identifier),
word1(b',', EProvides::ListEnd),
word1(b'}', EProvides::ListEnd),
min_indent,
EProvides::Open,
EProvides::IndentListEnd,
Spaced::SpaceBefore
@ -506,10 +504,8 @@ fn requires<'a>() -> impl Parser<
),
ERequires<'a>,
> {
let min_indent = 0;
and!(
spaces_around_keyword(
min_indent,
"requires",
ERequires::Requires,
ERequires::IndentRequires,
@ -523,7 +519,7 @@ fn requires<'a>() -> impl Parser<
fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> {
map!(
and!(
skip_second!(requires_rigids(0), space0_e(0, ERequires::ListStart)),
skip_second!(requires_rigids(), space0_e(ERequires::ListStart)),
requires_typed_ident()
),
|(rigids, signature)| { PlatformRequires { rigids, signature } }
@ -532,7 +528,6 @@ fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a
#[inline(always)]
fn requires_rigids<'a>(
min_indent: u32,
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>, ERequires<'a>> {
collection_trailing_sep_e!(
word1(b'{', ERequires::ListStart),
@ -542,7 +537,6 @@ fn requires_rigids<'a>(
),
word1(b',', ERequires::ListEnd),
word1(b'}', ERequires::ListEnd),
min_indent,
ERequires::Open,
ERequires::IndentListEnd,
Spaced::SpaceBefore
@ -554,12 +548,11 @@ fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>
skip_first!(
word1(b'{', ERequires::ListStart),
skip_second!(
space0_around_ee(
reset_min_indent(space0_around_ee(
specialize(ERequires::TypedIdent, loc!(typed_ident()),),
0,
ERequires::ListStart,
ERequires::ListEnd
),
)),
word1(b'}', ERequires::ListStart)
)
)
@ -574,11 +567,8 @@ fn exposes_values<'a>() -> impl Parser<
),
EExposes,
> {
let min_indent = 1;
and!(
spaces_around_keyword(
min_indent,
"exposes",
EExposes::Exposes,
EExposes::IndentExposes,
@ -589,7 +579,6 @@ fn exposes_values<'a>() -> impl Parser<
exposes_entry(EExposes::Identifier),
word1(b',', EExposes::ListEnd),
word1(b']', EExposes::ListEnd),
min_indent,
EExposes::Open,
EExposes::IndentListEnd,
Spaced::SpaceBefore
@ -598,7 +587,6 @@ fn exposes_values<'a>() -> impl Parser<
}
fn spaces_around_keyword<'a, E>(
min_indent: u32,
keyword: &'static str,
expectation: fn(Position) -> E,
indent_problem1: fn(Position) -> E,
@ -609,10 +597,10 @@ where
{
and!(
skip_second!(
backtrackable(space0_e(min_indent, indent_problem1)),
backtrackable(space0_e(indent_problem1)),
crate::parser::keyword_e(keyword, expectation)
),
space0_e(min_indent, indent_problem2)
space0_e(indent_problem2)
)
}
@ -625,11 +613,8 @@ fn exposes_modules<'a>() -> impl Parser<
),
EExposes,
> {
let min_indent = 1;
and!(
spaces_around_keyword(
min_indent,
"exposes",
EExposes::Exposes,
EExposes::IndentExposes,
@ -640,7 +625,6 @@ fn exposes_modules<'a>() -> impl Parser<
exposes_module(EExposes::Identifier),
word1(b',', EExposes::ListEnd),
word1(b']', EExposes::ListEnd),
min_indent,
EExposes::Open,
EExposes::IndentListEnd,
Spaced::SpaceBefore
@ -671,12 +655,9 @@ struct Packages<'a> {
#[inline(always)]
fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
let min_indent = 1;
map!(
and!(
spaces_around_keyword(
min_indent,
"packages",
EPackages::Packages,
EPackages::IndentPackages,
@ -687,7 +668,6 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
specialize(EPackages::PackageEntry, loc!(package_entry())),
word1(b',', EPackages::ListEnd),
word1(b'}', EPackages::ListEnd),
min_indent,
EPackages::Open,
EPackages::IndentListEnd,
Spaced::SpaceBefore
@ -715,11 +695,8 @@ fn generates<'a>() -> impl Parser<
),
EGenerates,
> {
let min_indent = 1;
and!(
spaces_around_keyword(
min_indent,
"generates",
EGenerates::Generates,
EGenerates::IndentGenerates,
@ -738,11 +715,8 @@ fn generates_with<'a>() -> impl Parser<
),
EGeneratesWith,
> {
let min_indent = 1;
and!(
spaces_around_keyword(
min_indent,
"with",
EGeneratesWith::With,
EGeneratesWith::IndentWith,
@ -753,7 +727,6 @@ fn generates_with<'a>() -> impl Parser<
exposes_entry(EGeneratesWith::Identifier),
word1(b',', EGeneratesWith::ListEnd),
word1(b']', EGeneratesWith::ListEnd),
min_indent,
EGeneratesWith::Open,
EGeneratesWith::IndentListEnd,
Spaced::SpaceBefore
@ -770,11 +743,8 @@ fn imports<'a>() -> impl Parser<
),
EImports,
> {
let min_indent = 1;
and!(
spaces_around_keyword(
min_indent,
"imports",
EImports::Imports,
EImports::IndentImports,
@ -785,7 +755,6 @@ fn imports<'a>() -> impl Parser<
loc!(imports_entry()),
word1(b',', EImports::ListEnd),
word1(b']', EImports::ListEnd),
min_indent,
EImports::Open,
EImports::IndentListEnd,
Spaced::SpaceBefore
@ -798,8 +767,6 @@ fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<
// e.g.
//
// printLine : Str -> Effect {}
let min_indent = 0;
map!(
and!(
and!(
@ -807,16 +774,15 @@ fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<
|_, pos| ETypedIdent::Identifier(pos),
lowercase_ident()
)),
space0_e(min_indent, ETypedIdent::IndentHasType)
space0_e(ETypedIdent::IndentHasType)
),
skip_first!(
word1(b':', ETypedIdent::HasType),
space0_before_e(
specialize(
ETypedIdent::Type,
type_annotation::located(min_indent, true)
reset_min_indent(type_annotation::located(true))
),
min_indent,
ETypedIdent::IndentType,
)
)
@ -846,8 +812,6 @@ where
#[inline(always)]
fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports> {
let min_indent = 1;
type Temp<'a> = (
(Option<&'a str>, ModuleName<'a>),
Option<Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
@ -872,7 +836,6 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
exposes_entry(EImports::Identifier),
word1(b',', EImports::SetEnd),
word1(b'}', EImports::SetEnd),
min_indent,
EImports::Open,
EImports::IndentSetEnd,
Spaced::SpaceBefore

View file

@ -13,7 +13,7 @@ pub enum NumLiteral<'a> {
}
pub fn positive_number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber> {
move |_arena, state: State<'a>| {
move |_arena, state: State<'a>, _min_indent: u32| {
match state.bytes().first() {
Some(first_byte) if (*first_byte as char).is_ascii_digit() => {
parse_number_base(false, state.bytes(), state)
@ -27,7 +27,7 @@ pub fn positive_number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber>
}
pub fn number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, ENumber> {
move |_arena, state: State<'a>| {
move |_arena, state: State<'a>, _min_indent: u32| {
match state.bytes().first() {
Some(first_byte) if *first_byte == b'-' => {
// drop the minus

View file

@ -729,7 +729,12 @@ pub struct FileError<'a, T> {
}
pub trait Parser<'a, Output, Error> {
fn parse(&self, _: &'a Bump, _: State<'a>) -> ParseResult<'a, Output, Error>;
fn parse(
&self,
alloc: &'a Bump,
state: State<'a>,
min_indent: u32,
) -> ParseResult<'a, Output, Error>;
#[cfg(not(feature = "parse_debug_trace"))]
fn trace(self, _message: &'static str) -> Self
@ -759,10 +764,15 @@ pub trait Parser<'a, Output, Error> {
impl<'a, F, Output, Error> Parser<'a, Output, Error> for F
where
Error: 'a,
F: Fn(&'a Bump, State<'a>) -> ParseResult<'a, Output, Error>,
F: Fn(&'a Bump, State<'a>, u32) -> ParseResult<'a, Output, Error>,
{
fn parse(&self, arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Output, Error> {
self(arena, state)
fn parse(
&self,
arena: &'a Bump,
state: State<'a>,
min_indent: u32,
) -> ParseResult<'a, Output, Error> {
self(arena, state, min_indent)
}
}
@ -800,7 +810,7 @@ where
);
INDENT.with(|i| *i.borrow_mut() += 1);
let res = self.parser.parse(arena, state);
let res = self.parser.parse(arena, state, min_indent);
INDENT.with(|i| *i.borrow_mut() = cur_indent);
let (progress, value, state) = match &res {
@ -827,8 +837,8 @@ where
P: Parser<'a, Val, Error>,
Val: 'a,
{
move |arena, state: State<'a>| {
let (progress, answer, state) = parser.parse(arena, state)?;
move |arena, state: State<'a>, min_indent: u32| {
let (progress, answer, state) = parser.parse(arena, state, min_indent)?;
Ok((progress, &*arena.alloc(answer), state))
}
@ -844,11 +854,11 @@ where
F: Fn(Progress, Before) -> P2,
Error: 'a,
{
move |arena, state| {
move |arena, state, min_indent| {
parser
.parse(arena, state)
.parse(arena, state, min_indent)
.and_then(|(progress, output, next_state)| {
transform(progress, output).parse(arena, next_state)
transform(progress, output).parse(arena, next_state, min_indent)
})
}
}
@ -858,13 +868,13 @@ where
P1: Parser<'a, Before, E>,
After: 'a,
E: 'a,
F: Fn(&'a Bump, State<'a>, Progress, Before) -> ParseResult<'a, After, E>,
F: Fn(&'a Bump, State<'a>, Progress, Before, u32) -> ParseResult<'a, After, E>,
{
move |arena, state| {
move |arena, state, min_indent| {
parser
.parse(arena, state)
.parse(arena, state, min_indent)
.and_then(|(progress, output, next_state)| {
transform(arena, next_state, progress, output)
transform(arena, next_state, progress, output, min_indent)
})
}
}
@ -874,7 +884,7 @@ where
ToError: Fn(Position) -> E,
E: 'a,
{
move |_, mut state: State<'a>| {
move |_, mut state: State<'a>, _min_indent| {
let width = keyword.len();
if !state.bytes().starts_with(keyword.as_bytes()) {
@ -908,10 +918,10 @@ where
P: Parser<'a, Val, Error>,
Error: 'a,
{
move |arena, state: State<'a>| {
move |arena, state: State<'a>, min_indent: u32| {
let start_bytes_len = state.bytes().len();
match parser.parse(arena, state) {
match parser.parse(arena, state, min_indent) {
Ok((elem_progress, first_output, next_state)) => {
// in practice, we want elements to make progress
debug_assert_eq!(elem_progress, MadeProgress);
@ -922,10 +932,10 @@ where
buf.push(first_output);
loop {
match delimiter.parse(arena, state) {
match delimiter.parse(arena, state, min_indent) {
Ok((_, (), next_state)) => {
// If the delimiter passed, check the element parser.
match parser.parse(arena, next_state) {
match parser.parse(arena, next_state, min_indent) {
Ok((element_progress, next_output, next_state)) => {
// in practice, we want elements to make progress
debug_assert_eq!(element_progress, MadeProgress);
@ -971,10 +981,10 @@ where
P: Parser<'a, Val, Error>,
Error: 'a,
{
move |arena, state: State<'a>| {
move |arena, state: State<'a>, min_indent: u32| {
let start_bytes_len = state.bytes().len();
match parser.parse(arena, state) {
match parser.parse(arena, state, min_indent) {
Ok((progress, first_output, next_state)) => {
// in practice, we want elements to make progress
debug_assert_eq!(progress, MadeProgress);
@ -984,10 +994,10 @@ where
buf.push(first_output);
loop {
match delimiter.parse(arena, state) {
match delimiter.parse(arena, state, min_indent) {
Ok((_, (), next_state)) => {
// If the delimiter passed, check the element parser.
match parser.parse(arena, next_state) {
match parser.parse(arena, next_state, min_indent) {
Ok((element_progress, next_output, next_state)) => {
// in practice, we want elements to make progress
debug_assert_eq!(element_progress, MadeProgress);
@ -1032,10 +1042,10 @@ where
P: Parser<'a, Val, Error>,
Error: 'a,
{
move |arena, state: State<'a>| {
move |arena, state: State<'a>, min_indent: u32| {
let start_bytes_len = state.bytes().len();
match parser.parse(arena, state) {
match parser.parse(arena, state, min_indent) {
Ok((progress, first_output, next_state)) => {
debug_assert_eq!(progress, MadeProgress);
let mut state = next_state;
@ -1044,10 +1054,10 @@ where
buf.push(first_output);
loop {
match delimiter.parse(arena, state) {
match delimiter.parse(arena, state, min_indent) {
Ok((_, (), next_state)) => {
// If the delimiter passed, check the element parser.
match parser.parse(arena, next_state) {
match parser.parse(arena, next_state, min_indent) {
Ok((_, next_output, next_state)) => {
state = next_state;
buf.push(next_output);
@ -1093,10 +1103,10 @@ where
V: Fn(Position) -> Error,
Error: 'a,
{
move |arena, state: State<'a>| {
move |arena, state: State<'a>, min_indent: u32| {
let start_bytes_len = state.bytes().len();
match parser.parse(arena, state) {
match parser.parse(arena, state, min_indent) {
Ok((progress, first_output, next_state)) => {
debug_assert_eq!(progress, MadeProgress);
let mut state = next_state;
@ -1105,10 +1115,10 @@ where
buf.push(first_output);
loop {
match delimiter.parse(arena, state) {
match delimiter.parse(arena, state, min_indent) {
Ok((_, (), next_state)) => {
// If the delimiter passed, check the element parser.
match parser.parse(arena, next_state) {
match parser.parse(arena, next_state, min_indent) {
Ok((_, next_output, next_state)) => {
state = next_state;
buf.push(next_output);
@ -1165,12 +1175,12 @@ where
P: Parser<'a, T, E>,
E: 'a,
{
move |arena: &'a Bump, state: State<'a>| {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
// We have to clone this because if the optional parser fails,
// we need to revert back to the original state.
let original_state = state.clone();
match parser.parse(arena, state) {
match parser.parse(arena, state, min_indent) {
Ok((progress, out1, state)) => Ok((progress, Some(out1), state)),
Err((_, _, _)) => {
// NOTE this will backtrack
@ -1189,12 +1199,12 @@ where
#[macro_export]
macro_rules! loc {
($parser:expr) => {
move |arena, state: $crate::state::State<'a>| {
move |arena, state: $crate::state::State<'a>, min_indent: u32| {
use roc_region::all::{Loc, Region};
let start = state.pos();
match $parser.parse(arena, state) {
match $parser.parse(arena, state, min_indent) {
Ok((progress, value, state)) => {
let end = state.pos();
let region = Region::new(start, end);
@ -1211,11 +1221,11 @@ macro_rules! loc {
#[macro_export]
macro_rules! skip_first {
($p1:expr, $p2:expr) => {
move |arena, state: $crate::state::State<'a>| {
move |arena, state: $crate::state::State<'a>, min_indent: u32| {
let original_state = state.clone();
match $p1.parse(arena, state) {
Ok((p1, _, state)) => match $p2.parse(arena, state) {
match $p1.parse(arena, state, min_indent) {
Ok((p1, _, state)) => match $p2.parse(arena, state, min_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), out2, state)),
Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)),
},
@ -1230,11 +1240,11 @@ macro_rules! skip_first {
#[macro_export]
macro_rules! skip_second {
($p1:expr, $p2:expr) => {
move |arena, state: $crate::state::State<'a>| {
move |arena, state: $crate::state::State<'a>, min_indent: u32| {
let original_state = state.clone();
match $p1.parse(arena, state) {
Ok((p1, out1, state)) => match $p2.parse(arena, state) {
match $p1.parse(arena, state, min_indent) {
Ok((p1, out1, state)) => match $p2.parse(arena, state, min_indent) {
Ok((p2, _, state)) => Ok((p1.or(p2), out1, state)),
Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)),
},
@ -1279,12 +1289,12 @@ macro_rules! collection {
#[macro_export]
macro_rules! collection_trailing_sep_e {
($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $open_problem:expr, $indent_problem:expr, $space_before:expr) => {
($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $open_problem:expr, $indent_problem:expr, $space_before:expr) => {
skip_first!(
$opening_brace,
|arena, state| {
let (_, spaces, state) = space0_e($min_indent, $indent_problem)
.parse(arena, state)?;
|arena, state, min_indent| {
let (_, spaces, state) = space0_e($indent_problem)
.parse(arena, state, min_indent)?;
let (_, (mut parsed_elems, mut final_comments), state) =
and!(
@ -1292,23 +1302,18 @@ macro_rules! collection_trailing_sep_e {
$delimiter,
$crate::blankspace::space0_before_optional_after(
$elem,
$min_indent,
$indent_problem,
$indent_problem
)
),
$crate::blankspace::space0_e(
// we use min_indent=0 because we want to parse incorrectly indented closing braces
// and later fix these up in the formatter.
0 /* min_indent */,
$indent_problem)
).parse(arena, state)?;
$crate::parser::reset_min_indent($crate::blankspace::space0_e($indent_problem))
).parse(arena, state, min_indent)?;
let (_,_, state) =
if parsed_elems.is_empty() {
one_of_with_error![$open_problem; $closing_brace].parse(arena, state)?
one_of_with_error![$open_problem; $closing_brace].parse(arena, state, min_indent)?
} else {
$closing_brace.parse(arena, state)?
$closing_brace.parse(arena, state, min_indent)?
};
if !spaces.is_empty() {
@ -1334,22 +1339,33 @@ macro_rules! collection_trailing_sep_e {
#[macro_export]
macro_rules! succeed {
($value:expr) => {
move |_arena: &'a bumpalo::Bump, state: $crate::state::State<'a>| {
move |_arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
Ok((NoProgress, $value, state))
}
};
}
pub fn fail<'a, T, E, F>(f: F) -> impl Parser<'a, T, E>
where
T: 'a,
E: 'a,
F: Fn(Position) -> E,
{
move |_arena: &'a bumpalo::Bump, state: State<'a>, _min_indent: u32| {
Err((NoProgress, f(state.pos()), state))
}
}
#[macro_export]
macro_rules! and {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>| {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| {
// 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((p1, out1, state)) => match $p2.parse(arena, state) {
match $p1.parse(arena, state, min_indent) {
Ok((p1, out1, state)) => match $p2.parse(arena, state, min_indent) {
Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)),
Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)),
},
@ -1362,12 +1378,12 @@ macro_rules! and {
#[macro_export]
macro_rules! one_of {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>| {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| {
match $p1.parse(arena, state) {
match $p1.parse(arena, state, min_indent) {
valid @ Ok(_) => valid,
Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)),
Err((NoProgress, _, state)) => $p2.parse( arena, state),
Err((NoProgress, _, state)) => $p2.parse(arena, state, min_indent),
}
}
};
@ -1383,8 +1399,8 @@ macro_rules! one_of {
#[macro_export]
macro_rules! maybe {
($p1:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>| match $p1
.parse(arena, state)
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| match $p1
.parse(arena, state, min_indent)
{
Ok((progress, value, state)) => Ok((progress, Some(value), state)),
Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)),
@ -1396,9 +1412,9 @@ macro_rules! maybe {
#[macro_export]
macro_rules! one_of_with_error {
($toerror:expr; $p1:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>| {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| {
match $p1.parse(arena, state) {
match $p1.parse(arena, state, min_indent) {
valid @ Ok(_) => valid,
Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state )),
Err((NoProgress, _, state)) => Err((MadeProgress, $toerror(state.pos()), state)),
@ -1411,13 +1427,44 @@ macro_rules! one_of_with_error {
};
}
pub fn reset_min_indent<'a, P, T, X: 'a>(parser: P) -> impl Parser<'a, T, X>
where
P: Parser<'a, T, X>,
{
move |arena, state, _min_indent| parser.parse(arena, state, 0)
}
pub fn set_min_indent<'a, P, T, X: 'a>(min_indent: u32, parser: P) -> impl Parser<'a, T, X>
where
P: Parser<'a, T, X>,
{
move |arena, state, _m| parser.parse(arena, state, min_indent)
}
pub fn increment_min_indent<'a, P, T, X: 'a>(parser: P) -> impl Parser<'a, T, X>
where
P: Parser<'a, T, X>,
{
move |arena, state, min_indent| parser.parse(arena, state, min_indent + 1)
}
pub fn absolute_column_min_indent<'a, P, T, X: 'a>(parser: P) -> impl Parser<'a, T, X>
where
P: Parser<'a, T, X>,
{
move |arena, state: State<'a>, _min_indent| {
let min_indent = state.column() + 1;
parser.parse(arena, state, min_indent)
}
}
pub fn specialize<'a, F, P, T, X, Y>(map_error: F, parser: P) -> impl Parser<'a, T, Y>
where
F: Fn(X, Position) -> Y,
P: Parser<'a, T, X>,
Y: 'a,
{
move |a, s| match parser.parse(a, s) {
move |a, s, min_indent| match parser.parse(a, s, min_indent) {
Ok(t) => Ok(t),
Err((p, error, s)) => Err((p, map_error(error, s.pos()), s)),
}
@ -1430,9 +1477,9 @@ where
P: Parser<'a, T, X>,
Y: 'a,
{
move |a, s: State<'a>| {
move |a, s: State<'a>, min_indent: u32| {
let start = s.pos();
match parser.parse(a, s) {
match parser.parse(a, s, min_indent) {
Ok(t) => Ok(t),
Err((p, error, s)) => Err((p, map_error(error, Region::new(start, s.pos())), s)),
}
@ -1446,7 +1493,7 @@ where
Y: 'a,
X: 'a,
{
move |a, s| match parser.parse(a, s) {
move |a, s, min_indent| match parser.parse(a, s, min_indent) {
Ok(t) => Ok(t),
Err((p, error, s)) => Err((p, map_error(a.alloc(error), s.pos()), s)),
}
@ -1459,7 +1506,7 @@ where
{
debug_assert_ne!(word, b'\n');
move |_arena: &'a Bump, state: State<'a>| match state.bytes().first() {
move |_arena: &'a Bump, state: State<'a>, _min_indent: u32| match state.bytes().first() {
Some(x) if *x == word => {
let state = state.advance(1);
Ok((MadeProgress, (), state))
@ -1503,7 +1550,7 @@ where
let needle = [word_1, word_2];
move |_arena: &'a Bump, state: State<'a>| {
move |_arena: &'a Bump, state: State<'a>, _min_indent: u32| {
if state.bytes().starts_with(&needle) {
let state = state.advance(2);
Ok((MadeProgress, (), state))
@ -1529,7 +1576,7 @@ where
let needle = [word_1, word_2, word_3];
move |_arena: &'a Bump, state: State<'a>| {
move |_arena: &'a Bump, state: State<'a>, _min_indent: u32| {
if state.bytes().starts_with(&needle) {
let state = state.advance(3);
Ok((MadeProgress, (), state))
@ -1552,9 +1599,9 @@ macro_rules! word1_check_indent {
#[macro_export]
macro_rules! map {
($parser:expr, $transform:expr) => {
move |arena, state| {
move |arena, state, min_indent| {
$parser
.parse(arena, state)
.parse(arena, state, min_indent)
.map(|(progress, output, next_state)| (progress, $transform(output), next_state))
}
};
@ -1563,9 +1610,9 @@ macro_rules! map {
#[macro_export]
macro_rules! map_with_arena {
($parser:expr, $transform:expr) => {
move |arena, state| {
move |arena, state, min_indent| {
$parser
.parse(arena, state)
.parse(arena, state, min_indent)
.map(|(progress, output, next_state)| {
(progress, $transform(arena, output), next_state)
})
@ -1576,12 +1623,12 @@ macro_rules! map_with_arena {
#[macro_export]
macro_rules! zero_or_more {
($parser:expr) => {
move |arena, state: State<'a>| {
move |arena, state: State<'a>, min_indent: u32| {
use bumpalo::collections::Vec;
let start_bytes_len = state.bytes().len();
match $parser.parse(arena, state) {
match $parser.parse(arena, state, min_indent) {
Ok((_, first_output, next_state)) => {
let mut state = next_state;
let mut buf = Vec::with_capacity_in(1, arena);
@ -1589,7 +1636,7 @@ macro_rules! zero_or_more {
buf.push(first_output);
loop {
match $parser.parse(arena, state) {
match $parser.parse(arena, state, min_indent) {
Ok((_, next_output, next_state)) => {
state = next_state;
buf.push(next_output);
@ -1632,10 +1679,10 @@ macro_rules! zero_or_more {
#[macro_export]
macro_rules! one_or_more {
($parser:expr, $to_error:expr) => {
move |arena, state: State<'a>| {
move |arena, state: State<'a>, min_indent: u32| {
use bumpalo::collections::Vec;
match $parser.parse(arena, state) {
match $parser.parse(arena, state, min_indent) {
Ok((_, first_output, next_state)) => {
let mut state = next_state;
let mut buf = Vec::with_capacity_in(1, arena);
@ -1643,7 +1690,7 @@ macro_rules! one_or_more {
buf.push(first_output);
loop {
match $parser.parse(arena, state) {
match $parser.parse(arena, state, min_indent) {
Ok((_, next_output, next_state)) => {
state = next_state;
buf.push(next_output);
@ -1668,20 +1715,22 @@ macro_rules! one_or_more {
#[macro_export]
macro_rules! debug {
($parser:expr) => {
move |arena, state: $crate::state::State<'a>| dbg!($parser.parse(arena, state))
move |arena, state: $crate::state::State<'a>, min_indent: u32| {
dbg!($parser.parse(arena, state, min_indent))
}
};
}
#[macro_export]
macro_rules! either {
($p1:expr, $p2:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>| match $p1
.parse(arena, state)
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| match $p1
.parse(arena, state, min_indent)
{
Ok((progress, output, state)) => {
Ok((progress, $crate::parser::Either::First(output), state))
}
Err((NoProgress, _, state)) => match $p2.parse(arena, state) {
Err((NoProgress, _, state)) => match $p2.parse(arena, state, min_indent) {
Ok((progress, output, state)) => {
Ok((progress, $crate::parser::Either::Second(output), state))
}
@ -1749,10 +1798,10 @@ where
P: Parser<'a, Val, Error>,
Error: 'a,
{
move |arena: &'a Bump, state: State<'a>| {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let old_state = state.clone();
match parser.parse(arena, state) {
match parser.parse(arena, state, min_indent) {
Ok((_, a, s1)) => Ok((NoProgress, a, s1)),
Err((_, f, _)) => Err((NoProgress, f, old_state)),
}

View file

@ -24,8 +24,8 @@ pub enum PatternType {
WhenBranch,
}
pub fn loc_closure_param<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
move |arena, state| parse_closure_param(arena, state, min_indent)
pub fn loc_closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
parse_closure_param
}
fn parse_closure_param<'a>(
@ -35,59 +35,58 @@ fn parse_closure_param<'a>(
) -> ParseResult<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
// An ident is the most common param, e.g. \foo -> ...
loc_ident_pattern_help(min_indent, true),
loc_ident_pattern_help(true),
// Underscore is also common, e.g. \_ -> ...
loc!(underscore_pattern_help()),
// You can destructure records in params, e.g. \{ x, y } -> ...
loc!(specialize(
EPattern::Record,
crate::pattern::record_pattern_help(min_indent)
crate::pattern::record_pattern_help()
)),
// If you wrap it in parens, you can match any arbitrary pattern at all.
// e.g. \User.UserId userId -> ...
specialize(EPattern::PInParens, loc_pattern_in_parens_help(min_indent))
specialize(EPattern::PInParens, loc_pattern_in_parens_help())
)
.parse(arena, state)
.parse(arena, state, min_indent)
}
pub fn loc_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize(EPattern::PInParens, loc_pattern_in_parens_help(min_indent)),
specialize(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
loc_ident_pattern_help(min_indent, true),
loc_ident_pattern_help(true),
loc!(specialize(
EPattern::Record,
crate::pattern::record_pattern_help(min_indent)
crate::pattern::record_pattern_help()
)),
loc!(specialize(EPattern::List, list_pattern_help(min_indent))),
loc!(specialize(EPattern::List, list_pattern_help())),
loc!(number_pattern_help()),
loc!(string_pattern_help()),
loc!(single_quote_pattern_help()),
)
}
fn loc_tag_pattern_args_help<'a>(
min_indent: u32,
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(min_indent, false))
fn loc_tag_pattern_args_help<'a>() -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(false))
}
/// Like `loc_tag_pattern_args_help`, but stops if a "has" keyword is seen (indicating an ability).
fn loc_type_def_tag_pattern_args_help<'a>(
min_indent: u32,
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(min_indent, true))
zero_or_more!(loc_tag_pattern_arg(true))
}
fn loc_tag_pattern_arg<'a>(
min_indent: u32,
stop_on_has_kw: bool,
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
// 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, original_state: State<'a>| {
let (_, spaces, state) = backtrackable(space0_e(min_indent, EPattern::IndentStart))
.parse(arena, original_state.clone())?;
move |arena, original_state: State<'a>, min_indent| {
let (_, spaces, state) = backtrackable(space0_e(EPattern::IndentStart)).parse(
arena,
original_state.clone(),
min_indent,
)?;
let (_, loc_pat, state) = loc_parse_tag_pattern_arg(min_indent, arena, state)?;
@ -113,10 +112,10 @@ fn loc_tag_pattern_arg<'a>(
}
}
pub fn loc_has_parser<'a>(min_indent: u32) -> impl Parser<'a, Loc<Has<'a>>, EPattern<'a>> {
pub fn loc_has_parser<'a>() -> impl Parser<'a, Loc<Has<'a>>, EPattern<'a>> {
then(
loc_tag_pattern_arg(min_indent, false),
|_arena, state, progress, pattern| {
loc_tag_pattern_arg(false),
|_arena, state, progress, pattern, _min_indent| {
if matches!(pattern.value, Pattern::Identifier("has")) {
Ok((progress, Loc::at(pattern.region, Has::Has), state))
} else {
@ -132,30 +131,26 @@ fn loc_parse_tag_pattern_arg<'a>(
state: State<'a>,
) -> ParseResult<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize(EPattern::PInParens, loc_pattern_in_parens_help(min_indent)),
specialize(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
loc_ident_pattern_help(min_indent, false),
loc_ident_pattern_help(false),
loc!(specialize(
EPattern::Record,
crate::pattern::record_pattern_help(min_indent)
crate::pattern::record_pattern_help()
)),
loc!(string_pattern_help()),
loc!(single_quote_pattern_help()),
loc!(number_pattern_help())
)
.parse(arena, state)
.parse(arena, state, min_indent)
}
fn loc_pattern_in_parens_help<'a>(
min_indent: u32,
) -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
between!(
word1(b'(', PInParens::Open),
space0_around_ee(
move |arena, state| specialize_ref(PInParens::Pattern, loc_pattern_help(min_indent))
.parse(arena, state),
min_indent,
specialize_ref(PInParens::Pattern, loc_pattern_help()),
PInParens::IndentOpen,
PInParens::IndentEnd,
),
@ -203,19 +198,18 @@ fn single_quote_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>>
)
}
fn list_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Pattern<'a>, PList<'a>> {
move |arena, state| {
fn list_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PList<'a>> {
move |arena, state, min_indent| {
let (_, pats, state) = collection_trailing_sep_e!(
word1(b'[', PList::Open),
list_element_pattern(min_indent),
list_element_pattern(),
word1(b',', PList::End),
word1(b']', PList::End),
min_indent,
PList::Open,
PList::IndentEnd,
Pattern::SpaceBefore
)
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
let result = Pattern::List(pats);
@ -223,18 +217,18 @@ fn list_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Pattern<'a>, PList<
}
}
fn list_element_pattern<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
fn list_element_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
one_of!(
three_list_rest_pattern_error(),
list_rest_pattern(),
specialize_ref(PList::Pattern, loc_pattern_help(min_indent)),
specialize_ref(PList::Pattern, loc_pattern_help()),
)
}
fn three_list_rest_pattern_error<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
then(
loc!(word3(b'.', b'.', b'.', PList::Rest)),
|_arena, state, _progress, word| {
|_arena, state, _progress, word, _min_indent| {
Err((MadeProgress, PList::Rest(word.region.start()), state))
},
)
@ -247,14 +241,13 @@ fn list_rest_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
}
fn loc_ident_pattern_help<'a>(
min_indent: u32,
can_have_arguments: bool,
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
move |arena: &'a Bump, state: State<'a>| {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let original_state = state.clone();
let (_, loc_ident, state) =
specialize(|_, pos| EPattern::Start(pos), loc!(parse_ident)).parse(arena, state)?;
let (_, loc_ident, state) = specialize(|_, pos| EPattern::Start(pos), loc!(parse_ident))
.parse(arena, state, min_indent)?;
match loc_ident.value {
Ident::Tag(tag) => {
@ -266,7 +259,7 @@ fn loc_ident_pattern_help<'a>(
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
if can_have_arguments {
let (_, loc_args, state) =
loc_type_def_tag_pattern_args_help(min_indent).parse(arena, state)?;
loc_type_def_tag_pattern_args_help().parse(arena, state, min_indent)?;
if loc_args.is_empty() {
Ok((MadeProgress, loc_tag, state))
@ -293,7 +286,7 @@ fn loc_ident_pattern_help<'a>(
// Make sure `@Foo Bar 1` is parsed as `@Foo (Bar) 1`, and not `@Foo (Bar 1)`
if can_have_arguments {
let (_, loc_args, state) =
loc_tag_pattern_args_help(min_indent).parse(arena, state)?;
loc_tag_pattern_args_help().parse(arena, state, min_indent)?;
if loc_args.is_empty() {
Ok((MadeProgress, loc_pat, state))
@ -372,11 +365,12 @@ fn loc_ident_pattern_help<'a>(
}
fn underscore_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
move |arena: &'a Bump, state: State<'a>| {
let (_, _, next_state) = word1(b'_', EPattern::Underscore).parse(arena, state)?;
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let (_, _, next_state) =
word1(b'_', EPattern::Underscore).parse(arena, state, min_indent)?;
let (_, output, final_state) =
optional(lowercase_ident_pattern).parse(arena, next_state)?;
optional(lowercase_ident_pattern).parse(arena, next_state, min_indent)?;
match output {
Some(name) => Ok((MadeProgress, Pattern::Underscore(name), final_state)),
@ -388,26 +382,26 @@ fn underscore_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, EPattern<'a>> {
fn lowercase_ident_pattern<'a>(
arena: &'a Bump,
state: State<'a>,
min_indent: u32,
) -> ParseResult<'a, &'a str, EPattern<'a>> {
let pos = state.pos();
specialize(move |_, _| EPattern::End(pos), lowercase_ident()).parse(arena, state)
specialize(move |_, _| EPattern::End(pos), lowercase_ident()).parse(arena, state, min_indent)
}
#[inline(always)]
fn record_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
move |arena, state| {
fn record_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
move |arena, state, min_indent| {
let (_, fields, state) = collection_trailing_sep_e!(
word1(b'{', PRecord::Open),
record_pattern_field(min_indent),
record_pattern_field(),
word1(b',', PRecord::End),
word1(b'}', PRecord::End),
min_indent,
PRecord::Open,
PRecord::IndentEnd,
Pattern::SpaceBefore
)
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
let result = Pattern::RecordDestructure(fields);
@ -415,10 +409,10 @@ fn record_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Pattern<'a>, PRec
}
}
fn record_pattern_field<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>> {
fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>> {
use crate::parser::Either::*;
move |arena, state: State<'a>| {
move |arena, state: State<'a>, min_indent: u32| {
// You must have a field name, e.g. "email"
// using the initial pos is important for error reporting
let pos = state.pos();
@ -426,10 +420,10 @@ fn record_pattern_field<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
move |_, _| PRecord::Field(pos),
lowercase_ident()
))
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
debug_assert_eq!(progress, MadeProgress);
let (_, spaces, state) = space0_e(min_indent, PRecord::IndentEnd).parse(arena, state)?;
let (_, spaces, state) = space0_e(PRecord::IndentEnd).parse(arena, state, min_indent)?;
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.)
@ -437,14 +431,13 @@ fn record_pattern_field<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
word1(b':', PRecord::Colon),
word1(b'?', PRecord::Optional)
))
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
match opt_loc_val {
Some(First(_)) => {
let val_parser = specialize_ref(PRecord::Pattern, loc_pattern_help(min_indent));
let (_, loc_val, state) =
space0_before_e(val_parser, min_indent, PRecord::IndentColon)
.parse(arena, state)?;
let val_parser = specialize_ref(PRecord::Pattern, loc_pattern_help());
let (_, loc_val, state) = space0_before_e(val_parser, PRecord::IndentColon)
.parse(arena, state, min_indent)?;
let Loc {
value: label,
@ -468,13 +461,11 @@ fn record_pattern_field<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
))
}
Some(Second(_)) => {
let val_parser = specialize_ref(PRecord::Expr, move |a, s| {
crate::expr::parse_loc_expr_no_multi_backpassing(min_indent, a, s)
});
let val_parser =
specialize_ref(PRecord::Expr, crate::expr::loc_expr_no_multi_backpassing());
let (_, loc_val, state) =
space0_before_e(val_parser, min_indent, PRecord::IndentColon)
.parse(arena, state)?;
let (_, loc_val, state) = space0_before_e(val_parser, PRecord::IndentColon)
.parse(arena, state, min_indent)?;
let Loc {
value: label,

View file

@ -1,7 +1,9 @@
use crate::ast::{EscapedChar, StrLiteral, StrSegment};
use crate::expr;
use crate::parser::Progress::{self, *};
use crate::parser::{allocated, loc, specialize_ref, word1, BadInputError, EString, Parser};
use crate::parser::{
allocated, loc, reset_min_indent, specialize_ref, word1, BadInputError, EString, Parser,
};
use crate::state::State;
use bumpalo::collections::vec::Vec;
use bumpalo::Bump;
@ -9,7 +11,7 @@ use bumpalo::Bump;
/// One or more ASCII hex digits. (Useful when parsing unicode escape codes,
/// which must consist entirely of ASCII hex digits.)
fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
move |arena, mut state: State<'a>| {
move |arena, mut state: State<'a>, _min_indent: u32| {
let mut buf = bumpalo::collections::String::new_in(arena);
for &byte in state.bytes().iter() {
@ -30,7 +32,7 @@ fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
}
pub fn parse_single_quote<'a>() -> impl Parser<'a, &'a str, EString<'a>> {
move |arena: &'a Bump, mut state: State<'a>| {
move |arena: &'a Bump, mut state: State<'a>, _min_indent: u32| {
if state.consume_mut("\'") {
// we will be parsing a single-quote-string
} else {
@ -165,7 +167,7 @@ fn utf8<'a>(
pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
use StrLiteral::*;
move |arena: &'a Bump, mut state: State<'a>| {
move |arena: &'a Bump, mut state: State<'a>, min_indent: u32| {
let is_multiline;
let indent = state.column();
@ -368,10 +370,13 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
// canonicalization error if that expression variant
// is not allowed inside a string interpolation.
let (_progress, loc_expr, new_state) = skip_second!(
specialize_ref(EString::Format, loc(allocated(expr::expr_help(0)))),
specialize_ref(
EString::Format,
loc(allocated(reset_min_indent(expr::expr_help())))
),
word1(b')', EString::FormatEnd)
)
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
// Advance the iterator past the expr we just parsed.
for _ in 0..(original_byte_count - new_state.bytes().len()) {
@ -398,7 +403,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, EString<'a>> {
loc(ascii_hex_digits()),
word1(b')', EString::CodePtEnd)
)
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
// Advance the iterator past the expr we just parsed.
for _ in 0..(original_byte_count - new_state.bytes().len()) {

View file

@ -34,7 +34,9 @@ pub fn parse_loc_with<'a>(
pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Defs<'a>, SyntaxError<'a>> {
let state = State::new(input.trim().as_bytes());
match module_defs().parse(arena, state) {
let min_indent = 0;
match module_defs().parse(arena, state, min_indent) {
Ok(tuple) => Ok(tuple.1),
Err(tuple) => Err(tuple.1),
}

View file

@ -9,54 +9,52 @@ use crate::expr::record_value_field;
use crate::ident::lowercase_ident;
use crate::keyword;
use crate::parser::{
allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, word3, EType,
ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, ParseResult, Parser,
absolute_column_min_indent, increment_min_indent, then, ERecord, ETypeAbilityImpl,
};
use crate::parser::{
allocated, backtrackable, fail, optional, specialize, specialize_ref, word1, word2, word3,
EType, ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, Parser,
Progress::{self, *},
};
use crate::parser::{then, ERecord, ETypeAbilityImpl};
use crate::state::State;
use bumpalo::collections::vec::Vec;
use bumpalo::Bump;
use roc_region::all::{Loc, Position, Region};
pub fn located<'a>(
min_indent: u32,
is_trailing_comma_valid: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
expression(min_indent, is_trailing_comma_valid, false)
expression(is_trailing_comma_valid, false)
}
pub fn located_opaque_signature<'a>(
min_indent: u32,
is_trailing_comma_valid: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
expression(min_indent, is_trailing_comma_valid, true)
expression(is_trailing_comma_valid, true)
}
#[inline(always)]
fn tag_union_type<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeTagUnion<'a>> {
move |arena, state| {
move |arena, state, min_indent| {
let (_, tags, state) = collection_trailing_sep_e!(
word1(b'[', ETypeTagUnion::Open),
loc!(tag_type(min_indent, false)),
loc!(tag_type(false)),
word1(b',', ETypeTagUnion::End),
word1(b']', ETypeTagUnion::End),
min_indent,
ETypeTagUnion::Open,
ETypeTagUnion::IndentEnd,
Tag::SpaceBefore
)
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
// This could be an open tag union, e.g. `[Foo, Bar]a`
let (_, ext, state) = optional(allocated(specialize_ref(
ETypeTagUnion::Type,
term(min_indent, stop_at_surface_has),
term(stop_at_surface_has),
)))
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
let result = TypeAnnotation::TagUnion { tags, ext };
@ -64,11 +62,11 @@ fn tag_union_type<'a>(
}
}
fn check_type_alias(
p: Progress,
annot: Loc<TypeAnnotation>,
) -> impl Parser<TypeHeader, ETypeInlineAlias> {
move |arena, state| match annot.value {
fn check_type_alias<'a>(
arena: &'a Bump,
annot: Loc<TypeAnnotation<'a>>,
) -> Result<TypeHeader<'a>, ETypeInlineAlias> {
match annot.value {
TypeAnnotation::Apply("", tag_name, vars) => {
let mut var_names = Vec::new_in(arena);
var_names.reserve(vars.len());
@ -76,11 +74,7 @@ fn check_type_alias(
if let TypeAnnotation::BoundVariable(v) = var.value {
var_names.push(Loc::at(var.region, Pattern::Identifier(v)));
} else {
return Err((
p,
ETypeInlineAlias::ArgumentNotLowercase(var.region.start()),
state,
));
return Err(ETypeInlineAlias::ArgumentNotLowercase(var.region.start()));
}
}
@ -93,64 +87,58 @@ fn check_type_alias(
vars: var_names.into_bump_slice(),
};
Ok((p, header, state))
Ok(header)
}
TypeAnnotation::Apply(_, _, _) => {
Err((p, ETypeInlineAlias::Qualified(annot.region.start()), state))
}
_ => Err((p, ETypeInlineAlias::NotAnAlias(annot.region.start()), state)),
TypeAnnotation::Apply(_, _, _) => Err(ETypeInlineAlias::Qualified(annot.region.start())),
_ => Err(ETypeInlineAlias::NotAnAlias(annot.region.start())),
}
}
fn parse_type_alias_after_as<'a>(min_indent: u32) -> impl Parser<'a, TypeHeader<'a>, EType<'a>> {
move |arena, state| {
space0_before_e(term(min_indent, false), min_indent, EType::TAsIndentStart)
.parse(arena, state)
.and_then(|(p, annot, state)| {
specialize(EType::TInlineAlias, check_type_alias(p, annot)).parse(arena, state)
})
}
fn parse_type_alias_after_as<'a>() -> impl Parser<'a, TypeHeader<'a>, EType<'a>> {
then(
space0_before_e(term(false), EType::TAsIndentStart),
// TODO: introduce a better combinator for this.
// `check_type_alias` doesn't need to modify the state or progress, but it needs to access `state.pos()`
|arena, state, progress, output, _min_indent| {
let res = check_type_alias(arena, output);
match res {
Ok(header) => Ok((progress, header, state)),
Err(err) => Err((progress, EType::TInlineAlias(err, state.pos()), state)),
}
},
)
}
fn fail_type_start<'a, T: 'a>() -> impl Parser<'a, T, EType<'a>> {
|_arena, state: State<'a>| Err((NoProgress, EType::TStart(state.pos()), state))
}
fn term<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
fn term<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
map_with_arena!(
and!(
one_of!(
loc_wildcard(),
loc_inferred(),
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
loc!(specialize(
EType::TRecord,
record_type(min_indent, stop_at_surface_has)
)),
specialize(EType::TInParens, loc_type_in_parens()),
loc!(specialize(EType::TRecord, record_type(stop_at_surface_has))),
loc!(specialize(
EType::TTagUnion,
tag_union_type(min_indent, stop_at_surface_has)
tag_union_type(stop_at_surface_has)
)),
loc!(applied_type(min_indent, stop_at_surface_has)),
loc!(applied_type(stop_at_surface_has)),
loc!(parse_type_variable(stop_at_surface_has)),
fail_type_start(),
fail(EType::TStart),
),
// Inline alias notation, e.g. [Nil, Cons a (List a)] as List a
one_of![
map!(
and!(
skip_second!(
backtrackable(space0_e(min_indent, EType::TIndentEnd)),
backtrackable(space0_e(EType::TIndentEnd)),
crate::parser::keyword_e(keyword::AS, EType::TEnd)
),
parse_type_alias_after_as(min_indent)
parse_type_alias_after_as()
),
Some
),
|_, state| Ok((NoProgress, None, state))
succeed!(None)
]
),
|arena: &'a Bump,
@ -187,27 +175,23 @@ fn loc_inferred<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
}
fn loc_applied_arg<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
use crate::ast::Spaceable;
map_with_arena!(
and!(
backtrackable(space0_e(min_indent, EType::TIndentStart)),
backtrackable(space0_e(EType::TIndentStart)),
one_of!(
loc_wildcard(),
loc_inferred(),
specialize(EType::TInParens, loc_type_in_parens(min_indent)),
loc!(specialize(
EType::TRecord,
record_type(min_indent, stop_at_surface_has)
)),
specialize(EType::TInParens, loc_type_in_parens()),
loc!(specialize(EType::TRecord, record_type(stop_at_surface_has))),
loc!(specialize(
EType::TTagUnion,
tag_union_type(min_indent, stop_at_surface_has)
tag_union_type(stop_at_surface_has)
)),
loc!(specialize(EType::TApply, parse_concrete_type)),
loc!(specialize(EType::TApply, concrete_type())),
loc!(parse_type_variable(stop_at_surface_has))
)
),
@ -222,18 +206,11 @@ fn loc_applied_arg<'a>(
)
}
fn loc_type_in_parens<'a>(
min_indent: u32,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, ETypeInParens<'a>> {
fn loc_type_in_parens<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, ETypeInParens<'a>> {
between!(
word1(b'(', ETypeInParens::Open),
space0_around_ee(
move |arena, state| specialize_ref(
ETypeInParens::Type,
expression(min_indent, true, false)
)
.parse(arena, state),
min_indent,
specialize_ref(ETypeInParens::Type, expression(true, false)),
ETypeInParens::IndentOpen,
ETypeInParens::IndentEnd,
),
@ -242,18 +219,14 @@ fn loc_type_in_parens<'a>(
}
#[inline(always)]
fn tag_type<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>> {
move |arena, state: State<'a>| {
let (_, name, state) = loc!(parse_tag_name(ETypeTagUnion::End)).parse(arena, state)?;
fn tag_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>> {
move |arena, state: State<'a>, min_indent: u32| {
let (_, name, state) =
loc!(parse_tag_name(ETypeTagUnion::End)).parse(arena, state, min_indent)?;
let (_, args, state) = specialize_ref(
ETypeTagUnion::Type,
loc_applied_args_e(min_indent, stop_at_surface_has),
)
.parse(arena, state)?;
let (_, args, state) =
specialize_ref(ETypeTagUnion::Type, loc_applied_args_e(stop_at_surface_has))
.parse(arena, state, min_indent)?;
let result = Tag::Apply {
name,
@ -269,19 +242,20 @@ where
F: Fn(Position) -> E,
E: 'a,
{
move |arena, state: State<'a>| match crate::ident::tag_name().parse(arena, state) {
move |arena, state: State<'a>, min_indent: u32| match crate::ident::tag_name()
.parse(arena, state, min_indent)
{
Ok(good) => Ok(good),
Err((progress, _, state)) => Err((progress, to_problem(state.pos()), state)),
}
}
fn record_type_field<'a>(
min_indent: u32,
) -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'a>>, ETypeRecord<'a>> {
fn record_type_field<'a>() -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'a>>, ETypeRecord<'a>>
{
use crate::parser::Either::*;
use AssignedField::*;
(move |arena, state: State<'a>| {
(move |arena, state: State<'a>, min_indent: u32| {
// You must have a field name, e.g. "email"
// using the initial pos is important for error reporting
let pos = state.pos();
@ -289,11 +263,11 @@ fn record_type_field<'a>(
move |_, _| ETypeRecord::Field(pos),
lowercase_ident()
))
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
debug_assert_eq!(progress, MadeProgress);
let (_, spaces, state) =
space0_e(min_indent, ETypeRecord::IndentEnd).parse(arena, state)?;
space0_e(ETypeRecord::IndentEnd).parse(arena, state, min_indent)?;
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.)
@ -301,15 +275,14 @@ fn record_type_field<'a>(
word1(b':', ETypeRecord::Colon),
word1(b'?', ETypeRecord::Optional)
))
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
let val_parser = specialize_ref(ETypeRecord::Type, expression(min_indent, true, false));
let val_parser = specialize_ref(ETypeRecord::Type, expression(true, false));
match opt_loc_val {
Some(First(_)) => {
let (_, loc_val, state) =
space0_before_e(val_parser, min_indent, ETypeRecord::IndentColon)
.parse(arena, state)?;
let (_, loc_val, state) = space0_before_e(val_parser, ETypeRecord::IndentColon)
.parse(arena, state, min_indent)?;
Ok((
MadeProgress,
@ -318,9 +291,8 @@ fn record_type_field<'a>(
))
}
Some(Second(_)) => {
let (_, loc_val, state) =
space0_before_e(val_parser, min_indent, ETypeRecord::IndentOptional)
.parse(arena, state)?;
let (_, loc_val, state) = space0_before_e(val_parser, ETypeRecord::IndentOptional)
.parse(arena, state, min_indent)?;
Ok((
MadeProgress,
@ -346,28 +318,26 @@ fn record_type_field<'a>(
#[inline(always)]
fn record_type<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> {
use crate::type_annotation::TypeAnnotation::*;
(move |arena, state| {
(move |arena, state, min_indent| {
let (_, fields, state) = collection_trailing_sep_e!(
// word1_check_indent!(b'{', TRecord::Open, min_indent, TRecord::IndentOpen),
word1(b'{', ETypeRecord::Open),
loc!(record_type_field(min_indent)),
loc!(record_type_field()),
word1(b',', ETypeRecord::End),
// word1_check_indent!(b'}', TRecord::End, min_indent, TRecord::IndentEnd),
word1(b'}', ETypeRecord::End),
min_indent,
ETypeRecord::Open,
ETypeRecord::IndentEnd,
AssignedField::SpaceBefore
)
.parse(arena, state)?;
.parse(arena, state, min_indent)?;
let field_term = specialize_ref(ETypeRecord::Type, term(min_indent, stop_at_surface_has));
let (_, ext, state) = optional(allocated(field_term)).parse(arena, state)?;
let field_term = specialize_ref(ETypeRecord::Type, term(stop_at_surface_has));
let (_, ext, state) = optional(allocated(field_term)).parse(arena, state, min_indent)?;
let result = Record { fields, ext };
@ -376,16 +346,13 @@ fn record_type<'a>(
.trace("type_annotation:record_type")
}
fn applied_type<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
fn applied_type<'a>(stop_at_surface_has: bool) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
map!(
and!(
specialize(EType::TApply, parse_concrete_type),
specialize(EType::TApply, concrete_type()),
// Optionally parse space-separated arguments for the constructor,
// e.g. `Str Float` in `Map Str Float`
loc_applied_args_e(min_indent, stop_at_surface_has)
loc_applied_args_e(stop_at_surface_has)
),
|(ctor, args): (TypeAnnotation<'a>, Vec<'a, Loc<TypeAnnotation<'a>>>)| {
match &ctor {
@ -406,29 +373,24 @@ fn applied_type<'a>(
}
fn loc_applied_args_e<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
zero_or_more!(loc_applied_arg(min_indent, stop_at_surface_has))
zero_or_more!(loc_applied_arg(stop_at_surface_has))
}
// Hash & Eq & ...
fn ability_chain<'a>(
min_indent: u32,
) -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
map!(
and!(
space0_before_optional_after(
specialize(EType::TApply, loc!(parse_concrete_type)),
min_indent,
specialize(EType::TApply, loc!(concrete_type())),
EType::TIndentStart,
EType::TIndentEnd,
),
zero_or_more!(skip_first!(
word1(b'&', EType::THasClause),
space0_before_optional_after(
specialize(EType::TApply, loc!(parse_concrete_type)),
min_indent,
specialize(EType::TApply, loc!(concrete_type())),
EType::TIndentStart,
EType::TIndentEnd,
)
@ -444,7 +406,7 @@ fn ability_chain<'a>(
)
}
fn has_clause<'a>(min_indent: u32) -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> {
fn has_clause<'a>() -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> {
map!(
// Suppose we are trying to parse "a has Hash"
and!(
@ -454,17 +416,14 @@ fn has_clause<'a>(min_indent: u32) -> impl Parser<'a, Loc<HasClause<'a>>, EType<
|_, pos| EType::TBadTypeVariable(pos),
loc!(map!(lowercase_ident(), Spaced::Item)),
),
min_indent,
EType::TIndentStart,
EType::TIndentEnd
),
then(
skip_first!(
// Parse "has"; we don't care about this keyword
word3(b'h', b'a', b's', EType::THasClause),
// Parse "Hash & ..."; this may be qualified from another module like "Hash.Hash"
|arena, state, _progress, _output| {
ability_chain(state.column() + 1).parse(arena, state)
}
absolute_column_min_indent(ability_chain())
)
),
|(var, abilities): (Loc<Spaced<'a, &'a str>>, Vec<'a, Loc<TypeAnnotation<'a>>>)| {
@ -485,24 +444,18 @@ fn has_clause<'a>(min_indent: u32) -> impl Parser<'a, Loc<HasClause<'a>>, EType<
/// Parse a chain of `has` clauses, e.g. " | a has Hash, b has Eq".
/// Returns the clauses and spaces before the starting "|", if there were any.
fn has_clause_chain<'a>(
min_indent: u32,
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [Loc<HasClause<'a>>]), EType<'a>> {
move |arena, state: State<'a>| {
let (_, (spaces_before, ()), state) = and!(
space0_e(min_indent, EType::TIndentStart),
word1(b'|', EType::TWhereBar)
)
.parse(arena, state)?;
move |arena, state: State<'a>, min_indent: u32| {
let (_, (spaces_before, ()), state) =
and!(space0_e(EType::TIndentStart), word1(b'|', EType::TWhereBar))
.parse(arena, state, min_indent)?;
let min_demand_indent = state.column() + 1;
// Parse the first clause (there must be one), then the rest
let (_, first_clause, state) = has_clause(min_demand_indent).parse(arena, state)?;
let (_, first_clause, state) = has_clause().parse(arena, state, min_indent)?;
let (_, mut clauses, state) = zero_or_more!(skip_first!(
word1(b',', EType::THasClause),
has_clause(min_demand_indent)
))
.parse(arena, state)?;
let (_, mut clauses, state) =
zero_or_more!(skip_first!(word1(b',', EType::THasClause), has_clause()))
.parse(arena, state, min_indent)?;
// Usually the number of clauses shouldn't be too large, so this is okay
clauses.insert(0, first_clause);
@ -516,8 +469,8 @@ fn has_clause_chain<'a>(
}
/// Parse a has-abilities clause, e.g. `has [Eq, Hash]`.
pub fn has_abilities<'a>(min_indent: u32) -> impl Parser<'a, Loc<HasAbilities<'a>>, EType<'a>> {
skip_first!(
pub fn has_abilities<'a>() -> impl Parser<'a, Loc<HasAbilities<'a>>, EType<'a>> {
increment_min_indent(skip_first!(
// Parse "has"; we don't care about this keyword
word3(b'h', b'a', b's', EType::THasClause),
// Parse "Hash"; this may be qualified from another module like "Hash.Hash"
@ -525,39 +478,33 @@ pub fn has_abilities<'a>(min_indent: u32) -> impl Parser<'a, Loc<HasAbilities<'a
loc!(map!(
collection_trailing_sep_e!(
word1(b'[', EType::TStart),
loc!(parse_has_ability(min_indent)),
loc!(parse_has_ability()),
word1(b',', EType::TEnd),
word1(b']', EType::TEnd),
min_indent + 1,
EType::TStart,
EType::TIndentEnd,
HasAbility::SpaceBefore
),
HasAbilities::Has
)),
min_indent + 1,
EType::TIndentEnd,
)
)
))
}
fn parse_has_ability<'a>(min_indent: u32) -> impl Parser<'a, HasAbility<'a>, EType<'a>> {
map!(
fn parse_has_ability<'a>() -> impl Parser<'a, HasAbility<'a>, EType<'a>> {
increment_min_indent(map!(
and!(
loc!(specialize(EType::TApply, parse_concrete_type)),
loc!(specialize(EType::TApply, concrete_type())),
optional(space0_before_e(
loc!(map!(
specialize(
EType::TAbilityImpl,
collection_trailing_sep_e!(
word1(b'{', ETypeAbilityImpl::Open),
specialize(
|e: ERecord<'_>, _| e.into(),
loc!(record_value_field(min_indent + 1))
),
specialize(|e: ERecord<'_>, _| e.into(), loc!(record_value_field())),
word1(b',', ETypeAbilityImpl::End),
word1(b'}', ETypeAbilityImpl::End),
min_indent,
ETypeAbilityImpl::Open,
ETypeAbilityImpl::IndentEnd,
AssignedField::SpaceBefore
@ -565,63 +512,49 @@ fn parse_has_ability<'a>(min_indent: u32) -> impl Parser<'a, HasAbility<'a>, ETy
),
HasImpls::HasImpls
)),
min_indent + 1,
EType::TIndentEnd
))
),
|(ability, impls): (_, Option<_>)| { HasAbility::HasAbility { ability, impls } }
)
))
}
fn expression<'a>(
min_indent: u32,
is_trailing_comma_valid: bool,
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
(move |arena, state: State<'a>| {
let (p1, first, state) = space0_before_e(
term(min_indent, stop_at_surface_has),
min_indent,
EType::TIndentStart,
)
.parse(arena, state)?;
(move |arena, state: State<'a>, min_indent: u32| {
let (p1, first, state) = space0_before_e(term(stop_at_surface_has), EType::TIndentStart)
.parse(arena, state, min_indent)?;
let result = and![
zero_or_more!(skip_first!(
word1(b',', EType::TFunctionArgument),
one_of![
space0_around_ee(
term(min_indent, stop_at_surface_has),
min_indent,
term(stop_at_surface_has),
EType::TIndentStart,
EType::TIndentEnd
),
|_, state: State<'a>| Err((
NoProgress,
EType::TFunctionArgument(state.pos()),
state
))
fail(EType::TFunctionArgument)
]
))
.trace("type_annotation:expression:rest_args"),
// TODO this space0 is dropped, so newlines just before the function arrow when there
// is only one argument are not seen by the formatter. Can we do better?
skip_second!(
space0_e(min_indent, EType::TIndentStart),
space0_e(EType::TIndentStart),
word2(b'-', b'>', EType::TStart)
)
.trace("type_annotation:expression:arrow")
]
.parse(arena, state.clone());
.parse(arena, state.clone(), min_indent);
let (progress, annot, state) = match result {
Ok((p2, (rest, _dropped_spaces), state)) => {
let (p3, return_type, state) = space0_before_e(
term(min_indent, stop_at_surface_has),
min_indent,
EType::TIndentStart,
)
.parse(arena, state)?;
let (p3, return_type, state) =
space0_before_e(term(stop_at_surface_has), EType::TIndentStart)
.parse(arena, state, min_indent)?;
let region = Region::span_across(&first.region, &return_type.region);
@ -641,11 +574,11 @@ fn expression<'a>(
Err(err) => {
if !is_trailing_comma_valid {
let (_, comma, _) = optional(skip_first!(
space0_e(min_indent, EType::TIndentStart),
space0_e(EType::TIndentStart),
word1(b',', EType::TStart)
))
.trace("check trailing comma")
.parse(arena, state.clone())?;
.parse(arena, state.clone(), min_indent)?;
if comma.is_some() {
// If the surrounding scope has declared that a trailing comma is not a valid state
@ -663,8 +596,7 @@ fn expression<'a>(
// Finally, try to parse a where clause if there is one.
// The where clause must be at least as deep as where the type annotation started.
let min_where_clause_indent = min_indent;
match has_clause_chain(min_where_clause_indent).parse(arena, state.clone()) {
match has_clause_chain().parse(arena, state.clone(), min_indent) {
Ok((where_progress, (spaces_before, has_chain), state)) => {
use crate::ast::Spaceable;
@ -709,30 +641,29 @@ fn expression<'a>(
// /// A bound type variable, e.g. `a` in `(a -> a)`
// BoundVariable(&'a str),
fn parse_concrete_type<'a>(
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, TypeAnnotation<'a>, ETypeApply> {
let initial_bytes = state.bytes();
fn concrete_type<'a>() -> impl Parser<'a, TypeAnnotation<'a>, ETypeApply> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let initial_bytes = state.bytes();
match crate::ident::concrete_type().parse(arena, state) {
Ok((_, (module_name, type_name), state)) => {
let answer = TypeAnnotation::Apply(module_name, type_name, &[]);
match crate::ident::concrete_type().parse(arena, state, min_indent) {
Ok((_, (module_name, type_name), state)) => {
let answer = TypeAnnotation::Apply(module_name, type_name, &[]);
Ok((MadeProgress, answer, state))
}
Err((NoProgress, _, state)) => Err((NoProgress, ETypeApply::End(state.pos()), state)),
Err((MadeProgress, _, mut state)) => {
// we made some progress, but ultimately failed.
// that means a malformed type name
let chomped = crate::ident::chomp_malformed(state.bytes());
let delta = initial_bytes.len() - state.bytes().len();
let parsed_str =
unsafe { std::str::from_utf8_unchecked(&initial_bytes[..chomped + delta]) };
Ok((MadeProgress, answer, state))
}
Err((NoProgress, _, state)) => Err((NoProgress, ETypeApply::End(state.pos()), state)),
Err((MadeProgress, _, mut state)) => {
// we made some progress, but ultimately failed.
// that means a malformed type name
let chomped = crate::ident::chomp_malformed(state.bytes());
let delta = initial_bytes.len() - state.bytes().len();
let parsed_str =
unsafe { std::str::from_utf8_unchecked(&initial_bytes[..chomped + delta]) };
state = state.advance(chomped);
state = state.advance(chomped);
Ok((MadeProgress, TypeAnnotation::Malformed(parsed_str), state))
Ok((MadeProgress, TypeAnnotation::Malformed(parsed_str), state))
}
}
}
}
@ -740,7 +671,9 @@ fn parse_concrete_type<'a>(
fn parse_type_variable<'a>(
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
move |arena, state: State<'a>| match crate::ident::lowercase_ident().parse(arena, state) {
move |arena, state: State<'a>, min_indent: u32| match crate::ident::lowercase_ident()
.parse(arena, state, min_indent)
{
Ok((_, name, state)) => {
if name == "has" && stop_at_surface_has {
Err((NoProgress, EType::TEnd(state.pos()), state))