mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
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:
parent
8dedd9f03c
commit
e3b600c282
16 changed files with 169 additions and 132 deletions
|
@ -1,3 +1 @@
|
|||
package "csv"
|
||||
exposes [Csv]
|
||||
packages {}
|
||||
package [Csv] {}
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
package "json"
|
||||
exposes [JsonParser]
|
||||
packages {}
|
||||
package [JsonParser] {}
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
package "builtins"
|
||||
exposes [Str, Num, Bool, Result, List, Dict, Set, Decode, Encode, Hash, Box, TotallyNotJson, Inspect]
|
||||
packages {}
|
||||
package [
|
||||
Str,
|
||||
Num,
|
||||
Bool,
|
||||
Result,
|
||||
List,
|
||||
Dict,
|
||||
Set,
|
||||
Decode,
|
||||
Encode,
|
||||
Hash,
|
||||
Box,
|
||||
TotallyNotJson,
|
||||
Inspect,
|
||||
] {}
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::spaces::RemoveSpaces;
|
|||
use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT};
|
||||
use crate::Buf;
|
||||
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::{
|
||||
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
|
||||
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.push_str("module");
|
||||
|
||||
if header.before_exposes.iter().all(|c| c.is_newline()) {
|
||||
buf.spaces(1);
|
||||
fmt_exposes(buf, header.exposes, 0);
|
||||
} else {
|
||||
let indent = INDENT;
|
||||
|
||||
fmt_default_spaces(buf, header.before_exposes, indent);
|
||||
let indent = fmt_spaces_with_outdent(buf, header.before_exposes, INDENT);
|
||||
fmt_exposes(buf, header.exposes, indent);
|
||||
};
|
||||
}
|
||||
|
||||
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.push_str("app");
|
||||
|
||||
let indent = INDENT;
|
||||
|
||||
if header.before_provides.iter().all(|c| c.is_newline()) {
|
||||
buf.spaces(1);
|
||||
fmt_exposes(buf, header.provides, 0);
|
||||
} else {
|
||||
fmt_default_spaces(buf, header.before_provides, indent);
|
||||
let indent = fmt_spaces_with_outdent(buf, header.before_provides, INDENT);
|
||||
fmt_exposes(buf, header.provides, indent);
|
||||
}
|
||||
|
||||
if header.before_packages.iter().all(|c| c.is_newline()) {
|
||||
buf.spaces(1);
|
||||
fmt_packages(buf, header.packages.value, 0);
|
||||
} else {
|
||||
fmt_default_spaces(buf, header.before_packages, indent);
|
||||
let indent = fmt_spaces_with_outdent(buf, header.before_packages, INDENT);
|
||||
fmt_packages(buf, header.packages.value, indent);
|
||||
}
|
||||
|
||||
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);
|
||||
0
|
||||
} else {
|
||||
fmt_default_spaces(buf, spaces, indent);
|
||||
indent
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fmt_package_header<'a>(buf: &mut Buf, header: &'a PackageHeader<'a>) {
|
||||
buf.indent(0);
|
||||
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);
|
||||
fmt_exposes(buf, header.exposes.item, indent);
|
||||
header.packages.keyword.format(buf, indent);
|
||||
fmt_packages(buf, header.packages.item, indent);
|
||||
let indent = fmt_spaces_with_outdent(buf, header.before_packages, INDENT);
|
||||
fmt_packages(buf, header.packages.value, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_platform_header<'a>(buf: &mut Buf, header: &'a PlatformHeader<'a>) {
|
||||
|
|
|
@ -299,9 +299,9 @@ impl<'a> RemoveSpaces<'a> for Module<'a> {
|
|||
.remove_spaces(arena),
|
||||
}),
|
||||
Header::Package(header) => Header::Package(PackageHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
before_exposes: &[],
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
before_packages: &[],
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
}),
|
||||
Header::Platform(header) => Header::Platform(PlatformHeader {
|
||||
|
|
|
@ -4851,12 +4851,12 @@ fn build_package_header<'a>(
|
|||
module_timing: ModuleTiming,
|
||||
) -> Result<(ModuleId, PQModuleName<'a>, ModuleHeader<'a>), LoadingProblem<'a>> {
|
||||
let exposes = bumpalo::collections::Vec::from_iter_in(
|
||||
unspace(arena, header.exposes.item.items).iter().copied(),
|
||||
unspace(arena, header.exposes.items).iter().copied(),
|
||||
arena,
|
||||
);
|
||||
let packages = unspace(arena, header.packages.item.items);
|
||||
let packages = unspace(arena, header.packages.value.items);
|
||||
let exposes_ids = get_exposes_ids(
|
||||
header.exposes.item.items,
|
||||
header.exposes.items,
|
||||
arena,
|
||||
&module_ids,
|
||||
&ident_ids_by_module,
|
||||
|
|
|
@ -294,12 +294,10 @@ pub struct ProvidesTo<'a> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct PackageHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
|
||||
pub exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>>,
|
||||
pub packages:
|
||||
KeywordItem<'a, PackagesKeyword, Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ModuleName<'a>>>>,
|
||||
pub before_packages: &'a [CommentOrNewline<'a>],
|
||||
pub packages: Loc<Collection<'a, Loc<Spaced<'a, PackageEntry<'a>>>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
|
@ -86,7 +86,7 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
|
|||
map!(
|
||||
skip_first!(
|
||||
keyword("package", EHeader::Start),
|
||||
increment_min_indent(package_header())
|
||||
increment_min_indent(one_of![package_header(), old_package_header()])
|
||||
),
|
||||
Header::Package
|
||||
),
|
||||
|
@ -377,14 +377,57 @@ fn old_app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
|||
#[inline(always)]
|
||||
fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
|
||||
record!(PackageHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(specialize_err(EHeader::PackageName, package_name())),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_modules()),
|
||||
packages: specialize_err(EHeader::Packages, packages()),
|
||||
before_exposes: space0_e(EHeader::IndentStart),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_module_collection()),
|
||||
before_packages: space0_e(EHeader::IndentStart),
|
||||
packages: specialize_err(EHeader::Packages, loc!(packages_collection())),
|
||||
})
|
||||
.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)]
|
||||
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
||||
record!(PlatformHeader {
|
||||
|
@ -638,14 +681,19 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
EExposes::IndentExposes,
|
||||
EExposes::IndentListStart
|
||||
),
|
||||
item: collection_trailing_sep_e!(
|
||||
item: exposes_module_collection(),
|
||||
})
|
||||
}
|
||||
|
||||
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>(
|
||||
|
|
|
@ -1 +1 @@
|
|||
package "rtfeldman/blah" exposes [] packages {}
|
||||
package [] {}
|
||||
|
|
|
@ -2,26 +2,10 @@ Module {
|
|||
comments: [],
|
||||
header: Package(
|
||||
PackageHeader {
|
||||
before_name: [],
|
||||
name: @8-24 PackageName(
|
||||
"rtfeldman/blah",
|
||||
),
|
||||
exposes: KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: [],
|
||||
item: ExposesKeyword,
|
||||
after: [],
|
||||
},
|
||||
item: [],
|
||||
},
|
||||
packages: KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: [],
|
||||
item: PackagesKeyword,
|
||||
after: [],
|
||||
},
|
||||
item: [],
|
||||
},
|
||||
before_exposes: [],
|
||||
exposes: [],
|
||||
before_packages: [],
|
||||
packages: @11-13 [],
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
package "rtfeldman/blah" exposes [] packages {}
|
||||
package [] {}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package "foo/barbaz"
|
||||
exposes [Foo, Bar]
|
||||
packages { foo: "./foo" }
|
||||
package [Foo, Bar] {
|
||||
foo: "./foo",
|
||||
}
|
||||
|
|
|
@ -2,46 +2,36 @@ Module {
|
|||
comments: [],
|
||||
header: Package(
|
||||
PackageHeader {
|
||||
before_name: [],
|
||||
name: @8-20 PackageName(
|
||||
"foo/barbaz",
|
||||
),
|
||||
exposes: KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: [
|
||||
Newline,
|
||||
],
|
||||
item: ExposesKeyword,
|
||||
after: [],
|
||||
},
|
||||
item: [
|
||||
@34-37 ModuleName(
|
||||
before_exposes: [],
|
||||
exposes: [
|
||||
@9-12 ModuleName(
|
||||
"Foo",
|
||||
),
|
||||
@39-42 ModuleName(
|
||||
@14-17 ModuleName(
|
||||
"Bar",
|
||||
),
|
||||
],
|
||||
},
|
||||
packages: KeywordItem {
|
||||
keyword: Spaces {
|
||||
before: [
|
||||
Newline,
|
||||
],
|
||||
item: PackagesKeyword,
|
||||
after: [],
|
||||
},
|
||||
item: [
|
||||
@59-71 PackageEntry {
|
||||
before_packages: [],
|
||||
packages: @19-39 [
|
||||
@25-37 SpaceBefore(
|
||||
SpaceAfter(
|
||||
PackageEntry {
|
||||
shorthand: "foo",
|
||||
spaces_after_shorthand: [],
|
||||
platform_marker: None,
|
||||
package_name: @64-71 PackageName(
|
||||
package_name: @30-37 PackageName(
|
||||
"./foo",
|
||||
),
|
||||
},
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
[
|
||||
Newline,
|
||||
],
|
||||
),
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
package "foo/barbaz"
|
||||
exposes [Foo, Bar]
|
||||
packages { foo: "./foo" }
|
||||
package [Foo, Bar] {
|
||||
foo: "./foo"
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
fn single_line_app() {
|
||||
module_formats_same(indoc!(
|
||||
|
|
|
@ -244,15 +244,14 @@ impl IterTokens for AppHeader<'_> {
|
|||
impl IterTokens for PackageHeader<'_> {
|
||||
fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> {
|
||||
let Self {
|
||||
before_name: _,
|
||||
name,
|
||||
before_exposes: _,
|
||||
exposes,
|
||||
before_packages: _,
|
||||
packages,
|
||||
} = self;
|
||||
|
||||
(name.iter_tokens(arena).into_iter())
|
||||
.chain(exposes.item.iter_tokens(arena))
|
||||
.chain(packages.item.iter_tokens(arena))
|
||||
(exposes.iter_tokens(arena).into_iter())
|
||||
.chain(packages.value.iter_tokens(arena))
|
||||
.collect_in(arena)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue