mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 15:27:45 +03:00
Parse package module header
This commit is contained in:
parent
d90f551670
commit
da595a86b0
@ -8,7 +8,7 @@ use bumpalo::Bump;
|
||||
use roc_parse::ast::{Collection, Header, Module, Spaced, Spaces};
|
||||
use roc_parse::header::{
|
||||
AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry,
|
||||
ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, PackageEntry,
|
||||
ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName, PackageEntry, PackageHeader,
|
||||
PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, PlatformRequires,
|
||||
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||
};
|
||||
@ -24,6 +24,9 @@ pub fn fmt_module<'a>(buf: &mut Buf<'_>, module: &'a Module<'a>) {
|
||||
Header::App(header) => {
|
||||
fmt_app_header(buf, header);
|
||||
}
|
||||
Header::Package(header) => {
|
||||
fmt_package_header(buf, header);
|
||||
}
|
||||
Header::Platform(header) => {
|
||||
fmt_platform_header(buf, header);
|
||||
}
|
||||
@ -226,6 +229,22 @@ pub fn fmt_app_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a AppHeader<'a>)
|
||||
header.provides.format(buf, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_package_header<'a, 'buf>(buf: &mut Buf<'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);
|
||||
|
||||
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);
|
||||
header.imports.keyword.format(buf, indent);
|
||||
fmt_imports(buf, header.imports.item, indent);
|
||||
}
|
||||
|
||||
pub fn fmt_platform_header<'a, 'buf>(buf: &mut Buf<'buf>, header: &'a PlatformHeader<'a>) {
|
||||
buf.indent(0);
|
||||
buf.push_str("platform");
|
||||
|
@ -9,8 +9,8 @@ use roc_parse::{
|
||||
},
|
||||
header::{
|
||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
|
||||
ModuleName, PackageEntry, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To,
|
||||
TypedIdent,
|
||||
ModuleName, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires,
|
||||
ProvidesTo, To, TypedIdent,
|
||||
},
|
||||
ident::UppercaseIdent,
|
||||
};
|
||||
@ -290,6 +290,13 @@ impl<'a> RemoveSpaces<'a> for Module<'a> {
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
provides: header.provides.remove_spaces(arena),
|
||||
}),
|
||||
Header::Package(header) => Header::Package(PackageHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
exposes: header.exposes.remove_spaces(arena),
|
||||
packages: header.packages.remove_spaces(arena),
|
||||
imports: header.imports.remove_spaces(arena),
|
||||
}),
|
||||
Header::Platform(header) => Header::Platform(PlatformHeader {
|
||||
before_name: &[],
|
||||
name: header.name.remove_spaces(arena),
|
||||
|
@ -3335,6 +3335,19 @@ fn load_platform_module<'a>(
|
||||
"expected platform/package module, got App with header\n{:?}",
|
||||
header
|
||||
))),
|
||||
Ok((
|
||||
ast::Module {
|
||||
header: ast::Header::Package(header),
|
||||
..
|
||||
},
|
||||
parser_state,
|
||||
)) => {
|
||||
todo!(
|
||||
"Send `packag` module using {:?} and {:?}",
|
||||
header,
|
||||
parser_state
|
||||
)
|
||||
}
|
||||
Ok((
|
||||
ast::Module {
|
||||
header: ast::Header::Platform(header),
|
||||
@ -3914,6 +3927,16 @@ fn parse_header<'a>(
|
||||
To::NewPackage(_package_name) => Ok((module_id, app_module_header_msg)),
|
||||
}
|
||||
}
|
||||
Ok((
|
||||
ast::Module {
|
||||
header: ast::Header::Package(header),
|
||||
..
|
||||
},
|
||||
parse_state,
|
||||
)) => {
|
||||
todo!("Parse header for {:?} --> {:?}", header, parse_state);
|
||||
}
|
||||
|
||||
Ok((
|
||||
ast::Module {
|
||||
header: ast::Header::Platform(header),
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
"app"
|
||||
"platform"
|
||||
"package"
|
||||
"provides"
|
||||
"requires"
|
||||
"exposes"
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::header::{AppHeader, HostedHeader, InterfaceHeader, PlatformHeader};
|
||||
use crate::header::{AppHeader, HostedHeader, InterfaceHeader, PackageHeader, PlatformHeader};
|
||||
use crate::ident::Ident;
|
||||
use bumpalo::collections::{String, Vec};
|
||||
use bumpalo::Bump;
|
||||
@ -90,6 +90,7 @@ pub struct Module<'a> {
|
||||
pub enum Header<'a> {
|
||||
Interface(InterfaceHeader<'a>),
|
||||
App(AppHeader<'a>),
|
||||
Package(PackageHeader<'a>),
|
||||
Platform(PlatformHeader<'a>),
|
||||
Hosted(HostedHeader<'a>),
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ use crate::ident::{lowercase_ident, UppercaseIdent};
|
||||
use crate::parser::{optional, then};
|
||||
use crate::parser::{specialize, word1, EPackageEntry, EPackageName, Parser};
|
||||
use crate::string_literal;
|
||||
use bumpalo::collections::Vec;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::Loc;
|
||||
use std::fmt::Debug;
|
||||
@ -214,14 +213,10 @@ pub struct PackageHeader<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<PackageName<'a>>,
|
||||
|
||||
pub exposes_keyword: Spaces<'a, ExposesKeyword>,
|
||||
pub exposes: Vec<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
|
||||
pub packages_keyword: Spaces<'a, PackagesKeyword>,
|
||||
pub packages: Vec<'a, (Loc<&'a str>, Loc<PackageName<'a>>)>,
|
||||
|
||||
pub imports_keyword: Spaces<'a, ImportsKeyword>,
|
||||
pub imports: Vec<'a, Loc<ImportsEntry<'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 imports: KeywordItem<'a, ImportsKeyword, Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -3,8 +3,8 @@ use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::header::{
|
||||
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
|
||||
HostedHeader, ImportsEntry, ImportsKeyword, InterfaceHeader, Keyword, KeywordItem, ModuleName,
|
||||
PackageEntry, PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo,
|
||||
RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||
PackageEntry, PackageHeader, PackagesKeyword, PlatformHeader, PlatformRequires,
|
||||
ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||
};
|
||||
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
|
||||
use crate::parser::Progress::{self, *};
|
||||
@ -67,6 +67,13 @@ fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
|
||||
),
|
||||
Header::App
|
||||
),
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword_e("package", EHeader::Start),
|
||||
increment_min_indent(package_header())
|
||||
),
|
||||
Header::Package
|
||||
),
|
||||
map!(
|
||||
skip_first!(
|
||||
keyword_e("platform", EHeader::Start),
|
||||
@ -183,6 +190,18 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|
||||
.trace("app_header")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> {
|
||||
record!(PackageHeader {
|
||||
before_name: space0_e(EHeader::IndentStart),
|
||||
name: loc!(specialize(EHeader::PackageName, package_name())),
|
||||
exposes: specialize(EHeader::Exposes, exposes_modules()),
|
||||
packages: specialize(EHeader::Packages, packages()),
|
||||
imports: specialize(EHeader::Imports, imports()),
|
||||
})
|
||||
.trace("package_header")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
|
||||
record!(PlatformHeader {
|
||||
|
@ -127,6 +127,7 @@ pub enum EHeader<'a> {
|
||||
Start(Position),
|
||||
ModuleName(Position),
|
||||
AppName(EString<'a>, Position),
|
||||
PackageName(EPackageName<'a>, Position),
|
||||
PlatformName(EPackageName<'a>, Position),
|
||||
IndentStart(Position),
|
||||
|
||||
|
@ -31,6 +31,7 @@ pub enum Token {
|
||||
KeywordTo = 0b_0010_1110,
|
||||
KeywordExposes = 0b_0010_1111,
|
||||
KeywordEffects = 0b_0011_0000,
|
||||
KeywordPackage = 0b_0111_1100,
|
||||
KeywordPlatform = 0b_0011_0001,
|
||||
KeywordRequires = 0b_0011_0010,
|
||||
KeywordDbg = 0b_0111_1011,
|
||||
@ -428,6 +429,7 @@ fn lex_ident(uppercase: bool, bytes: &[u8]) -> (Token, usize) {
|
||||
b"to" => Token::KeywordTo,
|
||||
b"exposes" => Token::KeywordExposes,
|
||||
b"effects" => Token::KeywordEffects,
|
||||
b"package" => Token::KeywordPackage,
|
||||
b"platform" => Token::KeywordPlatform,
|
||||
b"requires" => Token::KeywordRequires,
|
||||
ident => {
|
||||
|
@ -10,6 +10,7 @@ use std::fs::File;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::Path;
|
||||
use tar;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Compression {
|
||||
@ -124,7 +125,9 @@ fn write_archive<W: Write>(path: &Path, writer: W) -> io::Result<()> {
|
||||
let arena = Bump::new();
|
||||
let mut buf = Vec::new();
|
||||
|
||||
let _other_modules: &[Module<'_>] = match read_header(&arena, &mut buf, path)?.header {
|
||||
// TODO use this when finding .roc files by discovering them from the root module.
|
||||
// let other_modules: &[Module<'_>] =
|
||||
match read_header(&arena, &mut buf, path)?.header {
|
||||
Header::Interface(_) => {
|
||||
todo!();
|
||||
// TODO report error
|
||||
@ -137,9 +140,10 @@ fn write_archive<W: Write>(path: &Path, writer: W) -> io::Result<()> {
|
||||
todo!();
|
||||
// TODO report error
|
||||
}
|
||||
Header::Package(_) => {
|
||||
add_dot_roc_files(root_dir, &mut builder)?;
|
||||
}
|
||||
Header::Platform(PlatformHeader { imports: _, .. }) => {
|
||||
use walkdir::WalkDir;
|
||||
|
||||
// Add all the prebuilt host files to the archive.
|
||||
// These should all be in the same directory as the platform module.
|
||||
for entry in std::fs::read_dir(root_dir)? {
|
||||
@ -170,38 +174,7 @@ fn write_archive<W: Write>(path: &Path, writer: W) -> io::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively find all the .roc files and add them.
|
||||
// TODO we can do this more efficiently by parsing the platform module and finding
|
||||
// all of its dependencies. See below for a commented-out WIP sketch of this.
|
||||
//
|
||||
// The WalkDir approach is easier to implement, but has the downside of doing things
|
||||
// like traversing target/ and zig-cache/ which can be large but will never have
|
||||
// any .roc files in them!
|
||||
for entry in WalkDir::new(root_dir).into_iter().filter_entry(|entry| {
|
||||
let path = entry.path();
|
||||
|
||||
// We already got the prebuilt host files, so the only other things
|
||||
// we care about are .roc files.
|
||||
path.is_dir() || path.extension().and_then(OsStr::to_str) == Some("roc")
|
||||
}) {
|
||||
let entry = entry?;
|
||||
|
||||
// Only include files, not directories or symlinks.
|
||||
// Symlinks may not work on Windows, and directories will get automatically
|
||||
// added based on the paths of the files inside anyway. (In fact, if we don't
|
||||
// filter out directories in this step, then empty ones will end up getting added!)
|
||||
if entry.path().is_file() {
|
||||
builder.append_path_with_name(
|
||||
entry.path(),
|
||||
// Store it without the root path, so that (for example) we don't store
|
||||
// `examples/cli/main.roc` and therefore end up with the root of the tarball
|
||||
// being an `examples/cli/` dir instead of having `main.roc` in the root.
|
||||
entry.path().strip_prefix(root_dir).unwrap(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
&[]
|
||||
add_dot_roc_files(root_dir, &mut builder)?;
|
||||
}
|
||||
};
|
||||
|
||||
@ -246,6 +219,37 @@ fn write_archive<W: Write>(path: &Path, writer: W) -> io::Result<()> {
|
||||
builder.finish()
|
||||
}
|
||||
|
||||
fn add_dot_roc_files<W: Write>(
|
||||
root_dir: &Path,
|
||||
builder: &mut tar::Builder<W>,
|
||||
) -> Result<(), io::Error> {
|
||||
Ok(
|
||||
for entry in WalkDir::new(root_dir).into_iter().filter_entry(|entry| {
|
||||
let path = entry.path();
|
||||
|
||||
// Ignore everything except directories and .roc files
|
||||
path.is_dir() || path.extension().and_then(OsStr::to_str) == Some("roc")
|
||||
}) {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
// Only include files, not directories or symlinks.
|
||||
// Symlinks may not work on Windows, and directories will get automatically
|
||||
// added based on the paths of the files inside anyway. (In fact, if we don't
|
||||
// filter out directories in this step, then empty ones can sometimes be added!)
|
||||
if path.is_file() {
|
||||
builder.append_path_with_name(
|
||||
path,
|
||||
// Store it without the root path, so that (for example) we don't store
|
||||
// `examples/cli/main.roc` and therefore end up with the root of the tarball
|
||||
// being an `examples/cli/` dir instead of having `main.roc` in the root.
|
||||
path.strip_prefix(root_dir).unwrap(),
|
||||
)?;
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn read_header<'a>(
|
||||
arena: &'a Bump,
|
||||
buf: &'a mut Vec<u8>,
|
||||
|
@ -3299,6 +3299,8 @@ fn to_header_report<'a>(
|
||||
alloc.keyword("interface"),
|
||||
alloc.reflow(", "),
|
||||
alloc.keyword("app"),
|
||||
alloc.reflow(", "),
|
||||
alloc.keyword("package"),
|
||||
alloc.reflow(" or "),
|
||||
alloc.keyword("platform"),
|
||||
alloc.reflow("."),
|
||||
@ -3388,12 +3390,35 @@ fn to_header_report<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
EHeader::PackageName(_, pos) => {
|
||||
let surroundings = Region::new(start, *pos);
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(*pos));
|
||||
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I am partway through parsing a package header, but got stuck here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||
alloc.concat([
|
||||
alloc.reflow("I am expecting a package name next, like "),
|
||||
alloc.parser_suggestion("\"roc/core\""),
|
||||
alloc.reflow(". Package names must be quoted."),
|
||||
]),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "INVALID PACKAGE NAME".to_string(),
|
||||
severity: Severity::RuntimeError,
|
||||
}
|
||||
}
|
||||
|
||||
EHeader::PlatformName(_, pos) => {
|
||||
let surroundings = Region::new(start, *pos);
|
||||
let region = LineColumnRegion::from_pos(lines.convert_pos(*pos));
|
||||
|
||||
let doc = alloc.stack([
|
||||
alloc.reflow(r"I am partway through parsing a header, but got stuck here:"),
|
||||
alloc
|
||||
.reflow(r"I am partway through parsing a platform header, but got stuck here:"),
|
||||
alloc.region_with_subregion(lines.convert_region(surroundings), region),
|
||||
alloc.concat([
|
||||
alloc.reflow("I am expecting a platform name next, like "),
|
||||
@ -3405,7 +3430,7 @@ fn to_header_report<'a>(
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "WEIRD MODULE NAME".to_string(),
|
||||
title: "INVALID PLATFORM NAME".to_string(),
|
||||
severity: Severity::RuntimeError,
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user