Removed a copious amount of premature Rc-s
This commit is contained in:
@@ -6,12 +6,11 @@ use std::{fs, io};
|
||||
use chumsky::text::Character;
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
use crate::interner::{Interner, Sym};
|
||||
use crate::pipeline::error::{
|
||||
ErrorPosition, ProjectError, UnexpectedDirectory,
|
||||
};
|
||||
use crate::interner::Interner;
|
||||
use crate::pipeline::error::{ErrorPosition, ProjectError};
|
||||
use crate::utils::iter::box_once;
|
||||
use crate::utils::{BoxedIter, Cache};
|
||||
use crate::{Stok, VName};
|
||||
|
||||
/// All the data available about a failed source load call
|
||||
#[derive(Debug)]
|
||||
@@ -89,9 +88,9 @@ pub fn load_file(root: &Path, path: &[impl AsRef<str>]) -> IOResult {
|
||||
}
|
||||
|
||||
/// Generates a cached file loader for a directory
|
||||
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<_>>();
|
||||
pub fn mk_dir_cache(root: PathBuf, i: &Interner) -> Cache<VName, IOResult> {
|
||||
Cache::new(move |vname: VName, _this| -> IOResult {
|
||||
let path = vname.iter().map(|t| i.r(*t).as_str()).collect::<Vec<_>>();
|
||||
load_file(&root, &path)
|
||||
})
|
||||
}
|
||||
@@ -128,28 +127,9 @@ pub fn load_embed<T: 'static + RustEmbed>(path: &str, ext: &str) -> IOResult {
|
||||
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("/");
|
||||
) -> Cache<'a, Vec<Stok>, IOResult> {
|
||||
Cache::new(move |vname: VName, _this| -> IOResult {
|
||||
let path = i.extern_all(&vname).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(
|
||||
path: Sym,
|
||||
load_file: &impl Fn(Sym) -> IOResult,
|
||||
i: &Interner,
|
||||
) -> Result<Rc<String>, Rc<dyn ProjectError>> {
|
||||
if let Loaded::Code(s) = load_file(path)? {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(
|
||||
UnexpectedDirectory {
|
||||
path: i.r(path).iter().map(|t| i.r(*t)).cloned().collect(),
|
||||
}
|
||||
.rc(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,22 +2,22 @@ use std::hash::Hash;
|
||||
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
|
||||
use crate::interner::Sym;
|
||||
use crate::interner::Tok;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct AliasMap {
|
||||
pub targets: HashMap<Sym, Sym>,
|
||||
pub aliases: HashMap<Sym, HashSet<Sym>>,
|
||||
pub targets: HashMap<Vec<Tok<String>>, Vec<Tok<String>>>,
|
||||
pub aliases: HashMap<Vec<Tok<String>>, HashSet<Vec<Tok<String>>>>,
|
||||
}
|
||||
impl AliasMap {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn link(&mut self, alias: Sym, target: Sym) {
|
||||
let prev = self.targets.insert(alias, target);
|
||||
pub fn link(&mut self, alias: Vec<Tok<String>>, target: Vec<Tok<String>>) {
|
||||
let prev = self.targets.insert(alias.clone(), target.clone());
|
||||
debug_assert!(prev.is_none(), "Alias already has a target");
|
||||
multimap_entry(&mut self.aliases, &target).insert(alias);
|
||||
multimap_entry(&mut self.aliases, &target).insert(alias.clone());
|
||||
// Remove aliases of the alias
|
||||
if let Some(alts) = self.aliases.remove(&alias) {
|
||||
for alt in alts {
|
||||
@@ -26,17 +26,18 @@ impl AliasMap {
|
||||
self.aliases.get(&alt).map(HashSet::is_empty).unwrap_or(true),
|
||||
"Alias set of alias not empty"
|
||||
);
|
||||
let alt_target = self.targets.insert(alt.clone(), target.clone());
|
||||
debug_assert!(
|
||||
self.targets.insert(alt, target) == Some(alias),
|
||||
alt_target.as_ref() == Some(&alias),
|
||||
"Name not target of its own alias"
|
||||
);
|
||||
multimap_entry(&mut self.aliases, &target).insert(alt);
|
||||
multimap_entry(&mut self.aliases, &alias).insert(alt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(&self, alias: Sym) -> Option<Sym> {
|
||||
self.targets.get(&alias).copied()
|
||||
pub fn resolve(&self, alias: &[Tok<String>]) -> Option<&Vec<Tok<String>>> {
|
||||
self.targets.get(alias)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use super::alias_map::AliasMap;
|
||||
use super::decls::{InjectedAsFn, UpdatedFn};
|
||||
use crate::ast::{Expr, Rule};
|
||||
use crate::interner::{Interner, Sym, Tok};
|
||||
use crate::interner::Tok;
|
||||
use crate::pipeline::{ProjectExt, ProjectModule};
|
||||
use crate::representations::tree::{ModEntry, ModMember};
|
||||
use crate::representations::VName;
|
||||
use crate::utils::Substack;
|
||||
|
||||
fn resolve_rec(
|
||||
token: Sym,
|
||||
namespace: &[Tok<String>],
|
||||
alias_map: &AliasMap,
|
||||
i: &Interner,
|
||||
) -> Option<Vec<Tok<String>>> {
|
||||
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_rec(i.i(body), alias_map, i)?;
|
||||
if let Some(alias) = alias_map.resolve(namespace) {
|
||||
Some(alias.clone())
|
||||
} else if let Some((foot, body)) = namespace.split_last() {
|
||||
let mut new_beginning = resolve_rec(body, alias_map)?;
|
||||
new_beginning.push(*foot);
|
||||
Some(new_beginning)
|
||||
} else {
|
||||
@@ -27,25 +25,23 @@ fn resolve_rec(
|
||||
}
|
||||
|
||||
fn resolve(
|
||||
token: Sym,
|
||||
namespace: &[Tok<String>],
|
||||
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)))
|
||||
) -> Option<Vec<Tok<String>>> {
|
||||
injected_as(namespace).or_else(|| {
|
||||
let next_v = resolve_rec(namespace, alias_map)?;
|
||||
Some(injected_as(&next_v).unwrap_or(next_v))
|
||||
})
|
||||
}
|
||||
|
||||
fn process_expr(
|
||||
expr: &Expr,
|
||||
expr: &Expr<VName>,
|
||||
alias_map: &AliasMap,
|
||||
injected_as: &impl InjectedAsFn,
|
||||
i: &Interner,
|
||||
) -> Expr {
|
||||
) -> Expr<VName> {
|
||||
expr
|
||||
.map_names(&|n| resolve(n, alias_map, injected_as, i))
|
||||
.map_names(&|n| resolve(n, alias_map, injected_as))
|
||||
.unwrap_or_else(|| expr.clone())
|
||||
}
|
||||
|
||||
@@ -53,32 +49,23 @@ fn process_expr(
|
||||
/// Replace all aliases with the name they're originally defined as
|
||||
fn apply_aliases_rec(
|
||||
path: Substack<Tok<String>>,
|
||||
module: &ProjectModule,
|
||||
module: &ProjectModule<VName>,
|
||||
alias_map: &AliasMap,
|
||||
i: &Interner,
|
||||
injected_as: &impl InjectedAsFn,
|
||||
updated: &impl UpdatedFn,
|
||||
) -> ProjectModule {
|
||||
) -> ProjectModule<VName> {
|
||||
let items = (module.items.iter())
|
||||
.map(|(name, ent)| {
|
||||
let ModEntry { exported, member } = ent;
|
||||
let member = match member {
|
||||
ModMember::Item(expr) =>
|
||||
ModMember::Item(process_expr(expr, alias_map, injected_as, i)),
|
||||
ModMember::Item(process_expr(expr, alias_map, injected_as)),
|
||||
ModMember::Sub(module) => {
|
||||
let subpath = path.push(*name);
|
||||
let new_mod = if !updated(&subpath.iter().rev_vec_clone()) {
|
||||
module.clone()
|
||||
} else {
|
||||
let module = module.as_ref();
|
||||
Rc::new(apply_aliases_rec(
|
||||
subpath,
|
||||
module,
|
||||
alias_map,
|
||||
i,
|
||||
injected_as,
|
||||
updated,
|
||||
))
|
||||
apply_aliases_rec(subpath, module, alias_map, injected_as, updated)
|
||||
};
|
||||
ModMember::Sub(new_mod)
|
||||
},
|
||||
@@ -91,16 +78,12 @@ fn apply_aliases_rec(
|
||||
let Rule { pattern, prio, template } = rule;
|
||||
Rule {
|
||||
prio: *prio,
|
||||
pattern: Rc::new(
|
||||
(pattern.iter())
|
||||
.map(|expr| process_expr(expr, alias_map, injected_as, i))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
template: Rc::new(
|
||||
(template.iter())
|
||||
.map(|expr| process_expr(expr, alias_map, injected_as, i))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
pattern: (pattern.iter())
|
||||
.map(|expr| process_expr(expr, alias_map, injected_as))
|
||||
.collect::<Vec<_>>(),
|
||||
template: (template.iter())
|
||||
.map(|expr| process_expr(expr, alias_map, injected_as))
|
||||
.collect::<Vec<_>>(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -111,7 +94,7 @@ fn apply_aliases_rec(
|
||||
rules,
|
||||
exports: (module.extra.exports.iter())
|
||||
.map(|(k, v)| {
|
||||
(*k, resolve(*v, alias_map, injected_as, i).unwrap_or(*v))
|
||||
(*k, resolve(v, alias_map, injected_as).unwrap_or(v.clone()))
|
||||
})
|
||||
.collect(),
|
||||
file: module.extra.file.clone(),
|
||||
@@ -121,18 +104,10 @@ fn apply_aliases_rec(
|
||||
}
|
||||
|
||||
pub fn apply_aliases(
|
||||
module: &ProjectModule,
|
||||
module: &ProjectModule<VName>,
|
||||
alias_map: &AliasMap,
|
||||
i: &Interner,
|
||||
injected_as: &impl InjectedAsFn,
|
||||
updated: &impl UpdatedFn,
|
||||
) -> ProjectModule {
|
||||
apply_aliases_rec(
|
||||
Substack::Bottom,
|
||||
module,
|
||||
alias_map,
|
||||
i,
|
||||
injected_as,
|
||||
updated,
|
||||
)
|
||||
) -> ProjectModule<VName> {
|
||||
apply_aliases_rec(Substack::Bottom, module, alias_map, injected_as, updated)
|
||||
}
|
||||
|
||||
@@ -7,21 +7,23 @@ 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::representations::VName;
|
||||
use crate::utils::{pushed, unwrap_or, Substack};
|
||||
|
||||
/// Assert that a module identified by a path can see a given symbol
|
||||
fn assert_visible(
|
||||
source: &[Tok<String>], // must point to a file or submodule
|
||||
target: &[Tok<String>], // may point to a symbol or module of any kind
|
||||
project: &ProjectTree,
|
||||
project: &ProjectTree<VName>,
|
||||
i: &Interner,
|
||||
) -> Result<(), Rc<dyn ProjectError>> {
|
||||
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 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 private_root = (project.0)
|
||||
.walk_ref(&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!(
|
||||
@@ -32,7 +34,7 @@ fn assert_visible(
|
||||
panic!("")
|
||||
});
|
||||
let direct_parent = private_root
|
||||
.walk(&tgt_path[vis_ignored_len..], true)
|
||||
.walk_ref(&tgt_path[vis_ignored_len..], true)
|
||||
.map_err(|e| match e.kind {
|
||||
WalkErrorKind::Missing => panic!("checked in parsing"),
|
||||
WalkErrorKind::Private => {
|
||||
@@ -70,8 +72,8 @@ fn assert_visible(
|
||||
/// Populate target and alias maps from the module tree recursively
|
||||
fn collect_aliases_rec(
|
||||
path: Substack<Tok<String>>,
|
||||
module: &ProjectModule,
|
||||
project: &ProjectTree,
|
||||
module: &ProjectModule<VName>,
|
||||
project: &ProjectTree<VName>,
|
||||
alias_map: &mut AliasMap,
|
||||
i: &Interner,
|
||||
updated: &impl UpdatedFn,
|
||||
@@ -81,31 +83,29 @@ fn collect_aliases_rec(
|
||||
if !updated(&mod_path_v) {
|
||||
return Ok(());
|
||||
};
|
||||
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);
|
||||
for (&name, target_mod_name) in module.extra.imports_from.iter() {
|
||||
let target_sym_v = pushed(target_mod_name, 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_mod = (project.0.walk(target_mod_v, false))
|
||||
let target_mod = (project.0.walk_ref(target_mod_name, false))
|
||||
.expect("checked above in assert_visible");
|
||||
let target_sym =
|
||||
*target_mod.extra.exports.get(&name).unwrap_or_else(|| {
|
||||
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.extern_all(target_mod_name).join("::"),
|
||||
i.r(name)
|
||||
)
|
||||
});
|
||||
alias_map.link(sym_path, target_sym);
|
||||
})
|
||||
.clone();
|
||||
alias_map.link(sym_path_v, target_sym);
|
||||
}
|
||||
for (&name, entry) in module.items.iter() {
|
||||
let submodule = if let ModMember::Sub(s) = &entry.member {
|
||||
s.as_ref()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let submodule = unwrap_or!(&entry.member => ModMember::Sub; continue);
|
||||
collect_aliases_rec(
|
||||
path.push(name),
|
||||
submodule,
|
||||
@@ -120,8 +120,8 @@ fn collect_aliases_rec(
|
||||
|
||||
/// Populate target and alias maps from the module tree
|
||||
pub fn collect_aliases(
|
||||
module: &ProjectModule,
|
||||
project: &ProjectTree,
|
||||
module: &ProjectModule<VName>,
|
||||
project: &ProjectTree<VName>,
|
||||
alias_map: &mut AliasMap,
|
||||
i: &Interner,
|
||||
updated: &impl UpdatedFn,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::interner::{Sym, Tok};
|
||||
use crate::interner::Tok;
|
||||
|
||||
trait_set! {
|
||||
pub trait InjectedAsFn = Fn(&[Tok<String>]) -> Option<Sym>;
|
||||
pub trait InjectedAsFn = Fn(&[Tok<String>]) -> Option<Vec<Tok<String>>>;
|
||||
pub trait UpdatedFn = Fn(&[Tok<String>]) -> bool;
|
||||
}
|
||||
|
||||
@@ -7,18 +7,18 @@ use super::decls::{InjectedAsFn, UpdatedFn};
|
||||
use crate::interner::Interner;
|
||||
use crate::pipeline::error::ProjectError;
|
||||
use crate::pipeline::project_tree::ProjectTree;
|
||||
use crate::representations::VName;
|
||||
|
||||
/// Follow import chains to locate the original name of all tokens, then
|
||||
/// replace these aliases with the original names throughout the tree
|
||||
pub fn resolve_imports(
|
||||
project: ProjectTree,
|
||||
project: ProjectTree<VName>,
|
||||
i: &Interner,
|
||||
injected_as: &impl InjectedAsFn,
|
||||
updated: &impl UpdatedFn,
|
||||
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
||||
) -> Result<ProjectTree<VName>, Rc<dyn ProjectError>> {
|
||||
let mut map = AliasMap::new();
|
||||
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)))
|
||||
collect_aliases(&project.0, &project, &mut map, i, updated)?;
|
||||
let new_mod = apply_aliases(&project.0, &map, injected_as, updated);
|
||||
Ok(ProjectTree(new_mod))
|
||||
}
|
||||
|
||||
@@ -9,6 +9,6 @@ mod source_loader;
|
||||
|
||||
pub use parse_layer::parse_layer;
|
||||
pub use project_tree::{
|
||||
collect_consts, collect_rules, from_const_tree, ConstTree, ProjectExt,
|
||||
ProjectModule, ProjectTree,
|
||||
collect_consts, collect_rules, from_const_tree, vname_to_sym_tree, ConstTree,
|
||||
ProjectExt, ProjectModule, ProjectTree,
|
||||
};
|
||||
|
||||
@@ -3,8 +3,9 @@ use std::rc::Rc;
|
||||
use super::error::ProjectError;
|
||||
use super::file_loader::IOResult;
|
||||
use super::{import_resolution, project_tree, source_loader, ProjectTree};
|
||||
use crate::interner::{Interner, Sym, Tok};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::representations::sourcefile::FileEntry;
|
||||
use crate::representations::VName;
|
||||
|
||||
/// Using an IO callback, produce a project tree that includes the given
|
||||
/// target symbols or files if they're defined.
|
||||
@@ -14,35 +15,32 @@ use crate::representations::sourcefile::FileEntry;
|
||||
/// prelude which will be prepended to each individual file. Since the
|
||||
/// prelude gets compiled with each file, normally it should be a glob
|
||||
/// import pointing to a module in the environment.
|
||||
pub fn parse_layer(
|
||||
targets: &[Sym],
|
||||
loader: &impl Fn(Sym) -> IOResult,
|
||||
environment: &ProjectTree,
|
||||
pub fn parse_layer<'a>(
|
||||
targets: impl Iterator<Item = &'a [Tok<String>]>,
|
||||
loader: &impl Fn(&[Tok<String>]) -> IOResult,
|
||||
environment: &'a ProjectTree<VName>,
|
||||
prelude: &[FileEntry],
|
||||
i: &Interner,
|
||||
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
||||
) -> Result<ProjectTree<VName>, Rc<dyn ProjectError>> {
|
||||
// A path is injected if it is walkable in the injected tree
|
||||
let injected_as = |path: &[Tok<String>]| {
|
||||
let (item, modpath) = path.split_last()?;
|
||||
let module = environment.0.walk(modpath, false).ok()?;
|
||||
let inj = module.extra.exports.get(item).copied()?;
|
||||
Some(inj)
|
||||
let module = environment.0.walk_ref(modpath, false).ok()?;
|
||||
module.extra.exports.get(item).cloned()
|
||||
};
|
||||
let injected_names = |path: Tok<Vec<Tok<String>>>| {
|
||||
let module = environment.0.walk(&i.r(path)[..], false).ok()?;
|
||||
let module = environment.0.walk_ref(&i.r(path)[..], false).ok()?;
|
||||
Some(Rc::new(module.extra.exports.keys().copied().collect()))
|
||||
};
|
||||
let source =
|
||||
source_loader::load_source(targets, prelude, i, loader, &|path| {
|
||||
environment.0.walk(&i.r(path)[..], false).is_ok()
|
||||
environment.0.walk_ref(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().overlay(tree.0.as_ref().clone()),
|
||||
));
|
||||
let sum = ProjectTree(environment.0.clone().overlay(tree.0.clone()));
|
||||
let resolvd =
|
||||
import_resolution::resolve_imports(sum, i, &injected_as, &|path| {
|
||||
tree.0.walk(path, false).is_ok()
|
||||
tree.0.walk_ref(path, false).is_ok()
|
||||
})?;
|
||||
// Addition among modules favours the left hand side.
|
||||
Ok(resolvd)
|
||||
|
||||
@@ -12,8 +12,9 @@ use crate::pipeline::error::ProjectError;
|
||||
use crate::pipeline::source_loader::{LoadedSource, LoadedSourceTable};
|
||||
use crate::representations::sourcefile::{absolute_path, FileEntry, Member};
|
||||
use crate::representations::tree::{ModEntry, ModMember, Module};
|
||||
use crate::representations::{NameLike, VName};
|
||||
use crate::utils::iter::{box_empty, box_once};
|
||||
use crate::utils::{pushed, Substack};
|
||||
use crate::utils::{pushed, unwrap_or, Substack};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParsedSource<'a> {
|
||||
@@ -24,7 +25,7 @@ struct ParsedSource<'a> {
|
||||
|
||||
pub fn split_path<'a>(
|
||||
path: &'a [Tok<String>],
|
||||
proj: &'a ProjectTree,
|
||||
proj: &'a ProjectTree<impl NameLike>,
|
||||
) -> (&'a [Tok<String>], &'a [Tok<String>]) {
|
||||
let (end, body) = if let Some(s) = path.split_last() {
|
||||
s
|
||||
@@ -32,9 +33,9 @@ pub fn split_path<'a>(
|
||||
return (&[], &[]);
|
||||
};
|
||||
let mut module =
|
||||
proj.0.walk(body, false).expect("invalid path cannot be split");
|
||||
proj.0.walk_ref(body, false).expect("invalid path cannot be split");
|
||||
if let ModMember::Sub(m) = &module.items[end].member {
|
||||
module = m.clone();
|
||||
module = m;
|
||||
}
|
||||
let file =
|
||||
module.extra.file.as_ref().map(|s| &path[..s.len()]).unwrap_or(path);
|
||||
@@ -52,7 +53,7 @@ fn source_to_module(
|
||||
// context
|
||||
i: &Interner,
|
||||
filepath_len: usize,
|
||||
) -> Rc<Module<Expr, ProjectExt>> {
|
||||
) -> Module<Expr<VName>, ProjectExt<VName>> {
|
||||
let path_v = path.iter().rev_vec_clone();
|
||||
let imports = data
|
||||
.iter()
|
||||
@@ -70,13 +71,13 @@ fn source_to_module(
|
||||
let mut abs_path =
|
||||
absolute_path(&path_v, &imp_path_v, i).expect("tested in preparsing");
|
||||
let name = abs_path.pop().expect("importing the global context");
|
||||
(name, i.i(&abs_path))
|
||||
(name, abs_path)
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
let exports = data
|
||||
.iter()
|
||||
.flat_map(|ent| {
|
||||
let mk_ent = |name| (name, i.i(&pushed(&path_v, name)));
|
||||
let mk_ent = |name| (name, pushed(&path_v, name));
|
||||
match ent {
|
||||
FileEntry::Export(names) => Box::new(names.iter().copied().map(mk_ent)),
|
||||
FileEntry::Exported(mem) => match mem {
|
||||
@@ -86,8 +87,8 @@ fn source_to_module(
|
||||
let mut names = Vec::new();
|
||||
for e in rule.pattern.iter() {
|
||||
e.visit_names(Substack::Bottom, &mut |n| {
|
||||
if let Some([name]) = i.r(n).strip_prefix(&path_v[..]) {
|
||||
names.push((*name, n))
|
||||
if let Some([name]) = n.strip_prefix(&path_v[..]) {
|
||||
names.push((*name, n.clone()))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -109,58 +110,37 @@ fn source_to_module(
|
||||
.collect::<Vec<_>>();
|
||||
let items = data
|
||||
.into_iter()
|
||||
.filter_map(|ent| match ent {
|
||||
FileEntry::Exported(Member::Namespace(ns)) => {
|
||||
let prep_member = &preparsed.items[&ns.name].member;
|
||||
let new_prep = if let ModMember::Sub(s) = prep_member {
|
||||
s.as_ref()
|
||||
} else {
|
||||
panic!("preparsed missing a submodule")
|
||||
};
|
||||
let module = source_to_module(
|
||||
path.push(ns.name),
|
||||
new_prep,
|
||||
ns.body,
|
||||
i,
|
||||
filepath_len,
|
||||
);
|
||||
let member = ModMember::Sub(module);
|
||||
Some((ns.name, ModEntry { exported: true, member }))
|
||||
},
|
||||
FileEntry::Internal(Member::Namespace(ns)) => {
|
||||
let prep_member = &preparsed.items[&ns.name].member;
|
||||
let new_prep = if let ModMember::Sub(s) = prep_member {
|
||||
s.as_ref()
|
||||
} else {
|
||||
panic!("preparsed missing a submodule")
|
||||
};
|
||||
let module = source_to_module(
|
||||
path.push(ns.name),
|
||||
new_prep,
|
||||
ns.body,
|
||||
i,
|
||||
filepath_len,
|
||||
);
|
||||
let member = ModMember::Sub(module);
|
||||
Some((ns.name, ModEntry { exported: false, member }))
|
||||
},
|
||||
FileEntry::Exported(Member::Constant(Constant { name, value })) => {
|
||||
let member = ModMember::Item(value);
|
||||
Some((name, ModEntry { exported: true, member }))
|
||||
},
|
||||
FileEntry::Internal(Member::Constant(Constant { name, value })) => {
|
||||
let member = ModMember::Item(value);
|
||||
Some((name, ModEntry { exported: false, member }))
|
||||
},
|
||||
_ => None,
|
||||
.filter_map(|ent| {
|
||||
let member_to_item = |exported, member| match member {
|
||||
Member::Namespace(ns) => {
|
||||
let new_prep = unwrap_or!(
|
||||
&preparsed.items[&ns.name].member => ModMember::Sub;
|
||||
panic!("preparsed missing a submodule")
|
||||
);
|
||||
let module = source_to_module(
|
||||
path.push(ns.name),
|
||||
new_prep,
|
||||
ns.body,
|
||||
i,
|
||||
filepath_len,
|
||||
);
|
||||
let member = ModMember::Sub(module);
|
||||
Some((ns.name, ModEntry { exported, member }))
|
||||
},
|
||||
Member::Constant(Constant { name, value }) => {
|
||||
let member = ModMember::Item(value);
|
||||
Some((name, ModEntry { exported, member }))
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
match ent {
|
||||
FileEntry::Exported(member) => member_to_item(true, member),
|
||||
FileEntry::Internal(member) => member_to_item(false, member),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
// println!(
|
||||
// "Constructing file-module {} with members ({})",
|
||||
// i.extern_all(&path_v[..]).join("::"),
|
||||
// exports.keys().map(|t| i.r(*t)).join(", ")
|
||||
// );
|
||||
Rc::new(Module {
|
||||
Module {
|
||||
imports,
|
||||
items,
|
||||
extra: ProjectExt {
|
||||
@@ -169,14 +149,14 @@ fn source_to_module(
|
||||
rules,
|
||||
file: Some(path_v[..filepath_len].to_vec()),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn files_to_module(
|
||||
path: Substack<Tok<String>>,
|
||||
files: Vec<ParsedSource>,
|
||||
i: &Interner,
|
||||
) -> Rc<Module<Expr, ProjectExt>> {
|
||||
) -> Module<Expr<VName>, ProjectExt<VName>> {
|
||||
let lvl = path.len();
|
||||
debug_assert!(
|
||||
files.iter().map(|f| f.path.len()).max().unwrap() >= lvl,
|
||||
@@ -186,7 +166,7 @@ fn files_to_module(
|
||||
if files.len() == 1 && files[0].path.len() == lvl {
|
||||
return source_to_module(
|
||||
path,
|
||||
files[0].loaded.preparsed.0.as_ref(),
|
||||
&files[0].loaded.preparsed.0,
|
||||
files[0].parsed.clone(),
|
||||
i,
|
||||
path.len(),
|
||||
@@ -204,12 +184,9 @@ fn files_to_module(
|
||||
(namespace, ModEntry { exported: true, member })
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
let exports: HashMap<_, _> = items
|
||||
.keys()
|
||||
.copied()
|
||||
.map(|name| (name, i.i(&pushed(&path_v, name))))
|
||||
.collect();
|
||||
Rc::new(Module {
|
||||
let exports: HashMap<_, _> =
|
||||
items.keys().copied().map(|name| (name, pushed(&path_v, name))).collect();
|
||||
Module {
|
||||
items,
|
||||
imports: vec![],
|
||||
extra: ProjectExt {
|
||||
@@ -218,7 +195,7 @@ fn files_to_module(
|
||||
rules: vec![],
|
||||
file: None,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_tree(
|
||||
@@ -226,17 +203,13 @@ pub fn build_tree(
|
||||
i: &Interner,
|
||||
prelude: &[FileEntry],
|
||||
injected: &impl InjectedOperatorsFn,
|
||||
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
||||
) -> Result<ProjectTree<VName>, 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()
|
||||
.map(|(path, loaded)| {
|
||||
Ok((
|
||||
i.r(*path),
|
||||
loaded,
|
||||
parse_file(*path, &files, &ops_cache, i, prelude)?,
|
||||
))
|
||||
Ok((path, loaded, parse_file(path, &files, &ops_cache, i, prelude)?))
|
||||
})
|
||||
.collect::<Result<Vec<_>, Rc<dyn ProjectError>>>()?;
|
||||
// sort by similarity, then longest-first
|
||||
|
||||
@@ -3,11 +3,12 @@ use std::rc::Rc;
|
||||
use hashbrown::HashSet;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::interner::{Interner, Sym, Tok};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::pipeline::error::{ModuleNotFound, ProjectError};
|
||||
use crate::pipeline::source_loader::LoadedSourceTable;
|
||||
use crate::representations::tree::WalkErrorKind;
|
||||
use crate::utils::{split_max_prefix, unwrap_or, Cache};
|
||||
use crate::Sym;
|
||||
|
||||
pub type OpsResult = Result<Rc<HashSet<Tok<String>>>, Rc<dyn ProjectError>>;
|
||||
pub type ExportedOpsCache<'a> = Cache<'a, Sym, OpsResult>;
|
||||
@@ -31,16 +32,13 @@ pub fn collect_exported_ops(
|
||||
injected: &impl InjectedOperatorsFn,
|
||||
) -> OpsResult {
|
||||
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_max_prefix(path_s, &is_file);
|
||||
let (fpath_v, subpath_v) = unwrap_or!(name_split; return Ok(Rc::new(
|
||||
let name_split = split_max_prefix(path_s, &|n| loaded.contains_key(n));
|
||||
let (fpath, subpath) = 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()])
|
||||
if path_s.len() == coprefix(path_s.iter(), modname.iter()) {
|
||||
Some(modname[path_s.len()])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -48,24 +46,24 @@ pub fn collect_exported_ops(
|
||||
.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| {
|
||||
match walk_err.kind {
|
||||
WalkErrorKind::Private => {
|
||||
unreachable!("visibility is not being checked here")
|
||||
let preparsed = &loaded[fpath].preparsed;
|
||||
let module =
|
||||
preparsed.0.walk_ref(subpath, false).map_err(
|
||||
|walk_err| match walk_err.kind {
|
||||
WalkErrorKind::Private => {
|
||||
unreachable!("visibility is not being checked here")
|
||||
},
|
||||
WalkErrorKind::Missing => ModuleNotFound {
|
||||
file: i.extern_all(fpath),
|
||||
subpath: (subpath.iter())
|
||||
.take(walk_err.pos)
|
||||
.map(|t| i.r(*t))
|
||||
.cloned()
|
||||
.collect(),
|
||||
}
|
||||
.rc(),
|
||||
},
|
||||
WalkErrorKind::Missing => ModuleNotFound {
|
||||
file: i.extern_vec(fpath),
|
||||
subpath: (subpath_v.iter())
|
||||
.take(walk_err.pos)
|
||||
.map(|t| i.r(*t))
|
||||
.cloned()
|
||||
.collect(),
|
||||
}
|
||||
.rc(),
|
||||
}
|
||||
})?;
|
||||
)?;
|
||||
let out = (module.items.iter())
|
||||
.filter(|(_, v)| v.exported)
|
||||
.map(|(k, _)| *k)
|
||||
|
||||
@@ -19,7 +19,7 @@ fn tree_all_ops(
|
||||
ops.extend(module.items.keys().copied());
|
||||
for ent in module.items.values() {
|
||||
if let ModMember::Sub(m) = &ent.member {
|
||||
tree_all_ops(m.as_ref(), ops);
|
||||
tree_all_ops(m, ops);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,9 @@ pub fn collect_ops_for(
|
||||
ops_cache: &ExportedOpsCache,
|
||||
i: &Interner,
|
||||
) -> OpsResult {
|
||||
let tree = &loaded[&i.i(file)].preparsed.0;
|
||||
let tree = &loaded[file].preparsed.0;
|
||||
let mut ret = HashSet::new();
|
||||
tree_all_ops(tree.as_ref(), &mut ret);
|
||||
tree_all_ops(tree, &mut ret);
|
||||
tree.visit_all_imports(&mut |modpath, _module, import| {
|
||||
if let Some(n) = import.name {
|
||||
ret.insert(n);
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
use std::ops::Add;
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use super::{ProjectExt, ProjectModule, ProjectTree};
|
||||
use crate::ast::{Clause, Expr};
|
||||
use crate::foreign::{Atom, Atomic, ExternFn};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::interner::Tok;
|
||||
use crate::representations::location::Location;
|
||||
use crate::representations::tree::{ModEntry, ModMember, Module};
|
||||
use crate::representations::Primitive;
|
||||
use crate::representations::{Primitive, VName};
|
||||
use crate::utils::{pushed, Substack};
|
||||
|
||||
/// A lightweight module tree that can be built declaratively by hand to
|
||||
@@ -17,7 +16,7 @@ use crate::utils::{pushed, Substack};
|
||||
/// added convenience
|
||||
pub enum ConstTree {
|
||||
/// A function or constant
|
||||
Const(Expr),
|
||||
Const(Expr<VName>),
|
||||
/// A submodule
|
||||
Tree(HashMap<Tok<String>, ConstTree>),
|
||||
}
|
||||
@@ -67,8 +66,7 @@ fn from_const_tree_rec(
|
||||
path: Substack<Tok<String>>,
|
||||
consts: HashMap<Tok<String>, ConstTree>,
|
||||
file: &[Tok<String>],
|
||||
i: &Interner,
|
||||
) -> ProjectModule {
|
||||
) -> ProjectModule<VName> {
|
||||
let mut items = HashMap::new();
|
||||
let path_v = path.iter().rev_vec_clone();
|
||||
for (name, item) in consts {
|
||||
@@ -76,17 +74,13 @@ fn from_const_tree_rec(
|
||||
exported: true,
|
||||
member: match item {
|
||||
ConstTree::Const(c) => ModMember::Item(c),
|
||||
ConstTree::Tree(t) => ModMember::Sub(Rc::new(from_const_tree_rec(
|
||||
path.push(name),
|
||||
t,
|
||||
file,
|
||||
i,
|
||||
))),
|
||||
ConstTree::Tree(t) =>
|
||||
ModMember::Sub(from_const_tree_rec(path.push(name), t, file)),
|
||||
},
|
||||
});
|
||||
}
|
||||
let exports =
|
||||
items.keys().map(|name| (*name, i.i(&pushed(&path_v, *name)))).collect();
|
||||
items.keys().map(|name| (*name, pushed(&path_v, *name))).collect();
|
||||
Module {
|
||||
items,
|
||||
imports: vec![],
|
||||
@@ -103,8 +97,7 @@ fn from_const_tree_rec(
|
||||
pub fn from_const_tree(
|
||||
consts: HashMap<Tok<String>, ConstTree>,
|
||||
file: &[Tok<String>],
|
||||
i: &Interner,
|
||||
) -> ProjectTree {
|
||||
let module = from_const_tree_rec(Substack::Bottom, consts, file, i);
|
||||
ProjectTree(Rc::new(module))
|
||||
) -> ProjectTree<VName> {
|
||||
let module = from_const_tree_rec(Substack::Bottom, consts, file);
|
||||
ProjectTree(module)
|
||||
}
|
||||
|
||||
@@ -26,5 +26,6 @@ pub use build_tree::{build_tree, split_path};
|
||||
pub use collect_ops::InjectedOperatorsFn;
|
||||
pub use const_tree::{from_const_tree, ConstTree};
|
||||
pub use tree::{
|
||||
collect_consts, collect_rules, ProjectExt, ProjectModule, ProjectTree,
|
||||
collect_consts, collect_rules, vname_to_sym_tree, ProjectExt, ProjectModule,
|
||||
ProjectTree,
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::representations::sourcefile::{
|
||||
};
|
||||
use crate::representations::tree::{ModMember, Module};
|
||||
use crate::utils::iter::box_once;
|
||||
use crate::utils::{BoxedIter, Substack};
|
||||
use crate::utils::{unwrap_or, BoxedIter, Substack};
|
||||
|
||||
fn member_rec(
|
||||
// level
|
||||
@@ -21,20 +21,12 @@ fn member_rec(
|
||||
) -> Member {
|
||||
match member {
|
||||
Member::Namespace(Namespace { name, body }) => {
|
||||
let prepmember = &preparsed.items[&name].member;
|
||||
let subprep = if let ModMember::Sub(m) = prepmember {
|
||||
m.clone()
|
||||
} else {
|
||||
let subprep = unwrap_or!(
|
||||
&preparsed.items[&name].member => ModMember::Sub;
|
||||
unreachable!("This name must point to a namespace")
|
||||
};
|
||||
let new_body = entv_rec(
|
||||
mod_stack.push(name),
|
||||
subprep.as_ref(),
|
||||
body,
|
||||
path,
|
||||
ops_cache,
|
||||
i,
|
||||
);
|
||||
let new_body =
|
||||
entv_rec(mod_stack.push(name), subprep, body, path, ops_cache, i);
|
||||
Member::Namespace(Namespace { name, body: new_body })
|
||||
},
|
||||
any => any,
|
||||
|
||||
@@ -4,40 +4,35 @@ use super::add_prelude::add_prelude;
|
||||
use super::collect_ops::{collect_ops_for, ExportedOpsCache};
|
||||
use super::normalize_imports::normalize_imports;
|
||||
use super::prefix::prefix;
|
||||
use crate::interner::{Interner, Sym};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::parse;
|
||||
use crate::pipeline::error::ProjectError;
|
||||
use crate::pipeline::source_loader::LoadedSourceTable;
|
||||
use crate::representations::sourcefile::{normalize_namespaces, FileEntry};
|
||||
|
||||
pub fn parse_file(
|
||||
path: Sym,
|
||||
path: &[Tok<String>],
|
||||
loaded: &LoadedSourceTable,
|
||||
ops_cache: &ExportedOpsCache,
|
||||
i: &Interner,
|
||||
prelude: &[FileEntry],
|
||||
) -> Result<Vec<FileEntry>, Rc<dyn ProjectError>> {
|
||||
let ld = &loaded[&path];
|
||||
let ld = &loaded[path];
|
||||
// let ops_cache = collect_ops::mk_cache(loaded, i);
|
||||
let ops = collect_ops_for(&i.r(path)[..], loaded, ops_cache, i)?;
|
||||
let ops = collect_ops_for(path, loaded, ops_cache, i)?;
|
||||
let ops_vec = ops.iter().map(|t| i.r(*t)).cloned().collect::<Vec<_>>();
|
||||
let ctx = parse::ParsingContext {
|
||||
interner: i,
|
||||
ops: &ops_vec,
|
||||
file: Rc::new(i.extern_vec(path)),
|
||||
file: Rc::new(i.extern_all(path)),
|
||||
};
|
||||
let entries = parse::parse(ld.text.as_str(), ctx)
|
||||
.expect("This error should have been caught during loading");
|
||||
let with_prelude = add_prelude(entries, &i.r(path)[..], prelude);
|
||||
let impnormalized = normalize_imports(
|
||||
&ld.preparsed.0,
|
||||
with_prelude,
|
||||
&i.r(path)[..],
|
||||
ops_cache,
|
||||
i,
|
||||
);
|
||||
let with_prelude = add_prelude(entries, path, prelude);
|
||||
let impnormalized =
|
||||
normalize_imports(&ld.preparsed.0, with_prelude, path, ops_cache, i);
|
||||
let nsnormalized = normalize_namespaces(Box::new(impnormalized.into_iter()))
|
||||
.expect("This error should have been caught during preparsing");
|
||||
let prefixed = prefix(nsnormalized, &i.r(path)[..], ops_cache, i);
|
||||
let prefixed = prefix(nsnormalized, path, ops_cache, i);
|
||||
Ok(prefixed)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::collect_ops::ExportedOpsCache;
|
||||
use crate::ast::{Constant, Rule};
|
||||
use crate::interner::{Interner, Tok};
|
||||
@@ -16,14 +14,10 @@ fn member_rec(
|
||||
ops_cache: &ExportedOpsCache,
|
||||
i: &Interner,
|
||||
) -> Member {
|
||||
// let except = |op| imported.contains(&op);
|
||||
let except = |_| false;
|
||||
let prefix_v = path
|
||||
.iter()
|
||||
let prefix = (path.iter())
|
||||
.copied()
|
||||
.chain(mod_stack.iter().rev_vec_clone().into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
let prefix = i.i(&prefix_v);
|
||||
match data {
|
||||
Member::Namespace(Namespace { name, body }) => {
|
||||
let new_body = entv_rec(mod_stack.push(name), body, path, ops_cache, i);
|
||||
@@ -31,16 +25,16 @@ fn member_rec(
|
||||
},
|
||||
Member::Constant(constant) => Member::Constant(Constant {
|
||||
name: constant.name,
|
||||
value: constant.value.prefix(prefix, i, &except),
|
||||
value: constant.value.prefix(&prefix, &|_| false),
|
||||
}),
|
||||
Member::Rule(rule) => Member::Rule(Rule {
|
||||
prio: rule.prio,
|
||||
pattern: Rc::new(
|
||||
rule.pattern.iter().map(|e| e.prefix(prefix, i, &except)).collect(),
|
||||
),
|
||||
template: Rc::new(
|
||||
rule.template.iter().map(|e| e.prefix(prefix, i, &except)).collect(),
|
||||
),
|
||||
pattern: (rule.pattern.into_iter())
|
||||
.map(|e| e.prefix(&prefix, &|_| false))
|
||||
.collect(),
|
||||
template: (rule.template.into_iter())
|
||||
.map(|e| e.prefix(&prefix, &|_| false))
|
||||
.collect(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
use std::ops::Add;
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
|
||||
use crate::ast::{Expr, Rule};
|
||||
use crate::interner::{Interner, Sym, Tok};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::representations::tree::{ModMember, Module};
|
||||
use crate::representations::NameLike;
|
||||
use crate::tree::ModEntry;
|
||||
use crate::utils::Substack;
|
||||
use crate::{Sym, VName};
|
||||
|
||||
/// Additional data about a loaded module beyond the list of constants and
|
||||
/// submodules
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ProjectExt {
|
||||
pub struct ProjectExt<N: NameLike> {
|
||||
/// Pairs each foreign token to the module it was imported from
|
||||
pub imports_from: HashMap<Tok<String>, Sym>,
|
||||
pub imports_from: HashMap<Tok<String>, N>,
|
||||
/// Pairs each exported token to its original full name
|
||||
pub exports: HashMap<Tok<String>, Sym>,
|
||||
pub exports: HashMap<Tok<String>, N>,
|
||||
/// All rules defined in this module, exported or not
|
||||
pub rules: Vec<Rule>,
|
||||
pub rules: Vec<Rule<N>>,
|
||||
/// Filename, if known, for error reporting
|
||||
pub file: Option<Vec<Tok<String>>>,
|
||||
}
|
||||
|
||||
impl Add for ProjectExt {
|
||||
impl<N: NameLike> Add for ProjectExt<N> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, rhs: Self) -> Self::Output {
|
||||
@@ -38,33 +40,36 @@ impl Add for ProjectExt {
|
||||
}
|
||||
|
||||
/// A node in the tree describing the project
|
||||
pub type ProjectModule = Module<Expr, ProjectExt>;
|
||||
pub type ProjectModule<N> = Module<Expr<N>, ProjectExt<N>>;
|
||||
|
||||
/// Module corresponding to the root of a project
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProjectTree(pub Rc<ProjectModule>);
|
||||
pub struct ProjectTree<N: NameLike>(pub ProjectModule<N>);
|
||||
|
||||
fn collect_rules_rec(bag: &mut Vec<Rule>, module: &ProjectModule) {
|
||||
fn collect_rules_rec<N: NameLike>(
|
||||
bag: &mut Vec<Rule<N>>,
|
||||
module: &ProjectModule<N>,
|
||||
) {
|
||||
bag.extend(module.extra.rules.iter().cloned());
|
||||
for item in module.items.values() {
|
||||
if let ModMember::Sub(module) = &item.member {
|
||||
collect_rules_rec(bag, module.as_ref());
|
||||
collect_rules_rec(bag, module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect the complete list of rules to be used by the rule repository from
|
||||
/// the [ProjectTree]
|
||||
pub fn collect_rules(project: &ProjectTree) -> Vec<Rule> {
|
||||
pub fn collect_rules<N: NameLike>(project: &ProjectTree<N>) -> Vec<Rule<N>> {
|
||||
let mut rules = Vec::new();
|
||||
collect_rules_rec(&mut rules, project.0.as_ref());
|
||||
collect_rules_rec(&mut rules, &project.0);
|
||||
rules
|
||||
}
|
||||
|
||||
fn collect_consts_rec(
|
||||
fn collect_consts_rec<N: NameLike>(
|
||||
path: Substack<Tok<String>>,
|
||||
bag: &mut HashMap<Sym, Expr>,
|
||||
module: &ProjectModule,
|
||||
bag: &mut HashMap<Sym, Expr<N>>,
|
||||
module: &ProjectModule<N>,
|
||||
i: &Interner,
|
||||
) {
|
||||
for (key, entry) in module.items.iter() {
|
||||
@@ -81,11 +86,59 @@ fn collect_consts_rec(
|
||||
}
|
||||
|
||||
/// Extract the symbol table from a [ProjectTree]
|
||||
pub fn collect_consts(
|
||||
project: &ProjectTree,
|
||||
pub fn collect_consts<N: NameLike>(
|
||||
project: &ProjectTree<N>,
|
||||
i: &Interner,
|
||||
) -> HashMap<Sym, Expr> {
|
||||
) -> HashMap<Sym, Expr<N>> {
|
||||
let mut consts = HashMap::new();
|
||||
collect_consts_rec(Substack::Bottom, &mut consts, project.0.as_ref(), i);
|
||||
collect_consts_rec(Substack::Bottom, &mut consts, &project.0, i);
|
||||
consts
|
||||
}
|
||||
|
||||
fn vname_to_sym_tree_rec(
|
||||
tree: ProjectModule<VName>,
|
||||
i: &Interner,
|
||||
) -> ProjectModule<Sym> {
|
||||
let process_expr = |ex: Expr<VName>| ex.transform_names(&|n| i.i(&n));
|
||||
ProjectModule {
|
||||
imports: tree.imports,
|
||||
items: (tree.items.into_iter())
|
||||
.map(|(k, ModEntry { exported, member })| {
|
||||
(k, ModEntry {
|
||||
exported,
|
||||
member: match member {
|
||||
ModMember::Sub(module) =>
|
||||
ModMember::Sub(vname_to_sym_tree_rec(module, i)),
|
||||
ModMember::Item(ex) => ModMember::Item(process_expr(ex)),
|
||||
},
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
extra: ProjectExt {
|
||||
imports_from: (tree.extra.imports_from.into_iter())
|
||||
.map(|(k, v)| (k, i.i(&v)))
|
||||
.collect(),
|
||||
exports: (tree.extra.exports.into_iter())
|
||||
.map(|(k, v)| (k, i.i(&v)))
|
||||
.collect(),
|
||||
rules: (tree.extra.rules.into_iter())
|
||||
.map(|Rule { pattern, prio, template }| Rule {
|
||||
pattern: pattern.into_iter().map(process_expr).collect(),
|
||||
prio,
|
||||
template: template.into_iter().map(process_expr).collect(),
|
||||
})
|
||||
.collect(),
|
||||
file: tree.extra.file,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a flexible vname-based tree to a more rigid but faster symbol-based
|
||||
/// tree. The pipeline works with vnames, but the macro executor works with
|
||||
/// symbols.
|
||||
pub fn vname_to_sym_tree(
|
||||
tree: ProjectTree<VName>,
|
||||
i: &Interner,
|
||||
) -> ProjectTree<Sym> {
|
||||
ProjectTree(vname_to_sym_tree_rec(tree.0, i))
|
||||
}
|
||||
|
||||
@@ -3,22 +3,22 @@ use std::rc::Rc;
|
||||
|
||||
use super::loaded_source::{LoadedSource, LoadedSourceTable};
|
||||
use super::preparse::preparse;
|
||||
use crate::interner::{Interner, Sym};
|
||||
use crate::pipeline::error::ProjectError;
|
||||
use crate::pipeline::file_loader::{load_text, IOResult, Loaded};
|
||||
use crate::interner::{Interner, Tok};
|
||||
use crate::pipeline::error::{ProjectError, UnexpectedDirectory};
|
||||
use crate::pipeline::file_loader::{IOResult, Loaded};
|
||||
use crate::pipeline::import_abs_path::import_abs_path;
|
||||
use crate::representations::sourcefile::FileEntry;
|
||||
use crate::utils::split_max_prefix;
|
||||
use crate::utils::{split_max_prefix, unwrap_or};
|
||||
|
||||
/// Load the source at the given path or all within if it's a collection,
|
||||
/// and all sources imported from these.
|
||||
fn load_abs_path_rec(
|
||||
abs_path: Sym,
|
||||
abs_path: &[Tok<String>],
|
||||
table: &mut LoadedSourceTable,
|
||||
prelude: &[FileEntry],
|
||||
i: &Interner,
|
||||
get_source: &impl Fn(Sym) -> IOResult,
|
||||
is_injected_module: &impl Fn(Sym) -> bool,
|
||||
get_source: &impl Fn(&[Tok<String>]) -> IOResult,
|
||||
is_injected_module: &impl Fn(&[Tok<String>]) -> bool,
|
||||
) -> Result<(), Rc<dyn ProjectError>> {
|
||||
// # Termination
|
||||
//
|
||||
@@ -29,23 +29,26 @@ fn load_abs_path_rec(
|
||||
// contains no cycles.
|
||||
|
||||
// Termination: exit if entry already visited
|
||||
if table.contains_key(&abs_path) {
|
||||
if table.contains_key(abs_path) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// try splitting the path to file, swallowing any IO errors
|
||||
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)));
|
||||
let name_split = split_max_prefix(abs_path, &|p| {
|
||||
get_source(p).map(|l| l.is_code()).unwrap_or(false)
|
||||
});
|
||||
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 text = unwrap_or!(get_source(filename)? => Loaded::Code; {
|
||||
return Err(UnexpectedDirectory { path: i.extern_all(filename) }.rc())
|
||||
});
|
||||
let preparsed = preparse(
|
||||
filename.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||
text.as_str(),
|
||||
prelude,
|
||||
i,
|
||||
)?;
|
||||
table.insert(i.i(filename), LoadedSource {
|
||||
table.insert(filename.to_vec(), LoadedSource {
|
||||
text,
|
||||
preparsed: preparsed.clone(),
|
||||
});
|
||||
@@ -55,7 +58,7 @@ fn load_abs_path_rec(
|
||||
import_abs_path(filename, modpath, &import.nonglob_path(i), i)?;
|
||||
// recurse on imported module
|
||||
load_abs_path_rec(
|
||||
i.i(&abs_pathv),
|
||||
&abs_pathv,
|
||||
table,
|
||||
prelude,
|
||||
i,
|
||||
@@ -70,20 +73,20 @@ fn load_abs_path_rec(
|
||||
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;
|
||||
let parent = 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) };
|
||||
return if is_injected_module(parent) { Ok(()) } else { Err(e) };
|
||||
},
|
||||
};
|
||||
// recurse on all files and folders within
|
||||
for item in coll.iter() {
|
||||
let abs_subpath = (i.r(abs_path).iter())
|
||||
let abs_subpath = (abs_path.iter())
|
||||
.copied()
|
||||
.chain(iter::once(i.i(item)))
|
||||
.collect::<Vec<_>>();
|
||||
load_abs_path_rec(
|
||||
i.i(&abs_subpath),
|
||||
&abs_subpath,
|
||||
table,
|
||||
prelude,
|
||||
i,
|
||||
@@ -101,17 +104,17 @@ fn load_abs_path_rec(
|
||||
/// 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],
|
||||
pub fn load_source<'a>(
|
||||
targets: impl Iterator<Item = &'a [Tok<String>]>,
|
||||
prelude: &[FileEntry],
|
||||
i: &Interner,
|
||||
get_source: &impl Fn(Sym) -> IOResult,
|
||||
is_injected_module: &impl Fn(Sym) -> bool,
|
||||
get_source: &impl Fn(&[Tok<String>]) -> IOResult,
|
||||
is_injected_module: &impl Fn(&[Tok<String>]) -> bool,
|
||||
) -> Result<LoadedSourceTable, Rc<dyn ProjectError>> {
|
||||
let mut table = LoadedSourceTable::new();
|
||||
for target in targets {
|
||||
load_abs_path_rec(
|
||||
*target,
|
||||
target,
|
||||
&mut table,
|
||||
prelude,
|
||||
i,
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::preparse::Preparsed;
|
||||
use crate::interner::Sym;
|
||||
use crate::representations::VName;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoadedSource {
|
||||
@@ -10,4 +10,4 @@ pub struct LoadedSource {
|
||||
pub preparsed: Preparsed,
|
||||
}
|
||||
|
||||
pub type LoadedSourceTable = HashMap<Sym, LoadedSource>;
|
||||
pub type LoadedSourceTable = HashMap<VName, LoadedSource>;
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::representations::sourcefile::{
|
||||
use crate::representations::tree::{ModEntry, ModMember, Module};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Preparsed(pub Rc<Module<(), ()>>);
|
||||
pub struct Preparsed(pub Module<(), ()>);
|
||||
|
||||
/// Add an internal flat name if it does not exist yet
|
||||
fn add_intern<K: Eq + Hash>(map: &mut HashMap<K, ModEntry<(), ()>>, k: K) {
|
||||
@@ -33,22 +33,18 @@ fn add_export<K: Eq + Hash>(map: &mut HashMap<K, ModEntry<(), ()>>, k: K) {
|
||||
}
|
||||
|
||||
/// Convert source lines into a module
|
||||
fn to_module(
|
||||
src: &[FileEntry],
|
||||
prelude: &[FileEntry],
|
||||
i: &Interner,
|
||||
) -> Rc<Module<(), ()>> {
|
||||
fn to_module(src: &[FileEntry], prelude: &[FileEntry]) -> Module<(), ()> {
|
||||
let all_src = || src.iter().chain(prelude.iter());
|
||||
let imports = imports(all_src()).cloned().collect::<Vec<_>>();
|
||||
let mut items = all_src()
|
||||
.filter_map(|ent| match ent {
|
||||
FileEntry::Internal(Member::Namespace(ns)) => {
|
||||
let member = ModMember::Sub(to_module(&ns.body, prelude, i));
|
||||
let member = ModMember::Sub(to_module(&ns.body, prelude));
|
||||
let entry = ModEntry { exported: false, member };
|
||||
Some((ns.name, entry))
|
||||
},
|
||||
FileEntry::Exported(Member::Namespace(ns)) => {
|
||||
let member = ModMember::Sub(to_module(&ns.body, prelude, i));
|
||||
let member = ModMember::Sub(to_module(&ns.body, prelude));
|
||||
let entry = ModEntry { exported: true, member };
|
||||
Some((ns.name, entry))
|
||||
},
|
||||
@@ -70,20 +66,20 @@ fn to_module(
|
||||
FileEntry::Exported(Member::Constant(Constant { name, .. })) =>
|
||||
add_export(&mut items, *name),
|
||||
FileEntry::Internal(Member::Rule(rule)) => {
|
||||
let names = rule.collect_single_names(i);
|
||||
let names = rule.collect_single_names();
|
||||
for name in names {
|
||||
add_intern(&mut items, name)
|
||||
}
|
||||
},
|
||||
FileEntry::Exported(Member::Rule(rule)) => {
|
||||
let names = rule.collect_single_names(i);
|
||||
let names = rule.collect_single_names();
|
||||
for name in names {
|
||||
add_export(&mut items, name)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Rc::new(Module { imports, items, extra: () })
|
||||
Module { imports, items, extra: () }
|
||||
}
|
||||
|
||||
/// Preparse the module. At this stage, only the imports and
|
||||
@@ -112,5 +108,5 @@ pub fn preparse(
|
||||
}
|
||||
.rc()
|
||||
})?;
|
||||
Ok(Preparsed(to_module(&normalized, prelude, i)))
|
||||
Ok(Preparsed(to_module(&normalized, prelude)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user