diff --git a/src/load/mod.rs b/src/load/mod.rs index 4c3b8c51b9..6f1df69a2a 100644 --- a/src/load/mod.rs +++ b/src/load/mod.rs @@ -8,21 +8,22 @@ use crate::parse::parser::{Fail, Parser, State}; use crate::region::{Located, Region}; use bumpalo::collections::Vec; use bumpalo::Bump; -use std::future::Future; +use std::fs::read_to_string; use std::io; use std::path::{Path, PathBuf}; -use std::pin::Pin; -use tokio::fs::read_to_string; pub struct Loaded<'a> { pub requested_header: LoadedHeader<'a>, - pub dependent_headers: ImMap, LoadedHeader<'a>>, + pub dependent_headers: MutMap, LoadedHeader<'a>>, pub defs: MutMap, Result>>, Fail>>, + pub problems: Vec<'a, BuildProblem<'a>>, } -struct Env<'a> { +struct Env<'a, 'p> { pub arena: &'a Bump, - pub src_dir: &'a Path, + pub src_dir: &'p Path, + pub problems: Vec<'a, BuildProblem<'a>>, + pub loaded_headers: MutMap, LoadedHeader<'a>>, pub queue: Queue<'a>, } @@ -33,7 +34,7 @@ pub enum BuildProblem<'a> { FileNotFound(&'a Path), } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq)] pub enum LoadedHeader<'a> { Valid { scope: ImMap, (Symbol, Region)>, @@ -42,38 +43,32 @@ pub enum LoadedHeader<'a> { ParsingFailed(Fail), } -pub async fn load<'a>(arena: &'a Bump, src_dir: &'a Path, filename: &Path) -> Loaded<'a> { - let env = Env { +pub fn load<'a>(arena: &'a Bump, src_dir: &Path, filename: &Path) -> Loaded<'a> { + let mut env = Env { arena, src_dir, + problems: Vec::new_in(&arena), + loaded_headers: MutMap::default(), queue: MutMap::default(), }; - - /// 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 requested_header = load_filename(&mut env, filename); let mut defs = MutMap::default(); - // for (module_name, state) in env.queue { - // let loaded_defs = match module::module_defs().parse(arena, state) { - // Ok((defs, _)) => Ok(defs), - // Err((fail, _)) => Err(fail), - // }; + for (module_name, state) in env.queue { + let loaded_defs = match module::module_defs().parse(arena, state) { + Ok((defs, _)) => Ok(defs), + Err((fail, _)) => Err(fail), + }; - // defs.insert(module_name, loaded_defs); - // } + defs.insert(module_name, loaded_defs); + } Loaded { requested_header, - dependent_headers, + dependent_headers: env.loaded_headers, defs, + problems: env.problems, } } @@ -122,9 +117,7 @@ pub async fn load<'a>(arena: &'a Bump, src_dir: &'a Path, filename: &Path) -> Lo /// module's canonicalization. /// /// If a given import has not been loaded yet, load it too. -async fn load_module<'a>(env: &'a Env<'a>, - loaded_headers: ImMap, LoadedHeader<'a>>, - module_name: &ModuleName<'a>) -> (LoadedHeader<'a>, ImMap, LoadedHeader<'a>>) { +fn load_module<'a, 'p>(env: &mut Env<'a, 'p>, module_name: &ModuleName<'a>) -> LoadedHeader<'a> { // 1. Convert module_name to filename, using src_dir. // 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.) @@ -145,14 +138,11 @@ async fn load_module<'a>(env: &'a Env<'a>, // End with .roc filename.set_extension("roc"); - load_filename(env, loaded_headers,&filename).await + load_filename(env, &filename) } -async fn load_filename<'a, 'p>(env: &'a Env<'a>, - - loaded_headers: ImMap, LoadedHeader<'a>>, - filename: &Path) -> (LoadedHeader<'a>, ImMap, LoadedHeader<'a>>) { - let imports = match read_to_string(filename).await { +fn load_filename<'a, 'p>(env: &mut Env<'a, 'p>, filename: &Path) -> LoadedHeader<'a> { + match read_to_string(filename) { Ok(src) => { // 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 @@ -164,80 +154,64 @@ async fn load_filename<'a, 'p>(env: &'a Env<'a>, match module::module().parse(env.arena, state) { Ok((Module::Interface { header }, state)) => { + let mut scope = ImMap::default(); + // Enqueue the defs parsing job for background processing. - // env.queue.insert(header.name.value, state); - - header.imports + env.queue.insert(header.name.value, state); + + for loc_entry in header.imports { + load_import(env, loc_entry.region, &loc_entry.value, &mut scope); + } + + LoadedHeader::Valid { scope } } Ok((Module::App { header }, state)) => { + let mut scope = ImMap::default(); + // Enqueue the defs parsing job for background processing. // The app module has a module name of "" - // env.queue.insert(ModuleName::new(""), state); - - header.imports - } - Err((fail, _)) => { - return LoadedHeader::ParsingFailed(fail); + env.queue.insert(ModuleName::new(""), state); + + for loc_entry in header.imports { + load_import(env, loc_entry.region, &loc_entry.value, &mut scope); + } + + LoadedHeader::Valid { scope } } + Err((fail, _)) => LoadedHeader::ParsingFailed(fail), } } - Err(err) => return LoadedHeader::FileProblem(err.kind()), - }; + Err(err) => LoadedHeader::FileProblem(err.kind()), + } +} - let mut scope = ImMap::default(); - let mut headers = ImMap::default(); +fn load_import<'a, 'p>( + env: &mut Env<'a, 'p>, + region: Region, + entry: &ImportsEntry<'a>, + scope: &mut ImMap, (Symbol, Region)>, +) { + use crate::parse::ast::ImportsEntry::*; - 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; + match entry { + Module(module_name, exposes) => { + // If we haven't already loaded the module, load it! + if !env.loaded_headers.contains_key(&module_name) { + let loaded = load_module(env, module_name); - scope = scope.union(new_scope); + env.loaded_headers.insert(*module_name, loaded); + } - if let Some((module_name, loaded_header)) = opt_header { - headers.insert(module_name, loaded_header); + for loc_entry in exposes { + expose(*module_name, &loc_entry.value, loc_entry.region, scope) + } + } + + SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => { + // Ignore spaces. + load_import(env, region, *sub_entry, scope) } } - - (LoadedHeader::Valid { scope }, headers) -} - -type Scope<'a>= ImMap, (Symbol, Region)>; - -fn load_import<'a>( - env: &'a Env<'a>, - region: Region, - loaded_headers: ImMap, LoadedHeader<'a>>, - entry: &'a ImportsEntry<'a>, -) -> Pin, Option<(ModuleName<'a>, LoadedHeader<'a>)>)> + 'a>> { - Box::pin(async move { - use crate::parse::ast::ImportsEntry::*; - - match entry { - Module(module_name, exposes) => { - // If we haven't already loaded the module, load it! - let new_header = if !loaded_headers.contains_key(&module_name) { - let loaded = load_module(env, loaded_headers, module_name).await; - - 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) - } - - SpaceBefore(sub_entry, _) | SpaceAfter(sub_entry, _) => { - // Ignore spaces. - load_import(env, region, *sub_entry).await - } - } - }) } fn expose<'a>(