Finish introducing new header keywords

This commit is contained in:
Richard Feldman 2020-11-16 23:34:45 -05:00
parent ab1be6e394
commit a78434fd48
6 changed files with 404 additions and 210 deletions

View file

@ -1,8 +1,7 @@
use crate::spaces::{fmt_spaces, INDENT}; use crate::spaces::{fmt_spaces, INDENT};
use bumpalo::collections::{String, Vec}; use bumpalo::collections::{String, Vec};
use roc_parse::ast::{ use roc_parse::ast::Module;
AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, Module, PlatformHeader, use roc_parse::header::{AppHeader, ExposesEntry, ImportsEntry, InterfaceHeader, PlatformHeader};
};
use roc_region::all::Located; use roc_region::all::Located;
pub fn fmt_module<'a>(buf: &mut String<'a>, module: &'a Module<'a>) { pub fn fmt_module<'a>(buf: &mut String<'a>, module: &'a Module<'a>) {
@ -113,7 +112,7 @@ fn fmt_imports<'a>(
fn fmt_exposes<'a>( fn fmt_exposes<'a>(
buf: &mut String<'a>, buf: &mut String<'a>,
loc_entries: &'a Vec<'a, Located<ExposesEntry<'a>>>, loc_entries: &'a Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
indent: u16, indent: u16,
) { ) {
buf.push('['); buf.push('[');
@ -137,11 +136,11 @@ fn fmt_exposes<'a>(
buf.push(']'); buf.push(']');
} }
fn fmt_exposes_entry<'a>(buf: &mut String<'a>, entry: &'a ExposesEntry<'a>, indent: u16) { fn fmt_exposes_entry<'a>(buf: &mut String<'a>, entry: &'a ExposesEntry<'a, &'a str>, indent: u16) {
use roc_parse::ast::ExposesEntry::*; use roc_parse::header::ExposesEntry::*;
match entry { match entry {
Ident(ident) => buf.push_str(ident), Exposed(ident) => buf.push_str(ident),
SpaceBefore(sub_entry, spaces) => { SpaceBefore(sub_entry, spaces) => {
fmt_spaces(buf, spaces.iter(), indent); fmt_spaces(buf, spaces.iter(), indent);
@ -155,7 +154,7 @@ fn fmt_exposes_entry<'a>(buf: &mut String<'a>, entry: &'a ExposesEntry<'a>, inde
} }
fn fmt_imports_entry<'a>(buf: &mut String<'a>, entry: &'a ImportsEntry<'a>, indent: u16) { fn fmt_imports_entry<'a>(buf: &mut String<'a>, entry: &'a ImportsEntry<'a>, indent: u16) {
use roc_parse::ast::ImportsEntry::*; use roc_parse::header::ImportsEntry::*;
match entry { match entry {
Module(module, loc_exposes_entries) => { Module(module, loc_exposes_entries) => {
@ -176,6 +175,10 @@ fn fmt_imports_entry<'a>(buf: &mut String<'a>, entry: &'a ImportsEntry<'a>, inde
} }
} }
Package(_name, _entries) => {
todo!("TODO Format imported package");
}
SpaceBefore(sub_entry, spaces) => { SpaceBefore(sub_entry, spaces) => {
fmt_spaces(buf, spaces.iter(), indent); fmt_spaces(buf, spaces.iter(), indent);
fmt_imports_entry(buf, sub_entry, indent); fmt_imports_entry(buf, sub_entry, indent);

View file

@ -19,9 +19,8 @@ use roc_mono::ir::{
CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs, CapturedSymbols, ExternalSpecializations, PartialProc, PendingSpecialization, Proc, Procs,
}; };
use roc_mono::layout::{Layout, LayoutCache}; use roc_mono::layout::{Layout, LayoutCache};
use roc_parse::ast::{ use roc_parse::ast::{self, Attempting, TypeAnnotation};
self, Attempting, ExposesEntry, ImportsEntry, PlatformHeader, TypeAnnotation, TypedIdent, use roc_parse::header::{ExposesEntry, ImportsEntry, PlatformHeader, TypedIdent};
};
use roc_parse::module::module_defs; use roc_parse::module::module_defs;
use roc_parse::parser::{self, Fail, Parser}; use roc_parse::parser::{self, Fail, Parser};
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
@ -2087,7 +2086,7 @@ fn load_from_str<'a>(
fn send_header<'a>( fn send_header<'a>(
name: Located<roc_parse::header::ModuleName<'a>>, name: Located<roc_parse::header::ModuleName<'a>>,
filename: PathBuf, filename: PathBuf,
exposes: &'a [Located<ExposesEntry<'a>>], exposes: &'a [Located<ExposesEntry<'a, &'a str>>],
imports: &'a [Located<ImportsEntry<'a>>], imports: &'a [Located<ImportsEntry<'a>>],
parse_state: parser::State<'a>, parse_state: parser::State<'a>,
module_ids: Arc<Mutex<ModuleIds>>, module_ids: Arc<Mutex<ModuleIds>>,
@ -2737,7 +2736,7 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
} }
fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) { fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) {
use roc_parse::ast::ImportsEntry::*; use roc_parse::header::ImportsEntry::*;
match entry { match entry {
Module(module_name, exposes) => { Module(module_name, exposes) => {
@ -2757,11 +2756,11 @@ fn exposed_from_import(entry: &ImportsEntry<'_>) -> (ModuleName, Vec<Ident>) {
} }
} }
fn ident_from_exposed(entry: &ExposesEntry<'_>) -> Ident { fn ident_from_exposed(entry: &ExposesEntry<'_, &str>) -> Ident {
use roc_parse::ast::ExposesEntry::*; use roc_parse::header::ExposesEntry::*;
match entry { match entry {
Ident(ident) => (*ident).into(), Exposed(ident) => (*ident).into(),
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => ident_from_exposed(sub_entry), SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => ident_from_exposed(sub_entry),
} }
} }

View file

@ -1,7 +1,6 @@
use crate::header::{ModuleName, PackageName}; use crate::header::{AppHeader, ImportsEntry, InterfaceHeader, PlatformHeader, TypedIdent};
use crate::ident::Ident; use crate::ident::Ident;
use bumpalo::collections::String; use bumpalo::collections::String;
use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_module::operator::{BinOp, CalledVia, UnaryOp}; use roc_module::operator::{BinOp, CalledVia, UnaryOp};
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
@ -13,20 +12,6 @@ pub enum Module<'a> {
Platform { header: PlatformHeader<'a> }, Platform { header: PlatformHeader<'a> },
} }
#[derive(Clone, Debug, PartialEq)]
pub struct InterfaceHeader<'a> {
pub name: Loc<ModuleName<'a>>,
pub exposes: Vec<'a, Loc<ExposesEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
// Potential comments and newlines - these will typically all be empty.
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>],
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct WhenBranch<'a> { pub struct WhenBranch<'a> {
pub patterns: &'a [Loc<Pattern<'a>>], pub patterns: &'a [Loc<Pattern<'a>>],
@ -34,94 +19,6 @@ pub struct WhenBranch<'a> {
pub guard: Option<Loc<Expr<'a>>>, pub guard: Option<Loc<Expr<'a>>>,
} }
#[derive(Clone, Debug, PartialEq)]
pub struct AppHeader<'a> {
pub name: Loc<ModuleName<'a>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
// Potential comments and newlines - these will typically all be empty.
pub after_app_keyword: &'a [CommentOrNewline<'a>],
pub before_provides: &'a [CommentOrNewline<'a>],
pub after_provides: &'a [CommentOrNewline<'a>],
pub before_imports: &'a [CommentOrNewline<'a>],
pub after_imports: &'a [CommentOrNewline<'a>],
}
#[derive(Clone, Debug, PartialEq)]
pub struct PlatformHeader<'a> {
pub name: Loc<PackageName<'a>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a>>>,
pub requires: Vec<'a, Loc<TypedIdent<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
pub effects: Effects<'a>,
// Potential comments and newlines - these will typically all be empty.
pub after_platform_keyword: &'a [CommentOrNewline<'a>],
pub before_provides: &'a [CommentOrNewline<'a>],
pub after_provides: &'a [CommentOrNewline<'a>],
pub before_requires: &'a [CommentOrNewline<'a>],
pub after_requires: &'a [CommentOrNewline<'a>],
pub before_imports: &'a [CommentOrNewline<'a>],
pub after_imports: &'a [CommentOrNewline<'a>],
}
#[derive(Clone, Debug, PartialEq)]
pub struct Effects<'a> {
pub spaces_before_effects_keyword: &'a [CommentOrNewline<'a>],
pub spaces_after_effects_keyword: &'a [CommentOrNewline<'a>],
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
pub type_name: &'a str,
pub entries: Vec<'a, Loc<TypedIdent<'a>>>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum TypedIdent<'a> {
/// e.g.
///
/// printLine : Str -> Effect {}
Entry {
ident: Loc<&'a str>,
spaces_before_colon: &'a [CommentOrNewline<'a>],
ann: Loc<TypeAnnotation<'a>>,
},
// Spaces
SpaceBefore(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
}
#[derive(Clone, Debug, PartialEq)]
pub enum ExposesEntry<'a> {
/// e.g. `Task`
Ident(&'a str),
// Spaces
SpaceBefore(&'a ExposesEntry<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a ExposesEntry<'a>, &'a [CommentOrNewline<'a>]),
}
#[derive(Clone, Debug, PartialEq)]
pub enum ImportsEntry<'a> {
/// e.g. `Task` or `Task.{ Task, after }`
Module(ModuleName<'a>, Vec<'a, Loc<ExposesEntry<'a>>>),
// Spaces
SpaceBefore(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
}
impl<'a> ExposesEntry<'a> {
pub fn as_str(&'a self) -> &'a str {
use ExposesEntry::*;
match self {
Ident(string) => string,
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => sub_entry.as_str(),
}
}
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct WhenPattern<'a> { pub struct WhenPattern<'a> {
pub pattern: Loc<Pattern<'a>>, pub pattern: Loc<Pattern<'a>>,
@ -633,15 +530,6 @@ impl<'a> Spaceable<'a> for TypeAnnotation<'a> {
} }
} }
impl<'a> Spaceable<'a> for ExposesEntry<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
ExposesEntry::SpaceBefore(self, spaces)
}
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
ExposesEntry::SpaceAfter(self, spaces)
}
}
impl<'a> Spaceable<'a> for ImportsEntry<'a> { impl<'a> Spaceable<'a> for ImportsEntry<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self { fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
ImportsEntry::SpaceBefore(self, spaces) ImportsEntry::SpaceBefore(self, spaces)

View file

@ -1,15 +1,43 @@
use crate::ast::CommentOrNewline; use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
use crate::blankspace::space0;
use crate::ident::lowercase_ident;
use crate::module::package_name;
use crate::parser::{ascii_char, optional, Either, Parser};
use crate::string_literal;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use inlinable_string::InlinableString; use inlinable_string::InlinableString;
use roc_region::all::Loc; use roc_region::all::Loc;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct PackageName<'a> { pub struct PackageName<'a> {
pub account: &'a str, pub account: &'a str,
pub pkg: &'a str, pub pkg: &'a str,
} }
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub enum Version<'a> {
Exact(&'a str),
Range {
min: &'a str,
min_comparison: VersionComparison,
max: &'a str,
max_comparison: VersionComparison,
},
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum VersionComparison {
AllowsEqual,
DisallowsEqual,
}
#[derive(Clone, PartialEq, Debug)]
pub enum PackageOrPath<'a> {
Package(PackageName<'a>, Version<'a>),
Path(StrLiteral<'a>),
}
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct ModuleName<'a>(&'a str); pub struct ModuleName<'a>(&'a str);
impl<'a> Into<&'a str> for ModuleName<'a> { impl<'a> Into<&'a str> for ModuleName<'a> {
@ -34,15 +62,14 @@ impl<'a> ModuleName<'a> {
} }
} }
// TODO is this all duplicated from parse::ast?
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct InterfaceHeader<'a> { pub struct InterfaceHeader<'a> {
pub name: Loc<ModuleName<'a>>, pub name: Loc<ModuleName<'a>>,
pub exposes: Vec<'a, Loc<Exposes<'a>>>, pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub imports: Vec<'a, (ModuleName<'a>, Vec<'a, Loc<Imports<'a>>>)>, pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
// Potential comments and newlines - these will typically all be empty. // Potential comments and newlines - these will typically all be empty.
pub after_interface: &'a [CommentOrNewline<'a>], pub after_interface_keyword: &'a [CommentOrNewline<'a>],
pub before_exposes: &'a [CommentOrNewline<'a>], pub before_exposes: &'a [CommentOrNewline<'a>],
pub after_exposes: &'a [CommentOrNewline<'a>], pub after_exposes: &'a [CommentOrNewline<'a>],
pub before_imports: &'a [CommentOrNewline<'a>], pub before_imports: &'a [CommentOrNewline<'a>],
@ -51,29 +78,202 @@ pub struct InterfaceHeader<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct AppHeader<'a> { pub struct AppHeader<'a> {
pub imports: Vec<'a, (ModuleName<'a>, Loc<Imports<'a>>)>, pub name: Loc<StrLiteral<'a>>,
pub packages: Vec<'a, Loc<PackageEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub to: Loc<&'a str>,
// Potential comments and newlines - these will typically all be empty. // Potential comments and newlines - these will typically all be empty.
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>],
}
#[derive(Clone, Debug, PartialEq)]
pub struct PackageHeader<'a> {
pub name: Loc<PackageName<'a>>,
pub exposes: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageOrPath<'a>>)>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
// Potential comments and newlines - these will typically all be empty.
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 before_imports: &'a [CommentOrNewline<'a>],
pub after_imports: &'a [CommentOrNewline<'a>], pub after_imports: &'a [CommentOrNewline<'a>],
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Exposes<'a> { pub struct PlatformHeader<'a> {
/// e.g. `Task` pub name: Loc<PackageName<'a>>,
Ident(&'a str), pub requires: Vec<'a, Loc<TypedIdent<'a>>>,
pub exposes: Vec<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
pub packages: Vec<'a, Loc<PackageEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
pub provides: Vec<'a, Loc<ExposesEntry<'a, &'a str>>>,
pub effects: Effects<'a>,
// Spaces // Potential comments and newlines - these will typically all be empty.
SpaceBefore(&'a Exposes<'a>, &'a [CommentOrNewline<'a>]), pub after_platform_keyword: &'a [CommentOrNewline<'a>],
SpaceAfter(&'a Exposes<'a>, &'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>],
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Imports<'a> { pub struct Effects<'a> {
/// e.g. `Task` or `Task.{ Task, after }` pub spaces_before_effects_keyword: &'a [CommentOrNewline<'a>],
Ident(&'a str, Vec<'a, &'a str>), pub spaces_after_effects_keyword: &'a [CommentOrNewline<'a>],
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
pub type_name: &'a str,
pub entries: Vec<'a, Loc<TypedIdent<'a>>>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ExposesEntry<'a, T> {
/// e.g. `Task`
Exposed(T),
// Spaces // Spaces
SpaceBefore(&'a Imports<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a Imports<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a ExposesEntry<'a, T>, &'a [CommentOrNewline<'a>]),
}
impl<'a, T> Spaceable<'a> for ExposesEntry<'a, T> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
ExposesEntry::SpaceBefore(self, spaces)
}
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
ExposesEntry::SpaceAfter(self, spaces)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ImportsEntry<'a> {
/// e.g. `Task` or `Task.{ Task, after }`
Module(ModuleName<'a>, Vec<'a, Loc<ExposesEntry<'a, &'a str>>>),
/// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }`
Package(&'a str, Vec<'a, Loc<&'a ImportsEntry<'a>>>),
// Spaces
SpaceBefore(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a ImportsEntry<'a>, &'a [CommentOrNewline<'a>]),
}
impl<'a> ExposesEntry<'a, &'a str> {
pub fn as_str(&'a self) -> &'a str {
use ExposesEntry::*;
match self {
Exposed(string) => string,
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => sub_entry.as_str(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum TypedIdent<'a> {
/// e.g.
///
/// printLine : Str -> Effect {}
Entry {
ident: Loc<&'a str>,
spaces_before_colon: &'a [CommentOrNewline<'a>],
ann: Loc<TypeAnnotation<'a>>,
},
// Spaces
SpaceBefore(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
}
#[derive(Clone, Debug, PartialEq)]
pub enum PackageEntry<'a> {
Entry {
shorthand: &'a str,
spaces_after_shorthand: &'a [CommentOrNewline<'a>],
package_or_path: Loc<PackageOrPath<'a>>,
},
// Spaces
SpaceBefore(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a PackageEntry<'a>, &'a [CommentOrNewline<'a>]),
}
impl<'a> Spaceable<'a> for PackageEntry<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
PackageEntry::SpaceBefore(self, spaces)
}
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
PackageEntry::SpaceAfter(self, spaces)
}
}
pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>> {
move |arena, state| {
// 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) = optional(and!(
skip_second!(lowercase_ident(), ascii_char(b':')),
space0(1)
))
.parse(arena, state)?;
let (package_or_path, state) = loc!(package_or_path()).parse(arena, state)?;
let entry = match opt_shorthand {
Some((shorthand, spaces_after_shorthand)) => PackageEntry::Entry {
shorthand,
spaces_after_shorthand,
package_or_path,
},
None => PackageEntry::Entry {
shorthand: "",
spaces_after_shorthand: &[],
package_or_path,
},
};
Ok((entry, state))
}
}
fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>> {
map!(
either!(
string_literal::parse(),
and!(
package_name(),
skip_first!(one_or_more!(ascii_char(b' ')), package_version())
)
),
|answer| {
match answer {
Either::First(str_literal) => PackageOrPath::Path(str_literal),
Either::Second((name, version)) => PackageOrPath::Package(name, version),
}
}
)
}
fn package_version<'a>() -> impl Parser<'a, Version<'a>> {
move |_, _| todo!("TODO parse package version")
} }

View file

@ -1,15 +1,16 @@
use crate::ast::{ use crate::ast::{Attempting, CommentOrNewline, Def, Module};
AppHeader, Attempting, CommentOrNewline, Def, Effects, ExposesEntry, ImportsEntry,
InterfaceHeader, Module, PlatformHeader, TypedIdent,
};
use crate::blankspace::{space0, space0_around, space0_before, space1}; use crate::blankspace::{space0, space0_around, space0_before, space1};
use crate::expr::def; use crate::expr::def;
use crate::header::{ModuleName, PackageName}; use crate::header::{
package_entry, AppHeader, Effects, ExposesEntry, ImportsEntry, InterfaceHeader, ModuleName,
PackageEntry, PackageName, PlatformHeader, TypedIdent,
};
use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident}; use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident};
use crate::parser::{ use crate::parser::{
self, ascii_char, ascii_string, loc, optional, peek_utf8_char, peek_utf8_char_at, unexpected, self, ascii_char, ascii_string, loc, optional, peek_utf8_char, peek_utf8_char_at, unexpected,
unexpected_eof, ParseResult, Parser, State, unexpected_eof, ParseResult, Parser, State,
}; };
use crate::string_literal;
use crate::type_annotation; use crate::type_annotation;
use bumpalo::collections::{String, Vec}; use bumpalo::collections::{String, Vec};
use bumpalo::Bump; use bumpalo::Bump;
@ -44,7 +45,7 @@ pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
ascii_string("interface"), ascii_string("interface"),
and!(space1(1), loc!(module_name())) and!(space1(1), loc!(module_name()))
), ),
and!(exposes(), imports()) and!(exposes_values(), imports())
), ),
|( |(
(after_interface_keyword, name), (after_interface_keyword, name),
@ -176,25 +177,31 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>> {
fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>> { fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>> {
parser::map( parser::map(
and!( and!(
skip_first!(ascii_string("app"), and!(space1(1), loc!(module_name()))), skip_first!(
and!(provides(), imports()) ascii_string("app"),
and!(space1(1), loc!(string_literal::parse()))
),
and!(packages(), and!(imports(), provides_to()))
), ),
|( |(
(after_app_keyword, name), (after_app_keyword, name),
( (packages, (((before_imports, after_imports), imports), provides)),
((before_provides, after_provides), provides),
((before_imports, after_imports), imports),
),
)| { )| {
AppHeader { AppHeader {
name, name,
provides, packages: packages.entries,
imports, imports,
provides: provides.entries,
to: provides.to,
after_app_keyword, after_app_keyword,
before_provides, before_packages: packages.before_packages_keyword,
after_provides, after_packages: packages.after_packages_keyword,
before_imports, before_imports,
after_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,
} }
}, },
) )
@ -208,31 +215,49 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
ascii_string("platform"), ascii_string("platform"),
and!(space1(1), loc!(package_name())) and!(space1(1), loc!(package_name()))
), ),
and!(provides(), and!(requires(), and!(imports(), effects()))) and!(
and!(
and!(requires(), and!(exposes_modules(), packages())),
and!(imports(), provides_without_to())
),
effects()
)
), ),
|( |(
(after_platform_keyword, name), (after_platform_keyword, name),
( (
((before_provides, after_provides), provides),
( (
((before_requires, after_requires), requires), (
(((before_imports, after_imports), imports), effects), ((before_requires, after_requires), requires),
(((before_exposes, after_exposes), exposes), packages),
),
(
((before_imports, after_imports), imports),
((before_provides, after_provides), provides),
),
), ),
effects,
), ),
)| { )| {
PlatformHeader { PlatformHeader {
name, name,
provides,
requires, requires,
exposes,
packages: packages.entries,
imports, imports,
provides,
effects, effects,
after_platform_keyword, after_platform_keyword,
before_provides,
after_provides,
before_requires, before_requires,
after_requires, after_requires,
before_exposes,
after_exposes,
before_packages: packages.before_packages_keyword,
after_packages: packages.after_packages_keyword,
before_imports, before_imports,
after_imports, after_imports,
before_provides,
after_provides,
} }
}, },
) )
@ -243,39 +268,69 @@ pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>> {
zero_or_more!(space0_around(loc(def(0)), 0)) zero_or_more!(space0_around(loc(def(0)), 0))
} }
struct ProvidesTo<'a> {
entries: Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
to: Located<&'a str>,
before_provides_keyword: &'a [CommentOrNewline<'a>],
after_provides_keyword: &'a [CommentOrNewline<'a>],
before_to_keyword: &'a [CommentOrNewline<'a>],
after_to_keyword: &'a [CommentOrNewline<'a>],
}
#[inline(always)] #[inline(always)]
fn provides<'a>() -> impl Parser< fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>> {
map!(
and!(
and!(skip_second!(space1(1), ascii_string("provides")), space1(1)),
and!(
collection!(
ascii_char(b'['),
loc!(map!(unqualified_ident(), ExposesEntry::Exposed)),
ascii_char(b','),
ascii_char(b']'),
1
),
and!(
space1(1),
skip_first!(ascii_string("to"), and!(space1(1), loc!(lowercase_ident())))
)
)
),
|(
(before_provides_keyword, after_provides_keyword),
(entries, (before_to_keyword, (after_to_keyword, to))),
)| {
ProvidesTo {
entries,
to,
before_provides_keyword,
after_provides_keyword,
before_to_keyword,
after_to_keyword,
}
}
)
}
#[inline(always)]
fn provides_without_to<'a>() -> impl Parser<
'a, 'a,
( (
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<ExposesEntry<'a>>>, Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
), ),
> { > {
map!( and!(
and!( and!(skip_second!(space1(1), ascii_string("provides")), space1(1)),
and!(skip_second!(space1(1), ascii_string("provides")), space1(1)), collection!(
collection!( ascii_char(b'['),
ascii_char(b'['), loc!(map!(unqualified_ident(), ExposesEntry::Exposed)),
loc!(exposes_entry()), ascii_char(b','),
ascii_char(b','), ascii_char(b']'),
ascii_char(b']'), 1
1 )
) )
)
,
|((before_provides_keyword, after_provides_keyword), provides_entries| {
Provides {
provides_entries,
to,
before_provides_keyword,
after_provides_keyword,
before_to_keyword,
after_to_keyword,
}
}
)
}
}
} }
#[inline(always)] #[inline(always)]
@ -299,18 +354,18 @@ fn requires<'a>() -> impl Parser<
} }
#[inline(always)] #[inline(always)]
fn exposes<'a>() -> impl Parser< fn exposes_values<'a>() -> impl Parser<
'a, 'a,
( (
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<ExposesEntry<'a>>>, Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
), ),
> { > {
and!( and!(
and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)), and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)),
collection!( collection!(
ascii_char(b'['), ascii_char(b'['),
loc!(exposes_entry()), loc!(map!(unqualified_ident(), ExposesEntry::Exposed)),
ascii_char(b','), ascii_char(b','),
ascii_char(b']'), ascii_char(b']'),
1 1
@ -318,6 +373,56 @@ fn exposes<'a>() -> impl Parser<
) )
} }
#[inline(always)]
fn exposes_modules<'a>() -> impl Parser<
'a,
(
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
),
> {
and!(
and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)),
collection!(
ascii_char(b'['),
loc!(map!(module_name(), ExposesEntry::Exposed)),
ascii_char(b','),
ascii_char(b']'),
1
)
)
}
struct Packages<'a> {
entries: Vec<'a, Located<PackageEntry<'a>>>,
before_packages_keyword: &'a [CommentOrNewline<'a>],
after_packages_keyword: &'a [CommentOrNewline<'a>],
}
#[inline(always)]
fn packages<'a>() -> impl Parser<'a, Packages<'a>> {
map!(
and!(
and!(skip_second!(space1(1), ascii_string("packages")), space1(1)),
collection!(
ascii_char(b'{'),
loc!(package_entry()),
ascii_char(b','),
ascii_char(b'}'),
1
)
),
|((before_packages_keyword, after_packages_keyword), entries)| {
Packages {
entries,
before_packages_keyword,
after_packages_keyword,
}
}
)
}
#[inline(always)] #[inline(always)]
fn imports<'a>() -> impl Parser< fn imports<'a>() -> impl Parser<
'a, 'a,
@ -397,11 +502,6 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>> {
} }
} }
#[inline(always)]
fn exposes_entry<'a>() -> impl Parser<'a, ExposesEntry<'a>> {
map!(unqualified_ident(), ExposesEntry::Ident)
}
#[inline(always)] #[inline(always)]
fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> { fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> {
map_with_arena!( map_with_arena!(
@ -413,7 +513,7 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> {
ascii_char(b'.'), ascii_char(b'.'),
collection!( collection!(
ascii_char(b'{'), ascii_char(b'{'),
loc!(exposes_entry()), loc!(map!(unqualified_ident(), ExposesEntry::Exposed)),
ascii_char(b','), ascii_char(b','),
ascii_char(b'}'), ascii_char(b'}'),
1 1
@ -423,7 +523,7 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> {
|arena, |arena,
(module_name, opt_values): ( (module_name, opt_values): (
ModuleName<'a>, ModuleName<'a>,
Option<Vec<'a, Located<ExposesEntry<'a>>>> Option<Vec<'a, Located<ExposesEntry<'a, &'a str>>>>
)| { )| {
let exposed_values = opt_values.unwrap_or_else(|| Vec::new_in(arena)); let exposed_values = opt_values.unwrap_or_else(|| Vec::new_in(arena));

View file

@ -1037,7 +1037,11 @@ macro_rules! one_or_more {
} }
} }
} }
Err((_, new_state)) => Err(unexpected_eof(0, new_state.attempting, new_state)), Err((_, new_state)) => Err($crate::parser::unexpected_eof(
0,
new_state.attempting,
new_state,
)),
} }
} }
}; };
@ -1083,9 +1087,9 @@ macro_rules! either {
let original_attempting = state.attempting; let original_attempting = state.attempting;
match $p1.parse(arena, state) { match $p1.parse(arena, state) {
Ok((output, state)) => Ok((Either::First(output), state)), Ok((output, state)) => Ok(($crate::parser::Either::First(output), state)),
Err((_, state)) => match $p2.parse(arena, state) { Err((_, state)) => match $p2.parse(arena, state) {
Ok((output, state)) => Ok((Either::Second(output), state)), Ok((output, state)) => Ok(($crate::parser::Either::Second(output), state)),
Err((fail, state)) => Err(( Err((fail, state)) => Err((
Fail { Fail {
attempting: original_attempting, attempting: original_attempting,