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 inlinable_string::InlinableString; use roc_region::all::Loc; #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct PackageName<'a> { pub account: &'a str, pub pkg: &'a str, } #[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); impl<'a> Into<&'a str> for ModuleName<'a> { fn into(self) -> &'a str { self.0 } } impl<'a> Into for ModuleName<'a> { fn into(self) -> InlinableString { self.0.into() } } impl<'a> ModuleName<'a> { pub fn new(name: &'a str) -> Self { ModuleName(name) } pub fn as_str(&'a self) -> &'a str { self.0 } } #[derive(Clone, Debug, PartialEq)] pub struct InterfaceHeader<'a> { pub name: Loc>, pub exposes: Vec<'a, Loc>>, pub imports: Vec<'a, Loc>>, // 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)] pub enum To<'a> { ExistingPackage(&'a str), NewPackage(PackageOrPath<'a>), } #[derive(Clone, Debug, PartialEq)] pub struct AppHeader<'a> { pub name: Loc>, pub packages: Vec<'a, Loc>>, pub imports: Vec<'a, Loc>>, pub provides: Vec<'a, Loc>>, pub to: Loc>, // 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>, pub exposes: Vec<'a, Loc>>, pub packages: Vec<'a, (Loc<&'a str>, Loc>)>, pub imports: Vec<'a, Loc>>, // 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 after_imports: &'a [CommentOrNewline<'a>], } #[derive(Clone, Debug, PartialEq)] pub struct PlatformHeader<'a> { pub name: Loc>, pub requires: Vec<'a, Loc>>, pub exposes: Vec<'a, Loc>>>, pub packages: Vec<'a, Loc>>, pub imports: Vec<'a, Loc>>, pub provides: Vec<'a, Loc>>, pub effects: Effects<'a>, // Potential comments and newlines - these will typically all be empty. 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>], } /// e.g. fx.Effects #[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_shortname: &'a str, pub type_name: &'a str, pub entries: Vec<'a, Loc>>, } #[derive(Clone, Debug, PartialEq)] pub enum ExposesEntry<'a, T> { /// e.g. `Task` Exposed(T), // Spaces SpaceBefore(&'a ExposesEntry<'a, T>, &'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>>), /// e.g. `base.Task` or `base.Task.{ after }` or `base.{ Task.{ Task, after } }` Package( &'a str, ModuleName<'a>, Vec<'a, Loc>>, ), // 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>, }, // 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>, }, // 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)) } } pub 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") }