mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
First pass at async stuff
This commit is contained in:
parent
37cfb0f650
commit
29acb7a2b4
1 changed files with 94 additions and 68 deletions
162
src/load/mod.rs
162
src/load/mod.rs
|
@ -8,22 +8,21 @@ use crate::parse::parser::{Fail, Parser, State};
|
||||||
use crate::region::{Located, Region};
|
use crate::region::{Located, Region};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use std::fs::read_to_string;
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::pin::Pin;
|
||||||
|
use tokio::fs::read_to_string;
|
||||||
|
|
||||||
pub struct Loaded<'a> {
|
pub struct Loaded<'a> {
|
||||||
pub requested_header: LoadedHeader<'a>,
|
pub requested_header: LoadedHeader<'a>,
|
||||||
pub dependent_headers: MutMap<ModuleName<'a>, LoadedHeader<'a>>,
|
pub dependent_headers: ImMap<ModuleName<'a>, LoadedHeader<'a>>,
|
||||||
pub defs: MutMap<ModuleName<'a>, Result<Vec<'a, Located<Def<'a>>>, Fail>>,
|
pub defs: MutMap<ModuleName<'a>, Result<Vec<'a, Located<Def<'a>>>, Fail>>,
|
||||||
pub problems: Vec<'a, BuildProblem<'a>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Env<'a, 'p> {
|
struct Env<'a> {
|
||||||
pub arena: &'a Bump,
|
pub arena: &'a Bump,
|
||||||
pub src_dir: &'p Path,
|
pub src_dir: &'a Path,
|
||||||
pub problems: Vec<'a, BuildProblem<'a>>,
|
|
||||||
pub loaded_headers: MutMap<ModuleName<'a>, LoadedHeader<'a>>,
|
|
||||||
pub queue: Queue<'a>,
|
pub queue: Queue<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ pub enum BuildProblem<'a> {
|
||||||
FileNotFound(&'a Path),
|
FileNotFound(&'a Path),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum LoadedHeader<'a> {
|
pub enum LoadedHeader<'a> {
|
||||||
Valid {
|
Valid {
|
||||||
scope: ImMap<UnqualifiedIdent<'a>, (Symbol, Region)>,
|
scope: ImMap<UnqualifiedIdent<'a>, (Symbol, Region)>,
|
||||||
|
@ -43,32 +42,38 @@ pub enum LoadedHeader<'a> {
|
||||||
ParsingFailed(Fail),
|
ParsingFailed(Fail),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load<'a>(arena: &'a Bump, src_dir: &Path, filename: &Path) -> Loaded<'a> {
|
pub async fn load<'a>(arena: &'a Bump, src_dir: &'a Path, filename: &Path) -> Loaded<'a> {
|
||||||
let mut env = Env {
|
let env = Env {
|
||||||
arena,
|
arena,
|
||||||
src_dir,
|
src_dir,
|
||||||
problems: Vec::new_in(&arena),
|
|
||||||
loaded_headers: MutMap::default(),
|
|
||||||
queue: MutMap::default(),
|
queue: MutMap::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let requested_header = load_filename(&mut env, filename);
|
|
||||||
|
/// TODO proof of concept:
|
||||||
|
///
|
||||||
|
/// set up a job queue, and load *all* modules using that.
|
||||||
|
/// after each one loads, maintain a cache of "we've already started loading this"
|
||||||
|
/// so subsequent ones don't need to enqueue redundantly -
|
||||||
|
/// but also check again before running a fresh load!
|
||||||
|
/// Also, use a similar (maybe even the same?) queue for parsing defs in parallel
|
||||||
|
|
||||||
|
let (requested_header, dependent_headers) = load_filename(&env, filename).await;
|
||||||
let mut defs = MutMap::default();
|
let mut defs = MutMap::default();
|
||||||
|
|
||||||
for (module_name, state) in env.queue {
|
// for (module_name, state) in env.queue {
|
||||||
let loaded_defs = match module::module_defs().parse(arena, state) {
|
// let loaded_defs = match module::module_defs().parse(arena, state) {
|
||||||
Ok((defs, _)) => Ok(defs),
|
// Ok((defs, _)) => Ok(defs),
|
||||||
Err((fail, _)) => Err(fail),
|
// Err((fail, _)) => Err(fail),
|
||||||
};
|
// };
|
||||||
|
|
||||||
defs.insert(module_name, loaded_defs);
|
// defs.insert(module_name, loaded_defs);
|
||||||
}
|
// }
|
||||||
|
|
||||||
Loaded {
|
Loaded {
|
||||||
requested_header,
|
requested_header,
|
||||||
dependent_headers: env.loaded_headers,
|
dependent_headers,
|
||||||
defs,
|
defs,
|
||||||
problems: env.problems,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +122,9 @@ pub fn load<'a>(arena: &'a Bump, src_dir: &Path, filename: &Path) -> Loaded<'a>
|
||||||
/// module's canonicalization.
|
/// module's canonicalization.
|
||||||
///
|
///
|
||||||
/// If a given import has not been loaded yet, load it too.
|
/// If a given import has not been loaded yet, load it too.
|
||||||
fn load_module<'a, 'p>(env: &mut Env<'a, 'p>, module_name: &ModuleName<'a>) -> LoadedHeader<'a> {
|
async fn load_module<'a>(env: &'a Env<'a>,
|
||||||
|
loaded_headers: ImMap<ModuleName<'a>, LoadedHeader<'a>>,
|
||||||
|
module_name: &ModuleName<'a>) -> (LoadedHeader<'a>, ImMap<ModuleName<'a>, LoadedHeader<'a>>) {
|
||||||
// 1. Convert module_name to filename, using src_dir.
|
// 1. Convert module_name to filename, using src_dir.
|
||||||
// 2. Open that file for reading. (If there's a problem, record it and bail.)
|
// 2. Open that file for reading. (If there's a problem, record it and bail.)
|
||||||
// 3. Read the whole file into a string. (In the future, we can read just the header.)
|
// 3. Read the whole file into a string. (In the future, we can read just the header.)
|
||||||
|
@ -138,11 +145,14 @@ fn load_module<'a, 'p>(env: &mut Env<'a, 'p>, module_name: &ModuleName<'a>) -> L
|
||||||
// End with .roc
|
// End with .roc
|
||||||
filename.set_extension("roc");
|
filename.set_extension("roc");
|
||||||
|
|
||||||
load_filename(env, &filename)
|
load_filename(env, loaded_headers,&filename).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_filename<'a, 'p>(env: &mut Env<'a, 'p>, filename: &Path) -> LoadedHeader<'a> {
|
async fn load_filename<'a, 'p>(env: &'a Env<'a>,
|
||||||
match read_to_string(filename) {
|
|
||||||
|
loaded_headers: ImMap<ModuleName<'a>, LoadedHeader<'a>>,
|
||||||
|
filename: &Path) -> (LoadedHeader<'a>, ImMap<ModuleName<'a>, LoadedHeader<'a>>) {
|
||||||
|
let imports = match read_to_string(filename).await {
|
||||||
Ok(src) => {
|
Ok(src) => {
|
||||||
// TODO instead of env.arena.alloc(src), we should create a new buffer
|
// TODO instead of env.arena.alloc(src), we should create a new buffer
|
||||||
// in the arena as a Vec<'a, u8> and call .as_mut_slice() on it to
|
// in the arena as a Vec<'a, u8> and call .as_mut_slice() on it to
|
||||||
|
@ -154,64 +164,80 @@ fn load_filename<'a, 'p>(env: &mut Env<'a, 'p>, filename: &Path) -> LoadedHeader
|
||||||
|
|
||||||
match module::module().parse(env.arena, state) {
|
match module::module().parse(env.arena, state) {
|
||||||
Ok((Module::Interface { header }, state)) => {
|
Ok((Module::Interface { header }, state)) => {
|
||||||
let mut scope = ImMap::default();
|
|
||||||
|
|
||||||
// Enqueue the defs parsing job for background processing.
|
// Enqueue the defs parsing job for background processing.
|
||||||
env.queue.insert(header.name.value, state);
|
// env.queue.insert(header.name.value, state);
|
||||||
|
|
||||||
for loc_entry in header.imports {
|
header.imports
|
||||||
load_import(env, loc_entry.region, &loc_entry.value, &mut scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadedHeader::Valid { scope }
|
|
||||||
}
|
}
|
||||||
Ok((Module::App { header }, state)) => {
|
Ok((Module::App { header }, state)) => {
|
||||||
let mut scope = ImMap::default();
|
|
||||||
|
|
||||||
// Enqueue the defs parsing job for background processing.
|
// Enqueue the defs parsing job for background processing.
|
||||||
// The app module has a module name of ""
|
// The app module has a module name of ""
|
||||||
env.queue.insert(ModuleName::new(""), state);
|
// env.queue.insert(ModuleName::new(""), state);
|
||||||
|
|
||||||
for loc_entry in header.imports {
|
header.imports
|
||||||
load_import(env, loc_entry.region, &loc_entry.value, &mut scope);
|
}
|
||||||
}
|
Err((fail, _)) => {
|
||||||
|
return LoadedHeader::ParsingFailed(fail);
|
||||||
LoadedHeader::Valid { scope }
|
|
||||||
}
|
}
|
||||||
Err((fail, _)) => LoadedHeader::ParsingFailed(fail),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => LoadedHeader::FileProblem(err.kind()),
|
Err(err) => return LoadedHeader::FileProblem(err.kind()),
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
fn load_import<'a, 'p>(
|
let mut scope = ImMap::default();
|
||||||
env: &mut Env<'a, 'p>,
|
let mut headers = ImMap::default();
|
||||||
|
|
||||||
|
for loc_entry in imports {
|
||||||
|
let (new_scope, opt_header) =
|
||||||
|
load_import(env, loc_entry.region, loaded_headers, env.arena.alloc(loc_entry.value)).await;
|
||||||
|
|
||||||
|
scope = scope.union(new_scope);
|
||||||
|
|
||||||
|
if let Some((module_name, loaded_header)) = opt_header {
|
||||||
|
headers.insert(module_name, loaded_header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(LoadedHeader::Valid { scope }, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scope<'a>= ImMap<UnqualifiedIdent<'a>, (Symbol, Region)>;
|
||||||
|
|
||||||
|
fn load_import<'a>(
|
||||||
|
env: &'a Env<'a>,
|
||||||
region: Region,
|
region: Region,
|
||||||
entry: &ImportsEntry<'a>,
|
loaded_headers: ImMap<ModuleName<'a>, LoadedHeader<'a>>,
|
||||||
scope: &mut ImMap<UnqualifiedIdent<'a>, (Symbol, Region)>,
|
entry: &'a ImportsEntry<'a>,
|
||||||
) {
|
) -> Pin<Box<dyn Future<Output = (Scope<'a>, Option<(ModuleName<'a>, LoadedHeader<'a>)>)> + 'a>> {
|
||||||
use crate::parse::ast::ImportsEntry::*;
|
Box::pin(async move {
|
||||||
|
use crate::parse::ast::ImportsEntry::*;
|
||||||
|
|
||||||
match entry {
|
match entry {
|
||||||
Module(module_name, exposes) => {
|
Module(module_name, exposes) => {
|
||||||
// If we haven't already loaded the module, load it!
|
// If we haven't already loaded the module, load it!
|
||||||
if !env.loaded_headers.contains_key(&module_name) {
|
let new_header = if !loaded_headers.contains_key(&module_name) {
|
||||||
let loaded = load_module(env, module_name);
|
let loaded = load_module(env, loaded_headers, module_name).await;
|
||||||
|
|
||||||
env.loaded_headers.insert(*module_name, loaded);
|
Some((*module_name, loaded))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut scope = ImMap::default();
|
||||||
|
|
||||||
|
for loc_entry in exposes {
|
||||||
|
expose(*module_name, &loc_entry.value, loc_entry.region, &mut scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
(scope, new_header)
|
||||||
}
|
}
|
||||||
|
|
||||||
for loc_entry in exposes {
|
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => {
|
||||||
expose(*module_name, &loc_entry.value, loc_entry.region, scope)
|
// Ignore spaces.
|
||||||
|
load_import(env, region, *sub_entry).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => {
|
|
||||||
// Ignore spaces.
|
|
||||||
load_import(env, region, *sub_entry, scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expose<'a>(
|
fn expose<'a>(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue