New package header syntax

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

package [Csv] {
    parser: "../parser/main.roc"
}

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

[1] 418444862
This commit is contained in:
Agus Zubiaga 2024-03-04 19:58:22 -03:00
parent 8dedd9f03c
commit e3b600c282
No known key found for this signature in database
16 changed files with 169 additions and 132 deletions

View file

@ -1,3 +1 @@
package "csv" package [Csv] {}
exposes [Csv]
packages {}

View file

@ -1,3 +1 @@
package "json" package [JsonParser] {}
exposes [JsonParser]
packages {}

View file

@ -1,3 +1,15 @@
package "builtins" package [
exposes [Str, Num, Bool, Result, List, Dict, Set, Decode, Encode, Hash, Box, TotallyNotJson, Inspect] Str,
packages {} Num,
Bool,
Result,
List,
Dict,
Set,
Decode,
Encode,
Hash,
Box,
TotallyNotJson,
Inspect,
] {}

View file

@ -5,7 +5,7 @@ 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 bumpalo::Bump; use bumpalo::Bump;
use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces}; use roc_parse::ast::{Collection, CommentOrNewline, Header, Module, Spaced, Spaces};
use roc_parse::header::{ use roc_parse::header::{
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader,
@ -177,15 +177,8 @@ pub fn fmt_module_header<'a>(buf: &mut Buf, header: &'a ModuleHeader<'a>) {
buf.indent(0); buf.indent(0);
buf.push_str("module"); buf.push_str("module");
if header.before_exposes.iter().all(|c| c.is_newline()) { let indent = fmt_spaces_with_outdent(buf, header.before_exposes, INDENT);
buf.spaces(1); fmt_exposes(buf, header.exposes, indent);
fmt_exposes(buf, header.exposes, 0);
} else {
let indent = INDENT;
fmt_default_spaces(buf, header.before_exposes, indent);
fmt_exposes(buf, header.exposes, indent);
};
} }
pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) { pub fn fmt_hosted_header<'a>(buf: &mut Buf, header: &'a HostedHeader<'a>) {
@ -209,37 +202,32 @@ pub fn fmt_app_header<'a>(buf: &mut Buf, header: &'a AppHeader<'a>) {
buf.indent(0); buf.indent(0);
buf.push_str("app"); buf.push_str("app");
let indent = INDENT; let indent = fmt_spaces_with_outdent(buf, header.before_provides, INDENT);
fmt_exposes(buf, header.provides, indent);
if header.before_provides.iter().all(|c| c.is_newline()) { let indent = fmt_spaces_with_outdent(buf, header.before_packages, INDENT);
buf.spaces(1); fmt_packages(buf, header.packages.value, indent);
fmt_exposes(buf, header.provides, 0); }
} else {
fmt_default_spaces(buf, header.before_provides, indent);
fmt_exposes(buf, header.provides, indent);
}
if header.before_packages.iter().all(|c| c.is_newline()) { pub fn fmt_spaces_with_outdent(buf: &mut Buf, spaces: &[CommentOrNewline], indent: u16) -> u16 {
if spaces.iter().all(|c| c.is_newline()) {
buf.spaces(1); buf.spaces(1);
fmt_packages(buf, header.packages.value, 0); 0
} else { } else {
fmt_default_spaces(buf, header.before_packages, indent); fmt_default_spaces(buf, spaces, indent);
fmt_packages(buf, header.packages.value, indent); indent
} }
} }
pub fn fmt_package_header<'a>(buf: &mut Buf, header: &'a PackageHeader<'a>) { pub fn fmt_package_header<'a>(buf: &mut Buf, header: &'a PackageHeader<'a>) {
buf.indent(0); buf.indent(0);
buf.push_str("package"); buf.push_str("package");
let indent = INDENT;
fmt_default_spaces(buf, header.before_name, indent);
fmt_package_name(buf, header.name.value, indent); let indent = fmt_spaces_with_outdent(buf, header.before_exposes, INDENT);
fmt_exposes(buf, header.exposes, indent);
header.exposes.keyword.format(buf, indent); let indent = fmt_spaces_with_outdent(buf, header.before_packages, INDENT);
fmt_exposes(buf, header.exposes.item, indent); fmt_packages(buf, header.packages.value, indent);
header.packages.keyword.format(buf, indent);
fmt_packages(buf, header.packages.item, indent);
} }
pub fn fmt_platform_header<'a>(buf: &mut Buf, header: &'a PlatformHeader<'a>) { pub fn fmt_platform_header<'a>(buf: &mut Buf, header: &'a PlatformHeader<'a>) {

View file

@ -299,9 +299,9 @@ impl<'a> RemoveSpaces<'a> for Module<'a> {
.remove_spaces(arena), .remove_spaces(arena),
}), }),
Header::Package(header) => Header::Package(PackageHeader { Header::Package(header) => Header::Package(PackageHeader {
before_name: &[], before_exposes: &[],
name: header.name.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena), exposes: header.exposes.remove_spaces(arena),
before_packages: &[],
packages: header.packages.remove_spaces(arena), packages: header.packages.remove_spaces(arena),
}), }),
Header::Platform(header) => Header::Platform(PlatformHeader { Header::Platform(header) => Header::Platform(PlatformHeader {

View file

@ -4851,12 +4851,12 @@ fn build_package_header<'a>(
module_timing: ModuleTiming, module_timing: ModuleTiming,
) -> Result<(ModuleId, PQModuleName<'a>, ModuleHeader<'a>), LoadingProblem<'a>> { ) -> Result<(ModuleId, PQModuleName<'a>, ModuleHeader<'a>), LoadingProblem<'a>> {
let exposes = bumpalo::collections::Vec::from_iter_in( let exposes = bumpalo::collections::Vec::from_iter_in(
unspace(arena, header.exposes.item.items).iter().copied(), unspace(arena, header.exposes.items).iter().copied(),
arena, arena,
); );
let packages = unspace(arena, header.packages.item.items); let packages = unspace(arena, header.packages.value.items);
let exposes_ids = get_exposes_ids( let exposes_ids = get_exposes_ids(
header.exposes.item.items, header.exposes.items,
arena, arena,
&module_ids, &module_ids,
&ident_ids_by_module, &ident_ids_by_module,

View file

@ -294,12 +294,10 @@ pub struct ProvidesTo<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct PackageHeader<'a> { pub struct PackageHeader<'a> {
pub before_name: &'a [CommentOrNewline<'a>], pub before_exposes: &'a [CommentOrNewline<'a>],
pub name: Loc<PackageName<'a>>, pub exposes: Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
pub before_packages: &'a [CommentOrNewline<'a>],
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>, pub packages: Loc<Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
pub packages:
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]

View file

@ -86,7 +86,7 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
map!( map!(
skip_first!( skip_first!(
keyword("package", EHeader::Start), keyword("package", EHeader::Start),
increment_min_indent(package_header()) increment_min_indent(one_of![package_header(), old_package_header()])
), ),
Header::Package Header::Package
), ),
@ -377,14 +377,57 @@ fn old_app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
#[inline(always)] #[inline(always)]
fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> { fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
record!(PackageHeader { record!(PackageHeader {
before_name: space0_e(EHeader::IndentStart), before_exposes: space0_e(EHeader::IndentStart),
name: loc!(specialize_err(EHeader::PackageName, package_name())), exposes: specialize_err(EHeader::Exposes, exposes_module_collection()),
exposes: specialize_err(EHeader::Exposes, exposes_modules()), before_packages: space0_e(EHeader::IndentStart),
packages: specialize_err(EHeader::Packages, packages()), packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
}) })
.trace("package_header") .trace("package_header")
} }
#[derive(Debug, Clone, PartialEq)]
struct OldPackageHeader<'a> {
before_name: &'a [CommentOrNewline<'a>],
exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
packages:
Loc<KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>>,
}
#[inline(always)]
fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
map_with_arena!(
record!(OldPackageHeader {
before_name: skip_second!(
space0_e(EHeader::IndentStart),
specialize_err(EHeader::PackageName, package_name())
),
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
packages: specialize_err(EHeader::Packages, loc!(packages())),
}),
|arena: &'a bumpalo::Bump, old: OldPackageHeader<'a>| {
let before_exposes = merge_n_spaces!(
arena,
old.before_name,
old.exposes.keyword.before,
old.exposes.keyword.after
);
let before_packages = merge_spaces(
arena,
old.packages.value.keyword.before,
old.packages.value.keyword.after,
);
PackageHeader {
before_exposes,
exposes: old.exposes.item,
before_packages,
packages: old.packages.map(|kw| kw.item),
}
}
)
.trace("old_package_header")
}
#[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>> {
record!(PlatformHeader { record!(PlatformHeader {
@ -638,16 +681,21 @@ fn exposes_modules<'a>() -> impl Parser<
EExposes::IndentExposes, EExposes::IndentExposes,
EExposes::IndentListStart EExposes::IndentListStart
), ),
item: collection_trailing_sep_e!( item: exposes_module_collection(),
byte(b'[', EExposes::ListStart),
exposes_module(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore
),
}) })
} }
fn exposes_module_collection<'a>(
) -> impl Parser<'a, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>, EExposes> {
collection_trailing_sep_e!(
byte(b'[', EExposes::ListStart),
exposes_module(EExposes::Identifier),
byte(b',', EExposes::ListEnd),
byte(b']', EExposes::ListEnd),
Spaced::SpaceBefore
)
}
fn exposes_module<'a, F, E>( fn exposes_module<'a, F, E>(
to_expectation: F, to_expectation: F,
) -> impl Parser<'a, Loc<Spaced<'a, ModuleName<'a>>>, E> ) -> impl Parser<'a, Loc<Spaced<'a, ModuleName<'a>>>, E>

View file

@ -1 +1 @@
package "rtfeldman/blah" exposes [] packages {} package [] {}

View file

@ -2,26 +2,10 @@ Module {
comments: [], comments: [],
header: Package( header: Package(
PackageHeader { PackageHeader {
before_name: [], before_exposes: [],
name: @8-24 PackageName( exposes: [],
"rtfeldman/blah", before_packages: [],
), packages: @11-13 [],
exposes: KeywordItem {
keyword: Spaces {
before: [],
item: ExposesKeyword,
after: [],
},
item: [],
},
packages: KeywordItem {
keyword: Spaces {
before: [],
item: PackagesKeyword,
after: [],
},
item: [],
},
}, },
), ),
} }

View file

@ -1 +1 @@
package "rtfeldman/blah" exposes [] packages {} package [] {}

View file

@ -1,3 +1,3 @@
package "foo/barbaz" package [Foo, Bar] {
exposes [Foo, Bar] foo: "./foo",
packages { foo: "./foo" } }

View file

@ -2,46 +2,36 @@ Module {
comments: [], comments: [],
header: Package( header: Package(
PackageHeader { PackageHeader {
before_name: [], before_exposes: [],
name: @8-20 PackageName( exposes: [
"foo/barbaz", @9-12 ModuleName(
), "Foo",
exposes: KeywordItem { ),
keyword: Spaces { @14-17 ModuleName(
before: [ "Bar",
),
],
before_packages: [],
packages: @19-39 [
@25-37 SpaceBefore(
SpaceAfter(
PackageEntry {
shorthand: "foo",
spaces_after_shorthand: [],
platform_marker: None,
package_name: @30-37 PackageName(
"./foo",
),
},
[
Newline,
],
),
[
Newline, Newline,
], ],
item: ExposesKeyword, ),
after: [], ],
},
item: [
@34-37 ModuleName(
"Foo",
),
@39-42 ModuleName(
"Bar",
),
],
},
packages: KeywordItem {
keyword: Spaces {
before: [
Newline,
],
item: PackagesKeyword,
after: [],
},
item: [
@59-71 PackageEntry {
shorthand: "foo",
spaces_after_shorthand: [],
platform_marker: None,
package_name: @64-71 PackageName(
"./foo",
),
},
],
},
}, },
), ),
} }

View file

@ -1,3 +1,3 @@
package "foo/barbaz" package [Foo, Bar] {
exposes [Foo, Bar] foo: "./foo"
packages { foo: "./foo" } }

View file

@ -4840,6 +4840,28 @@ mod test_fmt {
); );
} }
#[test]
fn old_style_package_header_is_upgraded() {
module_formats_to(
indoc!(
"
package \"csv\"
exposes [Csv]
packages {
parser: \"parser/main.roc\"
}
"
),
indoc!(
"
package [Csv] {
parser: \"parser/main.roc\",
}
"
),
);
}
#[test] #[test]
fn single_line_app() { fn single_line_app() {
module_formats_same(indoc!( module_formats_same(indoc!(

View file

@ -244,15 +244,14 @@ impl IterTokens for AppHeader<'_> {
impl IterTokens for PackageHeader<'_> { impl IterTokens for PackageHeader<'_> {
fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> { fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> {
let Self { let Self {
before_name: _, before_exposes: _,
name,
exposes, exposes,
before_packages: _,
packages, packages,
} = self; } = self;
(name.iter_tokens(arena).into_iter()) (exposes.iter_tokens(arena).into_iter())
.chain(exposes.item.iter_tokens(arena)) .chain(packages.value.iter_tokens(arena))
.chain(packages.item.iter_tokens(arena))
.collect_in(arena) .collect_in(arena)
} }
} }