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

@ -944,24 +944,11 @@ mod cli_run {
&[], &[],
&[], &[],
&[], &[],
indoc!( format!(
r#" "\nThis roc file can print its own source code. The source is:\n\n{}\n",
This roc file can print its own source code. The source is: include_str!("../../../examples/cli/ingested-file.roc")
)
app "ingested-file" .as_str(),
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [
pf.Stdout,
pf.Task,
"ingested-file.roc" as ownCode : Str,
]
provides [main] to pf
main =
Stdout.line! "\nThis roc file can print its own source code. The source is:\n\n$(ownCode)"
"#
),
UseValgrind::No, UseValgrind::No,
TestCliCommands::Run, TestCliCommands::Run,
) )

View file

@ -1,6 +1,4 @@
app "formatted" app [main] { pf: "platform/main.roc" }
packages { pf: "platform/main.roc" } imports []
provides [main] to pf
main : Str main : Str
main = Dep1.value1 {} main = Dep1.value1 {}

View file

@ -1,6 +1,4 @@
app "formatted" app [main] { pf: "platform/main.roc" }
packages { pf: "platform/main.roc" }
provides [main] to pf
main : Str main : Str
main = Dep1.value1 {} main = Dep1.value1 {}

View file

@ -1,6 +1,4 @@
app "formatted" app [main] { pf: "platform/main.roc" }
packages { pf: "platform/main.roc" } imports []
provides [main] to pf
main : Str main : Str
main = Dep1.value1 {} main = Dep1.value1 {}

View file

@ -9,8 +9,9 @@ use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces};
use roc_parse::header::{ use roc_parse::header::{
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader,
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformRequires, PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformKeyword,
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent,
WithKeyword,
}; };
use roc_parse::ident::UppercaseIdent; use roc_parse::ident::UppercaseIdent;
use roc_region::all::Loc; use roc_region::all::Loc;
@ -75,6 +76,7 @@ keywords! {
RequiresKeyword, RequiresKeyword,
ProvidesKeyword, ProvidesKeyword,
ToKeyword, ToKeyword,
PlatformKeyword,
} }
impl<V: Formattable> Formattable for Option<V> { 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>) { pub fn fmt_app_header<'a>(buf: &mut Buf, header: &'a AppHeader<'a>) {
buf.indent(0); buf.indent(0);
buf.push_str("app"); buf.push_str("app");
let indent = INDENT; 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 { if header.before_packages.iter().all(|c| c.is_newline()) {
packages.keyword.format(buf, indent); buf.spaces(1);
fmt_packages(buf, packages.item, indent); 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>) { 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_str(entry.shorthand);
buf.push(':'); buf.push(':');
fmt_default_spaces(buf, entry.spaces_after_shorthand, indent); 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); 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), interface_imports: header.interface_imports.remove_spaces(arena),
}), }),
Header::App(header) => Header::App(AppHeader { Header::App(header) => Header::App(AppHeader {
before_name: &[], before_provides: &[],
name: header.name.remove_spaces(arena),
packages: header.packages.remove_spaces(arena),
imports: header.imports.remove_spaces(arena),
provides: header.provides.remove_spaces(arena), 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 { Header::Package(header) => Header::Package(PackageHeader {
before_name: &[], before_name: &[],
@ -405,6 +408,10 @@ impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
PackageEntry { PackageEntry {
shorthand: self.shorthand, shorthand: self.shorthand,
spaces_after_shorthand: &[], spaces_after_shorthand: &[],
platform_marker: match self.platform_marker {
Some(_) => Some(&[]),
None => None,
},
package_name: self.package_name.remove_spaces(arena), 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>>), ParsingFailed(FileError<'a, SyntaxError<'a>>),
UnexpectedHeader(String), 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, ErrJoiningWorkerThreads,
TriedToImportAppModule, TriedToImportAppModule,
@ -1667,6 +1679,34 @@ pub fn report_loading_problem(
LoadingProblem::FileProblem { filename, error } => { LoadingProblem::FileProblem { filename, error } => {
to_file_problem_report_string(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), err => todo!("Loading error: {:?}", err),
} }
} }
@ -3838,24 +3878,60 @@ fn parse_header<'a>(
let mut app_file_dir = filename.clone(); let mut app_file_dir = filename.clone();
app_file_dir.pop(); app_file_dir.pop();
let packages = if let Some(packages) = header.packages { let packages = unspace(arena, header.packages.value.items);
unspace(arena, packages.item.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 { } 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); let mut provides = bumpalo::collections::Vec::new_in(arena);
provides.extend(unspace(arena, header.provides.entries.items)); provides.extend(unspace(arena, header.provides.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));
}
}
let info = HeaderInfo { let info = HeaderInfo {
filename, filename,
@ -3864,11 +3940,10 @@ fn parse_header<'a>(
packages, packages,
header_type: HeaderType::App { header_type: HeaderType::App {
provides: provides.into_bump_slice(), provides: provides.into_bump_slice(),
output_name: header.name.value, to_platform,
to_platform: header.provides.to.value,
}, },
module_comments: comments, module_comments: comments,
header_imports: header.imports, header_imports: header.old_imports,
}; };
let (module_id, _, resolved_header) = let (module_id, _, resolved_header) =
@ -3892,29 +3967,12 @@ fn parse_header<'a>(
filename, 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 { Ok(HeaderOutput {
module_id, module_id,
msg: Msg::Many(messages), msg: Msg::Many(messages),
opt_platform_shorthand: Some(shorthand), opt_platform_shorthand: platform_shorthand,
}) })
} }
To::NewPackage(_package_name) => Ok(HeaderOutput {
module_id,
msg: Msg::Many(messages),
opt_platform_shorthand: None,
}),
}
}
Ok(( Ok((
ast::Module { ast::Module {
header: ast::Header::Package(header), header: ast::Header::Package(header),
@ -6277,6 +6335,96 @@ fn to_incorrect_module_name_report<'a>(
buf 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>( fn to_parse_problem_report<'a>(
problem: FileError<'a, SyntaxError<'a>>, problem: FileError<'a, SyntaxError<'a>>,
mut module_ids: ModuleIds, 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("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.reflow(r"Does the module header have an entry that looks like this?"),
alloc alloc
.parser_suggestion("packages { blah: \"…path or URL to platform…\" }") .parser_suggestion("app [main] { pf: platform \"…path or URL to platform…\" }")
.indent(4), .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>"), 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 interns = &loaded_module.interns;
let declarations = loaded_module.declarations_by_id.remove(&home).unwrap(); let declarations = loaded_module.declarations_by_id.remove(&home).unwrap();
for index in 0..declarations.len() { for index in 0..declarations.len() {
use roc_can::expr::DeclarationTag::*; 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(), 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> { 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), 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) (Module { header, ..self }, defs)
@ -174,6 +195,7 @@ impl<'a> Module<'a> {
}, },
if index == len - 1 { if index == len - 1 {
let mut after = spaced.after.to_vec(); let mut after = spaced.after.to_vec();
after.extend_from_slice(imports.item.final_comments());
after.push(CommentOrNewline::Newline); after.push(CommentOrNewline::Newline);
after.push(CommentOrNewline::Newline); after.push(CommentOrNewline::Newline);
arena.alloc(after) arena.alloc(after)

View file

@ -38,7 +38,6 @@ impl<'a> HeaderType<'a> {
#[derive(Debug)] #[derive(Debug)]
pub enum HeaderType<'a> { pub enum HeaderType<'a> {
App { App {
output_name: StrLiteral<'a>,
provides: &'a [Loc<ExposedName<'a>>], provides: &'a [Loc<ExposedName<'a>>],
to_platform: To<'a>, to_platform: To<'a>,
}, },
@ -85,11 +84,7 @@ impl<'a> HeaderType<'a> {
Self::Module { name, .. } | Self::Builtin { name, .. } | Self::Hosted { name, .. } => { Self::Module { name, .. } | Self::Builtin { name, .. } | Self::Hosted { name, .. } => {
Some(name.into()) Some(name.into())
} }
Self::App { Self::Platform {
output_name: StrLiteral::PlainLine(name),
..
}
| Self::Platform {
config_shorthand: name, config_shorthand: name,
.. ..
} }
@ -234,6 +229,8 @@ keywords! {
RequiresKeyword => "requires", RequiresKeyword => "requires",
ProvidesKeyword => "provides", ProvidesKeyword => "provides",
ToKeyword => "to", ToKeyword => "to",
PlatformKeyword => "platform",
// Deprecated
ImportsKeyword => "imports", ImportsKeyword => "imports",
} }
@ -276,14 +273,13 @@ pub enum To<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct AppHeader<'a> { pub struct AppHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>], pub before_provides: &'a [CommentOrNewline<'a>],
pub name: Loc<StrLiteral<'a>>, pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
pub before_packages: &'a [CommentOrNewline<'a>],
pub packages: pub packages: Loc<Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
Option<KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>>, // Old header pieces
pub imports: pub old_imports: Option<KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>>,
Option<KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>>, pub old_provides_to_new_package: Option<PackageName<'a>>,
pub provides: ProvidesTo<'a>,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -359,6 +355,7 @@ pub struct TypedIdent<'a> {
pub struct PackageEntry<'a> { pub struct PackageEntry<'a> {
pub shorthand: &'a str, pub shorthand: &'a str,
pub spaces_after_shorthand: &'a [CommentOrNewline<'a>], pub spaces_after_shorthand: &'a [CommentOrNewline<'a>],
pub platform_marker: Option<&'a [CommentOrNewline<'a>]>,
pub package_name: Loc<PackageName<'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) space0_e(EPackageEntry::IndentPackage)
)), )),
and!(
optional(skip_first!(
crate::parser::keyword(crate::keyword::PLATFORM, EPackageEntry::Platform),
space0_e(EPackageEntry::IndentPackage)
)),
loc!(specialize_err(EPackageEntry::BadPackage, package_name())) 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 { let entry = match opt_shorthand {
Some(((shorthand, spaces_before_colon), spaces_after_colon)) => PackageEntry { Some(((shorthand, spaces_before_colon), spaces_after_colon)) => PackageEntry {
shorthand, shorthand,
@ -390,11 +393,13 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
spaces_before_colon, spaces_before_colon,
spaces_after_colon, spaces_after_colon,
), ),
platform_marker,
package_name: package_or_path, package_name: package_or_path,
}, },
None => PackageEntry { None => PackageEntry {
shorthand: "", shorthand: "",
spaces_after_shorthand: &[], spaces_after_shorthand: &[],
platform_marker,
package_name: package_or_path, package_name: package_or_path,
}, },
}; };
@ -442,7 +447,7 @@ impl<'a> Malformed for HostedHeader<'a> {
impl<'a> Malformed for AppHeader<'a> { impl<'a> Malformed for AppHeader<'a> {
fn is_malformed(&self) -> bool { 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 IMPLEMENTS: &str = "implements";
pub const WHERE: &str = "where"; pub const WHERE: &str = "where";
// These keywords are valid in headers
pub const PLATFORM: &str = "platform";
pub const KEYWORDS: [&str; 11] = [ pub const KEYWORDS: [&str; 11] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, IMPORT, EXPECT, EXPECT_FX, CRASH, 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::ast::{Collection, CommentOrNewline, Defs, Header, Module, Spaced, Spaces};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::expr::merge_spaces;
use crate::header::{ use crate::header::{
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
HostedHeader, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword, KeywordItem, HostedHeader, ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword,
ModuleHeader, ModuleName, PackageEntry, PackageHeader, PackagesKeyword, PlatformHeader, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, PackagesKeyword,
PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword,
WithKeyword, TypedIdent, WithKeyword,
}; };
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent}; use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
use crate::parser::Progress::{self, *}; use crate::parser::Progress::{self, *};
@ -17,7 +18,7 @@ use crate::parser::{
use crate::state::State; use crate::state::State;
use crate::string_literal::{self, parse_str_literal}; use crate::string_literal::{self, parse_str_literal};
use crate::type_annotation; 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>> { fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|_arena, state: State<'a>, _min_indent: u32| { |_arena, state: State<'a>, _min_indent: u32| {
@ -68,7 +69,6 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
), ),
Header::Module Header::Module
), ),
// Old headers
map!( map!(
skip_first!( skip_first!(
keyword("interface", EHeader::Start), keyword("interface", EHeader::Start),
@ -79,7 +79,7 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
map!( map!(
skip_first!( skip_first!(
keyword("app", EHeader::Start), keyword("app", EHeader::Start),
increment_min_indent(app_header()) increment_min_indent(one_of![app_header(), old_app_header()])
), ),
Header::App Header::App
), ),
@ -118,11 +118,19 @@ fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
.trace("module_header") .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 /// Parse old interface headers so we can format them into module headers
#[inline(always)] #[inline(always)]
fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> { fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
use bumpalo::collections::Vec;
let before_exposes = map_with_arena!( let before_exposes = map_with_arena!(
and!( and!(
skip_second!( skip_second!(
@ -133,12 +141,7 @@ fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
), ),
|arena: &'a bumpalo::Bump, |arena: &'a bumpalo::Bump,
(before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| { (before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| {
let mut combined: Vec<CommentOrNewline> = merge_n_spaces!(arena, before_name, kw.before, kw.after)
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)
} }
); );
@ -237,18 +240,140 @@ fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> {
#[inline(always)] #[inline(always)]
fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
record!(AppHeader { record!(AppHeader {
before_name: space0_e(EHeader::IndentStart), before_provides: space0_e(EHeader::IndentStart),
name: loc!(crate::parser::specialize_err( provides: specialize_err(EHeader::Exposes, exposes_list()),
EHeader::AppName, before_packages: space0_e(EHeader::IndentStart),
string_literal::parse_str_literal() packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
)), old_imports: succeed!(None),
packages: optional(specialize_err(EHeader::Packages, packages())), old_provides_to_new_package: succeed!(None),
imports: optional(specialize_err(EHeader::Imports, imports())),
provides: specialize_err(EHeader::Provides, provides_to()),
}) })
.trace("app_header") .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)] #[inline(always)]
fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> { fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
record!(PackageHeader { record!(PackageHeader {
@ -544,20 +669,31 @@ fn packages<'a>() -> impl Parser<
EPackages<'a>, EPackages<'a>,
> { > {
record!(KeywordItem { record!(KeywordItem {
keyword: spaces_around_keyword( keyword: packages_kw(),
item: packages_collection()
})
}
#[inline(always)]
fn packages_kw<'a>() -> impl Parser<'a, Spaces<'a, PackagesKeyword>, EPackages<'a>> {
spaces_around_keyword(
PackagesKeyword, PackagesKeyword,
EPackages::Packages, EPackages::Packages,
EPackages::IndentPackages, EPackages::IndentPackages,
EPackages::IndentListStart EPackages::IndentListStart,
), )
item: collection_trailing_sep_e!( }
#[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), byte(b'{', EPackages::ListStart),
specialize_err(EPackages::PackageEntry, loc!(package_entry())), specialize_err(EPackages::PackageEntry, loc!(package_entry())),
byte(b',', EPackages::ListEnd), byte(b',', EPackages::ListEnd),
byte(b'}', EPackages::ListEnd), byte(b'}', EPackages::ListEnd),
Spaced::SpaceBefore Spaced::SpaceBefore
) )
})
} }
#[inline(always)] #[inline(always)]

View file

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

View file

@ -170,7 +170,9 @@ impl<'a> Input<'a> {
.parse(arena, state.clone(), min_indent) .parse(arena, state.clone(), min_indent)
.map_err(|(_, fail)| SyntaxError::Header(fail))?; .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 { Ok(Output::Full {
header, header,

View file

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

View file

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

View file

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

View file

@ -2,47 +2,12 @@ Module {
comments: [], comments: [],
header: App( header: App(
AppHeader { AppHeader {
before_name: [], before_provides: [],
name: @4-14 PlainLine( provides: [],
"test-app", before_packages: [],
), packages: @14-26 [],
packages: Some( old_imports: None,
KeywordItem { old_provides_to_new_package: None,
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",
),
},
}, },
), ),
} }

View file

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

View file

@ -2,73 +2,27 @@ Module {
comments: [], comments: [],
header: App( header: App(
AppHeader { AppHeader {
before_name: [], before_provides: [],
name: @4-15 PlainLine( provides: [
@6-15 ExposedName(
"quicksort", "quicksort",
), ),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
], ],
item: PackagesKeyword, before_packages: [],
after: [], packages: @18-47 [
}, @20-45 PackageEntry {
item: [
@31-47 PackageEntry {
shorthand: "pf", shorthand: "pf",
spaces_after_shorthand: [], spaces_after_shorthand: [],
package_name: @35-47 PackageName( platform_marker: Some(
[],
),
package_name: @33-45 PackageName(
"./platform", "./platform",
), ),
}, },
], ],
}, old_imports: None,
), old_provides_to_new_package: None,
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",
),
},
}, },
), ),
} }

View file

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

View file

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

View file

@ -2,98 +2,27 @@ Module {
comments: [], comments: [],
header: App( header: App(
AppHeader { AppHeader {
before_name: [], before_provides: [],
name: @4-15 PlainLine( provides: [
@6-15 ExposedName(
"quicksort", "quicksort",
), ),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
], ],
item: PackagesKeyword, before_packages: [],
after: [], packages: @19-49 [
}, @21-46 PackageEntry {
item: [
@31-47 PackageEntry {
shorthand: "pf", shorthand: "pf",
spaces_after_shorthand: [], spaces_after_shorthand: [],
package_name: @35-47 PackageName( platform_marker: Some(
[],
),
package_name: @34-46 PackageName(
"./platform", "./platform",
), ),
}, },
], ],
}, old_imports: None,
), old_provides_to_new_package: None,
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",
),
},
}, },
), ),
} }

View file

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

View file

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

View file

@ -2,31 +2,12 @@ Module {
comments: [], comments: [],
header: App( header: App(
AppHeader { AppHeader {
before_name: [], before_provides: [],
name: @4-14 PlainLine( provides: [],
"test-app", before_packages: [],
), packages: @7-9 [],
packages: None, old_imports: None,
imports: None, old_provides_to_new_package: 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",
),
),
},
}, },
), ),
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -52,6 +52,7 @@ Module {
@87-99 PackageEntry { @87-99 PackageEntry {
shorthand: "foo", shorthand: "foo",
spaces_after_shorthand: [], spaces_after_shorthand: [],
platform_marker: None,
package_name: @92-99 PackageName( package_name: @92-99 PackageName(
"./foo", "./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" app [quicksort, Flags, Model] { pf: "./platform" }
packages { pf: "./platform" }
imports [foo.Bar.Baz]
provides [quicksort] { Flags, Model } to pf

View file

@ -2,82 +2,31 @@ Module {
comments: [], comments: [],
header: App( header: App(
AppHeader { AppHeader {
before_name: [], before_provides: [],
name: @4-10 PlainLine( provides: [
"test", @5-14 ExposedName(
"quicksort",
),
@16-21 ExposedName(
"Flags",
),
@23-28 ExposedName(
"Model",
), ),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
], ],
item: PackagesKeyword, before_packages: [],
after: [], packages: @30-50 [
}, @32-48 PackageEntry {
item: [
@26-42 PackageEntry {
shorthand: "pf", shorthand: "pf",
spaces_after_shorthand: [], spaces_after_shorthand: [],
package_name: @30-42 PackageName( platform_marker: None,
package_name: @36-48 PackageName(
"./platform", "./platform",
), ),
}, },
], ],
}, old_imports: None,
), old_provides_to_new_package: None,
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",
),
],
types: Some(
[
@102-107 UppercaseIdent(
"Flags",
),
@109-114 UppercaseIdent(
"Model",
),
],
),
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @121-123 ExistingPackage(
"pf",
),
},
}, },
), ),
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,25 +3,26 @@ Full {
comments: [], comments: [],
header: App( header: App(
AppHeader { AppHeader {
before_name: [], before_provides: [
name: @4-6 PlainLine(
"",
),
packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline, Newline,
], ],
item: PackagesKeyword, provides: [
after: [], @74-78 ExposedName(
}, "main",
item: Collection { ),
],
before_packages: [
Newline,
],
packages: @6-44 Collection {
items: [ items: [
@30-37 SpaceBefore( @30-37 SpaceBefore(
PackageEntry { PackageEntry {
shorthand: "cli", shorthand: "cli",
spaces_after_shorthand: [], spaces_after_shorthand: [],
platform_marker: Some(
[],
),
package_name: @35-37 PackageName( package_name: @35-37 PackageName(
"", "",
), ),
@ -35,43 +36,8 @@ Full {
Newline, Newline,
], ],
}, },
}, old_imports: None,
), old_provides_to_new_package: None,
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",
),
},
}, },
), ),
}, },

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] #[test]
fn single_line_app() { fn single_line_app() {
module_formats_same(indoc!( module_formats_same(indoc!(
r#" 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!( module_formats_same(indoc!(
r#" r#"
# hello world # 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/dbg_multiline.expr,
pass/def_without_newline.expr, pass/def_without_newline.expr,
pass/destructure_tag_assignment.expr, pass/destructure_tag_assignment.expr,
pass/docs.expr,
pass/empty_app_header.header, pass/empty_app_header.header,
pass/empty_module_header.header, pass/empty_module_header.header,
pass/empty_hosted_header.header, pass/empty_hosted_header.header,
@ -333,7 +334,6 @@ mod test_snapshots {
pass/inline_import.expr, pass/inline_import.expr,
pass/inline_ingested_file.expr, pass/inline_ingested_file.expr,
pass/int_with_underscore.expr, pass/int_with_underscore.expr,
pass/module_with_newline.header,
pass/lambda_in_chain.expr, pass/lambda_in_chain.expr,
pass/lambda_indent.expr, pass/lambda_indent.expr,
pass/list_closing_indent_not_enough.expr, pass/list_closing_indent_not_enough.expr,
@ -349,6 +349,7 @@ mod test_snapshots {
pass/mixed_docs.expr, pass/mixed_docs.expr,
pass/module_def_newline.moduledefs, pass/module_def_newline.moduledefs,
pass/module_multiline_exposes.header, pass/module_multiline_exposes.header,
pass/module_with_newline.header,
pass/multi_backpassing.expr, pass/multi_backpassing.expr,
pass/multi_backpassing_in_def.moduledefs, pass/multi_backpassing_in_def.moduledefs,
pass/multi_backpassing_with_apply.expr, pass/multi_backpassing_with_apply.expr,
@ -386,9 +387,9 @@ mod test_snapshots {
pass/nonempty_hosted_header.header, pass/nonempty_hosted_header.header,
pass/nonempty_package_header.header, pass/nonempty_package_header.header,
pass/nonempty_platform_header.header, pass/nonempty_platform_header.header,
pass/docs.expr,
pass/not_multiline_string.expr, pass/not_multiline_string.expr,
pass/number_literal_suffixes.expr, pass/number_literal_suffixes.expr,
pass/old_app_header.full,
pass/one_backpassing.expr, pass/one_backpassing.expr,
pass/one_char_string.expr, pass/one_char_string.expr,
pass/one_def.expr, pass/one_def.expr,

View file

@ -225,17 +225,18 @@ impl IterTokens for ModuleHeader<'_> {
impl IterTokens for AppHeader<'_> { impl IterTokens for AppHeader<'_> {
fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> { fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> {
let Self { let Self {
before_name: _, before_provides: _,
name,
packages,
imports,
provides, provides,
before_packages: _,
packages,
old_imports,
old_provides_to_new_package: _,
} = self; } = self;
(name.iter_tokens(arena).into_iter()) (provides.iter_tokens(arena).into_iter())
.chain(packages.iter().flat_map(|p| p.item.iter_tokens(arena))) .chain(packages.value.iter_tokens(arena))
.chain(imports.iter().flat_map(|i| i.item.iter_tokens(arena)))
.chain(provides.iter_tokens(arena)) .chain(provides.iter_tokens(arena))
.chain(old_imports.iter().flat_map(|i| i.item.iter_tokens(arena)))
.collect_in(arena) .collect_in(arena)
} }
} }
@ -312,6 +313,7 @@ impl IterTokens for Loc<Spaced<'_, PackageEntry<'_>>> {
let PackageEntry { let PackageEntry {
shorthand: _, shorthand: _,
spaces_after_shorthand: _, spaces_after_shorthand: _,
platform_marker: _,
package_name, package_name,
} = self.value.item(); } = self.value.item();

View file

@ -124,6 +124,18 @@ pub(crate) mod diag {
error error
) )
} }
LoadingProblem::MultiplePlatformPackages { filename, .. } => {
format!(
"Multiple platform packages specified ({}). An app must specify exactly one platform.",
filename.display()
)
}
LoadingProblem::NoPlatformPackage { filename, .. } => {
format!(
"No platform package specified ({}). An app must specify exactly one platform.",
filename.display()
)
}
LoadingProblem::ParsingFailed(fe) => { LoadingProblem::ParsingFailed(fe) => {
let problem = &fe.problem.problem; let problem = &fe.problem.problem;
format!("Failed to parse Roc source file: {problem:?}") format!("Failed to parse Roc source file: {problem:?}")

View file

@ -1,7 +1,8 @@
app "args" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [pf.Stdout, pf.Arg, pf.Task.{ Task }] import pf.Stdout
provides [main] to pf import pf.Arg
import pf.Task exposing [Task]
main = main =
args = Arg.list! args = Arg.list!

View file

@ -1,7 +1,8 @@
app "countdown" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [pf.Stdin, pf.Stdout, pf.Task.{ await, loop }] import pf.Stdin
provides [main] to pf import pf.Stdout
import pf.Task exposing [await, loop]
main = main =
_ <- await (Stdout.line "\nLet's count down from 3 together - all you have to do is press <ENTER>.") _ <- await (Stdout.line "\nLet's count down from 3 together - all you have to do is press <ENTER>.")

View file

@ -1,7 +1,8 @@
app "echo" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [pf.Stdin, pf.Stdout, pf.Task.{ Task }] import pf.Stdin
provides [main] to pf import pf.Stdout
import pf.Task exposing [Task]
main = main =
_ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂") _ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂")

View file

@ -1,7 +1,6 @@
app "effects" app [main] { pf: platform "effects-platform/main.roc" }
packages { pf: "effects-platform/main.roc" }
imports [pf.Effect] import pf.Effect
provides [main] to pf
main : Effect.Effect {} main : Effect.Effect {}
main = main =

View file

@ -1,7 +1,9 @@
app "env" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [pf.Stdout, pf.Stderr, pf.Env, pf.Task.{ Task }] import pf.Stdout
provides [main] to pf import pf.Stderr
import pf.Env
import pf.Task exposing [Task]
main = main =
task = task =

View file

@ -1,7 +1,10 @@
app "false" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Task.{ Task }, pf.Stdout, pf.Stdin, Context.{ Context }, Variable.{ Variable }] import pf.Task exposing [Task]
provides [main] to pf import pf.Stdout
import pf.Stdin
import Context exposing [Context]
import Variable exposing [Variable]
# An interpreter for the False programming language: https://strlen.com/false-language/ # An interpreter for the False programming language: https://strlen.com/false-language/
# This is just a silly example to test this variety of program. # This is just a silly example to test this variety of program.

View file

@ -1,14 +1,11 @@
app "file-io" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [ import pf.Stdout
pf.Stdout, import pf.Task exposing [Task]
pf.Task.{ Task }, import pf.File
pf.File, import pf.Path
pf.Path, import pf.Env
pf.Env, import pf.Dir
pf.Dir,
]
provides [main] to pf
main : Task {} [Exit I32 Str]_ main : Task {} [Exit I32 Str]_
main = main =

View file

@ -1,7 +1,8 @@
app "form" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [pf.Stdin, pf.Stdout, pf.Task] import pf.Stdin
provides [main] to pf import pf.Stdout
import pf.Task exposing [await, Task]
main = main =
Stdout.line! "What's your first name?" Stdout.line! "What's your first name?"

View file

@ -1,7 +1,8 @@
app "http-get" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [pf.Http, pf.Task, pf.Stdout] import pf.Http
provides [main] to pf import pf.Task exposing [Task]
import pf.Stdout
main = main =
request = { request = {

View file

@ -1,11 +1,7 @@
app "ingested-file-bytes" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [ import pf.Stdout
pf.Stdout, import "../../LICENSE" as license : _ # A type hole can also be used here.
pf.Task,
"../../LICENSE" as license : _, # A type hole can also be used here.
]
provides [main] to pf
main = main =
# Due to how license is used, it will be a List U8. # Due to how license is used, it will be a List U8.

View file

@ -1,11 +1,7 @@
app "ingested-file" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [ import pf.Stdout
pf.Stdout, import "ingested-file.roc" as ownCode : Str
pf.Task,
"ingested-file.roc" as ownCode : Str,
]
provides [main] to pf
main = main =
Stdout.line! "\nThis roc file can print its own source code. The source is:\n\n$(ownCode)" Stdout.line! "\nThis roc file can print its own source code. The source is:\n\n$(ownCode)"

View file

@ -1,7 +1,6 @@
app "tui" app [main, Model] { pf: platform "tui-platform/main.roc" }
packages { pf: "tui-platform/main.roc" }
imports [pf.Program.{ Program }] import pf.Program exposing [Program]
provides [main] { Model } to pf
Model : Str Model : Str

View file

@ -1,7 +1,4 @@
app "rocLovesRust" app [main] { pf: platform "rust-platform/main.roc" }
packages { pf: "rust-platform/main.roc" }
imports []
provides [main] to pf
main = main =
msg = "Roc <3 Rust, also on stderr!\n" msg = "Roc <3 Rust, also on stderr!\n"

View file

@ -1,7 +1,6 @@
app "breakout" app [program, Model] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Game.{ Bounds, Elem, Event }] import pf.Game exposing [Bounds, Elem, Event]
provides [program] { Model } to pf
paddleWidth = 0.2 # width of the paddle, as a % of screen width paddleWidth = 0.2 # width of the paddle, as a % of screen width
paddleHeight = 50 # height of the paddle, in pixels paddleHeight = 50 # height of the paddle, in pixels

View file

@ -1,7 +1,6 @@
app "hello-gui" app [program, Model] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [pf.Game.{ Bounds, Elem, Event }] import pf.Game exposing [Bounds, Elem, Event]
provides [program] { Model } to pf
Model : { text : Str } Model : { text : Str }

View file

@ -1,7 +1,5 @@
app "hello-gui" app # [pf.Action.{ Action }, pf.Elem.{ button, text, row, col }]
packages { pf: "platform/main.roc" } [render] { pf: platform "platform/main.roc" }
imports [] # [pf.Action.{ Action }, pf.Elem.{ button, text, row, col }]
provides [render] to pf
render = render =
rgba = \r, g, b, a -> { r: r / 255, g: g / 255, b: b / 255, a } rgba = \r, g, b, a -> { r: r / 255, g: g / 255, b: b / 255, a }

View file

@ -1,7 +1,7 @@
app "helloWorld" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [pf.Stdout, pf.Task] import pf.Stdout
provides [main] to pf import pf.Task
main = main =
Stdout.line! "Hello, World!" Stdout.line! "Hello, World!"

View file

@ -1,13 +1,10 @@
# #
# Visualizes Roc values in a basic GUI # Visualizes Roc values in a basic GUI
# #
app "inspect-gui" app [render] { pf: platform "gui/platform/main.roc" }
packages { pf: "gui/platform/main.roc" }
imports [ import Community
Community, import GuiFormatter
GuiFormatter,
]
provides [render] to pf
render = render =
Community.empty Community.empty

View file

@ -1,14 +1,11 @@
# #
# Shows how Roc values can be logged # Shows how Roc values can be logged
# #
app "inspect-logging" app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br" }
imports [ import pf.Stdout
pf.Stdout, import pf.Task
pf.Task, import Community
Community,
]
provides [main] to pf
main = main =
Community.empty Community.empty

View file

@ -1,7 +1,4 @@
app "rocdemo" app [program] { pf: platform "platform.roc" }
packages { pf: "platform.roc" }
imports []
provides [program] to pf
interpolateString : Str -> Str interpolateString : Str -> Str
interpolateString = \name -> interpolateString = \name ->

View file

@ -1,7 +1,4 @@
app "libhello" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports []
provides [main] to pf
main : Str -> Str main : Str -> Str
main = \message -> main = \message ->

View file

@ -1,7 +1,4 @@
app "roc-app" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports []
provides [main] to pf
main : Str main : Str
main = "Hello from Roc!" main = "Hello from Roc!"

View file

@ -1,16 +1,12 @@
app "example" app [main] {
packages { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
cli: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br", parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
} }
imports [
cli.Stdout, import cli.Stdout
cli.Stderr, import cli.Stderr
cli.Task, import parser.Core exposing [Parser, buildPrimitiveParser, many]
parser.Core.{ Parser, buildPrimitiveParser, many }, import parser.String exposing [parseStr]
parser.String.{ parseStr },
]
provides [main] to cli
main = main =
lettersInput = "AAAiBByAABBwBtCCCiAyArBBx" lettersInput = "AAAiBByAABBwBtCCCiAyArBBx"

View file

@ -1,17 +1,14 @@
app "example" app [main] {
packages { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
pf: "https://github.com/roc-lang/basic-cli/releases/download/0.10.0/vNe6s9hWzoTZtFmNkvEICPErI9ptji_ySjicO6CkucY.tar.br",
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br", parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br",
} }
imports [
pf.Stdout, import pf.Stdout
pf.Stderr, import pf.Stderr
pf.Task, import pf.Task exposing [Task]
parser.Core.{ Parser, map, keep }, import parser.Core exposing [Parser, map, keep]
parser.String.{ strFromUtf8 }, import parser.String exposing [strFromUtf8]
parser.CSV.{ CSV }, import parser.CSV exposing [CSV]
]
provides [main] to pf
input : Str input : Str
input = "Airplane!,1980,\"Robert Hays,Julie Hagerty\"\r\nCaddyshack,1980,\"Chevy Chase,Rodney Dangerfield,Ted Knight,Michael O'Keefe,Bill Murray\"" input = "Airplane!,1980,\"Robert Hays,Julie Hagerty\"\r\nCaddyshack,1980,\"Chevy Chase,Rodney Dangerfield,Ted Knight,Michael O'Keefe,Bill Murray\""

View file

@ -1,11 +1,3 @@
app "rocLovesPlatforms" app [main] { pf: platform "c-platform/main.roc" }
packages { pf: "c-platform/main.roc" }
# To switch platforms, comment-out the line above and un-comment one below.
# packages { pf: "rust-platform/main.roc" }
# packages { pf: "swift-platform/main.roc" }
# packages { pf: "web-assembly-platform/main.roc" } # See ./web-assembly-platform/README.md
# packages { pf: "zig-platform/main.roc" }
imports []
provides [main] to pf
main = "Which platform am I running on now?\n" main = "Which platform am I running on now?\n"

View file

@ -1,6 +1,3 @@
app "rocLovesC" app [main] { pf: platform "c-platform/main.roc" }
packages { pf: "c-platform/main.roc" }
imports []
provides [main] to pf
main = "Roc <3 C!\n" main = "Roc <3 C!\n"

View file

@ -1,6 +1,3 @@
app "rocLovesRust" app [main] { pf: platform "rust-platform/main.roc" }
packages { pf: "rust-platform/main.roc" }
imports []
provides [main] to pf
main = "Roc <3 Rust!\n" main = "Roc <3 Rust!\n"

View file

@ -1,6 +1,3 @@
app "rocLovesSwift" app [main] { pf: platform "swift-platform/main.roc" }
packages { pf: "swift-platform/main.roc" }
imports []
provides [main] to pf
main = "Roc <3 Swift!\n" main = "Roc <3 Swift!\n"

View file

@ -1,6 +1,3 @@
app "rocLovesWebAssembly" app [main] { pf: platform "web-assembly-platform/main.roc" }
packages { pf: "web-assembly-platform/main.roc" }
imports []
provides [main] to pf
main = "Roc <3 Web Assembly!\n" main = "Roc <3 Web Assembly!\n"

View file

@ -1,6 +1,3 @@
app "rocLovesZig" app [main] { pf: platform "zig-platform/main.roc" }
packages { pf: "zig-platform/main.roc" }
imports []
provides [main] to pf
main = "Roc <3 Zig!\n" main = "Roc <3 Zig!\n"

View file

@ -1,7 +1,4 @@
app "libhello" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports []
provides [main] to pf
main : U64 -> Str main : U64 -> Str
main = \num -> main = \num ->

View file

@ -1,7 +1,4 @@
app "libhello" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports []
provides [main] to pf
main : U64 -> Str main : U64 -> Str
main = \num -> main = \num ->

View file

@ -1,12 +1,9 @@
## This is a documentation comment ## This is a documentation comment
# This is a comment # This is a comment
app "static-site" app [transformFileContent] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [ import pf.Html exposing [html, head, body, div, text, a, ul, li, link, meta]
pf.Html.{ html, head, body, div, text, a, ul, li, link, meta }, import pf.Html.Attributes exposing [httpEquiv, content, href, rel, lang, class, title]
pf.Html.Attributes.{ httpEquiv, content, href, rel, lang, class, title },
]
provides [transformFileContent] to pf
NavLink : { NavLink : {
# this is another comment # this is another comment

View file

@ -1,10 +1,7 @@
app "static-site" app [transformFileContent] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports [ import pf.Html exposing [html, head, body, div, text, a, ul, li, link, meta]
pf.Html.{ html, head, body, div, text, a, ul, li, link, meta }, import pf.Html.Attributes exposing [httpEquiv, content, href, rel, lang, class, title]
pf.Html.Attributes.{ httpEquiv, content, href, rel, lang, class, title },
]
provides [transformFileContent] to pf
NavLink : { NavLink : {
url : Str, url : Str,

View file

@ -1,6 +1,3 @@
app "swiftui" app [main] { pf: platform "platform/main.roc" }
packages { pf: "platform/main.roc" }
imports []
provides [main] to pf
main = "Roc <3 Swift!\n" main = "Roc <3 Swift!\n"

View file

@ -1,6 +1,5 @@
app "example-client" app [app] { pf: platform "platform/client-side.roc" }
packages { pf: "platform/client-side.roc" }
imports [ExampleApp.{ exampleApp }] import ExampleApp exposing [exampleApp]
provides [app] to pf
app = exampleApp app = exampleApp

View file

@ -1,6 +1,5 @@
app "example-server" app [app] { pf: platform "platform/server-side.roc" }
packages { pf: "platform/server-side.roc" }
imports [ExampleApp.{ exampleApp }] import ExampleApp exposing [exampleApp]
provides [app] to pf
app = exampleApp app = exampleApp

View file

@ -1,14 +1,11 @@
app "http" app [main] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.1/dCL3KsovvV-8A5D_W_0X_abynkcRcoAngsgF0xtvQsk.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-webserver/releases/download/0.1/dCL3KsovvV-8A5D_W_0X_abynkcRcoAngsgF0xtvQsk.tar.br" }
imports [ import pf.Stdout
pf.Stdout, import pf.Stderr
pf.Stderr, import pf.Task exposing [Task]
pf.Task.{ Task }, import pf.Http exposing [Request, Response]
pf.Http.{ Request, Response }, import pf.Utc
pf.Utc, import pf.Env
pf.Env,
]
provides [main] to pf
main : Request -> Task Response [] main : Request -> Task Response []
main = \req -> main = \req ->

View file

@ -1,12 +1,9 @@
app "echo" app [main] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.1/dCL3KsovvV-8A5D_W_0X_abynkcRcoAngsgF0xtvQsk.tar.br" }
packages { pf: "https://github.com/roc-lang/basic-webserver/releases/download/0.1/dCL3KsovvV-8A5D_W_0X_abynkcRcoAngsgF0xtvQsk.tar.br" }
imports [ import pf.Stdout
pf.Stdout, import pf.Task exposing [Task]
pf.Task.{ Task }, import pf.Http exposing [Request, Response]
pf.Http.{ Request, Response }, import pf.Utc
pf.Utc,
]
provides [main] to pf
main : Request -> Task Response [] main : Request -> Task Response []
main = \req -> main = \req ->