Merge pull request #1197 from rtfeldman/parse-platform-rigids

Parse platform rigids
This commit is contained in:
Richard Feldman 2021-04-14 22:12:58 -04:00 committed by GitHub
commit 79b5af951b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 231 additions and 33 deletions

View File

@ -1,5 +1,5 @@
platform examples/multi-module
requires { main : Str }
requires {}{ main : Str }
exposes []
packages {}
imports []

View File

@ -1,5 +1,5 @@
platform examples/multi-dep-thunk
requires { main : Str }
requires {}{ main : Str }
exposes []
packages {}
imports []

View File

@ -1146,7 +1146,7 @@ impl<'a> LoadStart<'a> {
let (root_id, root_msg) = {
let root_start_time = SystemTime::now();
load_filename(
let res_loaded = load_filename(
arena,
filename,
true,
@ -1154,7 +1154,26 @@ impl<'a> LoadStart<'a> {
Arc::clone(&arc_modules),
Arc::clone(&ident_ids_by_module),
root_start_time,
)?
);
match res_loaded {
Ok(good) => good,
Err(LoadingProblem::ParsingFailed(problem)) => {
let module_ids = Arc::try_unwrap(arc_modules)
.unwrap_or_else(|_| {
panic!("There were still outstanding Arc references to module_ids")
})
.into_inner()
.into_module_ids();
// if parsing failed, this module did not add any identifiers
let root_exposed_ident_ids = IdentIds::exposed_builtins(0);
let buf = to_parse_problem_report(problem, module_ids, root_exposed_ident_ids);
return Err(LoadingProblem::FormattedReport(buf));
}
Err(e) => return Err(e),
}
};
Ok(LoadStart {
@ -1535,14 +1554,41 @@ where
// This is where most of the main thread's work gets done.
// Everything up to this point has been setting up the threading
// system which lets this logic work efficiently.
state = update(
let constrained_ident_ids = state.constrained_ident_ids.clone();
let arc_modules = state.arc_modules.clone();
let res_state = update(
state,
msg,
msg_tx.clone(),
&injector,
worker_listeners,
arena,
)?;
);
match res_state {
Ok(new_state) => {
state = new_state;
}
Err(LoadingProblem::ParsingFailed(problem)) => {
shut_down_worker_threads!();
let module_ids = Arc::try_unwrap(arc_modules)
.unwrap_or_else(|_| {
panic!(r"There were still outstanding Arc references to module_ids")
})
.into_inner()
.into_module_ids();
let buf = to_parse_problem_report(
problem,
module_ids,
constrained_ident_ids,
);
return Err(LoadingProblem::FormattedReport(buf));
}
Err(e) => return Err(e),
}
}
}
}
@ -3224,7 +3270,7 @@ fn fabricate_pkg_config_module<'a>(
app_module_id,
packages: &[],
provides,
requires: header.requires.clone().into_bump_slice(),
requires: arena.alloc([header.requires.signature.clone()]),
imports: header.imports.clone().into_bump_slice(),
};

View File

@ -124,10 +124,34 @@ pub struct PackageHeader<'a> {
pub after_imports: &'a [CommentOrNewline<'a>],
}
#[derive(Clone, Debug, PartialEq)]
pub enum PlatformRigid<'a> {
Entry { rigid: &'a str, alias: &'a str },
// Spaces
SpaceBefore(&'a PlatformRigid<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a PlatformRigid<'a>, &'a [CommentOrNewline<'a>]),
}
impl<'a> Spaceable<'a> for PlatformRigid<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
PlatformRigid::SpaceBefore(self, spaces)
}
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
PlatformRigid::SpaceAfter(self, spaces)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PlatformRequires<'a> {
pub rigids: Vec<'a, Loc<PlatformRigid<'a>>>,
pub signature: Loc<TypedIdent<'a>>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct PlatformHeader<'a> {
pub name: Loc<PackageName<'a>>,
pub requires: Vec<'a, Loc<TypedIdent<'a>>>,
pub requires: PlatformRequires<'a>,
pub exposes: Vec<'a, Loc<ExposesEntry<'a, ModuleName<'a>>>>,
pub packages: Vec<'a, Loc<PackageEntry<'a>>>,
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,

View File

@ -1,13 +1,14 @@
use crate::ast::{CommentOrNewline, Def, Module};
use crate::blankspace::{space0_before_e, space0_e};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::header::{
package_entry, package_name, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry,
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, To, TypedIdent,
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, PlatformRequires, PlatformRigid, To,
TypedIdent,
};
use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident};
use crate::parser::Progress::{self, *};
use crate::parser::{
backtrackable, specialize, word1, Col, EEffects, EExposes, EHeader, EImports, EPackages,
backtrackable, specialize, word1, word2, Col, EEffects, EExposes, EHeader, EImports, EPackages,
EProvides, ERequires, ETypedIdent, Parser, Row, State, SyntaxError,
};
use crate::string_literal;
@ -406,11 +407,11 @@ fn requires<'a>() -> impl Parser<
'a,
(
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<TypedIdent<'a>>>,
PlatformRequires<'a>,
),
ERequires<'a>,
> {
let min_indent = 1;
let min_indent = 0;
and!(
spaces_around_keyword(
min_indent,
@ -420,14 +421,63 @@ fn requires<'a>() -> impl Parser<
ERequires::IndentRequires,
ERequires::IndentListStart
),
collection_e!(
word1(b'{', ERequires::ListStart),
specialize(ERequires::TypedIdent, loc!(typed_ident())),
word1(b',', ERequires::ListEnd),
word1(b'}', ERequires::ListEnd),
min_indent,
ERequires::Space,
ERequires::IndentListEnd
platform_requires()
)
}
#[inline(always)]
fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> {
map!(
and!(
skip_second!(
requires_rigids(0),
space0_e(0, ERequires::Space, ERequires::ListStart)
),
requires_typed_ident()
),
|(rigids, signature)| { PlatformRequires { rigids, signature } }
)
}
#[inline(always)]
fn requires_rigids<'a>(
min_indent: u16,
) -> impl Parser<'a, Vec<'a, Located<PlatformRigid<'a>>>, ERequires<'a>> {
collection_e!(
word1(b'{', ERequires::ListStart),
specialize(|_, r, c| ERequires::Rigid(r, c), loc!(requires_rigid())),
word1(b',', ERequires::ListEnd),
word1(b'}', ERequires::ListEnd),
min_indent,
ERequires::Space,
ERequires::IndentListEnd
)
}
#[inline(always)]
fn requires_rigid<'a>() -> impl Parser<'a, PlatformRigid<'a>, ()> {
map!(
and!(
lowercase_ident(),
skip_first!(word2(b'=', b'>', |_, _| ()), uppercase_ident())
),
|(rigid, alias)| PlatformRigid::Entry { rigid, alias }
)
}
#[inline(always)]
fn requires_typed_ident<'a>() -> impl Parser<'a, Located<TypedIdent<'a>>, ERequires<'a>> {
skip_first!(
word1(b'{', ERequires::ListStart),
skip_second!(
space0_around_ee(
specialize(ERequires::TypedIdent, loc!(typed_ident()),),
0,
ERequires::Space,
ERequires::ListStart,
ERequires::ListEnd
),
word1(b'}', ERequires::ListStart)
)
)
}

View File

@ -248,6 +248,7 @@ pub enum ERequires<'a> {
ListStart(Row, Col),
ListEnd(Row, Col),
TypedIdent(ETypedIdent<'a>, Row, Col),
Rigid(Row, Col),
Space(BadInputError, Row, Col),
}

View File

@ -26,7 +26,8 @@ mod test_parse {
use roc_parse::ast::{self, Def, EscapedChar, Spaceable, TypeAnnotation, WhenBranch};
use roc_parse::header::{
AppHeader, Effects, ExposesEntry, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
PackageName, PackageOrPath, PlatformHeader, To,
PackageName, PackageOrPath, PlatformHeader, PlatformRequires, PlatformRigid, To,
TypedIdent,
};
use roc_parse::module::module_defs;
use roc_parse::parser::{Parser, State, SyntaxError};
@ -3128,10 +3129,35 @@ mod test_parse {
spaces_after_effects_keyword: &[],
spaces_after_type_name: &[],
};
let requires = {
let region1 = Region::new(0, 0, 38, 47);
let region2 = Region::new(0, 0, 45, 47);
PlatformRequires {
rigids: Vec::new_in(&arena),
signature: Located::at(
region1,
TypedIdent::Entry {
ident: Located::new(0, 0, 38, 42, "main"),
spaces_before_colon: &[],
ann: Located::at(
region2,
TypeAnnotation::Record {
fields: &[],
ext: None,
final_comments: &[],
},
),
},
),
}
};
let header = PlatformHeader {
before_header: &[],
name: Located::new(0, 0, 9, 23, pkg_name),
requires: Vec::new_in(&arena),
requires,
exposes: Vec::new_in(&arena),
packages: Vec::new_in(&arena),
imports: Vec::new_in(&arena),
@ -3152,7 +3178,7 @@ mod test_parse {
let expected = roc_parse::ast::Module::Platform { header };
let src = "platform rtfeldman/blah requires {} exposes [] packages {} imports [] provides [] effects fx.Blah {}";
let src = "platform rtfeldman/blah requires {} { main : {} } exposes [] packages {} imports [] provides [] effects fx.Blah {}";
let actual = roc_parse::module::parse_header(&arena, State::new(src.as_bytes()))
.map(|tuple| tuple.0);
@ -3188,10 +3214,36 @@ mod test_parse {
spaces_after_effects_keyword: &[],
spaces_after_type_name: &[],
};
let requires = {
let region1 = Region::new(1, 1, 30, 39);
let region2 = Region::new(1, 1, 37, 39);
let region3 = Region::new(1, 1, 14, 26);
PlatformRequires {
rigids: bumpalo::vec![ in &arena; Located::at(region3, PlatformRigid::Entry { alias: "Model", rigid: "model" }) ],
signature: Located::at(
region1,
TypedIdent::Entry {
ident: Located::new(1, 1, 30, 34, "main"),
spaces_before_colon: &[],
ann: Located::at(
region2,
TypeAnnotation::Record {
fields: &[],
ext: None,
final_comments: &[],
},
),
},
),
}
};
let header = PlatformHeader {
before_header: &[],
name: Located::new(0, 0, 9, 19, pkg_name),
requires: Vec::new_in(&arena),
requires,
exposes: Vec::new_in(&arena),
packages,
imports,
@ -3215,7 +3267,7 @@ mod test_parse {
let src = indoc!(
r#"
platform foo/barbaz
requires {}
requires {model=>Model} { main : {} }
exposes []
packages { foo: "./foo" }
imports []

View File

@ -3238,6 +3238,31 @@ fn to_requires_report<'a>(
ERequires::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
ERequires::ListStart(row, col) => {
dbg!(row, col);
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
alloc.reflow(r"I am partway through parsing a header, but I got stuck here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("I am expecting the "),
alloc.keyword("requires"),
alloc.reflow(" keyword next, like "),
]),
alloc
.parser_suggestion("requires { main : Task I64 Str }")
.indent(4),
]);
Report {
filename,
doc,
title: "MISSING REQUIRES".to_string(),
}
}
_ => todo!("unhandled parse error {:?}", parse_problem),
}
}

View File

@ -1,5 +1,5 @@
platform folkertdev/foo
requires { main : Effect {} }
requires {model=>Model, msg=>Msg} {main : Effect {}}
exposes []
packages {}
imports [ Task ]

View File

@ -1,5 +1,5 @@
platform rtfeldman/roc-cli
requires { main : Task.Task {} * } # TODO FIXME
requires {}{ main : Task.Task {} * } # TODO FIXME
exposes [] # TODO FIXME actually expose modules
packages {}
imports [ Task ] # TODO FIXME Task.{ Task }

View File

@ -1,5 +1,5 @@
platform examples/hello-world
requires { main : Str }
requires {}{ main : Str }
exposes []
packages {}
imports []

View File

@ -1,5 +1,5 @@
platform examples/quicksort
requires { quicksort : List I64 -> List I64 }
requires {}{ quicksort : List I64 -> List I64 }
exposes []
packages {}
imports []

View File

@ -1,5 +1,5 @@
platform folkertdev/foo
requires { main : Task {} [] }
requires {}{ main : Task {} [] }
exposes []
packages {}
imports [ Task ]

View File

@ -1,10 +1,10 @@
platform folkertdev/foo
requires { main : {} }
requires {}{foo:Str}
exposes []
packages {}
imports [Cmd]
provides [ mainForHost ]
effects Effect
effects fx.Effect
{
putChar : I64 -> Effect {},
putLine : Str -> Effect {},