Merge branch 'main' of github.com:rtfeldman/roc into wasm_interp_imports

This commit is contained in:
Brian Carroll 2022-12-01 21:33:31 +00:00
commit 04d493c49e
No known key found for this signature in database
GPG key ID: 9CF4E3BF9C4722C7
32 changed files with 1890 additions and 1579 deletions

4
Cargo.lock generated
View file

@ -5860,7 +5860,7 @@ dependencies = [
[[package]] [[package]]
name = "wasm3" name = "wasm3"
version = "0.5.0" version = "0.5.0"
source = "git+https://github.com/roc-lang/wasm3-rs?rev=f0f807d1fc0a50d1d68e5799e54ee62c05af00f5#f0f807d1fc0a50d1d68e5799e54ee62c05af00f5" source = "git+https://github.com/roc-lang/wasm3-rs?rev=ba0cdab7404f7f2995a8c18e614ce020dabd6da0#ba0cdab7404f7f2995a8c18e614ce020dabd6da0"
dependencies = [ dependencies = [
"cty", "cty",
"wasm3-sys", "wasm3-sys",
@ -5869,7 +5869,7 @@ dependencies = [
[[package]] [[package]]
name = "wasm3-sys" name = "wasm3-sys"
version = "0.5.0" version = "0.5.0"
source = "git+https://github.com/roc-lang/wasm3-rs?rev=f0f807d1fc0a50d1d68e5799e54ee62c05af00f5#f0f807d1fc0a50d1d68e5799e54ee62c05af00f5" source = "git+https://github.com/roc-lang/wasm3-rs?rev=ba0cdab7404f7f2995a8c18e614ce020dabd6da0#ba0cdab7404f7f2995a8c18e614ce020dabd6da0"
dependencies = [ dependencies = [
"cc", "cc",
"cty", "cty",

View file

@ -112,7 +112,7 @@ target-lexicon = "0.12.3"
tempfile = "3.2.0" tempfile = "3.2.0"
unicode-segmentation = "1.10.0" unicode-segmentation = "1.10.0"
walkdir = "2.3.2" walkdir = "2.3.2"
wasm3 = { git = "https://github.com/roc-lang/wasm3-rs", rev = "f0f807d1fc0a50d1d68e5799e54ee62c05af00f5" } wasm3 = { git = "https://github.com/roc-lang/wasm3-rs", rev = "ba0cdab7404f7f2995a8c18e614ce020dabd6da0" }
wyhash = "0.5.0" wyhash = "0.5.0"
# TODO: Deal with the update of object to 0.27. # TODO: Deal with the update of object to 0.27.

View file

@ -235,14 +235,7 @@ mod cli_run {
) )
} }
} }
CliMode::Roc => { CliMode::Roc => run_roc_on(file, flags.clone(), stdin, roc_app_args, extra_env),
if !extra_env.is_empty() {
// TODO: `roc` and `roc dev` are currently buggy for `env.roc`
continue;
}
run_roc_on(file, flags.clone(), stdin, roc_app_args, extra_env)
}
CliMode::RocRun => run_roc_on( CliMode::RocRun => run_roc_on(
file, file,
iter::once(CMD_RUN).chain(flags.clone()), iter::once(CMD_RUN).chain(flags.clone()),

View file

@ -1491,7 +1491,14 @@ pub fn llvm_module_to_dylib(
) )
.unwrap(); .unwrap();
child.wait().unwrap(); let exit_status = child.wait().unwrap();
assert!(
exit_status.success(),
"\n___________\nLinking command failed with status {:?}:\n\n {:?}\n___________\n",
exit_status,
child
);
// Load the dylib // Load the dylib
let path = dylib_path.as_path().to_str().unwrap(); let path = dylib_path.as_path().to_str().unwrap();

View file

@ -63,9 +63,7 @@ pub trait Formattable {
_parens: Parens, _parens: Parens,
_newlines: Newlines, _newlines: Newlines,
indent: u16, indent: u16,
) { );
self.format(buf, indent);
}
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) { fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) {
self.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent); self.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
@ -96,18 +94,13 @@ where
} }
} }
impl<'a, T> Formattable for Collection<'a, T> pub fn is_collection_multiline<T: Formattable>(collection: &Collection<'_, T>) -> bool {
where
T: Formattable,
{
fn is_multiline(&self) -> bool {
// if there are any comments, they must go on their own line // if there are any comments, they must go on their own line
// because otherwise they'd comment out the closing delimiter // because otherwise they'd comment out the closing delimiter
!self.final_comments().is_empty() || !collection.final_comments().is_empty() ||
// if any of the items in the collection are multiline, // if any of the items in the collection are multiline,
// then the whole collection must be multiline // then the whole collection must be multiline
self.items.iter().any(Formattable::is_multiline) collection.items.iter().any(Formattable::is_multiline)
}
} }
/// A Located formattable value is also formattable /// A Located formattable value is also formattable
@ -577,7 +570,7 @@ impl<'a> Formattable for HasImpls<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
match self { match self {
HasImpls::SpaceBefore(_, _) | HasImpls::SpaceAfter(_, _) => true, HasImpls::SpaceBefore(_, _) | HasImpls::SpaceAfter(_, _) => true,
HasImpls::HasImpls(impls) => impls.is_multiline(), HasImpls::HasImpls(impls) => is_collection_multiline(impls),
} }
} }
@ -657,7 +650,7 @@ impl<'a> Formattable for HasAbilities<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
match self { match self {
HasAbilities::SpaceAfter(..) | HasAbilities::SpaceBefore(..) => true, HasAbilities::SpaceAfter(..) | HasAbilities::SpaceBefore(..) => true,
HasAbilities::Has(has_abilities) => has_abilities.is_multiline(), HasAbilities::Has(has_abilities) => is_collection_multiline(has_abilities),
} }
} }

View file

@ -1,7 +1,7 @@
use roc_parse::ast::{Collection, CommentOrNewline, ExtractSpaces}; use roc_parse::ast::{Collection, CommentOrNewline, ExtractSpaces};
use crate::{ use crate::{
annotation::{Formattable, Newlines}, annotation::{is_collection_multiline, Formattable, Newlines},
spaces::{fmt_comments_only, NewlineAt, INDENT}, spaces::{fmt_comments_only, NewlineAt, INDENT},
Buf, Buf,
}; };
@ -34,7 +34,7 @@ pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
Braces::Square => ']', Braces::Square => ']',
}; };
if items.is_multiline() { if is_collection_multiline(&items) {
let braces_indent = indent; let braces_indent = indent;
let item_indent = braces_indent + INDENT; let item_indent = braces_indent + INDENT;
if newline == Newlines::Yes { if newline == Newlines::Yes {

View file

@ -1,184 +1,248 @@
use crate::annotation::{Formattable, Newlines}; use crate::annotation::{is_collection_multiline, Formattable, Newlines, Parens};
use crate::collection::{fmt_collection, Braces}; use crate::collection::{fmt_collection, Braces};
use crate::expr::fmt_str_literal; use crate::expr::fmt_str_literal;
use crate::spaces::RemoveSpaces;
use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT}; use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT};
use crate::Buf; use crate::Buf;
use roc_parse::ast::{Collection, Module, Spaced}; use bumpalo::Bump;
use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces};
use roc_parse::header::{ use roc_parse::header::{
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
PackageName, PlatformHeader, PlatformRequires, To, TypedIdent, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, PackageEntry,
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, 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;
pub fn fmt_module<'a>(buf: &mut Buf<'_>, module: &'a Module<'a>) { pub fn fmt_module<'a>(buf: &mut Buf<'_>, module: &'a Module<'a>) {
match module { fmt_comments_only(buf, module.comments.iter(), NewlineAt::Bottom, 0);
Module::Interface { header } => { match &module.header {
Header::Interface(header) => {
fmt_interface_header(buf, header); fmt_interface_header(buf, header);
} }
Module::App { header } => { Header::App(header) => {
fmt_app_header(buf, header); fmt_app_header(buf, header);
} }
Module::Platform { header } => { Header::Platform(header) => {
fmt_platform_header(buf, header); fmt_platform_header(buf, header);
} }
Module::Hosted { header } => { Header::Hosted(header) => {
fmt_hosted_header(buf, header); fmt_hosted_header(buf, header);
} }
} }
} }
macro_rules! keywords {
($($name:ident),* $(,)?) => {
$(
impl Formattable for $name {
fn is_multiline(&self) -> bool {
false
}
fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: crate::annotation::Parens,
_newlines: Newlines,
indent: u16,
) {
buf.indent(indent);
buf.push_str($name::KEYWORD);
}
}
impl<'a> RemoveSpaces<'a> for $name {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
*self
}
}
)*
}
}
keywords! {
ExposesKeyword,
ImportsKeyword,
WithKeyword,
GeneratesKeyword,
PackageKeyword,
PackagesKeyword,
RequiresKeyword,
ProvidesKeyword,
ToKeyword,
}
impl<V: Formattable> Formattable for Option<V> {
fn is_multiline(&self) -> bool {
if let Some(v) = self {
v.is_multiline()
} else {
false
}
}
fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
parens: crate::annotation::Parens,
newlines: Newlines,
indent: u16,
) {
if let Some(v) = self {
v.format_with_options(buf, parens, newlines, indent);
}
}
}
impl<'a> Formattable for ProvidesTo<'a> {
fn is_multiline(&self) -> bool {
if let Some(types) = &self.types {
if is_collection_multiline(types) {
return true;
}
}
self.provides_keyword.is_multiline()
|| is_collection_multiline(&self.entries)
|| self.to_keyword.is_multiline()
}
fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: crate::annotation::Parens,
_newlines: Newlines,
indent: u16,
) {
self.provides_keyword.format(buf, indent);
fmt_provides(buf, self.entries, self.types, indent);
self.to_keyword.format(buf, indent);
fmt_to(buf, self.to.value, indent);
}
}
impl<'a> Formattable for PlatformRequires<'a> {
fn is_multiline(&self) -> bool {
is_collection_multiline(&self.rigids) || self.signature.is_multiline()
}
fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: crate::annotation::Parens,
_newlines: Newlines,
indent: u16,
) {
fmt_requires(buf, self, indent);
}
}
impl<'a, V: Formattable> Formattable for Spaces<'a, V> {
fn is_multiline(&self) -> bool {
!self.before.is_empty() || !self.after.is_empty() || self.item.is_multiline()
}
fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
parens: crate::annotation::Parens,
newlines: Newlines,
indent: u16,
) {
fmt_default_spaces(buf, self.before, indent);
self.item.format_with_options(buf, parens, newlines, indent);
fmt_default_spaces(buf, self.after, indent);
}
}
impl<'a, K: Formattable, V: Formattable> Formattable for KeywordItem<'a, K, V> {
fn is_multiline(&self) -> bool {
self.keyword.is_multiline() || self.item.is_multiline()
}
fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
parens: Parens,
newlines: Newlines,
indent: u16,
) {
self.keyword
.format_with_options(buf, parens, newlines, indent);
self.item.format_with_options(buf, parens, newlines, indent);
}
}
pub fn fmt_interface_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a InterfaceHeader<'a>) { pub fn fmt_interface_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a InterfaceHeader<'a>) {
let indent = INDENT;
fmt_comments_only(buf, header.before_header.iter(), NewlineAt::Bottom, indent);
buf.indent(0); buf.indent(0);
buf.push_str("interface"); buf.push_str("interface");
let indent = INDENT;
fmt_default_spaces(buf, header.before_name, indent);
// module name // module name
fmt_default_spaces(buf, header.after_interface_keyword, indent); buf.indent(indent);
buf.push_str(header.name.value.as_str()); buf.push_str(header.name.value.as_str());
// exposes header.exposes.keyword.format(buf, indent);
fmt_default_spaces(buf, header.before_exposes, indent); fmt_exposes(buf, header.exposes.item, indent);
buf.indent(indent); header.imports.keyword.format(buf, indent);
buf.push_str("exposes"); fmt_imports(buf, header.imports.item, indent);
fmt_default_spaces(buf, header.after_exposes, indent);
fmt_exposes(buf, header.exposes, indent);
// imports
fmt_default_spaces(buf, header.before_imports, indent);
buf.indent(indent);
buf.push_str("imports");
fmt_default_spaces(buf, header.after_imports, indent);
fmt_imports(buf, header.imports, indent);
} }
pub fn fmt_hosted_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a HostedHeader<'a>) { pub fn fmt_hosted_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a HostedHeader<'a>) {
let indent = INDENT;
fmt_comments_only(buf, header.before_header.iter(), NewlineAt::Bottom, indent);
buf.indent(0); buf.indent(0);
buf.push_str("hosted"); buf.push_str("hosted");
let indent = INDENT;
fmt_default_spaces(buf, header.before_name, indent);
// module name
fmt_default_spaces(buf, header.after_hosted_keyword, indent);
buf.push_str(header.name.value.as_str()); buf.push_str(header.name.value.as_str());
// exposes header.exposes.keyword.format(buf, indent);
fmt_default_spaces(buf, header.before_exposes, indent); fmt_exposes(buf, header.exposes.item, indent);
buf.indent(indent); header.imports.keyword.format(buf, indent);
buf.push_str("exposes"); fmt_imports(buf, header.imports.item, indent);
fmt_default_spaces(buf, header.after_exposes, indent); header.generates.format(buf, indent);
fmt_exposes(buf, header.exposes, indent); header.generates_with.keyword.format(buf, indent);
fmt_exposes(buf, header.generates_with.item, indent);
// imports
fmt_default_spaces(buf, header.before_imports, indent);
buf.indent(indent);
buf.push_str("imports");
fmt_default_spaces(buf, header.after_imports, indent);
fmt_imports(buf, header.imports, indent);
// generates
fmt_default_spaces(buf, header.before_generates, indent);
buf.indent(indent);
buf.push_str("generates");
fmt_default_spaces(buf, header.after_generates, indent);
buf.push_str(header.generates.into());
// with
fmt_default_spaces(buf, header.before_with, indent);
buf.indent(indent);
buf.push_str("with");
fmt_default_spaces(buf, header.after_with, indent);
fmt_exposes(buf, header.generates_with, indent);
} }
pub fn fmt_app_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a AppHeader<'a>) { pub fn fmt_app_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a AppHeader<'a>) {
let indent = INDENT;
fmt_comments_only(buf, header.before_header.iter(), NewlineAt::Bottom, indent);
buf.indent(0); buf.indent(0);
buf.push_str("app"); buf.push_str("app");
let indent = INDENT;
fmt_default_spaces(buf, header.before_name, indent);
fmt_default_spaces(buf, header.after_app_keyword, indent);
fmt_str_literal(buf, header.name.value, indent); fmt_str_literal(buf, header.name.value, indent);
// packages if let Some(packages) = &header.packages {
fmt_default_spaces(buf, header.before_packages, indent); packages.keyword.format(buf, indent);
buf.indent(indent); fmt_packages(buf, packages.item, indent);
buf.push_str("packages"); }
fmt_default_spaces(buf, header.after_packages, indent); if let Some(imports) = &header.imports {
fmt_packages(buf, header.packages, indent); imports.keyword.format(buf, indent);
fmt_imports(buf, imports.item, indent);
// imports }
fmt_default_spaces(buf, header.before_imports, indent); header.provides.format(buf, indent);
buf.indent(indent);
buf.push_str("imports");
fmt_default_spaces(buf, header.after_imports, indent);
fmt_imports(buf, header.imports, indent);
// provides
fmt_default_spaces(buf, header.before_provides, indent);
buf.indent(indent);
buf.push_str("provides");
fmt_default_spaces(buf, header.after_provides, indent);
fmt_provides(buf, header.provides, header.provides_types, indent);
fmt_default_spaces(buf, header.before_to, indent);
buf.indent(indent);
buf.push_str("to");
fmt_default_spaces(buf, header.after_to, indent);
fmt_to(buf, header.to.value, indent);
} }
pub fn fmt_platform_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a PlatformHeader<'a>) { pub fn fmt_platform_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a PlatformHeader<'a>) {
let indent = INDENT;
fmt_comments_only(buf, header.before_header.iter(), NewlineAt::Bottom, indent);
buf.indent(0); buf.indent(0);
buf.push_str("platform"); buf.push_str("platform");
let indent = INDENT;
fmt_default_spaces(buf, header.before_name, indent);
fmt_default_spaces(buf, header.after_platform_keyword, indent);
fmt_package_name(buf, header.name.value, indent); fmt_package_name(buf, header.name.value, indent);
// requires header.requires.format(buf, indent);
fmt_default_spaces(buf, header.before_requires, indent); header.exposes.keyword.format(buf, indent);
buf.indent(indent); fmt_exposes(buf, header.exposes.item, indent);
buf.push_str("requires"); header.packages.keyword.format(buf, indent);
fmt_default_spaces(buf, header.after_requires, indent); fmt_packages(buf, header.packages.item, indent);
fmt_requires(buf, &header.requires, indent); header.imports.keyword.format(buf, indent);
fmt_imports(buf, header.imports.item, indent);
// exposes header.provides.keyword.format(buf, indent);
fmt_default_spaces(buf, header.before_exposes, indent); fmt_provides(buf, header.provides.item, None, indent);
buf.indent(indent);
buf.push_str("exposes");
fmt_default_spaces(buf, header.after_exposes, indent);
fmt_exposes(buf, header.exposes, indent);
// packages
fmt_default_spaces(buf, header.before_packages, indent);
buf.indent(indent);
buf.push_str("packages");
fmt_default_spaces(buf, header.after_packages, indent);
fmt_packages(buf, header.packages, indent);
// imports
fmt_default_spaces(buf, header.before_imports, indent);
buf.indent(indent);
buf.push_str("imports");
fmt_default_spaces(buf, header.after_imports, indent);
fmt_imports(buf, header.imports, indent);
// provides
fmt_default_spaces(buf, header.before_provides, indent);
buf.indent(indent);
buf.push_str("provides");
fmt_default_spaces(buf, header.after_provides, indent);
fmt_provides(buf, header.provides, None, indent);
} }
fn fmt_requires<'a, 'buf>(buf: &mut Buf<'buf>, requires: &PlatformRequires<'a>, indent: u16) { fn fmt_requires<'a, 'buf>(buf: &mut Buf<'buf>, requires: &PlatformRequires<'a>, indent: u16) {
@ -195,7 +259,13 @@ impl<'a> Formattable for TypedIdent<'a> {
false false
} }
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) { fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: Parens,
_newlines: Newlines,
indent: u16,
) {
buf.indent(indent); buf.indent(indent);
buf.push_str(self.ident.value); buf.push_str(self.ident.value);
fmt_default_spaces(buf, self.spaces_before_colon, indent); fmt_default_spaces(buf, self.spaces_before_colon, indent);
@ -306,7 +376,13 @@ impl<'a> Formattable for ModuleName<'a> {
false false
} }
fn format<'buf>(&self, buf: &mut Buf<'buf>, _indent: u16) { fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: Parens,
_newlines: Newlines,
_indent: u16,
) {
buf.push_str(self.as_str()); buf.push_str(self.as_str());
} }
} }
@ -316,7 +392,13 @@ impl<'a> Formattable for ExposedName<'a> {
false false
} }
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) { fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: Parens,
_newlines: Newlines,
indent: u16,
) {
buf.indent(indent); buf.indent(indent);
buf.push_str(self.as_str()); buf.push_str(self.as_str());
} }
@ -341,7 +423,13 @@ impl<'a> Formattable for PackageEntry<'a> {
false false
} }
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) { fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: Parens,
_newlines: Newlines,
indent: u16,
) {
fmt_packages_entry(buf, self, indent); fmt_packages_entry(buf, self, indent);
} }
} }
@ -351,7 +439,13 @@ impl<'a> Formattable for ImportsEntry<'a> {
false false
} }
fn format<'buf>(&self, buf: &mut Buf<'buf>, indent: u16) { fn format_with_options<'buf>(
&self,
buf: &mut Buf<'buf>,
_parens: Parens,
_newlines: Newlines,
indent: u16,
) {
fmt_imports_entry(buf, self, indent); fmt_imports_entry(buf, self, indent);
} }
} }

View file

@ -4,12 +4,13 @@ use roc_module::called_via::{BinOp, UnaryOp};
use roc_parse::{ use roc_parse::{
ast::{ ast::{
AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr, Has, HasAbilities, AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr, Has, HasAbilities,
HasAbility, HasClause, HasImpls, Module, Pattern, Spaced, StrLiteral, StrSegment, Tag, HasAbility, HasClause, HasImpls, Header, Module, Pattern, Spaced, Spaces, StrLiteral,
TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch,
}, },
header::{ header::{
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName, AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
PackageEntry, PackageName, PlatformHeader, PlatformRequires, To, TypedIdent, ModuleName, PackageEntry, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To,
TypedIdent,
}, },
ident::UppercaseIdent, ident::UppercaseIdent,
}; };
@ -242,83 +243,74 @@ impl<'a> RemoveSpaces<'a> for Defs<'a> {
} }
} }
impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
Spaces {
before: &[],
item: self.item.remove_spaces(arena),
after: &[],
}
}
}
impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
KeywordItem {
keyword: self.keyword.remove_spaces(arena),
item: self.item.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
ProvidesTo {
provides_keyword: self.provides_keyword.remove_spaces(arena),
entries: self.entries.remove_spaces(arena),
types: self.types.remove_spaces(arena),
to_keyword: self.to_keyword.remove_spaces(arena),
to: self.to.remove_spaces(arena),
}
}
}
impl<'a> RemoveSpaces<'a> for Module<'a> { impl<'a> RemoveSpaces<'a> for Module<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self { fn remove_spaces(&self, arena: &'a Bump) -> Self {
match self { let header = match &self.header {
Module::Interface { header } => Module::Interface { Header::Interface(header) => Header::Interface(InterfaceHeader {
header: InterfaceHeader { before_name: &[],
name: header.name.remove_spaces(arena), name: header.name.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena), exposes: header.exposes.remove_spaces(arena),
imports: header.imports.remove_spaces(arena), imports: header.imports.remove_spaces(arena),
before_header: &[], }),
after_interface_keyword: &[], Header::App(header) => Header::App(AppHeader {
before_exposes: &[], before_name: &[],
after_exposes: &[],
before_imports: &[],
after_imports: &[],
},
},
Module::App { header } => Module::App {
header: AppHeader {
name: header.name.remove_spaces(arena), name: header.name.remove_spaces(arena),
packages: header.packages.remove_spaces(arena), packages: header.packages.remove_spaces(arena),
imports: header.imports.remove_spaces(arena), imports: header.imports.remove_spaces(arena),
provides: header.provides.remove_spaces(arena), provides: header.provides.remove_spaces(arena),
provides_types: header.provides_types.map(|ts| ts.remove_spaces(arena)), }),
to: header.to.remove_spaces(arena), Header::Platform(header) => Header::Platform(PlatformHeader {
before_header: &[], before_name: &[],
after_app_keyword: &[],
before_packages: &[],
after_packages: &[],
before_imports: &[],
after_imports: &[],
before_provides: &[],
after_provides: &[],
before_to: &[],
after_to: &[],
},
},
Module::Platform { header } => Module::Platform {
header: PlatformHeader {
name: header.name.remove_spaces(arena), name: header.name.remove_spaces(arena),
requires: header.requires.remove_spaces(arena), requires: header.requires.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena), exposes: header.exposes.remove_spaces(arena),
packages: header.packages.remove_spaces(arena), packages: header.packages.remove_spaces(arena),
imports: header.imports.remove_spaces(arena), imports: header.imports.remove_spaces(arena),
provides: header.provides.remove_spaces(arena), provides: header.provides.remove_spaces(arena),
before_header: &[], }),
after_platform_keyword: &[], Header::Hosted(header) => Header::Hosted(HostedHeader {
before_requires: &[], before_name: &[],
after_requires: &[],
before_exposes: &[],
after_exposes: &[],
before_packages: &[],
after_packages: &[],
before_imports: &[],
after_imports: &[],
before_provides: &[],
after_provides: &[],
},
},
Module::Hosted { header } => Module::Hosted {
header: HostedHeader {
name: header.name.remove_spaces(arena), name: header.name.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena), exposes: header.exposes.remove_spaces(arena),
imports: header.imports.remove_spaces(arena), imports: header.imports.remove_spaces(arena),
generates: header.generates.remove_spaces(arena), generates: header.generates.remove_spaces(arena),
generates_with: header.generates_with.remove_spaces(arena), generates_with: header.generates_with.remove_spaces(arena),
before_header: &[], }),
after_hosted_keyword: &[], };
before_exposes: &[], Module {
after_exposes: &[], comments: &[],
before_imports: &[], header,
after_imports: &[],
before_generates: &[],
after_generates: &[],
before_with: &[],
after_with: &[],
},
},
} }
} }
} }

View file

@ -3003,7 +3003,10 @@ fn update<'a>(
); );
} }
log!("re-launching specializations pass"); log!(
"re-launching make-specializations: pass {}",
state.make_specializations_pass.current_pass() + 1
);
state.make_specializations_pass.inc(); state.make_specializations_pass.inc();
@ -3293,25 +3296,43 @@ fn load_platform_module<'a>(
pkg_module_timing.parse_header = parse_header_duration; pkg_module_timing.parse_header = parse_header_duration;
match parsed { match parsed {
Ok((ast::Module::Interface { header }, _parse_state)) => { Ok((
Err(LoadingProblem::UnexpectedHeader(format!( ast::Module {
header: ast::Header::Interface(header),
..
},
_parse_state,
)) => Err(LoadingProblem::UnexpectedHeader(format!(
"expected platform/package module, got Interface with header\n{:?}", "expected platform/package module, got Interface with header\n{:?}",
header header
))) ))),
} Ok((
Ok((ast::Module::Hosted { header }, _parse_state)) => { ast::Module {
Err(LoadingProblem::UnexpectedHeader(format!( header: ast::Header::Hosted(header),
..
},
_parse_state,
)) => Err(LoadingProblem::UnexpectedHeader(format!(
"expected platform/package module, got Hosted module with header\n{:?}", "expected platform/package module, got Hosted module with header\n{:?}",
header header
))) ))),
} Ok((
Ok((ast::Module::App { header }, _parse_state)) => { ast::Module {
Err(LoadingProblem::UnexpectedHeader(format!( header: ast::Header::App(header),
..
},
_parse_state,
)) => Err(LoadingProblem::UnexpectedHeader(format!(
"expected platform/package module, got App with header\n{:?}", "expected platform/package module, got App with header\n{:?}",
header header
))) ))),
} Ok((
Ok((ast::Module::Platform { header }, parser_state)) => { ast::Module {
header: ast::Header::Platform(header),
..
},
parser_state,
)) => {
// make a `platform` module that ultimately exposes `main` to the host // make a `platform` module that ultimately exposes `main` to the host
let platform_module_msg = fabricate_platform_module( let platform_module_msg = fabricate_platform_module(
arena, arena,
@ -3356,7 +3377,13 @@ fn load_builtin_module_help<'a>(
let parsed = roc_parse::module::parse_header(arena, parse_state.clone()); let parsed = roc_parse::module::parse_header(arena, parse_state.clone());
match parsed { match parsed {
Ok((ast::Module::Interface { header }, parse_state)) => { Ok((
ast::Module {
header: ast::Header::Interface(header),
..
},
parse_state,
)) => {
let info = HeaderInfo { let info = HeaderInfo {
loc_name: Loc { loc_name: Loc {
region: header.name.region, region: header.name.region,
@ -3366,8 +3393,8 @@ fn load_builtin_module_help<'a>(
is_root_module, is_root_module,
opt_shorthand, opt_shorthand,
packages: &[], packages: &[],
exposes: unspace(arena, header.exposes.items), exposes: unspace(arena, header.exposes.item.items),
imports: unspace(arena, header.imports.items), imports: unspace(arena, header.imports.item.items),
extra: HeaderFor::Builtin { extra: HeaderFor::Builtin {
generates_with: &[], generates_with: &[],
}, },
@ -3643,7 +3670,13 @@ fn parse_header<'a>(
module_timing.parse_header = parse_header_duration; module_timing.parse_header = parse_header_duration;
match parsed { match parsed {
Ok((ast::Module::Interface { header }, parse_state)) => { Ok((
ast::Module {
header: ast::Header::Interface(header),
..
},
parse_state,
)) => {
verify_interface_matches_file_path(header.name, &filename, &parse_state)?; verify_interface_matches_file_path(header.name, &filename, &parse_state)?;
let header_name_region = header.name.region; let header_name_region = header.name.region;
@ -3657,8 +3690,8 @@ fn parse_header<'a>(
is_root_module, is_root_module,
opt_shorthand, opt_shorthand,
packages: &[], packages: &[],
exposes: unspace(arena, header.exposes.items), exposes: unspace(arena, header.exposes.item.items),
imports: unspace(arena, header.imports.items), imports: unspace(arena, header.imports.item.items),
extra: HeaderFor::Interface, extra: HeaderFor::Interface,
}; };
@ -3690,7 +3723,13 @@ fn parse_header<'a>(
Ok((module_id, Msg::Header(header))) Ok((module_id, Msg::Header(header)))
} }
Ok((ast::Module::Hosted { header }, parse_state)) => { Ok((
ast::Module {
header: ast::Header::Hosted(header),
..
},
parse_state,
)) => {
let info = HeaderInfo { let info = HeaderInfo {
loc_name: Loc { loc_name: Loc {
region: header.name.region, region: header.name.region,
@ -3700,11 +3739,11 @@ fn parse_header<'a>(
is_root_module, is_root_module,
opt_shorthand, opt_shorthand,
packages: &[], packages: &[],
exposes: unspace(arena, header.exposes.items), exposes: unspace(arena, header.exposes.item.items),
imports: unspace(arena, header.imports.items), imports: unspace(arena, header.imports.item.items),
extra: HeaderFor::Hosted { extra: HeaderFor::Hosted {
generates: header.generates, generates: header.generates.item,
generates_with: unspace(arena, header.generates_with.items), generates_with: unspace(arena, header.generates_with.item.items),
}, },
}; };
@ -3718,16 +3757,27 @@ fn parse_header<'a>(
Ok((module_id, Msg::Header(header))) Ok((module_id, Msg::Header(header)))
} }
Ok((ast::Module::App { header }, parse_state)) => { Ok((
ast::Module {
header: ast::Header::App(header),
..
},
parse_state,
)) => {
let mut app_file_dir = filename.clone(); let mut app_file_dir = filename.clone();
app_file_dir.pop(); app_file_dir.pop();
let packages = unspace(arena, header.packages.items); let packages = if let Some(packages) = header.packages {
unspace(arena, packages.item.items)
} else {
&[]
};
let mut exposes = bumpalo::collections::Vec::new_in(arena); let mut exposes = bumpalo::collections::Vec::new_in(arena);
exposes.extend(unspace(arena, header.provides.items));
if let Some(provided_types) = header.provides_types { exposes.extend(unspace(arena, header.provides.entries.items));
if let Some(provided_types) = header.provides.types {
for provided_type in unspace(arena, provided_types.items) { for provided_type in unspace(arena, provided_types.items) {
let string: &str = provided_type.value.into(); let string: &str = provided_type.value.into();
let exposed_name = ExposedName::new(string); let exposed_name = ExposedName::new(string);
@ -3748,9 +3798,13 @@ fn parse_header<'a>(
opt_shorthand, opt_shorthand,
packages, packages,
exposes, exposes,
imports: unspace(arena, header.imports.items), imports: if let Some(imports) = header.imports {
unspace(arena, imports.item.items)
} else {
&[]
},
extra: HeaderFor::App { extra: HeaderFor::App {
to_platform: header.to.value, to_platform: header.provides.to.value,
}, },
}; };
@ -3763,7 +3817,7 @@ fn parse_header<'a>(
); );
let app_module_header_msg = Msg::Header(resolved_header); let app_module_header_msg = Msg::Header(resolved_header);
match header.to.value { match header.provides.to.value {
To::ExistingPackage(existing_package) => { To::ExistingPackage(existing_package) => {
let opt_base_package = packages.iter().find_map(|loc_package_entry| { let opt_base_package = packages.iter().find_map(|loc_package_entry| {
let Loc { value, .. } = loc_package_entry; let Loc { value, .. } = loc_package_entry;
@ -3851,7 +3905,13 @@ fn parse_header<'a>(
To::NewPackage(_package_name) => Ok((module_id, app_module_header_msg)), To::NewPackage(_package_name) => Ok((module_id, app_module_header_msg)),
} }
} }
Ok((ast::Module::Platform { header }, parse_state)) => Ok(fabricate_platform_module( Ok((
ast::Module {
header: ast::Header::Platform(header),
..
},
parse_state,
)) => Ok(fabricate_platform_module(
arena, arena,
None, None,
None, None,
@ -4881,13 +4941,13 @@ fn fabricate_platform_module<'a>(
opt_shorthand, opt_shorthand,
opt_app_module_id, opt_app_module_id,
packages: &[], packages: &[],
provides: unspace(arena, header.provides.items), provides: unspace(arena, header.provides.item.items),
requires: &*arena.alloc([Loc::at( requires: &*arena.alloc([Loc::at(
header.requires.signature.region, header.requires.item.signature.region,
header.requires.signature.extract_spaces().item, header.requires.item.signature.extract_spaces().item,
)]), )]),
requires_types: unspace(arena, header.requires.rigids.items), requires_types: unspace(arena, header.requires.item.rigids.items),
imports: unspace(arena, header.imports.items), imports: unspace(arena, header.imports.item.items),
}; };
send_header_two( send_header_two(

View file

@ -454,6 +454,8 @@ impl<'a> Dependencies<'a> {
pub fn load_find_and_make_specializations_after_check(&mut self) -> MutSet<(ModuleId, Phase)> { pub fn load_find_and_make_specializations_after_check(&mut self) -> MutSet<(ModuleId, Phase)> {
let mut output = MutSet::default(); let mut output = MutSet::default();
// Take out the specialization dependency graph, as this should not be modified as we
// reload the build graph. We'll make sure the state is unaffected at the end of this call.
let mut make_specializations_dependents = MakeSpecializationsDependents::default(); let mut make_specializations_dependents = MakeSpecializationsDependents::default();
let default_make_specializations_dependents_len = make_specializations_dependents.0.len(); let default_make_specializations_dependents_len = make_specializations_dependents.0.len();
std::mem::swap( std::mem::swap(
@ -484,8 +486,9 @@ impl<'a> Dependencies<'a> {
self.add_dependency(module_dep, module, Phase::MakeSpecializations); self.add_dependency(module_dep, module, Phase::MakeSpecializations);
self.add_dependency(ModuleId::DERIVED_GEN, module, Phase::MakeSpecializations); self.add_dependency(ModuleId::DERIVED_GEN, module, Phase::MakeSpecializations);
// `module_dep` can't make its specializations until the current module does. // That `module_dep` can't make its specializations until the current module does
info.has_pred = true; // should already be accounted for in `make_specializations_dependents`, which we
// populated when initially building the graph.
} }
if module != ModuleId::DERIVED_GEN { if module != ModuleId::DERIVED_GEN {

View file

@ -8,7 +8,7 @@ use roc_collections::soa::{EitherIndex, Index, Slice};
use roc_module::called_via::{BinOp, CalledVia, UnaryOp}; use roc_module::called_via::{BinOp, CalledVia, UnaryOp};
use roc_region::all::{Loc, Position, Region}; use roc_region::all::{Loc, Position, Region};
#[derive(Debug)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Spaces<'a, T> { pub struct Spaces<'a, T> {
pub before: &'a [CommentOrNewline<'a>], pub before: &'a [CommentOrNewline<'a>],
pub item: T, pub item: T,
@ -81,11 +81,17 @@ impl<'a, T: ExtractSpaces<'a>> ExtractSpaces<'a> for Loc<T> {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Module<'a> { pub struct Module<'a> {
Interface { header: InterfaceHeader<'a> }, pub comments: &'a [CommentOrNewline<'a>],
App { header: AppHeader<'a> }, pub header: Header<'a>,
Platform { header: PlatformHeader<'a> }, }
Hosted { header: HostedHeader<'a> },
#[derive(Clone, Debug, PartialEq)]
pub enum Header<'a> {
Interface(InterfaceHeader<'a>),
App(AppHeader<'a>),
Platform(PlatformHeader<'a>),
Hosted(HostedHeader<'a>),
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -767,69 +773,173 @@ impl<'a> Pattern<'a> {
pub fn equivalent(&self, other: &Self) -> bool { pub fn equivalent(&self, other: &Self) -> bool {
use Pattern::*; use Pattern::*;
match (self, other) { match other {
(Identifier(x), Identifier(y)) => x == y, SpaceBefore(y, _) | SpaceAfter(y, _) => {
(Tag(x), Tag(y)) => x == y, return self.equivalent(y);
(Apply(constructor_x, args_x), Apply(constructor_y, args_y)) => { }
_ => {}
}
match self {
Tag(x) => {
if let Tag(y) = other {
x == y
} else {
false
}
}
Apply(constructor_x, args_x) => {
if let Apply(constructor_y, args_y) = other {
let equivalent_args = args_x let equivalent_args = args_x
.iter() .iter()
.zip(args_y.iter()) .zip(args_y.iter())
.all(|(p, q)| p.value.equivalent(&q.value)); .all(|(p, q)| p.value.equivalent(&q.value));
constructor_x.value.equivalent(&constructor_y.value) && equivalent_args constructor_x.value.equivalent(&constructor_y.value) && equivalent_args
} else {
false
} }
(RecordDestructure(fields_x), RecordDestructure(fields_y)) => fields_x }
RecordDestructure(fields_x) => {
if let RecordDestructure(fields_y) = other {
fields_x
.iter() .iter()
.zip(fields_y.iter()) .zip(fields_y.iter())
.all(|(p, q)| p.value.equivalent(&q.value)), .all(|(p, q)| p.value.equivalent(&q.value))
(RequiredField(x, inner_x), RequiredField(y, inner_y)) => { } else {
x == y && inner_x.value.equivalent(&inner_y.value) false
} }
(OptionalField(x, _), OptionalField(y, _)) }
| (OptionalField(x, _), Identifier(y)) RequiredField(x, inner_x) => {
| (Identifier(x), OptionalField(y, _)) => { if let RequiredField(y, inner_y) = other {
x == y && inner_x.value.equivalent(&inner_y.value)
} else {
false
}
}
// optional record fields can be annotated as: // optional record fields can be annotated as:
// { x, y } : { x : Int, y ? Bool } // { x, y } : { x : Int, y ? Bool }
// { x, y ? False } = rec // { x, y ? False } = rec
OptionalField(x, _) => match other {
Identifier(y) | OptionalField(y, _) => x == y,
_ => false,
},
Identifier(x) => match other {
Identifier(y) | OptionalField(y, _) => x == y,
_ => false,
},
NumLiteral(x) => {
if let NumLiteral(y) = other {
x == y x == y
} else {
false
}
} }
// Literal
(NumLiteral(x), NumLiteral(y)) => x == y,
(
NonBase10Literal { NonBase10Literal {
string: string_x, string: string_x,
base: base_x, base: base_x,
is_negative: is_negative_x, is_negative: is_negative_x,
}, } => {
NonBase10Literal { if let NonBase10Literal {
string: string_y, string: string_y,
base: base_y, base: base_y,
is_negative: is_negative_y, is_negative: is_negative_y,
} = other
{
string_x == string_y && base_x == base_y && is_negative_x == is_negative_y
} else {
false
}
}
FloatLiteral(x) => {
if let FloatLiteral(y) = other {
x == y
} else {
false
}
}
StrLiteral(x) => {
if let StrLiteral(y) = other {
x == y
} else {
false
}
}
Underscore(x) => {
if let Underscore(y) = other {
x == y
} else {
false
}
}
SpaceBefore(x, _) | SpaceAfter(x, _) => match other {
SpaceBefore(y, _) | SpaceAfter(y, _) => x.equivalent(y),
y => x.equivalent(y),
}, },
) => string_x == string_y && base_x == base_y && is_negative_x == is_negative_y, Malformed(x) => {
(FloatLiteral(x), FloatLiteral(y)) => x == y, if let Malformed(y) = other {
(StrLiteral(x), StrLiteral(y)) => x == y, x == y
(Underscore(x), Underscore(y)) => x == y, } else {
false
// Space }
(SpaceBefore(x, _), SpaceBefore(y, _)) => x.equivalent(y), }
(SpaceAfter(x, _), SpaceAfter(y, _)) => x.equivalent(y),
// Malformed
(Malformed(x), Malformed(y)) => x == y,
(
QualifiedIdentifier { QualifiedIdentifier {
module_name: a, module_name: a,
ident: x, ident: x,
}, } => {
QualifiedIdentifier { if let QualifiedIdentifier {
module_name: b, module_name: b,
ident: y, ident: y,
}, } = other
) => (a == b) && (x == y), {
a == b && x == y
// Different constructors } else {
_ => false, false
}
}
OpaqueRef(a) => {
if let OpaqueRef(b) = other {
a == b
} else {
false
}
}
SingleQuote(a) => {
if let SingleQuote(b) = other {
a == b
} else {
false
}
}
Tuple(items_x) => {
if let Tuple(items_y) = other {
items_x
.iter()
.zip(items_y.iter())
.all(|(p, q)| p.value.equivalent(&q.value))
} else {
false
}
}
List(items_x) => {
if let List(items_y) = other {
items_x
.iter()
.zip(items_y.iter())
.all(|(p, q)| p.value.equivalent(&q.value))
} else {
false
}
}
ListRest => matches!(other, ListRest),
MalformedIdent(str_x, _) => {
if let MalformedIdent(str_y, _) = other {
str_x == str_y
} else {
false
}
}
} }
} }
} }

View file

@ -565,7 +565,6 @@ pub fn parse_single_def<'a>(
let start = state.pos(); let start = state.pos();
let parse_dbg = crate::parser::keyword_e(crate::keyword::DBG, EExpect::Dbg);
let parse_expect_vanilla = crate::parser::keyword_e(crate::keyword::EXPECT, EExpect::Expect); let parse_expect_vanilla = crate::parser::keyword_e(crate::keyword::EXPECT, EExpect::Expect);
let parse_expect_fx = crate::parser::keyword_e(crate::keyword::EXPECT_FX, EExpect::Expect); let parse_expect_fx = crate::parser::keyword_e(crate::keyword::EXPECT_FX, EExpect::Expect);
let parse_expect = either!(parse_expect_fx, parse_expect_vanilla); let parse_expect = either!(parse_expect_fx, parse_expect_vanilla);
@ -577,26 +576,10 @@ pub fn parse_single_def<'a>(
) { ) {
Err((NoProgress, _)) => { Err((NoProgress, _)) => {
match parse_expect.parse(arena, state.clone(), min_indent) { match parse_expect.parse(arena, state.clone(), min_indent) {
Err((_, _)) => {
match parse_dbg.parse(arena, state, min_indent) {
Ok((_, _, state)) => parse_statement_inside_def(
arena,
state,
min_indent,
start,
spaces_before_current_start,
spaces_before_current,
|preceding_comment, loc_def_expr| ValueDef::Dbg {
condition: arena.alloc(loc_def_expr),
preceding_comment,
},
),
Err((_, _)) => { Err((_, _)) => {
// a hacky way to get expression-based error messages. TODO fix this // a hacky way to get expression-based error messages. TODO fix this
Ok((NoProgress, None, initial)) Ok((NoProgress, None, initial))
} }
}
}
Ok((_, expect_flavor, state)) => parse_statement_inside_def( Ok((_, expect_flavor, state)) => parse_statement_inside_def(
arena, arena,
state, state,
@ -1089,10 +1072,10 @@ fn opaque_signature_with_space_before<'a>(
EType::TIndentStart, EType::TIndentStart,
), ),
), ),
optional(specialize( optional(backtrackable(specialize(
EExpr::Type, EExpr::Type,
space0_before_e(type_annotation::has_abilities(), EType::TIndentStart,), space0_before_e(type_annotation::has_abilities(), EType::TIndentStart,),
)) )))
) )
} }
@ -2557,7 +2540,7 @@ fn record_help<'a>() -> impl Parser<
and!( and!(
// You can optionally have an identifier followed by an '&' to // You can optionally have an identifier followed by an '&' to
// make this a record update, e.g. { Foo.user & username: "blah" }. // make this a record update, e.g. { Foo.user & username: "blah" }.
optional(skip_second!( optional(backtrackable(skip_second!(
space0_around_ee( space0_around_ee(
// We wrap the ident in an Expr here, // We wrap the ident in an Expr here,
// so that we have a Spaceable value to work with, // so that we have a Spaceable value to work with,
@ -2568,7 +2551,7 @@ fn record_help<'a>() -> impl Parser<
ERecord::IndentAmpersand, ERecord::IndentAmpersand,
), ),
word1(b'&', ERecord::Ampersand) word1(b'&', ERecord::Ampersand)
)), ))),
loc!(skip_first!( loc!(skip_first!(
// We specifically allow space characters inside here, so that // We specifically allow space characters inside here, so that
// `{ }` can be successfully parsed as an empty record, and then // `{ }` can be successfully parsed as an empty record, and then

View file

@ -1,13 +1,13 @@
use crate::ast::{Collection, CommentOrNewline, Spaced, StrLiteral, TypeAnnotation}; use crate::ast::{Collection, CommentOrNewline, Spaced, Spaces, StrLiteral, TypeAnnotation};
use crate::blankspace::space0_e; use crate::blankspace::space0_e;
use crate::ident::{lowercase_ident, UppercaseIdent}; use crate::ident::{lowercase_ident, UppercaseIdent};
use crate::parser::Progress::*; use crate::parser::{optional, then};
use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser}; use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
use crate::state::State;
use crate::string_literal; use crate::string_literal;
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::Loc; use roc_region::all::Loc;
use std::fmt::Debug;
#[derive(Debug)] #[derive(Debug)]
pub enum HeaderFor<'a> { pub enum HeaderFor<'a> {
@ -124,40 +124,61 @@ impl<'a> ExposedName<'a> {
} }
} }
pub trait Keyword: Copy + Clone + Debug {
const KEYWORD: &'static str;
}
macro_rules! keywords {
($($name:ident => $string:expr),* $(,)?) => {
$(
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct $name;
impl Keyword for $name {
const KEYWORD: &'static str = $string;
}
)*
}
}
keywords! {
ExposesKeyword => "exposes",
ImportsKeyword => "imports",
WithKeyword => "with",
GeneratesKeyword => "generates",
PackageKeyword => "package",
PackagesKeyword => "packages",
RequiresKeyword => "requires",
ProvidesKeyword => "provides",
ToKeyword => "to",
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct KeywordItem<'a, K, V> {
pub keyword: Spaces<'a, K>,
pub item: V,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct InterfaceHeader<'a> { pub struct InterfaceHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>],
pub name: Loc<ModuleName<'a>>, pub name: Loc<ModuleName<'a>>,
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
// Potential comments and newlines - these will typically all be empty. pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
pub before_header: &'a [CommentOrNewline<'a>], pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
pub after_interface_keyword: &'a [CommentOrNewline<'a>],
pub before_exposes: &'a [CommentOrNewline<'a>],
pub after_exposes: &'a [CommentOrNewline<'a>],
pub before_imports: &'a [CommentOrNewline<'a>],
pub after_imports: &'a [CommentOrNewline<'a>],
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct HostedHeader<'a> { pub struct HostedHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>],
pub name: Loc<ModuleName<'a>>, pub name: Loc<ModuleName<'a>>,
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>, pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
pub generates: UppercaseIdent<'a>,
pub generates_with: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
// Potential comments and newlines - these will typically all be empty. pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
pub before_header: &'a [CommentOrNewline<'a>],
pub after_hosted_keyword: &'a [CommentOrNewline<'a>], pub generates: KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>,
pub before_exposes: &'a [CommentOrNewline<'a>], pub generates_with:
pub after_exposes: &'a [CommentOrNewline<'a>], KeywordItem<'a, WithKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
pub before_imports: &'a [CommentOrNewline<'a>],
pub after_imports: &'a [CommentOrNewline<'a>],
pub before_generates: &'a [CommentOrNewline<'a>],
pub after_generates: &'a [CommentOrNewline<'a>],
pub before_with: &'a [CommentOrNewline<'a>],
pub after_with: &'a [CommentOrNewline<'a>],
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -168,42 +189,39 @@ 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 name: Loc<StrLiteral<'a>>, pub name: Loc<StrLiteral<'a>>,
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
pub provides_types: Option<Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>>,
pub to: Loc<To<'a>>,
// Potential comments and newlines - these will typically all be empty. pub packages:
pub before_header: &'a [CommentOrNewline<'a>], Option<KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>>,
pub after_app_keyword: &'a [CommentOrNewline<'a>], pub imports:
pub before_packages: &'a [CommentOrNewline<'a>], Option<KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>>,
pub after_packages: &'a [CommentOrNewline<'a>], pub provides: ProvidesTo<'a>,
pub before_imports: &'a [CommentOrNewline<'a>], }
pub after_imports: &'a [CommentOrNewline<'a>],
pub before_provides: &'a [CommentOrNewline<'a>], #[derive(Clone, Debug, PartialEq)]
pub after_provides: &'a [CommentOrNewline<'a>], pub struct ProvidesTo<'a> {
pub before_to: &'a [CommentOrNewline<'a>], pub provides_keyword: Spaces<'a, ProvidesKeyword>,
pub after_to: &'a [CommentOrNewline<'a>], pub entries: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
pub types: Option<Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>>,
pub to_keyword: Spaces<'a, ToKeyword>,
pub to: Loc<To<'a>>,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct PackageHeader<'a> { pub struct PackageHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>],
pub name: Loc<PackageName<'a>>, pub name: Loc<PackageName<'a>>,
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageName<'a>>)>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
// Potential comments and newlines - these will typically all be empty. pub exposes_keyword: Spaces<'a, ExposesKeyword>,
pub before_header: &'a [CommentOrNewline<'a>], pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
pub after_package_keyword: &'a [CommentOrNewline<'a>],
pub before_exposes: &'a [CommentOrNewline<'a>], pub packages_keyword: Spaces<'a, PackagesKeyword>,
pub after_exposes: &'a [CommentOrNewline<'a>], pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageName<'a>>)>,
pub before_packages: &'a [CommentOrNewline<'a>],
pub after_packages: &'a [CommentOrNewline<'a>], pub imports_keyword: Spaces<'a, ImportsKeyword>,
pub before_imports: &'a [CommentOrNewline<'a>], pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
pub after_imports: &'a [CommentOrNewline<'a>],
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -214,26 +232,16 @@ pub struct PlatformRequires<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct PlatformHeader<'a> { pub struct PlatformHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>],
pub name: Loc<PackageName<'a>>, pub name: Loc<PackageName<'a>>,
pub requires: PlatformRequires<'a>,
pub exposes: Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
pub packages: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
pub imports: Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
pub provides: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
// Potential comments and newlines - these will typically all be empty. pub requires: KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>,
pub before_header: &'a [CommentOrNewline<'a>], pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
pub after_platform_keyword: &'a [CommentOrNewline<'a>], pub packages:
pub before_requires: &'a [CommentOrNewline<'a>], KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
pub after_requires: &'a [CommentOrNewline<'a>], pub imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
pub before_exposes: &'a [CommentOrNewline<'a>], pub provides:
pub after_exposes: &'a [CommentOrNewline<'a>], KeywordItem<'a, ProvidesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
pub before_packages: &'a [CommentOrNewline<'a>],
pub after_packages: &'a [CommentOrNewline<'a>],
pub before_imports: &'a [CommentOrNewline<'a>],
pub after_imports: &'a [CommentOrNewline<'a>],
pub before_provides: &'a [CommentOrNewline<'a>],
pub after_provides: &'a [CommentOrNewline<'a>],
} }
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
@ -270,24 +278,22 @@ pub struct PackageEntry<'a> {
} }
pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> { pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPackageEntry<'a>> {
move |arena, state, min_indent| { map!(
// You may optionally have a package shorthand, // You may optionally have a package shorthand,
// e.g. "uc" in `uc: roc/unicode 1.0.0` // e.g. "uc" in `uc: roc/unicode 1.0.0`
// //
// (Indirect dependencies don't have a shorthand.) // (Indirect dependencies don't have a shorthand.)
let (_, opt_shorthand, state) = maybe!(and!( and!(
optional(and!(
skip_second!( skip_second!(
specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()), specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()),
word1(b':', EPackageEntry::Colon) word1(b':', EPackageEntry::Colon)
), ),
space0_e(EPackageEntry::IndentPackage) space0_e(EPackageEntry::IndentPackage)
)) )),
.parse(arena, state, min_indent)?;
let (_, package_or_path, state) =
loc!(specialize(EPackageEntry::BadPackage, package_name())) loc!(specialize(EPackageEntry::BadPackage, package_name()))
.parse(arena, state, min_indent)?; ),
move |(opt_shorthand, package_or_path)| {
let entry = match opt_shorthand { let entry = match opt_shorthand {
Some((shorthand, spaces_after_shorthand)) => PackageEntry { Some((shorthand, spaces_after_shorthand)) => PackageEntry {
shorthand, shorthand,
@ -301,19 +307,18 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
}, },
}; };
Ok((MadeProgress, Spaced::Item(entry), state)) Spaced::Item(entry)
} }
)
} }
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> { pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, EPackageName<'a>> {
move |arena, state: State<'a>, min_indent: u32| { then(
let pos = state.pos(); loc!(specialize(EPackageName::BadPath, string_literal::parse())),
specialize(EPackageName::BadPath, string_literal::parse()) move |_arena, state, progress, text| match text.value {
.parse(arena, state, min_indent) StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), state)),
.and_then(|(progress, text, next_state)| match text { StrLiteral::Line(_) => Err((progress, EPackageName::Escapes(text.region.start()))),
StrLiteral::PlainLine(text) => Ok((progress, PackageName(text), next_state)), StrLiteral::Block(_) => Err((progress, EPackageName::Multiline(text.region.start()))),
StrLiteral::Line(_) => Err((progress, EPackageName::Escapes(pos))), },
StrLiteral::Block(_) => Err((progress, EPackageName::Multiline(pos))), )
})
}
} }

View file

@ -1,8 +1,10 @@
use crate::ast::{Collection, CommentOrNewline, Defs, Module, Spaced}; use crate::ast::{Collection, Defs, Header, Module, Spaced, Spaces};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::header::{ use crate::header::{
package_entry, package_name, AppHeader, ExposedName, HostedHeader, ImportsEntry, package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, PlatformRequires, To, TypedIdent, HostedHeader, ImportsEntry, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName,
PackageEntry, PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo,
RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
}; };
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent}; use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
use crate::parser::Progress::{self, *}; use crate::parser::Progress::{self, *};
@ -48,132 +50,63 @@ pub fn parse_header<'a>(
fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> { fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
use crate::parser::keyword_e; use crate::parser::keyword_e;
type Clos<'b> = Box<(dyn FnOnce(&'b [CommentOrNewline]) -> Module<'b> + 'b)>; record!(Module {
comments: space0_e(EHeader::IndentStart),
map!( header: one_of![
and!(
space0_e(EHeader::IndentStart),
one_of![
map!( map!(
skip_first!( skip_first!(
keyword_e("interface", EHeader::Start), keyword_e("interface", EHeader::Start),
increment_min_indent(interface_header()) increment_min_indent(interface_header())
), ),
|mut header: InterfaceHeader<'a>| -> Clos<'a> { Header::Interface
Box::new(|spaces| {
header.before_header = spaces;
Module::Interface { header }
})
}
), ),
map!( map!(
skip_first!( skip_first!(
keyword_e("app", EHeader::Start), keyword_e("app", EHeader::Start),
increment_min_indent(app_header()) increment_min_indent(app_header())
), ),
|mut header: AppHeader<'a>| -> Clos<'a> { Header::App
Box::new(|spaces| {
header.before_header = spaces;
Module::App { header }
})
}
), ),
map!( map!(
skip_first!( skip_first!(
keyword_e("platform", EHeader::Start), keyword_e("platform", EHeader::Start),
increment_min_indent(platform_header()) increment_min_indent(platform_header())
), ),
|mut header: PlatformHeader<'a>| -> Clos<'a> { Header::Platform
Box::new(|spaces| {
header.before_header = spaces;
Module::Platform { header }
})
}
), ),
map!( map!(
skip_first!( skip_first!(
keyword_e("hosted", EHeader::Start), keyword_e("hosted", EHeader::Start),
increment_min_indent(hosted_header()) increment_min_indent(hosted_header())
), ),
|mut header: HostedHeader<'a>| -> Clos<'a> { Header::Hosted
Box::new(|spaces| {
header.before_header = spaces;
Module::Hosted { header }
})
}
)
]
), ),
|(spaces, make_header): (&'a [CommentOrNewline], Clos<'a>)| { make_header(spaces) } ]
) })
} }
#[inline(always)] #[inline(always)]
fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> { fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> {
|arena, state, min_indent: u32| { record!(InterfaceHeader {
let (_, after_interface_keyword, state) = before_name: space0_e(EHeader::IndentStart),
space0_e(EHeader::IndentStart).parse(arena, state, min_indent)?; name: loc!(module_name_help(EHeader::ModuleName)),
let (_, name, state) = exposes: specialize(EHeader::Exposes, exposes_values()),
loc!(module_name_help(EHeader::ModuleName)).parse(arena, state, min_indent)?; imports: specialize(EHeader::Imports, imports()),
})
let (_, ((before_exposes, after_exposes), exposes), state) = .trace("interface_header")
specialize(EHeader::Exposes, exposes_values()).parse(arena, state, min_indent)?;
let (_, ((before_imports, after_imports), imports), state) =
specialize(EHeader::Imports, imports()).parse(arena, state, min_indent)?;
let header = InterfaceHeader {
name,
exposes,
imports,
before_header: &[] as &[_],
after_interface_keyword,
before_exposes,
after_exposes,
before_imports,
after_imports,
};
Ok((MadeProgress, header, state))
}
} }
#[inline(always)] #[inline(always)]
fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> { fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
|arena, state, min_indent: u32| { record!(HostedHeader {
let (_, after_hosted_keyword, state) = before_name: space0_e(EHeader::IndentStart),
space0_e(EHeader::IndentStart).parse(arena, state, min_indent)?; name: loc!(module_name_help(EHeader::ModuleName)),
let (_, name, state) = exposes: specialize(EHeader::Exposes, exposes_values()),
loc!(module_name_help(EHeader::ModuleName)).parse(arena, state, min_indent)?; imports: specialize(EHeader::Imports, imports()),
generates: specialize(EHeader::Generates, generates()),
let (_, ((before_exposes, after_exposes), exposes), state) = generates_with: specialize(EHeader::GeneratesWith, generates_with()),
specialize(EHeader::Exposes, exposes_values()).parse(arena, state, min_indent)?; })
let (_, ((before_imports, after_imports), imports), state) = .trace("hosted_header")
specialize(EHeader::Imports, imports()).parse(arena, state, min_indent)?;
let (_, ((before_generates, after_generates), generates), state) =
specialize(EHeader::Generates, generates()).parse(arena, state, min_indent)?;
let (_, ((before_with, after_with), generates_with), state) =
specialize(EHeader::GeneratesWith, generates_with()).parse(arena, state, min_indent)?;
let header = HostedHeader {
name,
exposes,
imports,
generates,
generates_with,
before_header: &[] as &[_],
after_hosted_keyword,
before_exposes,
after_exposes,
before_imports,
after_imports,
before_generates,
after_generates,
before_with,
after_with,
};
Ok((MadeProgress, header, state))
}
} }
fn chomp_module_name(buffer: &[u8]) -> Result<&str, Progress> { fn chomp_module_name(buffer: &[u8]) -> Result<&str, Progress> {
@ -237,127 +170,31 @@ 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>> {
|arena, state, min_indent: u32| { record!(AppHeader {
let (_, after_app_keyword, state) = before_name: space0_e(EHeader::IndentStart),
space0_e(EHeader::IndentStart).parse(arena, state, min_indent)?; name: loc!(crate::parser::specialize(
let (_, name, state) = loc!(crate::parser::specialize(
EHeader::AppName, EHeader::AppName,
string_literal::parse() string_literal::parse()
)) )),
.parse(arena, state, min_indent)?; packages: optional(specialize(EHeader::Packages, packages())),
imports: optional(specialize(EHeader::Imports, imports())),
let (_, opt_pkgs, state) = provides: specialize(EHeader::Provides, provides_to()),
maybe!(specialize(EHeader::Packages, packages())).parse(arena, state, min_indent)?; })
let (_, opt_imports, state) = .trace("app_header")
maybe!(specialize(EHeader::Imports, imports())).parse(arena, state, min_indent)?;
let (_, provides, state) =
specialize(EHeader::Provides, provides_to()).parse(arena, state, min_indent)?;
let (before_packages, after_packages, packages) = match opt_pkgs {
Some(pkgs) => {
let pkgs: Packages<'a> = pkgs; // rustc must be told the type here
(
pkgs.before_packages_keyword,
pkgs.after_packages_keyword,
pkgs.entries,
)
}
None => (&[] as _, &[] as _, Collection::empty()),
};
// rustc must be told the type here
#[allow(clippy::type_complexity)]
let opt_imports: Option<(
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
)> = opt_imports;
let ((before_imports, after_imports), imports) =
opt_imports.unwrap_or_else(|| ((&[] as _, &[] as _), Collection::empty()));
let provides: ProvidesTo<'a> = provides; // rustc must be told the type here
let header = AppHeader {
name,
packages,
imports,
provides: provides.entries,
provides_types: provides.types,
to: provides.to,
before_header: &[] as &[_],
after_app_keyword,
before_packages,
after_packages,
before_imports,
after_imports,
before_provides: provides.before_provides_keyword,
after_provides: provides.after_provides_keyword,
before_to: provides.before_to_keyword,
after_to: provides.after_to_keyword,
};
Ok((MadeProgress, header, state))
}
} }
#[inline(always)] #[inline(always)]
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|arena, state, min_indent: u32| { record!(PlatformHeader {
let (_, after_platform_keyword, state) = before_name: space0_e(EHeader::IndentStart),
space0_e(EHeader::IndentStart).parse(arena, state, min_indent)?; name: loc!(specialize(EHeader::PlatformName, package_name())),
let (_, name, state) = loc!(specialize(EHeader::PlatformName, package_name())) requires: specialize(EHeader::Requires, requires()),
.parse(arena, state, min_indent)?; exposes: specialize(EHeader::Exposes, exposes_modules()),
packages: specialize(EHeader::Packages, packages()),
let (_, ((before_requires, after_requires), requires), state) = imports: specialize(EHeader::Imports, imports()),
specialize(EHeader::Requires, requires()).parse(arena, state, min_indent)?; provides: specialize(EHeader::Provides, provides_exposed()),
})
let (_, ((before_exposes, after_exposes), exposes), state) = .trace("platform_header")
specialize(EHeader::Exposes, exposes_modules()).parse(arena, state, min_indent)?;
let (_, packages, state) =
specialize(EHeader::Packages, packages()).parse(arena, state, min_indent)?;
let (_, ((before_imports, after_imports), imports), state) =
specialize(EHeader::Imports, imports()).parse(arena, state, min_indent)?;
let (_, ((before_provides, after_provides), (provides, _provides_type)), state) =
specialize(EHeader::Provides, provides_without_to()).parse(arena, state, min_indent)?;
let header = PlatformHeader {
name,
requires,
exposes,
packages: packages.entries,
imports,
provides,
before_header: &[] as &[_],
after_platform_keyword,
before_requires,
after_requires,
before_exposes,
after_exposes,
before_packages: packages.before_packages_keyword,
after_packages: packages.after_packages_keyword,
before_imports,
after_imports,
before_provides,
after_provides,
};
Ok((MadeProgress, header, state))
}
}
#[derive(Debug)]
struct ProvidesTo<'a> {
entries: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
types: Option<Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>>,
to: Loc<To<'a>>,
before_provides_keyword: &'a [CommentOrNewline<'a>],
after_provides_keyword: &'a [CommentOrNewline<'a>],
before_to_keyword: &'a [CommentOrNewline<'a>],
after_to_keyword: &'a [CommentOrNewline<'a>],
} }
fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> { fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
@ -372,57 +209,14 @@ fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> {
#[inline(always)] #[inline(always)]
fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> { fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> {
map!( record!(ProvidesTo {
and!( provides_keyword: spaces_around_keyword(
provides_without_to(), ProvidesKeyword,
and!(
spaces_around_keyword(
"to",
EProvides::To,
EProvides::IndentTo,
EProvides::IndentListStart
),
loc!(provides_to_package())
)
),
|(
((before_provides_keyword, after_provides_keyword), (entries, provides_types)),
((before_to_keyword, after_to_keyword), to),
)| {
ProvidesTo {
entries,
types: provides_types,
to,
before_provides_keyword,
after_provides_keyword,
before_to_keyword,
after_to_keyword,
}
}
)
}
#[inline(always)]
fn provides_without_to<'a>() -> impl Parser<
'a,
(
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
(
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
Option<Collection<'a, Loc<Spaced<'a, UppercaseIdent<'a>>>>>,
),
),
EProvides<'a>,
> {
and!(
spaces_around_keyword(
"provides",
EProvides::Provides, EProvides::Provides,
EProvides::IndentProvides, EProvides::IndentProvides,
EProvides::IndentListStart EProvides::IndentListStart
), ),
and!( entries: collection_trailing_sep_e!(
collection_trailing_sep_e!(
word1(b'[', EProvides::ListStart), word1(b'[', EProvides::ListStart),
exposes_entry(EProvides::Identifier), exposes_entry(EProvides::Identifier),
word1(b',', EProvides::ListEnd), word1(b',', EProvides::ListEnd),
@ -430,10 +224,39 @@ fn provides_without_to<'a>() -> impl Parser<
EProvides::IndentListEnd, EProvides::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
), ),
// Optionally types: optional(backtrackable(provides_types())),
optional(provides_types()) to_keyword: spaces_around_keyword(
) ToKeyword,
) EProvides::To,
EProvides::IndentTo,
EProvides::IndentListStart
),
to: loc!(provides_to_package()),
})
.trace("provides_to")
}
fn provides_exposed<'a>() -> impl Parser<
'a,
KeywordItem<'a, ProvidesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
EProvides<'a>,
> {
record!(KeywordItem {
keyword: spaces_around_keyword(
ProvidesKeyword,
EProvides::Provides,
EProvides::IndentProvides,
EProvides::IndentListStart
),
item: collection_trailing_sep_e!(
word1(b'[', EProvides::ListStart),
exposes_entry(EProvides::Identifier),
word1(b',', EProvides::ListEnd),
word1(b']', EProvides::ListEnd),
EProvides::IndentListEnd,
Spaced::SpaceBefore
),
})
} }
#[inline(always)] #[inline(always)]
@ -491,34 +314,25 @@ where
} }
#[inline(always)] #[inline(always)]
fn requires<'a>() -> impl Parser< fn requires<'a>(
'a, ) -> impl Parser<'a, KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>, ERequires<'a>> {
( record!(KeywordItem {
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), keyword: spaces_around_keyword(
PlatformRequires<'a>, RequiresKeyword,
),
ERequires<'a>,
> {
and!(
spaces_around_keyword(
"requires",
ERequires::Requires, ERequires::Requires,
ERequires::IndentRequires, ERequires::IndentRequires,
ERequires::IndentListStart ERequires::IndentListStart
), ),
platform_requires() item: platform_requires(),
) })
} }
#[inline(always)] #[inline(always)]
fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> { fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> {
map!( record!(PlatformRequires {
and!( rigids: skip_second!(requires_rigids(), space0_e(ERequires::ListStart)),
skip_second!(requires_rigids(), space0_e(ERequires::ListStart)), signature: requires_typed_ident()
requires_typed_ident() })
),
|(rigids, signature)| { PlatformRequires { rigids, signature } }
)
} }
#[inline(always)] #[inline(always)]
@ -555,20 +369,17 @@ fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>
#[inline(always)] #[inline(always)]
fn exposes_values<'a>() -> impl Parser< fn exposes_values<'a>() -> impl Parser<
'a, 'a,
( KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
),
EExposes, EExposes,
> { > {
and!( record!(KeywordItem {
spaces_around_keyword( keyword: spaces_around_keyword(
"exposes", ExposesKeyword,
EExposes::Exposes, EExposes::Exposes,
EExposes::IndentExposes, EExposes::IndentExposes,
EExposes::IndentListStart EExposes::IndentListStart
), ),
collection_trailing_sep_e!( item: collection_trailing_sep_e!(
word1(b'[', EExposes::ListStart), word1(b'[', EExposes::ListStart),
exposes_entry(EExposes::Identifier), exposes_entry(EExposes::Identifier),
word1(b',', EExposes::ListEnd), word1(b',', EExposes::ListEnd),
@ -576,52 +387,58 @@ fn exposes_values<'a>() -> impl Parser<
EExposes::IndentListEnd, EExposes::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
) })
} }
fn spaces_around_keyword<'a, E>( fn spaces_around_keyword<'a, K: Keyword, E>(
keyword: &'static str, keyword_item: K,
expectation: fn(Position) -> E, expectation: fn(Position) -> E,
indent_problem1: fn(Position) -> E, indent_problem1: fn(Position) -> E,
indent_problem2: fn(Position) -> E, indent_problem2: fn(Position) -> E,
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), E> ) -> impl Parser<'a, Spaces<'a, K>, E>
where where
E: 'a + SpaceProblem, E: 'a + SpaceProblem,
{ {
map!(
and!( and!(
skip_second!( skip_second!(
backtrackable(space0_e(indent_problem1)), backtrackable(space0_e(indent_problem1)),
crate::parser::keyword_e(keyword, expectation) crate::parser::keyword_e(K::KEYWORD, expectation)
), ),
space0_e(indent_problem2) space0_e(indent_problem2)
),
|(before, after)| {
Spaces {
before,
item: keyword_item,
after,
}
}
) )
} }
#[inline(always)] #[inline(always)]
fn exposes_modules<'a>() -> impl Parser< fn exposes_modules<'a>() -> impl Parser<
'a, 'a,
( KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
),
EExposes, EExposes,
> { > {
and!( record!(KeywordItem {
spaces_around_keyword( keyword: spaces_around_keyword(
"exposes", ExposesKeyword,
EExposes::Exposes, EExposes::Exposes,
EExposes::IndentExposes, EExposes::IndentExposes,
EExposes::IndentListStart EExposes::IndentListStart
), ),
collection_trailing_sep_e!( item: collection_trailing_sep_e!(
word1(b'[', EExposes::ListStart), word1(b'[', EExposes::ListStart),
exposes_module(EExposes::Identifier), exposes_module(EExposes::Identifier),
word1(b',', EExposes::ListEnd), word1(b',', EExposes::ListEnd),
word1(b']', EExposes::ListEnd), word1(b']', EExposes::ListEnd),
EExposes::IndentListEnd, EExposes::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) ),
) })
} }
fn exposes_module<'a, F, E>( fn exposes_module<'a, F, E>(
@ -638,24 +455,20 @@ where
)) ))
} }
#[derive(Debug)]
struct Packages<'a> {
entries: Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>,
before_packages_keyword: &'a [CommentOrNewline<'a>],
after_packages_keyword: &'a [CommentOrNewline<'a>],
}
#[inline(always)] #[inline(always)]
fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> { fn packages<'a>() -> impl Parser<
map!( 'a,
and!( KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
spaces_around_keyword( EPackages<'a>,
"packages", > {
record!(KeywordItem {
keyword: spaces_around_keyword(
PackagesKeyword,
EPackages::Packages, EPackages::Packages,
EPackages::IndentPackages, EPackages::IndentPackages,
EPackages::IndentListStart EPackages::IndentListStart
), ),
collection_trailing_sep_e!( item: collection_trailing_sep_e!(
word1(b'{', EPackages::ListStart), word1(b'{', EPackages::ListStart),
specialize(EPackages::PackageEntry, loc!(package_entry())), specialize(EPackages::PackageEntry, loc!(package_entry())),
word1(b',', EPackages::ListEnd), word1(b',', EPackages::ListEnd),
@ -663,57 +476,37 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
EPackages::IndentListEnd, EPackages::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
), })
|((before_packages_keyword, after_packages_keyword), entries): (
(_, _),
Collection<'a, _>
)| {
Packages {
entries,
before_packages_keyword,
after_packages_keyword,
}
}
)
} }
#[inline(always)] #[inline(always)]
fn generates<'a>() -> impl Parser< fn generates<'a>(
'a, ) -> impl Parser<'a, KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>, EGenerates> {
( record!(KeywordItem {
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), keyword: spaces_around_keyword(
UppercaseIdent<'a>, GeneratesKeyword,
),
EGenerates,
> {
and!(
spaces_around_keyword(
"generates",
EGenerates::Generates, EGenerates::Generates,
EGenerates::IndentGenerates, EGenerates::IndentGenerates,
EGenerates::IndentTypeStart EGenerates::IndentTypeStart
), ),
specialize(|(), pos| EGenerates::Identifier(pos), uppercase()) item: specialize(|(), pos| EGenerates::Identifier(pos), uppercase())
) })
} }
#[inline(always)] #[inline(always)]
fn generates_with<'a>() -> impl Parser< fn generates_with<'a>() -> impl Parser<
'a, 'a,
( KeywordItem<'a, WithKeyword, Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>>,
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
),
EGeneratesWith, EGeneratesWith,
> { > {
and!( record!(KeywordItem {
spaces_around_keyword( keyword: spaces_around_keyword(
"with", WithKeyword,
EGeneratesWith::With, EGeneratesWith::With,
EGeneratesWith::IndentWith, EGeneratesWith::IndentWith,
EGeneratesWith::IndentListStart EGeneratesWith::IndentListStart
), ),
collection_trailing_sep_e!( item: collection_trailing_sep_e!(
word1(b'[', EGeneratesWith::ListStart), word1(b'[', EGeneratesWith::ListStart),
exposes_entry(EGeneratesWith::Identifier), exposes_entry(EGeneratesWith::Identifier),
word1(b',', EGeneratesWith::ListEnd), word1(b',', EGeneratesWith::ListEnd),
@ -721,26 +514,23 @@ fn generates_with<'a>() -> impl Parser<
EGeneratesWith::IndentListEnd, EGeneratesWith::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
) })
} }
#[inline(always)] #[inline(always)]
fn imports<'a>() -> impl Parser< fn imports<'a>() -> impl Parser<
'a, 'a,
( KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>,
),
EImports, EImports,
> { > {
and!( record!(KeywordItem {
spaces_around_keyword( keyword: spaces_around_keyword(
"imports", ImportsKeyword,
EImports::Imports, EImports::Imports,
EImports::IndentImports, EImports::IndentImports,
EImports::IndentListStart EImports::IndentListStart
), ),
collection_trailing_sep_e!( item: collection_trailing_sep_e!(
word1(b'[', EImports::ListStart), word1(b'[', EImports::ListStart),
loc!(imports_entry()), loc!(imports_entry()),
word1(b',', EImports::ListEnd), word1(b',', EImports::ListEnd),
@ -748,7 +538,8 @@ fn imports<'a>() -> impl Parser<
EImports::IndentListEnd, EImports::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
) })
.trace("imports")
} }
#[inline(always)] #[inline(always)]
@ -810,15 +601,15 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
and!( and!(
and!( and!(
// e.g. `pf.` // e.g. `pf.`
maybe!(skip_second!( optional(backtrackable(skip_second!(
shortname(), shortname(),
word1(b'.', EImports::ShorthandDot) word1(b'.', EImports::ShorthandDot)
)), ))),
// e.g. `Task` // e.g. `Task`
module_name_help(EImports::ModuleName) module_name_help(EImports::ModuleName)
), ),
// e.g. `.{ Task, after}` // e.g. `.{ Task, after}`
maybe!(skip_first!( optional(skip_first!(
word1(b'.', EImports::ExposingDot), word1(b'.', EImports::ExposingDot),
collection_trailing_sep_e!( collection_trailing_sep_e!(
word1(b'{', EImports::SetStart), word1(b'{', EImports::SetStart),
@ -842,4 +633,5 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
Spaced::Item(entry) Spaced::Item(entry)
} }
) )
.trace("imports_entry")
} }

View file

@ -852,13 +852,14 @@ where
self.message self.message
); );
let previous_state = state.clone();
INDENT.with(|i| *i.borrow_mut() += 1); INDENT.with(|i| *i.borrow_mut() += 1);
let res = self.parser.parse(arena, state, min_indent); let res = self.parser.parse(arena, state, min_indent);
INDENT.with(|i| *i.borrow_mut() = cur_indent); INDENT.with(|i| *i.borrow_mut() = cur_indent);
let (progress, value, state) = match &res { let (progress, value, state) = match &res {
Ok((progress, result, state)) => (progress, Ok(result), state), Ok((progress, result, state)) => (progress, Ok(result), state),
Err((progress, error)) => (progress, Err(error), state), Err((progress, error)) => (progress, Err(error), &previous_state),
}; };
println!( println!(
@ -1229,11 +1230,8 @@ where
match parser.parse(arena, state, min_indent) { match parser.parse(arena, state, min_indent) {
Ok((progress, out1, state)) => Ok((progress, Some(out1), state)), Ok((progress, out1, state)) => Ok((progress, Some(out1), state)),
Err((_, _)) => { Err((MadeProgress, e)) => Err((MadeProgress, e)),
// NOTE this will backtrack Err((NoProgress, _)) => Ok((NoProgress, None, original_state)),
// TODO can we get rid of some of the potential backtracking?
Ok((NoProgress, None, original_state))
}
} }
} }
} }
@ -1435,6 +1433,25 @@ macro_rules! and {
}; };
} }
/// Take as input something that looks like a struct literal where values are parsers
/// and return a parser that runs each parser and returns a struct literal with the
/// results.
#[macro_export]
macro_rules! record {
($name:ident $(:: $name_ext:ident)* { $($field:ident: $parser:expr),* $(,)? }) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| {
let mut state = state;
let mut progress = NoProgress;
$(
let (new_progress, $field, new_state) = $parser.parse(arena, state, min_indent)?;
state = new_state;
progress = progress.or(new_progress);
)*
Ok((progress, $name $(:: $name_ext)* { $($field),* }, state))
}
};
}
/// Similar to `and`, but we modify the min_indent of the second parser to be /// Similar to `and`, but we modify the min_indent of the second parser to be
/// 1 greater than the line_indent() at the start of the first parser. /// 1 greater than the line_indent() at the start of the first parser.
#[macro_export] #[macro_export]
@ -1507,21 +1524,6 @@ macro_rules! one_of {
}; };
} }
#[macro_export]
macro_rules! maybe {
($p1:expr) => {
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, min_indent: u32| {
let original_state = state.clone();
match $p1.parse(arena, state, min_indent) {
Ok((progress, value, state)) => Ok((progress, Some(value), state)),
Err((MadeProgress, fail)) => Err((MadeProgress, fail)),
Err((NoProgress, _)) => Ok((NoProgress, None, original_state)),
}
}
};
}
#[macro_export] #[macro_export]
macro_rules! one_of_with_error { macro_rules! one_of_with_error {
($toerror:expr; $p1:expr) => { ($toerror:expr; $p1:expr) => {

View file

@ -349,9 +349,8 @@ fn record_type_field<'a>() -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'
fn record_type<'a>( fn record_type<'a>(
stop_at_surface_has: bool, stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> { ) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> {
map!( record!(TypeAnnotation::Record {
and!( fields: collection_trailing_sep_e!(
collection_trailing_sep_e!(
word1(b'{', ETypeRecord::Open), word1(b'{', ETypeRecord::Open),
loc!(record_type_field()), loc!(record_type_field()),
word1(b',', ETypeRecord::End), word1(b',', ETypeRecord::End),
@ -359,13 +358,11 @@ fn record_type<'a>(
ETypeRecord::IndentEnd, ETypeRecord::IndentEnd,
AssignedField::SpaceBefore AssignedField::SpaceBefore
), ),
optional(allocated(specialize_ref( ext: optional(allocated(specialize_ref(
ETypeRecord::Type, ETypeRecord::Type,
term(stop_at_surface_has) term(stop_at_surface_has)
))) )))
), })
|(fields, ext)| { TypeAnnotation::Record { fields, ext } }
)
.trace("type_annotation:record_type") .trace("type_annotation:record_type")
} }
@ -515,10 +512,9 @@ pub fn has_abilities<'a>() -> impl Parser<'a, Loc<HasAbilities<'a>>, EType<'a>>
} }
fn parse_has_ability<'a>() -> impl Parser<'a, HasAbility<'a>, EType<'a>> { fn parse_has_ability<'a>() -> impl Parser<'a, HasAbility<'a>, EType<'a>> {
increment_min_indent(map!( increment_min_indent(record!(HasAbility::HasAbility {
and!( ability: loc!(specialize(EType::TApply, concrete_type())),
loc!(specialize(EType::TApply, concrete_type())), impls: optional(backtrackable(space0_before_e(
optional(space0_before_e(
loc!(map!( loc!(map!(
specialize( specialize(
EType::TAbilityImpl, EType::TAbilityImpl,
@ -534,10 +530,8 @@ fn parse_has_ability<'a>() -> impl Parser<'a, HasAbility<'a>, EType<'a>> {
HasImpls::HasImpls HasImpls::HasImpls
)), )),
EType::TIndentEnd EType::TIndentEnd
)) )))
), }))
|(ability, impls): (_, Option<_>)| { HasAbility::HasAbility { ability, impls } }
))
} }
fn expression<'a>( fn expression<'a>(
@ -594,10 +588,10 @@ fn expression<'a>(
} }
Err(err) => { Err(err) => {
if !is_trailing_comma_valid { if !is_trailing_comma_valid {
let (_, comma, _) = optional(skip_first!( let (_, comma, _) = optional(backtrackable(skip_first!(
space0_e(EType::TIndentStart), space0_e(EType::TIndentStart),
word1(b',', EType::TStart) word1(b',', EType::TStart)
)) )))
.trace("check trailing comma") .trace("check trailing comma")
.parse(arena, state.clone(), min_indent)?; .parse(arena, state.clone(), min_indent)?;

View file

@ -1,24 +1,48 @@
App { Module {
header: AppHeader { comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-14 PlainLine( name: @4-14 PlainLine(
"test-app", "test-app",
), ),
packages: [], packages: Some(
imports: [], KeywordItem {
provides: [], keyword: Spaces {
provides_types: None, 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( to: @53-57 ExistingPackage(
"blah", "blah",
), ),
before_header: [],
after_app_keyword: [],
before_packages: [],
after_packages: [],
before_imports: [],
after_imports: [],
before_provides: [],
after_provides: [],
before_to: [],
after_to: [],
}, },
},
),
} }

View file

@ -1,23 +1,45 @@
Hosted { Module {
header: HostedHeader { comments: [],
header: Hosted(
HostedHeader {
before_name: [],
name: @7-10 ModuleName( name: @7-10 ModuleName(
"Foo", "Foo",
), ),
exposes: [], exposes: KeywordItem {
imports: [], keyword: Spaces {
generates: UppercaseIdent( before: [],
item: ExposesKeyword,
after: [],
},
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [],
item: ImportsKeyword,
after: [],
},
item: [],
},
generates: KeywordItem {
keyword: Spaces {
before: [],
item: GeneratesKeyword,
after: [],
},
item: UppercaseIdent(
"Bar", "Bar",
), ),
generates_with: [],
before_header: [],
after_hosted_keyword: [],
before_exposes: [],
after_exposes: [],
before_imports: [],
after_imports: [],
before_generates: [],
after_generates: [],
before_with: [],
after_with: [],
}, },
generates_with: KeywordItem {
keyword: Spaces {
before: [],
item: WithKeyword,
after: [],
},
item: [],
},
},
),
} }

View file

@ -1,15 +1,27 @@
Interface { Module {
header: InterfaceHeader { comments: [],
header: Interface(
InterfaceHeader {
before_name: [],
name: @10-13 ModuleName( name: @10-13 ModuleName(
"Foo", "Foo",
), ),
exposes: [], exposes: KeywordItem {
imports: [], keyword: Spaces {
before_header: [], before: [],
after_interface_keyword: [], item: ExposesKeyword,
before_exposes: [], after: [],
after_exposes: [],
before_imports: [],
after_imports: [],
}, },
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [],
item: ImportsKeyword,
after: [],
},
item: [],
},
},
),
} }

View file

@ -1,9 +1,18 @@
Platform { Module {
header: PlatformHeader { comments: [],
header: Platform(
PlatformHeader {
before_name: [],
name: @9-25 PackageName( name: @9-25 PackageName(
"rtfeldman/blah", "rtfeldman/blah",
), ),
requires: PlatformRequires { requires: KeywordItem {
keyword: Spaces {
before: [],
item: RequiresKeyword,
after: [],
},
item: PlatformRequires {
rigids: [], rigids: [],
signature: @40-49 TypedIdent { signature: @40-49 TypedIdent {
ident: @40-44 "main", ident: @40-44 "main",
@ -14,21 +23,39 @@ Platform {
}, },
}, },
}, },
exposes: [],
packages: [],
imports: [],
provides: [],
before_header: [],
after_platform_keyword: [],
before_requires: [],
after_requires: [],
before_exposes: [],
after_exposes: [],
before_packages: [],
after_packages: [],
before_imports: [],
after_imports: [],
before_provides: [],
after_provides: [],
}, },
exposes: KeywordItem {
keyword: Spaces {
before: [],
item: ExposesKeyword,
after: [],
},
item: [],
},
packages: KeywordItem {
keyword: Spaces {
before: [],
item: PackagesKeyword,
after: [],
},
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [],
item: ImportsKeyword,
after: [],
},
item: [],
},
provides: KeywordItem {
keyword: Spaces {
before: [],
item: ProvidesKeyword,
after: [],
},
item: [],
},
},
),
} }

View file

@ -1,9 +1,21 @@
App { Module {
header: AppHeader { comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-15 PlainLine( name: @4-15 PlainLine(
"quicksort", "quicksort",
), ),
packages: [ packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@31-47 PackageEntry { @31-47 PackageEntry {
shorthand: "pf", shorthand: "pf",
spaces_after_shorthand: [], spaces_after_shorthand: [],
@ -12,7 +24,18 @@ App {
), ),
}, },
], ],
imports: [ },
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [
@64-75 Package( @64-75 Package(
"foo", "foo",
ModuleName( ModuleName(
@ -21,30 +44,31 @@ App {
[], [],
), ),
], ],
provides: [ },
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@93-102 ExposedName( @93-102 ExposedName(
"quicksort", "quicksort",
), ),
], ],
provides_types: None, types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @108-110 ExistingPackage( to: @108-110 ExistingPackage(
"pf", "pf",
), ),
before_header: [],
after_app_keyword: [],
before_packages: [
Newline,
],
after_packages: [],
before_imports: [
Newline,
],
after_imports: [],
before_provides: [
Newline,
],
after_provides: [],
before_to: [],
after_to: [],
}, },
},
),
} }

View file

@ -1,9 +1,21 @@
App { Module {
header: AppHeader { comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-15 PlainLine( name: @4-15 PlainLine(
"quicksort", "quicksort",
), ),
packages: [ packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@31-47 PackageEntry { @31-47 PackageEntry {
shorthand: "pf", shorthand: "pf",
spaces_after_shorthand: [], spaces_after_shorthand: [],
@ -12,7 +24,18 @@ App {
), ),
}, },
], ],
imports: [ },
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [
@65-141 Package( @65-141 Package(
"foo", "foo",
ModuleName( ModuleName(
@ -46,30 +69,31 @@ App {
}, },
), ),
], ],
provides: [ },
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@159-168 ExposedName( @159-168 ExposedName(
"quicksort", "quicksort",
), ),
], ],
provides_types: None, types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @175-177 ExistingPackage( to: @175-177 ExistingPackage(
"pf", "pf",
), ),
before_header: [],
after_app_keyword: [],
before_packages: [
Newline,
],
after_packages: [],
before_imports: [
Newline,
],
after_imports: [],
before_provides: [
Newline,
],
after_provides: [],
before_to: [],
after_to: [],
}, },
},
),
} }

View file

@ -1,9 +1,20 @@
Platform { Module {
header: PlatformHeader { comments: [],
header: Platform(
PlatformHeader {
before_name: [],
name: @9-14 PackageName( name: @9-14 PackageName(
"cli", "cli",
), ),
requires: PlatformRequires { requires: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: RequiresKeyword,
after: [],
},
item: PlatformRequires {
rigids: [], rigids: [],
signature: @32-49 TypedIdent { signature: @32-49 TypedIdent {
ident: @32-36 "main", ident: @32-36 "main",
@ -24,9 +35,38 @@ Platform {
), ),
}, },
}, },
exposes: [], },
packages: [], exposes: KeywordItem {
imports: [ keyword: Spaces {
before: [
LineComment(
" TODO FIXME",
),
],
item: ExposesKeyword,
after: [],
},
item: [],
},
packages: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [
@110-123 Module( @110-123 Module(
ModuleName( ModuleName(
"Task", "Task",
@ -38,34 +78,21 @@ Platform {
], ],
), ),
], ],
provides: [ },
provides: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
item: [
@141-152 ExposedName( @141-152 ExposedName(
"mainForHost", "mainForHost",
), ),
], ],
before_header: [],
after_platform_keyword: [],
before_requires: [
Newline,
],
after_requires: [],
before_exposes: [
LineComment(
" TODO FIXME",
),
],
after_exposes: [],
before_packages: [
Newline,
],
after_packages: [],
before_imports: [
Newline,
],
after_imports: [],
before_provides: [
Newline,
],
after_provides: [],
}, },
},
),
} }

View file

@ -1,15 +1,27 @@
Interface { Module {
header: InterfaceHeader { comments: [],
header: Interface(
InterfaceHeader {
before_name: [],
name: @10-11 ModuleName( name: @10-11 ModuleName(
"T", "T",
), ),
exposes: [], exposes: KeywordItem {
imports: [], keyword: Spaces {
before_header: [], before: [],
after_interface_keyword: [], item: ExposesKeyword,
before_exposes: [], after: [],
after_exposes: [],
before_imports: [],
after_imports: [],
}, },
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [],
item: ImportsKeyword,
after: [],
},
item: [],
},
},
),
} }

View file

@ -1,26 +1,32 @@
App { Module {
header: AppHeader { comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-14 PlainLine( name: @4-14 PlainLine(
"test-app", "test-app",
), ),
packages: [], packages: None,
imports: [], imports: None,
provides: [], provides: ProvidesTo {
provides_types: None, provides_keyword: Spaces {
before: [],
item: ProvidesKeyword,
after: [],
},
entries: [],
types: None,
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @30-38 NewPackage( to: @30-38 NewPackage(
PackageName( PackageName(
"./blah", "./blah",
), ),
), ),
before_header: [],
after_app_keyword: [],
before_packages: [],
after_packages: [],
before_imports: [],
after_imports: [],
before_provides: [],
after_provides: [],
before_to: [],
after_to: [],
}, },
},
),
} }

View file

@ -1,15 +1,27 @@
Interface { Module {
header: InterfaceHeader { comments: [],
header: Interface(
InterfaceHeader {
before_name: [],
name: @10-21 ModuleName( name: @10-21 ModuleName(
"Foo.Bar.Baz", "Foo.Bar.Baz",
), ),
exposes: [], exposes: KeywordItem {
imports: [], keyword: Spaces {
before_header: [], before: [],
after_interface_keyword: [], item: ExposesKeyword,
before_exposes: [], after: [],
after_exposes: [],
before_imports: [],
after_imports: [],
}, },
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [],
item: ImportsKeyword,
after: [],
},
item: [],
},
},
),
} }

View file

@ -1,9 +1,22 @@
Hosted { Module {
header: HostedHeader { comments: [],
header: Hosted(
HostedHeader {
before_name: [],
name: @7-10 ModuleName( name: @7-10 ModuleName(
"Foo", "Foo",
), ),
exposes: Collection { exposes: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ExposesKeyword,
after: [
Newline,
],
},
item: Collection {
items: [ items: [
@45-50 SpaceBefore( @45-50 SpaceBefore(
ExposedName( ExposedName(
@ -34,7 +47,18 @@ Hosted {
Newline, Newline,
], ],
}, },
imports: Collection { },
imports: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [
Newline,
],
},
item: Collection {
items: [ items: [
@143-147 SpaceBefore( @143-147 SpaceBefore(
Module( Module(
@ -70,10 +94,28 @@ Hosted {
Newline, Newline,
], ],
}, },
generates: UppercaseIdent( },
generates: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: GeneratesKeyword,
after: [],
},
item: UppercaseIdent(
"Bar", "Bar",
), ),
generates_with: Collection { },
generates_with: KeywordItem {
keyword: Spaces {
before: [],
item: WithKeyword,
after: [
Newline,
],
},
item: Collection {
items: [ items: [
@239-242 SpaceBefore( @239-242 SpaceBefore(
ExposedName( ExposedName(
@ -104,27 +146,7 @@ Hosted {
Newline, Newline,
], ],
}, },
before_header: [],
after_hosted_keyword: [],
before_exposes: [
Newline,
],
after_exposes: [
Newline,
],
before_imports: [
Newline,
],
after_imports: [
Newline,
],
before_generates: [
Newline,
],
after_generates: [],
before_with: [],
after_with: [
Newline,
],
}, },
},
),
} }

View file

@ -1,9 +1,20 @@
Platform { Module {
header: PlatformHeader { comments: [],
header: Platform(
PlatformHeader {
before_name: [],
name: @9-21 PackageName( name: @9-21 PackageName(
"foo/barbaz", "foo/barbaz",
), ),
requires: PlatformRequires { requires: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: RequiresKeyword,
after: [],
},
item: PlatformRequires {
rigids: [ rigids: [
@36-41 UppercaseIdent( @36-41 UppercaseIdent(
"Model", "Model",
@ -18,8 +29,26 @@ Platform {
}, },
}, },
}, },
exposes: [], },
packages: [ exposes: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ExposesKeyword,
after: [],
},
item: [],
},
packages: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@87-99 PackageEntry { @87-99 PackageEntry {
shorthand: "foo", shorthand: "foo",
spaces_after_shorthand: [], spaces_after_shorthand: [],
@ -28,33 +57,31 @@ Platform {
), ),
}, },
], ],
imports: [], },
provides: [ imports: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [],
},
provides: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
item: [
@132-143 ExposedName( @132-143 ExposedName(
"mainForHost", "mainForHost",
), ),
], ],
before_header: [],
after_platform_keyword: [],
before_requires: [
Newline,
],
after_requires: [],
before_exposes: [
Newline,
],
after_exposes: [],
before_packages: [
Newline,
],
after_packages: [],
before_imports: [
Newline,
],
after_imports: [],
before_provides: [
Newline,
],
after_provides: [],
}, },
},
),
} }

View file

@ -1,9 +1,21 @@
App { Module {
header: AppHeader { comments: [],
header: App(
AppHeader {
before_name: [],
name: @4-10 PlainLine( name: @4-10 PlainLine(
"test", "test",
), ),
packages: [ packages: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@26-42 PackageEntry { @26-42 PackageEntry {
shorthand: "pf", shorthand: "pf",
spaces_after_shorthand: [], spaces_after_shorthand: [],
@ -12,7 +24,18 @@ App {
), ),
}, },
], ],
imports: [ },
),
imports: Some(
KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [
@59-70 Package( @59-70 Package(
"foo", "foo",
ModuleName( ModuleName(
@ -21,12 +44,22 @@ App {
[], [],
), ),
], ],
provides: [ },
),
provides: ProvidesTo {
provides_keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
entries: [
@88-97 ExposedName( @88-97 ExposedName(
"quicksort", "quicksort",
), ),
], ],
provides_types: Some( types: Some(
[ [
@102-107 UppercaseIdent( @102-107 UppercaseIdent(
"Flags", "Flags",
@ -36,24 +69,15 @@ App {
), ),
], ],
), ),
to_keyword: Spaces {
before: [],
item: ToKeyword,
after: [],
},
to: @121-123 ExistingPackage( to: @121-123 ExistingPackage(
"pf", "pf",
), ),
before_header: [],
after_app_keyword: [],
before_packages: [
Newline,
],
after_packages: [],
before_imports: [
Newline,
],
after_imports: [],
before_provides: [
Newline,
],
after_provides: [],
before_to: [],
after_to: [],
}, },
},
),
} }

View file

@ -1,9 +1,20 @@
Platform { Module {
header: PlatformHeader { comments: [],
header: Platform(
PlatformHeader {
before_name: [],
name: @9-21 PackageName( name: @9-21 PackageName(
"test/types", "test/types",
), ),
requires: PlatformRequires { requires: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: RequiresKeyword,
after: [],
},
item: PlatformRequires {
rigids: [ rigids: [
@37-42 UppercaseIdent( @37-42 UppercaseIdent(
"Flags", "Flags",
@ -33,35 +44,51 @@ Platform {
), ),
}, },
}, },
exposes: [], },
packages: [], exposes: KeywordItem {
imports: [], keyword: Spaces {
provides: [ before: [
Newline,
],
item: ExposesKeyword,
after: [],
},
item: [],
},
packages: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [],
},
imports: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ImportsKeyword,
after: [],
},
item: [],
},
provides: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: ProvidesKeyword,
after: [],
},
item: [
@141-152 ExposedName( @141-152 ExposedName(
"mainForHost", "mainForHost",
), ),
], ],
before_header: [],
after_platform_keyword: [],
before_requires: [
Newline,
],
after_requires: [],
before_exposes: [
Newline,
],
after_exposes: [],
before_packages: [
Newline,
],
after_packages: [],
before_imports: [
Newline,
],
after_imports: [],
before_provides: [
Newline,
],
after_provides: [],
}, },
},
),
} }

View file

@ -158,11 +158,6 @@ pub fn generate_stub_lib(
.collect(); .collect();
if let EntryPoint::Executable { platform_path, .. } = &loaded.entry_point { if let EntryPoint::Executable { platform_path, .. } = &loaded.entry_point {
let platform_path = input_path
.to_path_buf()
.parent()
.unwrap()
.join(platform_path);
let stub_lib = if let target_lexicon::OperatingSystem::Windows = triple.operating_system { let stub_lib = if let target_lexicon::OperatingSystem::Windows = triple.operating_system {
platform_path.with_file_name("libapp.obj") platform_path.with_file_name("libapp.obj")
} else { } else {

View file

@ -1,7 +1,7 @@
use brotli::enc::BrotliEncoderParams; use brotli::enc::BrotliEncoderParams;
use bumpalo::Bump; use bumpalo::Bump;
use flate2::write::GzEncoder; use flate2::write::GzEncoder;
use roc_parse::ast::Module; use roc_parse::ast::{Header, Module};
use roc_parse::header::PlatformHeader; use roc_parse::header::PlatformHeader;
use roc_parse::module::parse_header; use roc_parse::module::parse_header;
use roc_parse::state::State; use roc_parse::state::State;
@ -124,22 +124,20 @@ fn write_archive<W: Write>(path: &Path, writer: W) -> io::Result<()> {
let arena = Bump::new(); let arena = Bump::new();
let mut buf = Vec::new(); let mut buf = Vec::new();
let _other_modules: &[Module<'_>] = match read_header(&arena, &mut buf, path)? { let _other_modules: &[Module<'_>] = match read_header(&arena, &mut buf, path)?.header {
Module::Interface { .. } => { Header::Interface(_) => {
todo!(); todo!();
// TODO report error // TODO report error
} }
Module::App { .. } => { Header::App(_) => {
todo!(); todo!();
// TODO report error // TODO report error
} }
Module::Hosted { .. } => { Header::Hosted(_) => {
todo!(); todo!();
// TODO report error // TODO report error
} }
Module::Platform { Header::Platform(PlatformHeader { imports: _, .. }) => {
header: PlatformHeader { imports: _, .. },
} => {
use walkdir::WalkDir; use walkdir::WalkDir;
// Add all the prebuilt host files to the archive. // Add all the prebuilt host files to the archive.