mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-01 10:52:18 +00:00
Introduce record!
combinator
... and refactor header parser to fully use combinators, in support of future combinator-based superpowers
This commit is contained in:
parent
ec6db293f5
commit
2b91af02df
26 changed files with 1709 additions and 1486 deletions
|
@ -8,7 +8,7 @@ use roc_collections::soa::{EitherIndex, Index, Slice};
|
|||
use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Spaces<'a, T> {
|
||||
pub before: &'a [CommentOrNewline<'a>],
|
||||
pub item: T,
|
||||
|
@ -81,11 +81,17 @@ impl<'a, T: ExtractSpaces<'a>> ExtractSpaces<'a> for Loc<T> {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Module<'a> {
|
||||
Interface { header: InterfaceHeader<'a> },
|
||||
App { header: AppHeader<'a> },
|
||||
Platform { header: PlatformHeader<'a> },
|
||||
Hosted { header: HostedHeader<'a> },
|
||||
pub struct Module<'a> {
|
||||
pub comments: &'a [CommentOrNewline<'a>],
|
||||
pub header: Header<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Header<'a> {
|
||||
Interface(InterfaceHeader<'a>),
|
||||
App(AppHeader<'a>),
|
||||
Platform(PlatformHeader<'a>),
|
||||
Hosted(HostedHeader<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
|
|
|
@ -1089,10 +1089,10 @@ fn opaque_signature_with_space_before<'a>(
|
|||
EType::TIndentStart,
|
||||
),
|
||||
),
|
||||
optional(specialize(
|
||||
optional(backtrackable(specialize(
|
||||
EExpr::Type,
|
||||
space0_before_e(type_annotation::has_abilities(), EType::TIndentStart,),
|
||||
))
|
||||
)))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2557,7 +2557,7 @@ fn record_help<'a>() -> impl Parser<
|
|||
and!(
|
||||
// You can optionally have an identifier followed by an '&' to
|
||||
// make this a record update, e.g. { Foo.user & username: "blah" }.
|
||||
optional(skip_second!(
|
||||
optional(backtrackable(skip_second!(
|
||||
space0_around_ee(
|
||||
// We wrap the ident in an Expr here,
|
||||
// so that we have a Spaceable value to work with,
|
||||
|
@ -2568,7 +2568,7 @@ fn record_help<'a>() -> impl Parser<
|
|||
ERecord::IndentAmpersand,
|
||||
),
|
||||
word1(b'&', ERecord::Ampersand)
|
||||
)),
|
||||
))),
|
||||
loc!(skip_first!(
|
||||
// We specifically allow space characters inside here, so that
|
||||
// `{ }` can be successfully parsed as an empty record, and then
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use crate::ast::{Collection, CommentOrNewline, Spaced, StrLiteral, TypeAnnotation};
|
||||
use crate::ast::{Collection, CommentOrNewline, Spaced, Spaces, StrLiteral, TypeAnnotation};
|
||||
use crate::blankspace::space0_e;
|
||||
use crate::ident::{lowercase_ident, UppercaseIdent};
|
||||
use crate::parser::Progress::*;
|
||||
use crate::parser::{optional, then};
|
||||
use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
|
||||
use crate::state::State;
|
||||
use crate::string_literal;
|
||||
use bumpalo::collections::Vec;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::Loc;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum HeaderFor<'a> {
|
||||
|
@ -124,40 +124,61 @@ impl<'a> ExposedName<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Keyword: Copy + Clone + Debug {
|
||||
const KEYWORD: &'static str;
|
||||
}
|
||||
|
||||
macro_rules! keywords {
|
||||
($($name:ident => $string:expr),* $(,)?) => {
|
||||
$(
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct $name;
|
||||
|
||||
impl Keyword for $name {
|
||||
const KEYWORD: &'static str = $string;
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
keywords! {
|
||||
ExposesKeyword => "exposes",
|
||||
ImportsKeyword => "imports",
|
||||
WithKeyword => "with",
|
||||
GeneratesKeyword => "generates",
|
||||
PackageKeyword => "package",
|
||||
PackagesKeyword => "packages",
|
||||
RequiresKeyword => "requires",
|
||||
ProvidesKeyword => "provides",
|
||||
ToKeyword => "to",
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct KeywordItem<'a, K, V> {
|
||||
pub keyword: Spaces<'a, K>,
|
||||
pub item: V,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct InterfaceHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
pub after_interface_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub after_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct HostedHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<ModuleName<'a>>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub generates: UppercaseIdent<'a>,
|
||||
pub generates_with: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
pub after_hosted_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub after_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
pub before_generates: &'a [CommentOrNewline<'a>],
|
||||
pub after_generates: &'a [CommentOrNewline<'a>],
|
||||
pub before_with: &'a [CommentOrNewline<'a>],
|
||||
pub after_with: &'a [CommentOrNewline<'a>],
|
||||
pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
|
||||
|
||||
pub generates: KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>,
|
||||
pub generates_with:
|
||||
KeywordItem<'a, WithKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -168,42 +189,39 @@ pub enum To<'a> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct AppHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<StrLiteral<'a>>,
|
||||
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub provides_types: Option<Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>>,
|
||||
pub to: Loc<To<'a>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
pub after_app_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_packages: &'a [CommentOrNewline<'a>],
|
||||
pub after_packages: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
pub before_provides: &'a [CommentOrNewline<'a>],
|
||||
pub after_provides: &'a [CommentOrNewline<'a>],
|
||||
pub before_to: &'a [CommentOrNewline<'a>],
|
||||
pub after_to: &'a [CommentOrNewline<'a>],
|
||||
pub packages:
|
||||
Option<KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>>,
|
||||
pub imports:
|
||||
Option<KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>>,
|
||||
pub provides: ProvidesTo<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ProvidesTo<'a> {
|
||||
pub provides_keyword: Spaces<'a, ProvidesKeyword>,
|
||||
pub entries: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub types: Option<Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>>,
|
||||
|
||||
pub to_keyword: Spaces<'a, ToKeyword>,
|
||||
pub to: Loc<To<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PackageHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageName<'a>>)>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
pub after_package_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub after_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub before_packages: &'a [CommentOrNewline<'a>],
|
||||
pub after_packages: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
pub exposes_keyword: Spaces<'a, ExposesKeyword>,
|
||||
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
|
||||
pub packages_keyword: Spaces<'a, PackagesKeyword>,
|
||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageName<'a>>)>,
|
||||
|
||||
pub imports_keyword: Spaces<'a, ImportsKeyword>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -214,26 +232,16 @@ pub struct PlatformRequires<'a> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PlatformHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
pub requires: PlatformRequires<'a>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
|
||||
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub before_header: &'a [CommentOrNewline<'a>],
|
||||
pub after_platform_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub before_requires: &'a [CommentOrNewline<'a>],
|
||||
pub after_requires: &'a [CommentOrNewline<'a>],
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub after_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub before_packages: &'a [CommentOrNewline<'a>],
|
||||
pub after_packages: &'a [CommentOrNewline<'a>],
|
||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||
pub before_provides: &'a [CommentOrNewline<'a>],
|
||||
pub after_provides: &'a [CommentOrNewline<'a>],
|
||||
pub requires: KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>,
|
||||
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
||||
pub packages:
|
||||
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
|
||||
pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
|
||||
pub provides:
|
||||
KeywordItem<'a, ProvidesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
|
@ -270,50 +278,47 @@ pub struct PackageEntry<'a> {
|
|||
}
|
||||
|
||||
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
|
||||
move |arena, state, min_indent| {
|
||||
map!(
|
||||
// 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 (_, opt_shorthand, state) = maybe!(and!(
|
||||
skip_second!(
|
||||
specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()),
|
||||
word1(b':', EPackageEntry::Colon)
|
||||
),
|
||||
space0_e(EPackageEntry::IndentPackage)
|
||||
))
|
||||
.parse(arena, state, min_indent)?;
|
||||
|
||||
let (_, package_or_path, state) =
|
||||
and!(
|
||||
optional(and!(
|
||||
skip_second!(
|
||||
specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()),
|
||||
word1(b':', EPackageEntry::Colon)
|
||||
),
|
||||
space0_e(EPackageEntry::IndentPackage)
|
||||
)),
|
||||
loc!(specialize(EPackageEntry::BadPackage, package_name()))
|
||||
.parse(arena, state, min_indent)?;
|
||||
),
|
||||
move |(opt_shorthand, package_or_path)| {
|
||||
let entry = match opt_shorthand {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
||||
shorthand,
|
||||
spaces_after_shorthand,
|
||||
package_name: package_or_path,
|
||||
},
|
||||
None => PackageEntry {
|
||||
shorthand: "",
|
||||
spaces_after_shorthand: &[],
|
||||
package_name: package_or_path,
|
||||
},
|
||||
};
|
||||
|
||||
let entry = match opt_shorthand {
|
||||
Some((shorthand, spaces_after_shorthand)) => PackageEntry {
|
||||
shorthand,
|
||||
spaces_after_shorthand,
|
||||
package_name: package_or_path,
|
||||
},
|
||||
None => PackageEntry {
|
||||
shorthand: "",
|
||||
spaces_after_shorthand: &[],
|
||||
package_name: package_or_path,
|
||||
},
|
||||
};
|
||||
|
||||
Ok((MadeProgress, Spaced::Item(entry), state))
|
||||
}
|
||||
Spaced::Item(entry)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> {
|
||||
move |arena, state: State<'a>, min_indent: u32| {
|
||||
let pos = state.pos();
|
||||
specialize(EPackageName::BadPath, string_literal::parse())
|
||||
.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))),
|
||||
StrLiteral::Block(_) => Err((progress, EPackageName::Multiline(pos))),
|
||||
})
|
||||
}
|
||||
then(
|
||||
loc!(specialize(EPackageName::BadPath, string_literal::parse())),
|
||||
move |_arena, state, progress, text| match text.value {
|
||||
StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), state)),
|
||||
StrLiteral::Line(_) => Err((progress, EPackageName::Escapes(text.region.start()))),
|
||||
StrLiteral::Block(_) => Err((progress, EPackageName::Multiline(text.region.start()))),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use crate::ast::{Collection, CommentOrNewline, Defs, Module, Spaced};
|
||||
use crate::ast::{Collection, Defs, Header, Module, Spaced, Spaces};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::header::{
|
||||
package_entry, package_name, AppHeader, ExposedName, HostedHeader, ImportsEntry,
|
||||
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, PlatformRequires, To, TypedIdent,
|
||||
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
|
||||
HostedHeader, ImportsEntry, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName,
|
||||
PackageEntry, PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo,
|
||||
RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||
};
|
||||
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
|
||||
use crate::parser::Progress::{self, *};
|
||||
|
@ -48,132 +50,63 @@ pub fn parse_header<'a>(
|
|||
fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
|
||||
use crate::parser::keyword_e;
|
||||
|
||||
type Clos<'b> = Box<(dyn FnOnce(&'b [CommentOrNewline]) -> Module<'b> + 'b)>;
|
||||
|
||||
map!(
|
||||
and!(
|
||||
space0_e(EHeader::IndentStart),
|
||||
one_of![
|
||||
map!(
|
||||
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;
|
||||
Module::Interface { header }
|
||||
})
|
||||
}
|
||||
record!(Module {
|
||||
comments: space0_e(EHeader::IndentStart),
|
||||
header: one_of![
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword_e("interface", EHeader::Start),
|
||||
increment_min_indent(interface_header())
|
||||
),
|
||||
map!(
|
||||
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;
|
||||
Module::App { header }
|
||||
})
|
||||
}
|
||||
Header::Interface
|
||||
),
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword_e("app", EHeader::Start),
|
||||
increment_min_indent(app_header())
|
||||
),
|
||||
map!(
|
||||
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;
|
||||
Module::Platform { header }
|
||||
})
|
||||
}
|
||||
Header::App
|
||||
),
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword_e("platform", EHeader::Start),
|
||||
increment_min_indent(platform_header())
|
||||
),
|
||||
map!(
|
||||
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;
|
||||
Module::Hosted { header }
|
||||
})
|
||||
}
|
||||
)
|
||||
]
|
||||
),
|
||||
|(spaces, make_header): (&'a [CommentOrNewline], Clos<'a>)| { make_header(spaces) }
|
||||
)
|
||||
Header::Platform
|
||||
),
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword_e("hosted", EHeader::Start),
|
||||
increment_min_indent(hosted_header())
|
||||
),
|
||||
Header::Hosted
|
||||
),
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> {
|
||||
|arena, state, min_indent: u32| {
|
||||
let (_, after_interface_keyword, 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, min_indent)?;
|
||||
let (_, ((before_imports, after_imports), imports), state) =
|
||||
specialize(EHeader::Imports, imports()).parse(arena, state, min_indent)?;
|
||||
|
||||
let header = InterfaceHeader {
|
||||
name,
|
||||
exposes,
|
||||
imports,
|
||||
before_header: &[] as &[_],
|
||||
after_interface_keyword,
|
||||
before_exposes,
|
||||
after_exposes,
|
||||
before_imports,
|
||||
after_imports,
|
||||
};
|
||||
|
||||
Ok((MadeProgress, header, state))
|
||||
}
|
||||
record!(InterfaceHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(module_name_help(EHeader::ModuleName)),
|
||||
exposes: specialize(EHeader::Exposes, exposes_values()),
|
||||
imports: specialize(EHeader::Imports, imports()),
|
||||
})
|
||||
.trace("interface_header")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
|
||||
|arena, state, min_indent: u32| {
|
||||
let (_, after_hosted_keyword, 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, min_indent)?;
|
||||
let (_, ((before_imports, after_imports), imports), state) =
|
||||
specialize(EHeader::Imports, imports()).parse(arena, state, min_indent)?;
|
||||
let (_, ((before_generates, after_generates), generates), 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, min_indent)?;
|
||||
|
||||
let header = HostedHeader {
|
||||
name,
|
||||
exposes,
|
||||
imports,
|
||||
generates,
|
||||
generates_with,
|
||||
before_header: &[] as &[_],
|
||||
after_hosted_keyword,
|
||||
before_exposes,
|
||||
after_exposes,
|
||||
before_imports,
|
||||
after_imports,
|
||||
before_generates,
|
||||
after_generates,
|
||||
before_with,
|
||||
after_with,
|
||||
};
|
||||
|
||||
Ok((MadeProgress, header, state))
|
||||
}
|
||||
record!(HostedHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(module_name_help(EHeader::ModuleName)),
|
||||
exposes: specialize(EHeader::Exposes, exposes_values()),
|
||||
imports: specialize(EHeader::Imports, imports()),
|
||||
generates: specialize(EHeader::Generates, generates()),
|
||||
generates_with: specialize(EHeader::GeneratesWith, generates_with()),
|
||||
})
|
||||
.trace("hosted_header")
|
||||
}
|
||||
|
||||
fn chomp_module_name(buffer: &[u8]) -> Result<&str, Progress> {
|
||||
|
@ -237,127 +170,31 @@ fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> {
|
|||
|
||||
#[inline(always)]
|
||||
fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
||||
|arena, state, min_indent: u32| {
|
||||
let (_, after_app_keyword, state) =
|
||||
space0_e(EHeader::IndentStart).parse(arena, state, min_indent)?;
|
||||
let (_, name, state) = loc!(crate::parser::specialize(
|
||||
record!(AppHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(crate::parser::specialize(
|
||||
EHeader::AppName,
|
||||
string_literal::parse()
|
||||
))
|
||||
.parse(arena, state, min_indent)?;
|
||||
|
||||
let (_, opt_pkgs, state) =
|
||||
maybe!(specialize(EHeader::Packages, packages())).parse(arena, state, min_indent)?;
|
||||
let (_, opt_imports, state) =
|
||||
maybe!(specialize(EHeader::Imports, imports())).parse(arena, state, min_indent)?;
|
||||
let (_, provides, state) =
|
||||
specialize(EHeader::Provides, provides_to()).parse(arena, state, min_indent)?;
|
||||
|
||||
let (before_packages, after_packages, packages) = match opt_pkgs {
|
||||
Some(pkgs) => {
|
||||
let pkgs: Packages<'a> = pkgs; // rustc must be told the type here
|
||||
|
||||
(
|
||||
pkgs.before_packages_keyword,
|
||||
pkgs.after_packages_keyword,
|
||||
pkgs.entries,
|
||||
)
|
||||
}
|
||||
None => (&[] as _, &[] as _, Collection::empty()),
|
||||
};
|
||||
|
||||
// rustc must be told the type here
|
||||
#[allow(clippy::type_complexity)]
|
||||
let opt_imports: Option<(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
)> = opt_imports;
|
||||
|
||||
let ((before_imports, after_imports), imports) =
|
||||
opt_imports.unwrap_or_else(|| ((&[] as _, &[] as _), Collection::empty()));
|
||||
let provides: ProvidesTo<'a> = provides; // rustc must be told the type here
|
||||
|
||||
let header = AppHeader {
|
||||
name,
|
||||
packages,
|
||||
imports,
|
||||
provides: provides.entries,
|
||||
provides_types: provides.types,
|
||||
to: provides.to,
|
||||
before_header: &[] as &[_],
|
||||
after_app_keyword,
|
||||
before_packages,
|
||||
after_packages,
|
||||
before_imports,
|
||||
after_imports,
|
||||
before_provides: provides.before_provides_keyword,
|
||||
after_provides: provides.after_provides_keyword,
|
||||
before_to: provides.before_to_keyword,
|
||||
after_to: provides.after_to_keyword,
|
||||
};
|
||||
|
||||
Ok((MadeProgress, header, state))
|
||||
}
|
||||
)),
|
||||
packages: optional(specialize(EHeader::Packages, packages())),
|
||||
imports: optional(specialize(EHeader::Imports, imports())),
|
||||
provides: specialize(EHeader::Provides, provides_to()),
|
||||
})
|
||||
.trace("app_header")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
||||
|arena, state, min_indent: u32| {
|
||||
let (_, after_platform_keyword, 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, min_indent)?;
|
||||
|
||||
let (_, ((before_exposes, after_exposes), exposes), state) =
|
||||
specialize(EHeader::Exposes, exposes_modules()).parse(arena, state, min_indent)?;
|
||||
|
||||
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, min_indent)?;
|
||||
|
||||
let (_, ((before_provides, after_provides), (provides, _provides_type)), state) =
|
||||
specialize(EHeader::Provides, provides_without_to()).parse(arena, state, min_indent)?;
|
||||
|
||||
let header = PlatformHeader {
|
||||
name,
|
||||
requires,
|
||||
exposes,
|
||||
packages: packages.entries,
|
||||
imports,
|
||||
provides,
|
||||
before_header: &[] as &[_],
|
||||
after_platform_keyword,
|
||||
before_requires,
|
||||
after_requires,
|
||||
before_exposes,
|
||||
after_exposes,
|
||||
before_packages: packages.before_packages_keyword,
|
||||
after_packages: packages.after_packages_keyword,
|
||||
before_imports,
|
||||
after_imports,
|
||||
before_provides,
|
||||
after_provides,
|
||||
};
|
||||
|
||||
Ok((MadeProgress, header, state))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ProvidesTo<'a> {
|
||||
entries: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
types: Option<Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>>,
|
||||
to: Loc<To<'a>>,
|
||||
|
||||
before_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||
after_provides_keyword: &'a [CommentOrNewline<'a>],
|
||||
before_to_keyword: &'a [CommentOrNewline<'a>],
|
||||
after_to_keyword: &'a [CommentOrNewline<'a>],
|
||||
record!(PlatformHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(specialize(EHeader::PlatformName, package_name())),
|
||||
requires: specialize(EHeader::Requires, requires()),
|
||||
exposes: specialize(EHeader::Exposes, exposes_modules()),
|
||||
packages: specialize(EHeader::Packages, packages()),
|
||||
imports: specialize(EHeader::Imports, imports()),
|
||||
provides: specialize(EHeader::Provides, provides_exposed()),
|
||||
})
|
||||
.trace("platform_header")
|
||||
}
|
||||
|
||||
fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
|
||||
|
@ -372,68 +209,54 @@ 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>> {
|
||||
map!(
|
||||
and!(
|
||||
provides_without_to(),
|
||||
and!(
|
||||
spaces_around_keyword(
|
||||
"to",
|
||||
EProvides::To,
|
||||
EProvides::IndentTo,
|
||||
EProvides::IndentListStart
|
||||
),
|
||||
loc!(provides_to_package())
|
||||
)
|
||||
),
|
||||
|(
|
||||
((before_provides_keyword, after_provides_keyword), (entries, provides_types)),
|
||||
((before_to_keyword, after_to_keyword), to),
|
||||
)| {
|
||||
ProvidesTo {
|
||||
entries,
|
||||
types: provides_types,
|
||||
to,
|
||||
before_provides_keyword,
|
||||
after_provides_keyword,
|
||||
before_to_keyword,
|
||||
after_to_keyword,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn provides_without_to<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
(
|
||||
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
Option<Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>>,
|
||||
),
|
||||
),
|
||||
EProvides<'a>,
|
||||
> {
|
||||
and!(
|
||||
spaces_around_keyword(
|
||||
"provides",
|
||||
record!(ProvidesTo {
|
||||
provides_keyword: spaces_around_keyword(
|
||||
ProvidesKeyword,
|
||||
EProvides::Provides,
|
||||
EProvides::IndentProvides,
|
||||
EProvides::IndentListStart
|
||||
),
|
||||
and!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'[', EProvides::ListStart),
|
||||
exposes_entry(EProvides::Identifier),
|
||||
word1(b',', EProvides::ListEnd),
|
||||
word1(b']', EProvides::ListEnd),
|
||||
EProvides::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
),
|
||||
// Optionally
|
||||
optional(provides_types())
|
||||
)
|
||||
)
|
||||
entries: collection_trailing_sep_e!(
|
||||
word1(b'[', EProvides::ListStart),
|
||||
exposes_entry(EProvides::Identifier),
|
||||
word1(b',', EProvides::ListEnd),
|
||||
word1(b']', EProvides::ListEnd),
|
||||
EProvides::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
),
|
||||
types: optional(backtrackable(provides_types())),
|
||||
to_keyword: spaces_around_keyword(
|
||||
ToKeyword,
|
||||
EProvides::To,
|
||||
EProvides::IndentTo,
|
||||
EProvides::IndentListStart
|
||||
),
|
||||
to: loc!(provides_to_package()),
|
||||
})
|
||||
.trace("provides_to")
|
||||
}
|
||||
|
||||
fn provides_exposed<'a>() -> impl Parser<
|
||||
'a,
|
||||
KeywordItem<'a, ProvidesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
EProvides<'a>,
|
||||
> {
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
ProvidesKeyword,
|
||||
EProvides::Provides,
|
||||
EProvides::IndentProvides,
|
||||
EProvides::IndentListStart
|
||||
),
|
||||
item: collection_trailing_sep_e!(
|
||||
word1(b'[', EProvides::ListStart),
|
||||
exposes_entry(EProvides::Identifier),
|
||||
word1(b',', EProvides::ListEnd),
|
||||
word1(b']', EProvides::ListEnd),
|
||||
EProvides::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -491,34 +314,25 @@ where
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn requires<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
PlatformRequires<'a>,
|
||||
),
|
||||
ERequires<'a>,
|
||||
> {
|
||||
and!(
|
||||
spaces_around_keyword(
|
||||
"requires",
|
||||
fn requires<'a>(
|
||||
) -> impl Parser<'a, KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>, ERequires<'a>> {
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
RequiresKeyword,
|
||||
ERequires::Requires,
|
||||
ERequires::IndentRequires,
|
||||
ERequires::IndentListStart
|
||||
),
|
||||
platform_requires()
|
||||
)
|
||||
item: platform_requires(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
skip_second!(requires_rigids(), space0_e(ERequires::ListStart)),
|
||||
requires_typed_ident()
|
||||
),
|
||||
|(rigids, signature)| { PlatformRequires { rigids, signature } }
|
||||
)
|
||||
record!(PlatformRequires {
|
||||
rigids: skip_second!(requires_rigids(), space0_e(ERequires::ListStart)),
|
||||
signature: requires_typed_ident()
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -555,20 +369,17 @@ fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>
|
|||
#[inline(always)]
|
||||
fn exposes_values<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
EExposes,
|
||||
> {
|
||||
and!(
|
||||
spaces_around_keyword(
|
||||
"exposes",
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
ExposesKeyword,
|
||||
EExposes::Exposes,
|
||||
EExposes::IndentExposes,
|
||||
EExposes::IndentListStart
|
||||
),
|
||||
collection_trailing_sep_e!(
|
||||
item: collection_trailing_sep_e!(
|
||||
word1(b'[', EExposes::ListStart),
|
||||
exposes_entry(EExposes::Identifier),
|
||||
word1(b',', EExposes::ListEnd),
|
||||
|
@ -576,52 +387,58 @@ fn exposes_values<'a>() -> impl Parser<
|
|||
EExposes::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn spaces_around_keyword<'a, E>(
|
||||
keyword: &'static str,
|
||||
fn spaces_around_keyword<'a, K: Keyword, E>(
|
||||
keyword_item: K,
|
||||
expectation: fn(Position) -> E,
|
||||
indent_problem1: fn(Position) -> E,
|
||||
indent_problem2: fn(Position) -> E,
|
||||
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), E>
|
||||
) -> impl Parser<'a, Spaces<'a, K>, E>
|
||||
where
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
and!(
|
||||
skip_second!(
|
||||
backtrackable(space0_e(indent_problem1)),
|
||||
crate::parser::keyword_e(keyword, expectation)
|
||||
map!(
|
||||
and!(
|
||||
skip_second!(
|
||||
backtrackable(space0_e(indent_problem1)),
|
||||
crate::parser::keyword_e(K::KEYWORD, expectation)
|
||||
),
|
||||
space0_e(indent_problem2)
|
||||
),
|
||||
space0_e(indent_problem2)
|
||||
|(before, after)| {
|
||||
Spaces {
|
||||
before,
|
||||
item: keyword_item,
|
||||
after,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn exposes_modules<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
|
||||
),
|
||||
KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
||||
EExposes,
|
||||
> {
|
||||
and!(
|
||||
spaces_around_keyword(
|
||||
"exposes",
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
ExposesKeyword,
|
||||
EExposes::Exposes,
|
||||
EExposes::IndentExposes,
|
||||
EExposes::IndentListStart
|
||||
),
|
||||
collection_trailing_sep_e!(
|
||||
item: collection_trailing_sep_e!(
|
||||
word1(b'[', EExposes::ListStart),
|
||||
exposes_module(EExposes::Identifier),
|
||||
word1(b',', EExposes::ListEnd),
|
||||
word1(b']', EExposes::ListEnd),
|
||||
EExposes::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn exposes_module<'a, F, E>(
|
||||
|
@ -638,82 +455,58 @@ where
|
|||
))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Packages<'a> {
|
||||
entries: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
|
||||
before_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||
after_packages_keyword: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
spaces_around_keyword(
|
||||
"packages",
|
||||
EPackages::Packages,
|
||||
EPackages::IndentPackages,
|
||||
EPackages::IndentListStart
|
||||
),
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'{', EPackages::ListStart),
|
||||
specialize(EPackages::PackageEntry, loc!(package_entry())),
|
||||
word1(b',', EPackages::ListEnd),
|
||||
word1(b'}', EPackages::ListEnd),
|
||||
EPackages::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
),
|
||||
|((before_packages_keyword, after_packages_keyword), entries): (
|
||||
(_, _),
|
||||
Collection<'a, _>
|
||||
)| {
|
||||
Packages {
|
||||
entries,
|
||||
before_packages_keyword,
|
||||
after_packages_keyword,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn generates<'a>() -> impl Parser<
|
||||
fn packages<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
UppercaseIdent<'a>,
|
||||
),
|
||||
EGenerates,
|
||||
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
|
||||
EPackages<'a>,
|
||||
> {
|
||||
and!(
|
||||
spaces_around_keyword(
|
||||
"generates",
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
PackagesKeyword,
|
||||
EPackages::Packages,
|
||||
EPackages::IndentPackages,
|
||||
EPackages::IndentListStart
|
||||
),
|
||||
item: collection_trailing_sep_e!(
|
||||
word1(b'{', EPackages::ListStart),
|
||||
specialize(EPackages::PackageEntry, loc!(package_entry())),
|
||||
word1(b',', EPackages::ListEnd),
|
||||
word1(b'}', EPackages::ListEnd),
|
||||
EPackages::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn generates<'a>(
|
||||
) -> impl Parser<'a, KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>, EGenerates> {
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
GeneratesKeyword,
|
||||
EGenerates::Generates,
|
||||
EGenerates::IndentGenerates,
|
||||
EGenerates::IndentTypeStart
|
||||
),
|
||||
specialize(|(), pos| EGenerates::Identifier(pos), uppercase())
|
||||
)
|
||||
item: specialize(|(), pos| EGenerates::Identifier(pos), uppercase())
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn generates_with<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
),
|
||||
KeywordItem<'a, WithKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
|
||||
EGeneratesWith,
|
||||
> {
|
||||
and!(
|
||||
spaces_around_keyword(
|
||||
"with",
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
WithKeyword,
|
||||
EGeneratesWith::With,
|
||||
EGeneratesWith::IndentWith,
|
||||
EGeneratesWith::IndentListStart
|
||||
),
|
||||
collection_trailing_sep_e!(
|
||||
item: collection_trailing_sep_e!(
|
||||
word1(b'[', EGeneratesWith::ListStart),
|
||||
exposes_entry(EGeneratesWith::Identifier),
|
||||
word1(b',', EGeneratesWith::ListEnd),
|
||||
|
@ -721,26 +514,23 @@ fn generates_with<'a>() -> impl Parser<
|
|||
EGeneratesWith::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn imports<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
|
||||
),
|
||||
KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
|
||||
EImports,
|
||||
> {
|
||||
and!(
|
||||
spaces_around_keyword(
|
||||
"imports",
|
||||
record!(KeywordItem {
|
||||
keyword: spaces_around_keyword(
|
||||
ImportsKeyword,
|
||||
EImports::Imports,
|
||||
EImports::IndentImports,
|
||||
EImports::IndentListStart
|
||||
),
|
||||
collection_trailing_sep_e!(
|
||||
item: collection_trailing_sep_e!(
|
||||
word1(b'[', EImports::ListStart),
|
||||
loc!(imports_entry()),
|
||||
word1(b',', EImports::ListEnd),
|
||||
|
@ -748,7 +538,8 @@ fn imports<'a>() -> impl Parser<
|
|||
EImports::IndentListEnd,
|
||||
Spaced::SpaceBefore
|
||||
)
|
||||
)
|
||||
})
|
||||
.trace("imports")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -810,15 +601,15 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
|
|||
and!(
|
||||
and!(
|
||||
// e.g. `pf.`
|
||||
maybe!(skip_second!(
|
||||
optional(backtrackable(skip_second!(
|
||||
shortname(),
|
||||
word1(b'.', EImports::ShorthandDot)
|
||||
)),
|
||||
))),
|
||||
// e.g. `Task`
|
||||
module_name_help(EImports::ModuleName)
|
||||
),
|
||||
// e.g. `.{ Task, after}`
|
||||
maybe!(skip_first!(
|
||||
optional(skip_first!(
|
||||
word1(b'.', EImports::ExposingDot),
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'{', EImports::SetStart),
|
||||
|
@ -842,4 +633,5 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
|
|||
Spaced::Item(entry)
|
||||
}
|
||||
)
|
||||
.trace("imports_entry")
|
||||
}
|
||||
|
|
|
@ -852,13 +852,14 @@ where
|
|||
self.message
|
||||
);
|
||||
|
||||
let previous_state = state.clone();
|
||||
INDENT.with(|i| *i.borrow_mut() += 1);
|
||||
let res = self.parser.parse(arena, state, min_indent);
|
||||
INDENT.with(|i| *i.borrow_mut() = cur_indent);
|
||||
|
||||
let (progress, value, state) = match &res {
|
||||
Ok((progress, result, state)) => (progress, Ok(result), state),
|
||||
Err((progress, error)) => (progress, Err(error), state),
|
||||
Err((progress, error)) => (progress, Err(error), &previous_state),
|
||||
};
|
||||
|
||||
println!(
|
||||
|
@ -1229,11 +1230,8 @@ where
|
|||
|
||||
match parser.parse(arena, state, min_indent) {
|
||||
Ok((progress, out1, state)) => Ok((progress, Some(out1), state)),
|
||||
Err((_, _)) => {
|
||||
// NOTE this will backtrack
|
||||
// TODO can we get rid of some of the potential backtracking?
|
||||
Ok((NoProgress, None, original_state))
|
||||
}
|
||||
Err((MadeProgress, e)) => Err((MadeProgress, e)),
|
||||
Err((NoProgress, _)) => Ok((NoProgress, None, original_state)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1435,6 +1433,25 @@ macro_rules! and {
|
|||
};
|
||||
}
|
||||
|
||||
/// Take as input something that looks like a struct literal where values are parsers
|
||||
/// and return a parser that runs each parser and returns a struct literal with the
|
||||
/// results.
|
||||
#[macro_export]
|
||||
macro_rules! record {
|
||||
($name:ident $(:: $name_ext:ident)* { $($field:ident: $parser:expr),* $(,)? }) => {
|
||||
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| {
|
||||
let mut state = state;
|
||||
let mut progress = NoProgress;
|
||||
$(
|
||||
let (new_progress, $field, new_state) = $parser.parse(arena, state, min_indent)?;
|
||||
state = new_state;
|
||||
progress = progress.or(new_progress);
|
||||
)*
|
||||
Ok((progress, $name $(:: $name_ext)* { $($field),* }, state))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Similar to `and`, but we modify the min_indent of the second parser to be
|
||||
/// 1 greater than the line_indent() at the start of the first parser.
|
||||
#[macro_export]
|
||||
|
@ -1507,21 +1524,6 @@ macro_rules! one_of {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! maybe {
|
||||
($p1:expr) => {
|
||||
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| {
|
||||
let original_state = state.clone();
|
||||
|
||||
match $p1.parse(arena, state, min_indent) {
|
||||
Ok((progress, value, state)) => Ok((progress, Some(value), state)),
|
||||
Err((MadeProgress, fail)) => Err((MadeProgress, fail)),
|
||||
Err((NoProgress, _)) => Ok((NoProgress, None, original_state)),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! one_of_with_error {
|
||||
($toerror:expr; $p1:expr) => {
|
||||
|
|
|
@ -349,23 +349,20 @@ fn record_type_field<'a>() -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'
|
|||
fn record_type<'a>(
|
||||
stop_at_surface_has: bool,
|
||||
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'{', ETypeRecord::Open),
|
||||
loc!(record_type_field()),
|
||||
word1(b',', ETypeRecord::End),
|
||||
word1(b'}', ETypeRecord::End),
|
||||
ETypeRecord::IndentEnd,
|
||||
AssignedField::SpaceBefore
|
||||
),
|
||||
optional(allocated(specialize_ref(
|
||||
ETypeRecord::Type,
|
||||
term(stop_at_surface_has)
|
||||
)))
|
||||
record!(TypeAnnotation::Record {
|
||||
fields: collection_trailing_sep_e!(
|
||||
word1(b'{', ETypeRecord::Open),
|
||||
loc!(record_type_field()),
|
||||
word1(b',', ETypeRecord::End),
|
||||
word1(b'}', ETypeRecord::End),
|
||||
ETypeRecord::IndentEnd,
|
||||
AssignedField::SpaceBefore
|
||||
),
|
||||
|(fields, ext)| { TypeAnnotation::Record { fields, ext } }
|
||||
)
|
||||
ext: optional(allocated(specialize_ref(
|
||||
ETypeRecord::Type,
|
||||
term(stop_at_surface_has)
|
||||
)))
|
||||
})
|
||||
.trace("type_annotation:record_type")
|
||||
}
|
||||
|
||||
|
@ -515,29 +512,26 @@ pub fn has_abilities<'a>() -> impl Parser<'a, Loc<HasAbilities<'a>>, EType<'a>>
|
|||
}
|
||||
|
||||
fn parse_has_ability<'a>() -> impl Parser<'a, HasAbility<'a>, EType<'a>> {
|
||||
increment_min_indent(map!(
|
||||
and!(
|
||||
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())),
|
||||
word1(b',', ETypeAbilityImpl::End),
|
||||
word1(b'}', ETypeAbilityImpl::End),
|
||||
ETypeAbilityImpl::IndentEnd,
|
||||
AssignedField::SpaceBefore
|
||||
)
|
||||
),
|
||||
HasImpls::HasImpls
|
||||
)),
|
||||
EType::TIndentEnd
|
||||
))
|
||||
),
|
||||
|(ability, impls): (_, Option<_>)| { HasAbility::HasAbility { ability, impls } }
|
||||
))
|
||||
increment_min_indent(record!(HasAbility::HasAbility {
|
||||
ability: loc!(specialize(EType::TApply, concrete_type())),
|
||||
impls: optional(backtrackable(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())),
|
||||
word1(b',', ETypeAbilityImpl::End),
|
||||
word1(b'}', ETypeAbilityImpl::End),
|
||||
ETypeAbilityImpl::IndentEnd,
|
||||
AssignedField::SpaceBefore
|
||||
)
|
||||
),
|
||||
HasImpls::HasImpls
|
||||
)),
|
||||
EType::TIndentEnd
|
||||
)))
|
||||
}))
|
||||
}
|
||||
|
||||
fn expression<'a>(
|
||||
|
@ -594,10 +588,10 @@ fn expression<'a>(
|
|||
}
|
||||
Err(err) => {
|
||||
if !is_trailing_comma_valid {
|
||||
let (_, comma, _) = optional(skip_first!(
|
||||
let (_, comma, _) = optional(backtrackable(skip_first!(
|
||||
space0_e(EType::TIndentStart),
|
||||
word1(b',', EType::TStart)
|
||||
))
|
||||
)))
|
||||
.trace("check trailing comma")
|
||||
.parse(arena, state.clone(), min_indent)?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue