The pipeline is finally reasonably clean

This commit is contained in:
2023-09-12 01:26:46 +01:00
parent 6693d93944
commit 8c866967a9
86 changed files with 1959 additions and 1393 deletions

View File

@@ -0,0 +1,59 @@
use std::slice;
use chumsky::primitive::Container;
use hashbrown::HashMap;
use crate::representations::project::{ProjectMod, ItemKind, ProjectEntry};
use crate::tree::ModMember;
use crate::utils::{pushed, unwrap_or};
use crate::{ProjectTree, VName, Tok, NameLike};
use super::walk_with_links::{walk_with_links, Target};
pub struct AliasCache {
data: HashMap<Vec<Tok<String>>, Option<Vec<Tok<String>>>>,
}
impl AliasCache {
pub fn new() -> Self {
Self { data: HashMap::new() }
}
/// Finds the absolute nsname corresponding to the given name in the given
/// context, if it's imported. If the name is defined locally, returns None
/// to avoid allocating several vectors for every local variable.
pub fn resolv_name<'a>(
&'a mut self,
root: &ProjectMod<VName>,
location: &[Tok<String>],
name: Tok<String>
) -> Option<&'a [Tok<String>]> {
let full_path = pushed(location, name);
if let Some(result) = self.data.get(&full_path) {
return result.as_deref();
}
let (ent, finalp) = walk_with_links(root, location.iter().cloned())
.expect("This path should be valid");
let m = unwrap_or!{ent => Target::Mod; panic!("Must be a module")};
let result = m.extra.imports_from.get(&name).map(|next| {
self.resolv_name(root, &next, name).unwrap_or(&next)
});
self.data.insert(full_path, result.map(|s| s.to_vec()));
return result;
}
/// Find the absolute target of a
pub fn resolv_vec<'a>(
&'a mut self,
root: &ProjectMod<VName>,
modname: &[Tok<String>],
vname: &[Tok<String>],
) -> Option<&'a [Tok<String>]> {
let (name, ns) = vname.split_last().expect("name cannot be empty");
if ns.is_empty() {
self.resolv_name(modname, name)
} else {
let origin = self.resolv_vec(modname, ns)?;
self.resolv_name(origin, name)
}
}
}

View File

@@ -0,0 +1,5 @@
// mod alias_cache;
mod resolve_aliases;
mod walk_with_links;
pub use resolve_aliases::resolve_aliases;

View File

@@ -0,0 +1,95 @@
use std::iter;
use itertools::Itertools;
use super::walk_with_links::walk_with_links;
use crate::ast::{Expr, Rule};
use crate::representations::project::{
ItemKind, ProjectExt, ProjectItem, ProjectMod,
};
use crate::tree::{ModEntry, ModMember, Module};
use crate::utils::pushed::pushed;
use crate::{Interner, ProjectTree, Tok, VName};
fn resolve_aliases_rec(
root: &ProjectMod<VName>,
module: &ProjectMod<VName>,
updated: &impl Fn(&[Tok<String>]) -> bool,
is_root: bool,
) -> ProjectMod<VName> {
if !is_root && !updated(&module.extra.path) {
return module.clone();
}
let process_expr = |expr: &Expr<VName>| {
expr
.map_names(&|n| {
let full_name = (module.extra.path.iter()).chain(n.iter()).cloned();
match walk_with_links(root, full_name, false) {
Ok(rep) => Some(rep.abs_path),
// Ok(_) => None,
Err(e) => {
let leftovers = e.tail.collect::<Vec<_>>();
if !leftovers.is_empty() {
let full_name = (module.extra.path.iter())
.chain(n.iter())
.cloned()
.collect::<Vec<_>>();
let _ = walk_with_links(root, full_name.iter().cloned(), true);
panic!(
"Invalid path {} while resolving {} should have been noticed \
earlier",
(e.abs_path.into_iter())
.chain(iter::once(e.name))
.chain(leftovers.into_iter())
.join("::"),
Interner::extern_all(&full_name).join("::"),
);
}
Some(pushed(e.abs_path, e.name))
},
}
})
.unwrap_or_else(|| expr.clone())
};
Module {
extra: ProjectExt {
path: module.extra.path.clone(),
file: module.extra.file.clone(),
imports_from: module.extra.imports_from.clone(),
rules: (module.extra.rules.iter())
.map(|Rule { pattern, prio, template }| Rule {
pattern: pattern.iter().map(process_expr).collect(),
template: template.iter().map(process_expr).collect(),
prio: *prio,
})
.collect(),
},
entries: module
.entries
.iter()
.map(|(k, v)| {
(k.clone(), ModEntry {
exported: v.exported,
member: match &v.member {
ModMember::Sub(module) =>
ModMember::Sub(resolve_aliases_rec(root, module, updated, false)),
ModMember::Item(item) => ModMember::Item(ProjectItem {
is_op: item.is_op,
kind: match &item.kind {
ItemKind::Const(value) => ItemKind::Const(process_expr(value)),
other => other.clone(),
},
}),
},
})
})
.collect(),
}
}
pub fn resolve_aliases(
project: ProjectTree<VName>,
updated: &impl Fn(&[Tok<String>]) -> bool,
) -> ProjectTree<VName> {
ProjectTree(resolve_aliases_rec(&project.0, &project.0, updated, true))
}

View File

@@ -0,0 +1,124 @@
#[allow(unused)] // for doc
use crate::representations::project::ProjectEntry;
use crate::representations::project::{ItemKind, ProjectItem, ProjectMod};
use crate::tree::ModMember;
use crate::utils::{unwrap_or, BoxedIter};
use crate::{Interner, NameLike, Tok, VName};
/// The destination of a linked walk. [ProjectEntry] cannot be used for this
/// purpose because it might be the project root.
pub enum Target<'a, N: NameLike> {
Mod(&'a ProjectMod<N>),
Leaf(&'a ProjectItem<N>),
}
pub struct WalkReport<'a, N: NameLike> {
pub target: Target<'a, N>,
pub abs_path: VName,
pub aliased: bool,
}
pub struct LinkWalkError<'a> {
/// The last known valid path
pub abs_path: VName,
/// The name that wasn't found
pub name: Tok<String>,
/// Leftover steps
pub tail: BoxedIter<'a, Tok<String>>,
/// Whether an alias was ever encountered
pub aliased: bool,
}
fn walk_with_links_rec<'a, 'b, N: NameLike>(
mut abs_path: VName,
root: &'a ProjectMod<N>,
cur: &'a ProjectMod<N>,
prev_tgt: Target<'a, N>,
aliased: bool,
mut path: impl Iterator<Item = Tok<String>> + 'b,
l: bool,
) -> Result<WalkReport<'a, N>, LinkWalkError<'b>> {
let name = unwrap_or! {path.next();
// ends on this module
return Ok(WalkReport{ target: prev_tgt, abs_path, aliased })
};
if l {
eprintln!(
"Resolving {} in {}",
name,
Interner::extern_all(&abs_path).join("::")
)
}
let entry = unwrap_or! {cur.entries.get(&name); {
// panic!("No entry {name} on {}", Interner::extern_all(&cur.extra.path).join("::"));
// leads into a missing branch
return Err(LinkWalkError{ abs_path, aliased, name, tail: Box::new(path) })
}};
match &entry.member {
ModMember::Sub(m) => {
// leads into submodule
abs_path.push(name);
walk_with_links_rec(abs_path, root, m, Target::Mod(m), aliased, path, l)
},
ModMember::Item(item) => match &item.kind {
ItemKind::Alias(alias) => {
// leads into alias (reset acc, cur, cur_entry)
if l {
eprintln!(
"{} points to {}",
Interner::extern_all(&abs_path).join("::"),
Interner::extern_all(alias).join("::")
)
}
abs_path.clone_from(alias);
abs_path.extend(path);
let path_acc = Vec::with_capacity(abs_path.len());
let new_path = abs_path.into_iter();
let tgt = Target::Mod(root);
walk_with_links_rec(path_acc, root, root, tgt, true, new_path, l)
},
ItemKind::Const(_) | ItemKind::None => {
abs_path.push(name);
match path.next() {
Some(name) => {
// leads into leaf
let tail = Box::new(path);
Err(LinkWalkError { abs_path, aliased, name, tail })
},
None => {
// ends on leaf
let target = Target::Leaf(item);
Ok(WalkReport { target, abs_path, aliased })
},
}
},
},
}
}
/// Execute a walk down the tree, following aliases.
/// If the path ends on an alias, that alias is also resolved.
/// If the path leads out of the tree, the shortest failing path is returned
pub fn walk_with_links<'a, N: NameLike>(
root: &ProjectMod<N>,
path: impl Iterator<Item = Tok<String>> + 'a,
l: bool,
) -> Result<WalkReport<'_, N>, LinkWalkError<'a>> {
let path_acc = path.size_hint().1.map_or_else(Vec::new, Vec::with_capacity);
let mut result = walk_with_links_rec(
path_acc,
root,
root,
Target::Mod(root),
false,
path,
l,
);
// cut off excess preallocated space within normal vector growth policy
let abs_path = match &mut result {
Ok(rep) => &mut rep.abs_path,
Err(err) => &mut err.abs_path,
};
abs_path.shrink_to(abs_path.len().next_power_of_two());
result
}