STL rework

- fixed lots of bugs
- overlay libraries work correctly and reliably
- the STL is an overlay library
- examples updated
This commit is contained in:
2023-06-17 21:12:23 +01:00
parent 5bb8a12fc2
commit aebbf51228
91 changed files with 1444 additions and 1395 deletions

View File

@@ -1,8 +1,11 @@
//! File system implementation of the source loader callback
//! Source loader callback definition and builtin implementations
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::{fs, io};
use chumsky::text::Character;
use rust_embed::RustEmbed;
use crate::interner::{Interner, Sym};
use crate::pipeline::error::{
ErrorPosition, ProjectError, UnexpectedDirectory,
@@ -86,13 +89,52 @@ pub fn load_file(root: &Path, path: &[impl AsRef<str>]) -> IOResult {
}
/// Generates a cached file loader for a directory
pub fn mk_cache(root: PathBuf, i: &Interner) -> Cache<Sym, IOResult> {
pub fn mk_dir_cache(root: PathBuf, i: &Interner) -> Cache<Sym, IOResult> {
Cache::new(move |token: Sym, _this| -> IOResult {
let path = i.r(token).iter().map(|t| i.r(*t).as_str()).collect::<Vec<_>>();
load_file(&root, &path)
})
}
/// Load a file from the specified path from an embed table
pub fn load_embed<T: 'static + RustEmbed>(path: &str, ext: &str) -> IOResult {
let file_path = path.to_string() + ext;
if let Some(file) = T::get(&file_path) {
let s = file.data.iter().map(|c| c.to_char()).collect::<String>();
Ok(Loaded::Code(Rc::new(s)))
} else {
let entries = T::iter()
.map(|c| c.to_string())
.filter_map(|path: String| {
let item_prefix = path.to_string() + "/";
path.strip_prefix(&item_prefix).map(|subpath| {
let item_name = subpath
.split_inclusive('/')
.next()
.expect("Exact match excluded earlier");
item_name
.strip_suffix('/') // subdirectory
.or_else(|| item_name.strip_suffix(ext)) // file
.expect("embed should be filtered to extension")
.to_string()
})
})
.collect::<Vec<String>>();
Ok(Loaded::Collection(Rc::new(entries)))
}
}
/// Generates a cached file loader for a [RustEmbed]
pub fn mk_embed_cache<'a, T: 'static + RustEmbed>(
ext: &'a str,
i: &'a Interner,
) -> Cache<'a, Sym, IOResult> {
Cache::new(move |token: Sym, _this| -> IOResult {
let path = i.extern_vec(token).join("/");
load_embed::<T>(&path, ext)
})
}
/// Loads the string contents of a file at the given location.
/// If the path points to a directory, raises an error.
pub fn load_text(

View File

@@ -3,14 +3,14 @@ use std::rc::Rc;
use hashbrown::HashMap;
use super::alias_map::AliasMap;
use super::decls::InjectedAsFn;
use super::decls::{InjectedAsFn, UpdatedFn};
use crate::ast::{Expr, Rule};
use crate::interner::{Interner, Sym, Tok};
use crate::pipeline::{ProjectExt, ProjectModule};
use crate::representations::tree::{ModEntry, ModMember};
use crate::utils::Substack;
fn resolve(
fn resolve_rec(
token: Sym,
alias_map: &AliasMap,
i: &Interner,
@@ -18,7 +18,7 @@ fn resolve(
if let Some(alias) = alias_map.resolve(token) {
Some(i.r(alias).clone())
} else if let Some((foot, body)) = i.r(token).split_last() {
let mut new_beginning = resolve(i.i(body), alias_map, i)?;
let mut new_beginning = resolve_rec(i.i(body), alias_map, i)?;
new_beginning.push(*foot);
Some(new_beginning)
} else {
@@ -26,6 +26,18 @@ fn resolve(
}
}
fn resolve(
token: Sym,
alias_map: &AliasMap,
injected_as: &impl InjectedAsFn,
i: &Interner,
) -> Option<Sym> {
injected_as(&i.r(token)[..]).or_else(|| {
let next_v = resolve_rec(token, alias_map, i)?;
Some(injected_as(&next_v).unwrap_or_else(|| i.i(&next_v)))
})
}
fn process_expr(
expr: &Expr,
alias_map: &AliasMap,
@@ -33,16 +45,7 @@ fn process_expr(
i: &Interner,
) -> Expr {
expr
.map_names(&|n| {
injected_as(&i.r(n)[..]).or_else(|| {
let next_v = resolve(n, alias_map, i)?;
// println!("Resolved alias {} to {}",
// i.extern_vec(n).join("::"),
// i.extern_all(&next_v).join("::")
// );
Some(injected_as(&next_v).unwrap_or_else(|| i.i(&next_v)))
})
})
.map_names(&|n| resolve(n, alias_map, injected_as, i))
.unwrap_or_else(|| expr.clone())
}
@@ -54,10 +57,9 @@ fn apply_aliases_rec(
alias_map: &AliasMap,
i: &Interner,
injected_as: &impl InjectedAsFn,
updated: &impl UpdatedFn,
) -> ProjectModule {
let items = module
.items
.iter()
let items = (module.items.iter())
.map(|(name, ent)| {
let ModEntry { exported, member } = ent;
let member = match member {
@@ -65,9 +67,7 @@ fn apply_aliases_rec(
ModMember::Item(process_expr(expr, alias_map, injected_as, i)),
ModMember::Sub(module) => {
let subpath = path.push(*name);
let is_ignored =
injected_as(&subpath.iter().rev_vec_clone()).is_some();
let new_mod = if is_ignored {
let new_mod = if !updated(&subpath.iter().rev_vec_clone()) {
module.clone()
} else {
let module = module.as_ref();
@@ -77,6 +77,7 @@ fn apply_aliases_rec(
alias_map,
i,
injected_as,
updated,
))
};
ModMember::Sub(new_mod)
@@ -85,23 +86,18 @@ fn apply_aliases_rec(
(*name, ModEntry { exported: *exported, member })
})
.collect::<HashMap<_, _>>();
let rules = module
.extra
.rules
.iter()
let rules = (module.extra.rules.iter())
.map(|rule| {
let Rule { pattern, prio, template } = rule;
Rule {
prio: *prio,
pattern: Rc::new(
pattern
.iter()
(pattern.iter())
.map(|expr| process_expr(expr, alias_map, injected_as, i))
.collect::<Vec<_>>(),
),
template: Rc::new(
template
.iter()
(template.iter())
.map(|expr| process_expr(expr, alias_map, injected_as, i))
.collect::<Vec<_>>(),
),
@@ -113,7 +109,11 @@ fn apply_aliases_rec(
imports: module.imports.clone(),
extra: ProjectExt {
rules,
exports: module.extra.exports.clone(),
exports: (module.extra.exports.iter())
.map(|(k, v)| {
(*k, resolve(*v, alias_map, injected_as, i).unwrap_or(*v))
})
.collect(),
file: module.extra.file.clone(),
imports_from: module.extra.imports_from.clone(),
},
@@ -125,6 +125,14 @@ pub fn apply_aliases(
alias_map: &AliasMap,
i: &Interner,
injected_as: &impl InjectedAsFn,
updated: &impl UpdatedFn,
) -> ProjectModule {
apply_aliases_rec(Substack::Bottom, module, alias_map, i, injected_as)
apply_aliases_rec(
Substack::Bottom,
module,
alias_map,
i,
injected_as,
updated,
)
}

View File

@@ -1,12 +1,13 @@
use core::panic;
use std::rc::Rc;
use super::alias_map::AliasMap;
use super::decls::InjectedAsFn;
use super::decls::UpdatedFn;
use crate::interner::{Interner, Tok};
use crate::pipeline::error::{NotExported, ProjectError};
use crate::pipeline::project_tree::{split_path, ProjectModule, ProjectTree};
use crate::representations::tree::{ModMember, WalkErrorKind};
use crate::utils::{pushed, Substack};
use crate::utils::{pushed, unwrap_or, Substack};
/// Assert that a module identified by a path can see a given symbol
fn assert_visible(
@@ -15,36 +16,40 @@ fn assert_visible(
project: &ProjectTree,
i: &Interner,
) -> Result<(), Rc<dyn ProjectError>> {
let (tgt_item, tgt_path) = if let Some(s) = target.split_last() {
s
} else {
return Ok(());
};
let (tgt_item, tgt_path) = unwrap_or!(target.split_last(); return Ok(()));
let shared_len =
source.iter().zip(tgt_path.iter()).take_while(|(a, b)| a == b).count();
let shared_root =
project.0.walk(&tgt_path[..shared_len], false).expect("checked in parsing");
let direct_parent =
shared_root.walk(&tgt_path[shared_len..], true).map_err(|e| {
match e.kind {
WalkErrorKind::Missing => panic!("checked in parsing"),
WalkErrorKind::Private => {
let full_path = &tgt_path[..shared_len + e.pos];
let (file, sub) = split_path(full_path, project);
let (ref_file, ref_sub) = split_path(source, project);
NotExported {
file: i.extern_all(file),
subpath: i.extern_all(sub),
referrer_file: i.extern_all(ref_file),
referrer_subpath: i.extern_all(ref_sub),
}
.rc()
},
}
let vis_ignored_len = usize::min(tgt_path.len(), shared_len + 1);
let private_root =
(project.0).walk(&tgt_path[..vis_ignored_len], false).unwrap_or_else(|e| {
let path_slc = &tgt_path[..vis_ignored_len];
let bad_path = i.extern_all(path_slc).join("::");
eprintln!(
"Error while walking {bad_path}; {:?} on step {}",
e.kind, e.pos
);
eprintln!("looking from {}", i.extern_all(source).join("::"));
panic!("")
});
let direct_parent = private_root
.walk(&tgt_path[vis_ignored_len..], true)
.map_err(|e| match e.kind {
WalkErrorKind::Missing => panic!("checked in parsing"),
WalkErrorKind::Private => {
let full_path = &tgt_path[..shared_len + e.pos];
let (file, sub) = split_path(full_path, project);
let (ref_file, ref_sub) = split_path(source, project);
NotExported {
file: i.extern_all(file),
subpath: i.extern_all(sub),
referrer_file: i.extern_all(ref_file),
referrer_subpath: i.extern_all(ref_sub),
}
.rc()
},
})?;
let tgt_item_exported = direct_parent.extra.exports.contains_key(tgt_item);
let target_prefixes_source =
shared_len == tgt_path.len() && source.get(shared_len) == Some(tgt_item);
let target_prefixes_source = shared_len == tgt_path.len();
if !tgt_item_exported && !target_prefixes_source {
let (file, sub) = split_path(target, project);
let (ref_file, ref_sub) = split_path(source, project);
@@ -69,20 +74,30 @@ fn collect_aliases_rec(
project: &ProjectTree,
alias_map: &mut AliasMap,
i: &Interner,
injected_as: &impl InjectedAsFn,
updated: &impl UpdatedFn,
) -> Result<(), Rc<dyn ProjectError>> {
// Assume injected module has been alias-resolved
let mod_path_v = path.iter().rev_vec_clone();
if injected_as(&mod_path_v).is_some() {
if !updated(&mod_path_v) {
return Ok(());
};
for (&name, &target_mod) in module.extra.imports_from.iter() {
let target_mod_v = i.r(target_mod);
for (&name, &target_mod_name) in module.extra.imports_from.iter() {
let target_mod_v = i.r(target_mod_name);
let target_sym_v = pushed(target_mod_v, name);
assert_visible(&mod_path_v, &target_sym_v, project, i)?;
let sym_path_v = pushed(&mod_path_v, name);
let sym_path = i.i(&sym_path_v);
let target_sym = i.i(&target_sym_v);
let target_mod = (project.0.walk(target_mod_v, false))
.expect("checked above in assert_visible");
let target_sym =
*target_mod.extra.exports.get(&name).unwrap_or_else(|| {
panic!(
"error in {}, {} has no member {}",
i.extern_all(&mod_path_v).join("::"),
i.extern_all(target_mod_v).join("::"),
i.r(name)
)
});
alias_map.link(sym_path, target_sym);
}
for (&name, entry) in module.items.iter() {
@@ -97,7 +112,7 @@ fn collect_aliases_rec(
project,
alias_map,
i,
injected_as,
updated,
)?
}
Ok(())
@@ -109,14 +124,7 @@ pub fn collect_aliases(
project: &ProjectTree,
alias_map: &mut AliasMap,
i: &Interner,
injected_as: &impl InjectedAsFn,
updated: &impl UpdatedFn,
) -> Result<(), Rc<dyn ProjectError>> {
collect_aliases_rec(
Substack::Bottom,
module,
project,
alias_map,
i,
injected_as,
)
collect_aliases_rec(Substack::Bottom, module, project, alias_map, i, updated)
}

View File

@@ -4,4 +4,5 @@ use crate::interner::{Sym, Tok};
trait_set! {
pub trait InjectedAsFn = Fn(&[Tok<String>]) -> Option<Sym>;
pub trait UpdatedFn = Fn(&[Tok<String>]) -> bool;
}

View File

@@ -1,11 +1,9 @@
use std::rc::Rc;
use itertools::Itertools;
use super::alias_map::AliasMap;
use super::apply_aliases::apply_aliases;
use super::collect_aliases::collect_aliases;
use super::decls::InjectedAsFn;
use super::decls::{InjectedAsFn, UpdatedFn};
use crate::interner::Interner;
use crate::pipeline::error::ProjectError;
use crate::pipeline::project_tree::ProjectTree;
@@ -16,21 +14,11 @@ pub fn resolve_imports(
project: ProjectTree,
i: &Interner,
injected_as: &impl InjectedAsFn,
updated: &impl UpdatedFn,
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
let mut map = AliasMap::new();
collect_aliases(project.0.as_ref(), &project, &mut map, i, injected_as)?;
println!(
"Aliases: {{{:?}}}",
map
.targets
.iter()
.map(|(kt, vt)| format!(
"{} => {}",
i.extern_vec(*kt).join("::"),
i.extern_vec(*vt).join("::")
))
.join(", ")
);
let new_mod = apply_aliases(project.0.as_ref(), &map, i, injected_as);
collect_aliases(project.0.as_ref(), &project, &mut map, i, updated)?;
let new_mod =
apply_aliases(project.0.as_ref(), &map, i, injected_as, updated);
Ok(ProjectTree(Rc::new(new_mod)))
}

View File

@@ -6,7 +6,6 @@ mod import_resolution;
mod parse_layer;
mod project_tree;
mod source_loader;
mod split_name;
pub use parse_layer::parse_layer;
pub use project_tree::{

View File

@@ -34,13 +34,16 @@ pub fn parse_layer(
};
let source =
source_loader::load_source(targets, prelude, i, loader, &|path| {
injected_as(path).is_some()
environment.0.walk(&i.r(path)[..], false).is_ok()
})?;
let tree = project_tree::build_tree(source, i, prelude, &injected_names)?;
let sum = ProjectTree(Rc::new(
environment.0.as_ref().clone() + tree.0.as_ref().clone(),
environment.0.as_ref().clone().overlay(tree.0.as_ref().clone()),
));
let resolvd = import_resolution::resolve_imports(sum, i, &injected_as)?;
let resolvd =
import_resolution::resolve_imports(sum, i, &injected_as, &|path| {
tree.0.walk(path, false).is_ok()
})?;
// Addition among modules favours the left hand side.
Ok(resolvd)
}

View File

@@ -57,11 +57,7 @@ fn source_to_module(
let imports = data
.iter()
.filter_map(|ent| {
if let FileEntry::Import(impv) = ent {
Some(impv.iter())
} else {
None
}
if let FileEntry::Import(impv) = ent { Some(impv.iter()) } else { None }
})
.flatten()
.cloned()
@@ -182,6 +178,10 @@ fn files_to_module(
i: &Interner,
) -> Rc<Module<Expr, ProjectExt>> {
let lvl = path.len();
debug_assert!(
files.iter().map(|f| f.path.len()).max().unwrap() >= lvl,
"path is longer than any of the considered file paths"
);
let path_v = path.iter().rev_vec_clone();
if files.len() == 1 && files[0].path.len() == lvl {
return source_to_module(
@@ -227,6 +227,7 @@ pub fn build_tree(
prelude: &[FileEntry],
injected: &impl InjectedOperatorsFn,
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
assert!(!files.is_empty(), "A tree requires at least one module");
let ops_cache = collect_ops::mk_cache(&files, i, injected);
let mut entries = files
.iter()

View File

@@ -1,16 +1,13 @@
use std::println;
use std::rc::Rc;
use hashbrown::HashSet;
use itertools::Itertools;
use trait_set::trait_set;
use crate::interner::{Interner, Sym, Tok};
use crate::pipeline::error::{ModuleNotFound, ProjectError};
use crate::pipeline::source_loader::LoadedSourceTable;
use crate::pipeline::split_name::split_name;
use crate::representations::tree::WalkErrorKind;
use crate::utils::Cache;
use crate::utils::{split_max_prefix, unwrap_or, Cache};
pub type OpsResult = Result<Rc<HashSet<Tok<String>>>, Rc<dyn ProjectError>>;
pub type ExportedOpsCache<'a> = Cache<'a, Sym, OpsResult>;
@@ -33,34 +30,24 @@ pub fn collect_exported_ops(
i: &Interner,
injected: &impl InjectedOperatorsFn,
) -> OpsResult {
if let Some(ops) = injected(path) {
if path == i.i(&[i.i("prelude")][..]) {
println!("%%% Prelude exported ops %%%");
println!("{}", ops.iter().map(|t| i.r(*t)).join(", "));
}
return Ok(ops);
}
let injected = injected(path).unwrap_or_else(|| Rc::new(HashSet::new()));
let is_file = |n: &[Tok<String>]| loaded.contains_key(&i.i(n));
let path_s = &i.r(path)[..];
let name_split = split_name(path_s, &is_file);
let (fpath_v, subpath_v) = if let Some(f) = name_split {
f
} else {
return Ok(Rc::new(
loaded
.keys()
.copied()
.filter_map(|modname| {
let modname_s = i.r(modname);
if path_s.len() == coprefix(path_s.iter(), modname_s.iter()) {
Some(modname_s[path_s.len()])
} else {
None
}
})
.collect::<HashSet<_>>(),
));
};
let name_split = split_max_prefix(path_s, &is_file);
let (fpath_v, subpath_v) = unwrap_or!(name_split; return Ok(Rc::new(
(loaded.keys())
.copied()
.filter_map(|modname| {
let modname_s = i.r(modname);
if path_s.len() == coprefix(path_s.iter(), modname_s.iter()) {
Some(modname_s[path_s.len()])
} else {
None
}
})
.chain(injected.iter().copied())
.collect::<HashSet<_>>(),
)));
let fpath = i.i(fpath_v);
let preparsed = &loaded[&fpath].preparsed;
let module = preparsed.0.walk(subpath_v, false).map_err(|walk_err| {
@@ -70,8 +57,7 @@ pub fn collect_exported_ops(
},
WalkErrorKind::Missing => ModuleNotFound {
file: i.extern_vec(fpath),
subpath: subpath_v
.iter()
subpath: (subpath_v.iter())
.take(walk_err.pos)
.map(|t| i.r(*t))
.cloned()
@@ -80,12 +66,11 @@ pub fn collect_exported_ops(
.rc(),
}
})?;
let out: HashSet<_> =
module.items.iter().filter(|(_, v)| v.exported).map(|(k, _)| *k).collect();
if path == i.i(&[i.i("prelude")][..]) {
println!("%%% Prelude exported ops %%%");
println!("{}", out.iter().map(|t| i.r(*t)).join(", "));
}
let out = (module.items.iter())
.filter(|(_, v)| v.exported)
.map(|(k, _)| *k)
.chain(injected.iter().copied())
.collect::<HashSet<_>>();
Ok(Rc::new(out))
}

View File

@@ -1,7 +1,6 @@
use std::rc::Rc;
use hashbrown::HashSet;
use itertools::Itertools;
use super::exported_ops::{ExportedOpsCache, OpsResult};
use crate::interner::{Interner, Tok};
@@ -34,13 +33,11 @@ pub fn collect_ops_for(
) -> OpsResult {
let tree = &loaded[&i.i(file)].preparsed.0;
let mut ret = HashSet::new();
println!("collecting ops for {}", i.extern_all(file).join("::"));
tree_all_ops(tree.as_ref(), &mut ret);
tree.visit_all_imports(&mut |modpath, _module, import| {
if let Some(n) = import.name {
ret.insert(n);
} else {
println!("\tglob import from {}", i.extern_vec(import.path).join("::"));
let path = import_abs_path(file, modpath, &i.r(import.path)[..], i)
.expect("This error should have been caught during loading");
ret.extend(ops_cache.find(&i.i(&path))?.iter().copied());
@@ -48,9 +45,5 @@ pub fn collect_ops_for(
Ok::<_, Rc<dyn ProjectError>>(())
})?;
ret.drain_filter(|t| !is_op(i.r(*t)));
if file == &[i.i("map")][..] {
println!(" %%% ops in map %%% ");
println!("{}", ret.iter().map(|t| i.r(*t)).join(", "))
}
Ok(Rc::new(ret))
}

View File

@@ -41,6 +41,7 @@ impl Add for ProjectExt {
pub type ProjectModule = Module<Expr, ProjectExt>;
/// Module corresponding to the root of a project
#[derive(Debug, Clone)]
pub struct ProjectTree(pub Rc<ProjectModule>);
fn collect_rules_rec(bag: &mut Vec<Rule>, module: &ProjectModule) {

View File

@@ -3,12 +3,12 @@ use std::rc::Rc;
use super::loaded_source::{LoadedSource, LoadedSourceTable};
use super::preparse::preparse;
use crate::interner::{Interner, Sym, Tok};
use crate::interner::{Interner, Sym};
use crate::pipeline::error::ProjectError;
use crate::pipeline::file_loader::{load_text, IOResult, Loaded};
use crate::pipeline::import_abs_path::import_abs_path;
use crate::pipeline::split_name::split_name;
use crate::representations::sourcefile::FileEntry;
use crate::utils::split_max_prefix;
/// Load the source at the given path or all within if it's a collection,
/// and all sources imported from these.
@@ -18,31 +18,67 @@ fn load_abs_path_rec(
prelude: &[FileEntry],
i: &Interner,
get_source: &impl Fn(Sym) -> IOResult,
is_injected: &impl Fn(&[Tok<String>]) -> bool,
is_injected_module: &impl Fn(Sym) -> bool,
) -> Result<(), Rc<dyn ProjectError>> {
let abs_pathv = i.r(abs_path);
// short-circuit if this import is defined externally or already known
if is_injected(abs_pathv) | table.contains_key(&abs_path) {
// # Termination
//
// Every recursion of this function either
// - adds one of the files in the source directory to `table` or
// - recursively traverses a directory tree
// therefore eventually the function exits, assuming that the directory tree
// contains no cycles.
// Termination: exit if entry already visited
if table.contains_key(&abs_path) {
return Ok(());
}
// try splitting the path to file, swallowing any IO errors
let is_file = |p| (get_source)(p).map(|l| l.is_code()).unwrap_or(false);
let name_split = split_name(abs_pathv, &|p| is_file(i.i(p)));
let filename = if let Some((f, _)) = name_split {
f
let is_file = |sym| get_source(sym).map(|l| l.is_code()).unwrap_or(false);
let name_split = split_max_prefix(&i.r(abs_path)[..], &|p| is_file(i.i(p)));
if let Some((filename, _)) = name_split {
// if the filename is valid, load, preparse and record this file
let text = load_text(i.i(filename), &get_source, i)?;
let preparsed = preparse(
filename.iter().map(|t| i.r(*t)).cloned().collect(),
text.as_str(),
prelude,
i,
)?;
table.insert(i.i(filename), LoadedSource {
text,
preparsed: preparsed.clone(),
});
// recurse on all imported modules
preparsed.0.visit_all_imports(&mut |modpath, _module, import| {
let abs_pathv =
import_abs_path(filename, modpath, &import.nonglob_path(i), i)?;
// recurse on imported module
load_abs_path_rec(
i.i(&abs_pathv),
table,
prelude,
i,
get_source,
is_injected_module,
)
})
} else {
// If the path could not be split to file, load it as directory
let coll = if let Loaded::Collection(c) = (get_source)(abs_path)? {
c
}
// ^^ raise any IO error that was previously swallowed
else {
panic!("split_name returned None but the path is a file")
// If the path is not within a file, load it as directory
let coll = match get_source(abs_path) {
Ok(Loaded::Collection(coll)) => coll,
Ok(Loaded::Code(_)) =>
unreachable!("split_name returned None but the path is a file"),
Err(e) => {
let parent = i.r(abs_path).split_last().expect("import path nonzero").1;
// exit without error if it was injected, or raise any IO error that was
// previously swallowed
return if is_injected_module(i.i(parent)) { Ok(()) } else { Err(e) };
},
};
// recurse on all files and folders within
for item in coll.iter() {
let abs_subpath = abs_pathv
.iter()
let abs_subpath = (i.r(abs_path).iter())
.copied()
.chain(iter::once(i.i(item)))
.collect::<Vec<_>>();
@@ -52,48 +88,36 @@ fn load_abs_path_rec(
prelude,
i,
get_source,
is_injected,
is_injected_module,
)?
}
return Ok(());
};
// otherwise load, preparse and record this file
let text = load_text(i.i(filename), &get_source, i)?;
let preparsed = preparse(
filename.iter().map(|t| i.r(*t)).cloned().collect(),
text.as_str(),
prelude,
i,
)?;
table.insert(abs_path, LoadedSource { text, preparsed: preparsed.clone() });
// recurse on all imported modules
preparsed.0.visit_all_imports(&mut |modpath, _module, import| {
let abs_pathv =
import_abs_path(filename, modpath, &import.nonglob_path(i), i)?;
// recurse on imported module
load_abs_path_rec(
i.i(&abs_pathv),
table,
prelude,
i,
get_source,
is_injected,
)
})
Ok(())
}
}
/// Load and preparse all files reachable from the load targets via
/// imports that aren't injected.
///
/// is_injected_module must return false for injected symbols, but may return
/// true for parents of injected modules that are not directly part of the
/// injected data (the ProjectTree doesn't make a distinction between the two)
pub fn load_source(
targets: &[Sym],
prelude: &[FileEntry],
i: &Interner,
get_source: &impl Fn(Sym) -> IOResult,
is_injected: &impl Fn(&[Tok<String>]) -> bool,
is_injected_module: &impl Fn(Sym) -> bool,
) -> Result<LoadedSourceTable, Rc<dyn ProjectError>> {
let mut table = LoadedSourceTable::new();
for target in targets {
load_abs_path_rec(*target, &mut table, prelude, i, get_source, is_injected)?
load_abs_path_rec(
*target,
&mut table,
prelude,
i,
get_source,
is_injected_module,
)?
}
Ok(table)
}

View File

@@ -1,16 +0,0 @@
use crate::interner::Tok;
#[allow(clippy::type_complexity)]
// FIXME couldn't find a good factoring
pub fn split_name<'a>(
path: &'a [Tok<String>],
is_valid: &impl Fn(&[Tok<String>]) -> bool,
) -> Option<(&'a [Tok<String>], &'a [Tok<String>])> {
for split in (0..=path.len()).rev() {
let (filename, subpath) = path.split_at(split);
if is_valid(filename) {
return Some((filename, subpath));
}
}
None
}