New app header syntax

Implements the new app header syntax as discussed in Zulip [1].

    app [main] {
	cli: platform "../platform/main.roc",
	json: "../json/main.roc"
    }

Old headers still parse and are automatically upgraded to the new
syntax by the formatter.

[1] 418444862
This commit is contained in:
Agus Zubiaga 2024-03-04 19:01:16 -03:00
parent 057a18573a
commit 8dedd9f03c
No known key found for this signature in database
90 changed files with 1044 additions and 1056 deletions

View file

@ -9,8 +9,9 @@ use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces};
use roc_parse::header::{
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader,
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformRequires,
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformKeyword,
PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
WithKeyword,
};
use roc_parse::ident::UppercaseIdent;
use roc_region::all::Loc;
@ -75,6 +76,7 @@ keywords! {
RequiresKeyword,
ProvidesKeyword,
ToKeyword,
PlatformKeyword,
}
impl<V: Formattable> Formattable for Option<V> {
@ -206,20 +208,24 @@ pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) {
pub fn fmt_app_header<'a>(buf: &mut Buf, header: &'a AppHeader<'a>) {
buf.indent(0);
buf.push_str("app");
let indent = INDENT;
fmt_default_spaces(buf, header.before_name, indent);
fmt_str_literal(buf, header.name.value, indent);
if header.before_provides.iter().all(|c| c.is_newline()) {
buf.spaces(1);
fmt_exposes(buf, header.provides, 0);
} else {
fmt_default_spaces(buf, header.before_provides, indent);
fmt_exposes(buf, header.provides, indent);
}
if let Some(packages) = &header.packages {
packages.keyword.format(buf, indent);
fmt_packages(buf, packages.item, indent);
if header.before_packages.iter().all(|c| c.is_newline()) {
buf.spaces(1);
fmt_packages(buf, header.packages.value, 0);
} else {
fmt_default_spaces(buf, header.before_packages, indent);
fmt_packages(buf, header.packages.value, indent);
}
if let Some(imports) = &header.imports {
imports.keyword.format(buf, indent);
fmt_imports(buf, imports.item, indent);
}
header.provides.format(buf, indent);
}
pub fn fmt_package_header<'a>(buf: &mut Buf, header: &'a PackageHeader<'a>) {
@ -464,6 +470,15 @@ fn fmt_packages_entry(buf: &mut Buf, entry: &PackageEntry, indent: u16) {
buf.push_str(entry.shorthand);
buf.push(':');
fmt_default_spaces(buf, entry.spaces_after_shorthand, indent);
let indent = indent + INDENT;
if let Some(spaces_after) = entry.platform_marker {
buf.indent(indent);
buf.push_str(roc_parse::keyword::PLATFORM);
fmt_default_spaces(buf, spaces_after, indent);
}
fmt_package_name(buf, entry.package_name.value, indent);
}

View file

@ -289,11 +289,14 @@ impl<'a> RemoveSpaces<'a> for Module<'a> {
interface_imports: header.interface_imports.remove_spaces(arena),
}),
Header::App(header) => Header::App(AppHeader {
before_name: &[],
name: header.name.remove_spaces(arena),
packages: header.packages.remove_spaces(arena),
imports: header.imports.remove_spaces(arena),
before_provides: &[],
provides: header.provides.remove_spaces(arena),
before_packages: &[],
packages: header.packages.remove_spaces(arena),
old_imports: header.old_imports.remove_spaces(arena),
old_provides_to_new_package: header
.old_provides_to_new_package
.remove_spaces(arena),
}),
Header::Package(header) => Header::Package(PackageHeader {
before_name: &[],
@ -405,6 +408,10 @@ impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
PackageEntry {
shorthand: self.shorthand,
spaces_after_shorthand: &[],
platform_marker: match self.platform_marker {
Some(_) => Some(&[]),
None => None,
},
package_name: self.package_name.remove_spaces(arena),
}
}

View file

@ -0,0 +1,8 @@
platform "test-platform"
requires {} { main : * }
exposes []
packages {}
provides [mainForHost]
mainForHost : {} -> {}
mainForHost = \{} -> {}

View file

@ -948,6 +948,18 @@ pub enum LoadingProblem<'a> {
},
ParsingFailed(FileError<'a, SyntaxError<'a>>),
UnexpectedHeader(String),
MultiplePlatformPackages {
filename: PathBuf,
module_id: ModuleId,
source: &'a [u8],
region: Region,
},
NoPlatformPackage {
filename: PathBuf,
module_id: ModuleId,
source: &'a [u8],
region: Region,
},
ErrJoiningWorkerThreads,
TriedToImportAppModule,
@ -1667,6 +1679,34 @@ pub fn report_loading_problem(
LoadingProblem::FileProblem { filename, error } => {
to_file_problem_report_string(filename, error)
}
LoadingProblem::NoPlatformPackage {
filename,
module_id,
source,
region,
} => to_no_platform_package_report(
module_ids,
IdentIds::exposed_builtins(0),
module_id,
filename,
region,
source,
render,
),
LoadingProblem::MultiplePlatformPackages {
filename,
module_id,
source,
region,
} => to_multiple_platform_packages_report(
module_ids,
IdentIds::exposed_builtins(0),
module_id,
filename,
region,
source,
render,
),
err => todo!("Loading error: {:?}", err),
}
}
@ -3838,24 +3878,60 @@ fn parse_header<'a>(
let mut app_file_dir = filename.clone();
app_file_dir.pop();
let packages = if let Some(packages) = header.packages {
unspace(arena, packages.item.items)
} else {
&[]
let packages = unspace(arena, header.packages.value.items);
let mut platform_shorthand = None;
for package in packages.iter() {
if package.value.platform_marker.is_some() {
if platform_shorthand.is_some() {
let mut module_ids = (*module_ids).lock();
let module_id = module_ids.get_or_insert(
arena.alloc(PQModuleName::Unqualified(ModuleName::APP.into())),
);
return Err(LoadingProblem::MultiplePlatformPackages {
module_id,
filename,
source: src_bytes,
region: header.packages.region,
});
}
platform_shorthand = Some(package.value.shorthand);
}
}
let to_platform = match platform_shorthand {
Some(shorthand) => To::ExistingPackage(shorthand),
None => {
if let Some(new_pkg) = header.old_provides_to_new_package {
// This is a piece of old syntax that's used by the REPL and a number of
// tests where an ephemeral platform package is specified that is never
// loaded.
// We can remove this when we have upgraded everything to the new syntax.
To::NewPackage(new_pkg)
} else {
let mut module_ids = (*module_ids).lock();
let module_id = module_ids.get_or_insert(
arena.alloc(PQModuleName::Unqualified(ModuleName::APP.into())),
);
return Err(LoadingProblem::NoPlatformPackage {
module_id,
filename,
region: header.packages.region,
source: src_bytes,
});
}
}
};
let mut provides = bumpalo::collections::Vec::new_in(arena);
provides.extend(unspace(arena, header.provides.entries.items));
if let Some(provided_types) = header.provides.types {
for provided_type in unspace(arena, provided_types.items) {
let string: &str = provided_type.value.into();
let exposed_name = ExposedName::new(string);
provides.push(Loc::at(provided_type.region, exposed_name));
}
}
provides.extend(unspace(arena, header.provides.items));
let info = HeaderInfo {
filename,
@ -3864,11 +3940,10 @@ fn parse_header<'a>(
packages,
header_type: HeaderType::App {
provides: provides.into_bump_slice(),
output_name: header.name.value,
to_platform: header.provides.to.value,
to_platform,
},
module_comments: comments,
header_imports: header.imports,
header_imports: header.old_imports,
};
let (module_id, _, resolved_header) =
@ -3892,28 +3967,11 @@ fn parse_header<'a>(
filename,
);
// Look at the app module's `to` keyword to determine which package was the platform.
match header.provides.to.value {
To::ExistingPackage(shorthand) => {
if !packages
.iter()
.any(|loc_package_entry| loc_package_entry.value.shorthand == shorthand)
{
todo!("Gracefully handle platform shorthand after `to` that didn't map to a shorthand specified in `packages`");
}
Ok(HeaderOutput {
module_id,
msg: Msg::Many(messages),
opt_platform_shorthand: Some(shorthand),
})
}
To::NewPackage(_package_name) => Ok(HeaderOutput {
module_id,
msg: Msg::Many(messages),
opt_platform_shorthand: None,
}),
}
Ok(HeaderOutput {
module_id,
msg: Msg::Many(messages),
opt_platform_shorthand: platform_shorthand,
})
}
Ok((
ast::Module {
@ -6277,6 +6335,96 @@ fn to_incorrect_module_name_report<'a>(
buf
}
fn to_no_platform_package_report(
module_ids: ModuleIds,
all_ident_ids: IdentIdsByModule,
module_id: ModuleId,
filename: PathBuf,
region: Region,
src: &[u8],
render: RenderTarget,
) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
// SAFETY: if the module was not UTF-8, that would be reported as a parsing problem, rather
// than an incorrect module name problem (the latter can happen only after parsing).
let src = unsafe { from_utf8_unchecked(src) };
let src_lines = src.lines().collect::<Vec<_>>();
let lines = LineInfo::new(src);
let interns = Interns {
module_ids,
all_ident_ids,
};
let alloc = RocDocAllocator::new(&src_lines, module_id, &interns);
let doc = alloc.stack([
alloc.reflow("This app does not specify a platform:"),
alloc.region(lines.convert_region(region)),
alloc.reflow("Make sure you have exactly one package specified as `platform`:"),
alloc
.parser_suggestion(" app [main] {\n pf: platform \"…path or URL to platform…\"\n ^^^^^^^^\n }"),
alloc.reflow("Tip: See an example in the tutorial:\n\n<https://www.roc-lang.org/tutorial#building-an-application>"),
]);
let report = Report {
filename,
doc,
title: "UNSPECIFIED PLATFORM".to_string(),
severity: Severity::RuntimeError,
};
let mut buf = String::new();
let palette = DEFAULT_PALETTE;
report.render(render, &mut buf, &alloc, &palette);
buf
}
fn to_multiple_platform_packages_report(
module_ids: ModuleIds,
all_ident_ids: IdentIdsByModule,
module_id: ModuleId,
filename: PathBuf,
region: Region,
src: &[u8],
render: RenderTarget,
) -> String {
use roc_reporting::report::{Report, RocDocAllocator, DEFAULT_PALETTE};
use ven_pretty::DocAllocator;
// SAFETY: if the module was not UTF-8, that would be reported as a parsing problem, rather
// than an incorrect module name problem (the latter can happen only after parsing).
let src = unsafe { from_utf8_unchecked(src) };
let src_lines = src.lines().collect::<Vec<_>>();
let lines = LineInfo::new(src);
let interns = Interns {
module_ids,
all_ident_ids,
};
let alloc = RocDocAllocator::new(&src_lines, module_id, &interns);
let doc = alloc.stack([
alloc.reflow("This app specifies multiple packages as `platform`:"),
alloc.region(lines.convert_region(region)),
alloc.reflow("Roc apps must specify exactly one platform."),
]);
let report = Report {
filename,
doc,
title: "MULTIPLE PLATFORMS".to_string(),
severity: Severity::RuntimeError,
};
let mut buf = String::new();
let palette = DEFAULT_PALETTE;
report.render(render, &mut buf, &alloc, &palette);
buf
}
fn to_parse_problem_report<'a>(
problem: FileError<'a, SyntaxError<'a>>,
mut module_ids: ModuleIds,
@ -6342,7 +6490,7 @@ fn report_cannot_run(
alloc.reflow("I could not find a platform based on your input file."),
alloc.reflow(r"Does the module header have an entry that looks like this?"),
alloc
.parser_suggestion("packages { blah: \"…path or URL to platform…\" }")
.parser_suggestion("app [main] { pf: platform \"…path or URL to platform…\" }")
.indent(4),
alloc.reflow("Tip: The following part of the tutorial has an example of specifying a platform:\n\n<https://www.roc-lang.org/tutorial#building-an-application>"),
]);

View file

@ -272,6 +272,7 @@ fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&st
let interns = &loaded_module.interns;
let declarations = loaded_module.declarations_by_id.remove(&home).unwrap();
for index in 0..declarations.len() {
use roc_can::expr::DeclarationTag::*;

View file

@ -48,6 +48,18 @@ impl<'a, T> Spaced<'a, T> {
Spaced::SpaceBefore(next, _spaces) | Spaced::SpaceAfter(next, _spaces) => next.item(),
}
}
pub fn map<U, F: Fn(&T) -> U>(&self, arena: &'a Bump, f: F) -> Spaced<'a, U> {
match self {
Spaced::Item(item) => Spaced::Item(f(item)),
Spaced::SpaceBefore(next, spaces) => {
Spaced::SpaceBefore(arena.alloc(next.map(arena, f)), spaces)
}
Spaced::SpaceAfter(next, spaces) => {
Spaced::SpaceAfter(arena.alloc(next.map(arena, f)), spaces)
}
}
}
}
impl<'a, T: Debug> Debug for Spaced<'a, T> {
@ -108,7 +120,16 @@ impl<'a> Module<'a> {
}),
Self::header_imports_to_defs(arena, header.interface_imports),
),
header => (header, Defs::default()),
Header::App(header) => (
Header::App(AppHeader {
old_imports: None,
..header
}),
Self::header_imports_to_defs(arena, header.old_imports),
),
Header::Package(_) | Header::Platform(_) | Header::Hosted(_) => {
(self.header, Defs::default())
}
};
(Module { header, ..self }, defs)
@ -174,6 +195,7 @@ impl<'a> Module<'a> {
},
if index == len - 1 {
let mut after = spaced.after.to_vec();
after.extend_from_slice(imports.item.final_comments());
after.push(CommentOrNewline::Newline);
after.push(CommentOrNewline::Newline);
arena.alloc(after)

View file

@ -38,7 +38,6 @@ impl<'a> HeaderType<'a> {
#[derive(Debug)]
pub enum HeaderType<'a> {
App {
output_name: StrLiteral<'a>,
provides: &'a [Loc<ExposedName<'a>>],
to_platform: To<'a>,
},
@ -85,11 +84,7 @@ impl<'a> HeaderType<'a> {
Self::Module { name, .. } | Self::Builtin { name, .. } | Self::Hosted { name, .. } => {
Some(name.into())
}
Self::App {
output_name: StrLiteral::PlainLine(name),
..
}
| Self::Platform {
Self::Platform {
config_shorthand: name,
..
}
@ -234,6 +229,8 @@ keywords! {
RequiresKeyword => "requires",
ProvidesKeyword => "provides",
ToKeyword => "to",
PlatformKeyword => "platform",
// Deprecated
ImportsKeyword => "imports",
}
@ -276,14 +273,13 @@ 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:
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>,
pub before_provides: &'a [CommentOrNewline<'a>],
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
pub before_packages: &'a [CommentOrNewline<'a>],
pub packages: Loc<Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
// Old header pieces
pub old_imports: Option<KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>>,
pub old_provides_to_new_package: Option<PackageName<'a>>,
}
#[derive(Clone, Debug, PartialEq)]
@ -359,6 +355,7 @@ pub struct TypedIdent<'a> {
pub struct PackageEntry<'a> {
pub shorthand: &'a str,
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
pub platform_marker: Option<&'a [CommentOrNewline<'a>]>,
pub package_name: Loc<PackageName<'a>>,
}
@ -379,9 +376,15 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
),
space0_e(EPackageEntry::IndentPackage)
)),
loc!(specialize_err(EPackageEntry::BadPackage, package_name()))
and!(
optional(skip_first!(
crate::parser::keyword(crate::keyword::PLATFORM, EPackageEntry::Platform),
space0_e(EPackageEntry::IndentPackage)
)),
loc!(specialize_err(EPackageEntry::BadPackage, package_name()))
)
),
move |arena, (opt_shorthand, package_or_path)| {
move |arena, (opt_shorthand, (platform_marker, package_or_path))| {
let entry = match opt_shorthand {
Some(((shorthand, spaces_before_colon), spaces_after_colon)) => PackageEntry {
shorthand,
@ -390,11 +393,13 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
spaces_before_colon,
spaces_after_colon,
),
platform_marker,
package_name: package_or_path,
},
None => PackageEntry {
shorthand: "",
spaces_after_shorthand: &[],
platform_marker,
package_name: package_or_path,
},
};
@ -442,7 +447,7 @@ impl<'a> Malformed for HostedHeader<'a> {
impl<'a> Malformed for AppHeader<'a> {
fn is_malformed(&self) -> bool {
self.name.is_malformed()
false
}
}

View file

@ -18,6 +18,9 @@ pub const EXPOSING: &str = "exposing";
pub const IMPLEMENTS: &str = "implements";
pub const WHERE: &str = "where";
// These keywords are valid in headers
pub const PLATFORM: &str = "platform";
pub const KEYWORDS: [&str; 11] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, EXPECT_FX, CRASH,
];

View file

@ -1,11 +1,12 @@
use crate::ast::{Collection, CommentOrNewline, Defs, Header, Module, Spaced, Spaces};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::expr::merge_spaces;
use crate::header::{
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
HostedHeader, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword, KeywordItem,
ModuleHeader, ModuleName, PackageEntry, PackageHeader, PackagesKeyword, PlatformHeader,
PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
WithKeyword,
HostedHeader, ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword,
KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, 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, *};
@ -17,7 +18,7 @@ use crate::parser::{
use crate::state::State;
use crate::string_literal::{self, parse_str_literal};
use crate::type_annotation;
use roc_region::all::{Loc, Position};
use roc_region::all::{Loc, Position, Region};
fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|_arena, state: State<'a>, _min_indent: u32| {
@ -68,7 +69,6 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
),
Header::Module
),
// Old headers
map!(
skip_first!(
keyword("interface", EHeader::Start),
@ -79,7 +79,7 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
map!(
skip_first!(
keyword("app", EHeader::Start),
increment_min_indent(app_header())
increment_min_indent(one_of![app_header(), old_app_header()])
),
Header::App
),
@ -118,11 +118,19 @@ fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
.trace("module_header")
}
macro_rules! merge_n_spaces {
($arena:expr, $($slice:expr),*) => {
{
let mut merged = bumpalo::collections::Vec::with_capacity_in(0 $(+ $slice.len())*, $arena);
$(merged.extend_from_slice($slice);)*
merged.into_bump_slice()
}
};
}
/// Parse old interface headers so we can format them into module headers
#[inline(always)]
fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
use bumpalo::collections::Vec;
let before_exposes = map_with_arena!(
and!(
skip_second!(
@ -133,12 +141,7 @@ fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
),
|arena: &'a bumpalo::Bump,
(before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| {
let mut combined: Vec<CommentOrNewline> =
Vec::with_capacity_in(before_name.len() + kw.before.len() + kw.after.len(), arena);
combined.extend(before_name);
combined.extend(kw.before);
combined.extend(kw.after);
arena.alloc(combined)
merge_n_spaces!(arena, before_name, kw.before, kw.after)
}
);
@ -237,18 +240,140 @@ fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> {
#[inline(always)]
fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
record!(AppHeader {
before_name: space0_e(EHeader::IndentStart),
name: loc!(crate::parser::specialize_err(
EHeader::AppName,
string_literal::parse_str_literal()
)),
packages: optional(specialize_err(EHeader::Packages, packages())),
imports: optional(specialize_err(EHeader::Imports, imports())),
provides: specialize_err(EHeader::Provides, provides_to()),
before_provides: space0_e(EHeader::IndentStart),
provides: specialize_err(EHeader::Exposes, exposes_list()),
before_packages: space0_e(EHeader::IndentStart),
packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
old_imports: succeed!(None),
old_provides_to_new_package: succeed!(None),
})
.trace("app_header")
}
struct OldAppHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>],
pub packages: Option<Loc<OldAppPackages<'a>>>,
pub imports: Option<KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>>,
pub provides: ProvidesTo<'a>,
}
type OldAppPackages<'a> =
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>;
#[inline(always)]
fn old_app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
let old = record!(OldAppHeader {
before_name: skip_second!(
space0_e(EHeader::IndentStart),
loc!(crate::parser::specialize_err(
EHeader::AppName,
string_literal::parse_str_literal()
))
),
packages: optional(specialize_err(EHeader::Packages, loc!(packages()))),
imports: optional(specialize_err(EHeader::Imports, imports())),
provides: specialize_err(EHeader::Provides, provides_to()),
});
map_with_arena!(old, |arena: &'a bumpalo::Bump, old: OldAppHeader<'a>| {
let mut before_packages: &'a [CommentOrNewline] = &[];
let packages = match old.packages {
Some(packages) => {
before_packages = merge_spaces(
arena,
packages.value.keyword.before,
packages.value.keyword.after,
);
if let To::ExistingPackage(platform_shorthand) = old.provides.to.value {
packages.map(|coll| {
coll.item.map_items(arena, |loc_spaced_pkg| {
if loc_spaced_pkg.value.item().shorthand == platform_shorthand {
loc_spaced_pkg.map(|spaced_pkg| {
spaced_pkg.map(arena, |pkg| {
let mut new_pkg = *pkg;
new_pkg.platform_marker = Some(merge_spaces(
arena,
old.provides.to_keyword.before,
old.provides.to_keyword.after,
));
new_pkg
})
})
} else {
*loc_spaced_pkg
}
})
})
} else {
packages.map(|kw| kw.item)
}
}
None => Loc {
region: Region::zero(),
value: Collection::empty(),
},
};
let provides = match old.provides.types {
Some(types) => {
let mut combined_items = bumpalo::collections::Vec::with_capacity_in(
old.provides.entries.items.len() + types.items.len(),
arena,
);
combined_items.extend_from_slice(old.provides.entries.items);
for loc_spaced_type_ident in types.items {
combined_items.push(loc_spaced_type_ident.map(|spaced_type_ident| {
spaced_type_ident.map(arena, |type_ident| {
ExposedName::new(From::from(*type_ident))
})
}));
}
let value_comments = old.provides.entries.final_comments();
let type_comments = types.final_comments();
let mut combined_comments = bumpalo::collections::Vec::with_capacity_in(
value_comments.len() + type_comments.len(),
arena,
);
combined_comments.extend_from_slice(value_comments);
combined_comments.extend_from_slice(type_comments);
Collection::with_items_and_comments(
arena,
combined_items.into_bump_slice(),
combined_comments.into_bump_slice(),
)
}
None => old.provides.entries,
};
AppHeader {
before_provides: merge_spaces(
arena,
old.before_name,
old.provides.provides_keyword.before,
),
provides,
before_packages: merge_spaces(
arena,
before_packages,
old.provides.provides_keyword.after,
),
packages,
old_imports: old.imports.and_then(imports_none_if_empty),
old_provides_to_new_package: match old.provides.to.value {
To::NewPackage(new_pkg) => Some(new_pkg),
To::ExistingPackage(_) => None,
},
}
})
}
#[inline(always)]
fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
record!(PackageHeader {
@ -544,22 +669,33 @@ fn packages<'a>() -> impl Parser<
EPackages<'a>,
> {
record!(KeywordItem {
keyword: spaces_around_keyword(
PackagesKeyword,
EPackages::Packages,
EPackages::IndentPackages,
EPackages::IndentListStart
),
item: collection_trailing_sep_e!(
byte(b'{', EPackages::ListStart),
specialize_err(EPackages::PackageEntry, loc!(package_entry())),
byte(b',', EPackages::ListEnd),
byte(b'}', EPackages::ListEnd),
Spaced::SpaceBefore
)
keyword: packages_kw(),
item: packages_collection()
})
}
#[inline(always)]
fn packages_kw<'a>() -> impl Parser<'a, Spaces<'a, PackagesKeyword>, EPackages<'a>> {
spaces_around_keyword(
PackagesKeyword,
EPackages::Packages,
EPackages::IndentPackages,
EPackages::IndentListStart,
)
}
#[inline(always)]
fn packages_collection<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>, EPackages<'a>> {
collection_trailing_sep_e!(
byte(b'{', EPackages::ListStart),
specialize_err(EPackages::PackageEntry, loc!(package_entry())),
byte(b',', EPackages::ListEnd),
byte(b'}', EPackages::ListEnd),
Spaced::SpaceBefore
)
}
#[inline(always)]
fn generates<'a>(
) -> impl Parser<'a, KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>, EGenerates> {

View file

@ -211,6 +211,8 @@ pub enum EPackageEntry<'a> {
Shorthand(Position),
Colon(Position),
IndentPackage(Position),
IndentPlatform(Position),
Platform(Position),
Space(BadInputError, Position),
}

View file

@ -170,7 +170,9 @@ impl<'a> Input<'a> {
.parse(arena, state.clone(), min_indent)
.map_err(|(_, fail)| SyntaxError::Header(fail))?;
let module_defs = parse_module_defs(arena, state, Defs::default()).unwrap();
let (header, defs) = header.upgrade_header_imports(arena);
let module_defs = parse_module_defs(arena, state, defs).unwrap();
Ok(Output::Full {
header,

View file

@ -1 +1 @@
Header(Imports(ListEnd(@87), @65))
Header(Exposes(ListEnd(@11), @4))

View file

@ -1,4 +1 @@
app "test-missing-comma"
packages { pf: "platform/main.roc" }
imports [pf.Task Base64]
provides [main, @Foo] to pf
app [main, @Foo] { pf: platform "platform/main.roc" }

View file

@ -1 +1 @@
app "test-app" packages {} imports [] provides [] to blah
app [] {}

View file

@ -2,47 +2,12 @@ Module {
comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-14 PlainLine(
"test-app",
),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [],
item: PackagesKeyword,
after: [],
},
item: [],
},
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [],
item: ImportsKeyword,
after: [],
},
item: [],
},
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [],
item: ProvidesKeyword,
after: [],
},
entries: [],
types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @53-57 ExistingPackage(
"blah",
),
},
before_provides: [],
provides: [],
before_packages: [],
packages: @14-26 [],
old_imports: None,
old_provides_to_new_package: None,
},
),
}

View file

@ -1,4 +1 @@
app "quicksort"
packages { pf: "./platform" }
imports [foo.Bar.Baz]
provides [quicksort] to pf
app [quicksort] { pf: platform "./platform" }

View file

@ -2,73 +2,27 @@ Module {
comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-15 PlainLine(
"quicksort",
),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@31-47 PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [],
package_name: @35-47 PackageName(
"./platform",
),
},
],
},
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [
@64-75 Package(
"foo",
ModuleName(
"Bar.Baz",
),
[],
),
],
},
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@93-102 ExposedName(
"quicksort",
),
],
types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @108-110 ExistingPackage(
"pf",
before_provides: [],
provides: [
@6-15 ExposedName(
"quicksort",
),
},
],
before_packages: [],
packages: @18-47 [
@20-45 PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [],
platform_marker: Some(
[],
),
package_name: @33-45 PackageName(
"./platform",
),
},
],
old_imports: None,
old_provides_to_new_package: None,
},
),
}

View file

@ -1,4 +1 @@
app "quicksort"
packages { pf: "./platform" }
imports [ foo.Bar.Baz ]
provides [ quicksort ] to pf
app [ quicksort ] { pf: platform "./platform" }

View file

@ -1,8 +1 @@
app "quicksort"
packages { pf: "./platform" }
imports [foo.Bar.{
Baz,
FortyTwo,
# I'm a happy comment
}]
provides [quicksort] to pf
app [quicksort] { pf: platform "./platform" }

View file

@ -2,98 +2,27 @@ Module {
comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-15 PlainLine(
"quicksort",
),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@31-47 PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [],
package_name: @35-47 PackageName(
"./platform",
),
},
],
},
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [
@65-141 Package(
"foo",
ModuleName(
"Bar",
),
Collection {
items: [
@83-86 SpaceBefore(
ExposedName(
"Baz",
),
[
Newline,
],
),
@96-104 SpaceBefore(
ExposedName(
"FortyTwo",
),
[
Newline,
],
),
],
final_comments: [
Newline,
LineComment(
" I'm a happy comment",
),
],
},
),
],
},
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@159-168 ExposedName(
"quicksort",
),
],
types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @175-177 ExistingPackage(
"pf",
before_provides: [],
provides: [
@6-15 ExposedName(
"quicksort",
),
},
],
before_packages: [],
packages: @19-49 [
@21-46 PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [],
platform_marker: Some(
[],
),
package_name: @34-46 PackageName(
"./platform",
),
},
],
old_imports: None,
old_provides_to_new_package: None,
},
),
}

View file

@ -1,8 +1 @@
app "quicksort"
packages { pf: "./platform", }
imports [ foo.Bar.{
Baz,
FortyTwo,
# I'm a happy comment
} ]
provides [ quicksort, ] to pf
app [ quicksort, ] { pf: platform "./platform", }

View file

@ -1 +1 @@
app "test-app" provides [] to "./blah"
app [] {}

View file

@ -2,31 +2,12 @@ Module {
comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-14 PlainLine(
"test-app",
),
packages: None,
imports: None,
provides: ProvidesTo {
provides_keyword: Spaces {
before: [],
item: ProvidesKeyword,
after: [],
},
entries: [],
types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @30-38 NewPackage(
PackageName(
"./blah",
),
),
},
before_provides: [],
provides: [],
before_packages: [],
packages: @7-9 [],
old_imports: None,
old_provides_to_new_package: None,
},
),
}

View file

@ -1 +1 @@
app "test-app" provides [] to "./blah"
app [] {}

View file

@ -1,10 +1,7 @@
app "hello"
packages {
pf:
app [main] {
pf:
"https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
}
imports [pf.Stdout]
provides [main] to pf
}
main =
Stdout.line "I'm a Roc application!"

View file

@ -3,80 +3,32 @@ Full {
comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-11 PlainLine(
"hello",
),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
before_provides: [],
provides: [
@5-9 ExposedName(
"main",
),
],
before_packages: [],
packages: @11-134 [
@13-132 SpaceAfter(
PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@27-146 SpaceAfter(
PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [
Newline,
],
package_name: @31-146 PackageName(
"https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
),
},
[
Newline,
],
platform_marker: None,
package_name: @17-132 PackageName(
"https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
),
],
},
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [
@162-171 Package(
"pf",
ModuleName(
"Stdout",
),
[],
),
],
},
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
[
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@187-191 ExposedName(
"main",
),
],
types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @196-198 ExistingPackage(
"pf",
),
},
],
old_imports: None,
old_provides_to_new_package: None,
},
),
},
@ -85,7 +37,7 @@ Full {
Index(2147483648),
],
regions: [
@200-247,
@136-183,
],
space_before: [
Slice(start = 0, length = 2),
@ -100,17 +52,17 @@ Full {
type_defs: [],
value_defs: [
Body(
@200-204 Identifier {
@136-140 Identifier {
ident: "main",
},
@211-247 SpaceBefore(
@147-183 SpaceBefore(
Apply(
@211-222 Var {
@147-158 Var {
module_name: "Stdout",
ident: "line",
},
[
@223-247 Str(
@159-183 Str(
PlainLine(
"I'm a Roc application!",
),

View file

@ -1,9 +1,6 @@
app "hello"
packages { pf:
app [main] { pf:
"https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br"
}
imports [pf.Stdout]
provides [main] to pf
main =
Stdout.line "I'm a Roc application!"

View file

@ -35,6 +35,7 @@ Module {
@59-71 PackageEntry {
shorthand: "foo",
spaces_after_shorthand: [],
platform_marker: None,
package_name: @64-71 PackageName(
"./foo",
),

View file

@ -52,6 +52,7 @@ Module {
@87-99 PackageEntry {
shorthand: "foo",
spaces_after_shorthand: [],
platform_marker: None,
package_name: @92-99 PackageName(
"./foo",
),

View file

@ -0,0 +1,8 @@
app [main] {
cli: platform "../basic-cli/platform/main.roc",
}
import cli.Stdout
main =
Stdout.line "hello"

View file

@ -0,0 +1,114 @@
Full {
header: Module {
comments: [],
header: App(
AppHeader {
before_provides: [
Newline,
],
provides: [
@143-147 ExposedName(
"main",
),
],
before_packages: [
Newline,
],
packages: @20-88 Collection {
items: [
@44-81 SpaceBefore(
PackageEntry {
shorthand: "cli",
spaces_after_shorthand: [],
platform_marker: Some(
[],
),
package_name: @49-81 PackageName(
"../basic-cli/platform/main.roc",
),
},
[
Newline,
],
),
],
final_comments: [
Newline,
],
},
old_imports: None,
old_provides_to_new_package: None,
},
),
},
module_defs: Defs {
tags: [
Index(2147483648),
Index(2147483649),
],
regions: [
@111-121,
@157-187,
],
space_before: [
Slice(start = 0, length = 3),
Slice(start = 6, length = 2),
],
space_after: [
Slice(start = 3, length = 3),
Slice(start = 8, length = 0),
],
spaces: [
Newline,
Newline,
Newline,
Newline,
Newline,
Newline,
Newline,
Newline,
],
type_defs: [],
value_defs: [
ModuleImport(
ModuleImport {
before_name: [],
name: @111-121 ImportedModuleName {
package: Some(
"cli",
),
name: ModuleName(
"Stdout",
),
},
alias: None,
exposed: None,
},
),
Body(
@157-161 Identifier {
ident: "main",
},
@168-187 SpaceBefore(
Apply(
@168-179 Var {
module_name: "Stdout",
ident: "line",
},
[
@180-187 Str(
PlainLine(
"hello",
),
),
],
Space,
),
[
Newline,
],
),
),
],
},
}

View file

@ -0,0 +1,12 @@
app "old-app-header"
packages {
cli: "../basic-cli/platform/main.roc",
}
imports [
cli.Stdout,
]
provides [main] to cli
main =
Stdout.line "hello"

View file

@ -1,4 +1 @@
app "test"
packages { pf: "./platform" }
imports [foo.Bar.Baz]
provides [quicksort] { Flags, Model } to pf
app [quicksort, Flags, Model] { pf: "./platform" }

View file

@ -2,82 +2,31 @@ Module {
comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-10 PlainLine(
"test",
),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@26-42 PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [],
package_name: @30-42 PackageName(
"./platform",
),
},
],
},
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [
@59-70 Package(
"foo",
ModuleName(
"Bar.Baz",
),
[],
),
],
},
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@88-97 ExposedName(
"quicksort",
before_provides: [],
provides: [
@5-14 ExposedName(
"quicksort",
),
@16-21 ExposedName(
"Flags",
),
@23-28 ExposedName(
"Model",
),
],
before_packages: [],
packages: @30-50 [
@32-48 PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [],
platform_marker: None,
package_name: @36-48 PackageName(
"./platform",
),
],
types: Some(
[
@102-107 UppercaseIdent(
"Flags",
),
@109-114 UppercaseIdent(
"Model",
),
],
),
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @121-123 ExistingPackage(
"pf",
),
},
],
old_imports: None,
old_provides_to_new_package: None,
},
),
}

View file

@ -1,4 +1 @@
app "test"
packages { pf: "./platform" }
imports [ foo.Bar.Baz ]
provides [ quicksort ] { Flags, Model, } to pf
app [quicksort, Flags, Model] { pf: "./platform" }

View file

@ -1,6 +1,3 @@
app "example"
packages { pf: "path" }
imports [pf.Stdout]
provides [main] to pf
app [main] { pf: platform "path" }
main = Stdout.line "Hello"

View file

@ -3,73 +3,27 @@ Full {
comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-13 PlainLine(
"example",
),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@29-40 PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [],
package_name: @34-40 PackageName(
"path",
),
},
],
},
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [
@57-66 Package(
"pf",
ModuleName(
"Stdout",
),
[],
),
],
},
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@84-88 ExposedName(
"main",
),
],
types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @94-96 ExistingPackage(
"pf",
before_provides: [],
provides: [
@6-10 ExposedName(
"main",
),
},
],
before_packages: [],
packages: @13-37 [
@15-35 PackageEntry {
shorthand: "pf",
spaces_after_shorthand: [],
platform_marker: Some(
[],
),
package_name: @29-35 PackageName(
"path",
),
},
],
old_imports: None,
old_provides_to_new_package: None,
},
),
},
@ -78,7 +32,7 @@ Full {
Index(2147483648),
],
regions: [
@98-124,
@39-65,
],
space_before: [
Slice(start = 0, length = 2),
@ -93,16 +47,16 @@ Full {
type_defs: [],
value_defs: [
Body(
@98-102 Identifier {
@39-43 Identifier {
ident: "main",
},
@105-124 Apply(
@105-116 Var {
@46-65 Apply(
@46-57 Var {
module_name: "Stdout",
ident: "line",
},
[
@117-124 Str(
@58-65 Str(
PlainLine(
"Hello",
),

View file

@ -1,6 +1,3 @@
app "example"
packages { pf : "path" }
imports [ pf.Stdout ]
provides [ main ] to pf
app [ main ] { pf : platform "path" }
main = Stdout.line "Hello"
main = Stdout.line "Hello"

View file

@ -1,11 +1,8 @@
app "desugar-bang"
packages {
cli: "../basic-cli/platform/main.roc",
}
imports [
cli.Stdout,
]
provides [main] to cli
app [main] {
cli: "../basic-cli/platform/main.roc",
}
import cli.Stdout
main =
# is this a valid statement?

View file

@ -3,120 +3,83 @@ Full {
comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-18 PlainLine(
"desugar-bang",
),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: Collection {
items: [
@42-79 SpaceBefore(
PackageEntry {
shorthand: "cli",
spaces_after_shorthand: [],
package_name: @47-79 PackageName(
"../basic-cli/platform/main.roc",
),
},
[
Newline,
],
before_provides: [],
provides: [
@5-9 ExposedName(
"main",
),
],
before_packages: [],
packages: @11-55 Collection {
items: [
@15-52 SpaceBefore(
PackageEntry {
shorthand: "cli",
spaces_after_shorthand: [],
platform_marker: None,
package_name: @20-52 PackageName(
"../basic-cli/platform/main.roc",
),
],
final_comments: [
},
[
Newline,
],
},
},
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: Collection {
items: [
@109-119 SpaceBefore(
Package(
"cli",
ModuleName(
"Stdout",
),
[],
),
[
Newline,
],
),
],
final_comments: [
Newline,
],
},
},
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@141-145 ExposedName(
"main",
),
],
types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @150-153 ExistingPackage(
"cli",
),
final_comments: [
Newline,
],
},
old_imports: None,
old_provides_to_new_package: None,
},
),
},
module_defs: Defs {
tags: [
Index(2147483648),
Index(2147483649),
],
regions: [
@155-299,
@57-74,
@76-220,
],
space_before: [
Slice(start = 0, length = 2),
Slice(start = 2, length = 2),
],
space_after: [
Slice(start = 2, length = 0),
Slice(start = 4, length = 0),
],
spaces: [
Newline,
Newline,
Newline,
Newline,
],
type_defs: [],
value_defs: [
ModuleImport(
ModuleImport {
before_name: [],
name: @64-74 ImportedModuleName {
package: Some(
"cli",
),
name: ModuleName(
"Stdout",
),
},
alias: None,
exposed: None,
},
),
Body(
@155-159 Identifier {
@76-80 Identifier {
ident: "main",
},
@199-299 SpaceBefore(
@120-220 SpaceBefore(
Defs(
Defs {
tags: [
@ -124,8 +87,8 @@ Full {
Index(2147483649),
],
regions: [
@199-211,
@241-284,
@120-132,
@162-205,
],
space_before: [
Slice(start = 0, length = 0),
@ -139,18 +102,18 @@ Full {
type_defs: [],
value_defs: [
Stmt(
@199-211 BinOps(
@120-132 BinOps(
[
(
@199-204 Str(
@120-125 Str(
PlainLine(
"Foo",
),
),
@205-207 Pizza,
@126-128 Pizza,
),
],
@208-211 TaskAwaitBang(
@129-132 TaskAwaitBang(
Var {
module_name: "A",
ident: "x",
@ -159,33 +122,33 @@ Full {
),
),
Stmt(
@241-284 SpaceBefore(
@162-205 SpaceBefore(
BinOps(
[
(
@241-246 Str(
@162-167 Str(
PlainLine(
"Bar",
),
),
@247-249 Pizza,
@168-170 Pizza,
),
],
@250-284 Apply(
@250-253 TaskAwaitBang(
@171-205 Apply(
@171-174 TaskAwaitBang(
Var {
module_name: "B",
ident: "y",
},
),
[
@264-284 SpaceBefore(
@185-205 SpaceBefore(
Record(
[
@266-282 RequiredValue(
@266-272 "config",
@187-203 RequiredValue(
@187-193 "config",
[],
@274-282 Str(
@195-203 Str(
PlainLine(
"config",
),
@ -212,14 +175,14 @@ Full {
),
],
},
@290-299 SpaceBefore(
@211-220 SpaceBefore(
Apply(
@290-293 Var {
@211-214 Var {
module_name: "C",
ident: "z",
},
[
@294-299 Str(
@215-220 Str(
PlainLine(
"Bar",
),

View file

@ -1,11 +1,8 @@
app "desugar-bang"
packages {
cli: "../basic-cli/platform/main.roc",
}
imports [
cli.Stdout,
]
provides [main] to cli
app [main] {
cli: "../basic-cli/platform/main.roc",
}
import cli.Stdout
main =
# is this a valid statement?

View file

@ -1,9 +1,6 @@
app ""
packages {
cli: "",
}
imports []
provides [main] to cli
app [main] {
cli: platform "",
}
main =
"jq --version"

View file

@ -3,75 +3,41 @@ Full {
comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-6 PlainLine(
"",
),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: Collection {
items: [
@30-37 SpaceBefore(
PackageEntry {
shorthand: "cli",
spaces_after_shorthand: [],
package_name: @35-37 PackageName(
"",
),
},
[
Newline,
],
before_provides: [
Newline,
],
provides: [
@74-78 ExposedName(
"main",
),
],
before_packages: [
Newline,
],
packages: @6-44 Collection {
items: [
@30-37 SpaceBefore(
PackageEntry {
shorthand: "cli",
spaces_after_shorthand: [],
platform_marker: Some(
[],
),
],
final_comments: [
package_name: @35-37 PackageName(
"",
),
},
[
Newline,
],
},
},
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [],
},
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@74-78 ExposedName(
"main",
),
],
types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @83-86 ExistingPackage(
"cli",
),
final_comments: [
Newline,
],
},
old_imports: None,
old_provides_to_new_package: None,
},
),
},

View file

@ -4818,11 +4818,33 @@ mod test_fmt {
));
}
#[test]
fn old_style_app_header_is_upgraded() {
module_formats_to(
indoc!(
"
app \"test\"
packages {
pf: \"platform/main.roc\"
}
provides [main] to pf
"
),
indoc!(
"
app [main] {
pf: platform \"platform/main.roc\",
}
"
),
);
}
#[test]
fn single_line_app() {
module_formats_same(indoc!(
r#"
app "Foo" packages { pf: "platform/main.roc" } imports [] provides [main] to pf"#
app [main] { pf: platform "platform/main.roc" }"#
));
}
@ -5831,7 +5853,7 @@ mod test_fmt {
module_formats_same(indoc!(
r#"
# hello world
app "test" packages {} imports [] provides [] to "./platform"
app [] { pf: platform "./platform" }
"#
));

View file

@ -300,6 +300,7 @@ mod test_snapshots {
pass/dbg_multiline.expr,
pass/def_without_newline.expr,
pass/destructure_tag_assignment.expr,
pass/docs.expr,
pass/empty_app_header.header,
pass/empty_module_header.header,
pass/empty_hosted_header.header,
@ -333,7 +334,6 @@ mod test_snapshots {
pass/inline_import.expr,
pass/inline_ingested_file.expr,
pass/int_with_underscore.expr,
pass/module_with_newline.header,
pass/lambda_in_chain.expr,
pass/lambda_indent.expr,
pass/list_closing_indent_not_enough.expr,
@ -349,6 +349,7 @@ mod test_snapshots {
pass/mixed_docs.expr,
pass/module_def_newline.moduledefs,
pass/module_multiline_exposes.header,
pass/module_with_newline.header,
pass/multi_backpassing.expr,
pass/multi_backpassing_in_def.moduledefs,
pass/multi_backpassing_with_apply.expr,
@ -386,9 +387,9 @@ mod test_snapshots {
pass/nonempty_hosted_header.header,
pass/nonempty_package_header.header,
pass/nonempty_platform_header.header,
pass/docs.expr,
pass/not_multiline_string.expr,
pass/number_literal_suffixes.expr,
pass/old_app_header.full,
pass/one_backpassing.expr,
pass/one_char_string.expr,
pass/one_def.expr,