From 3366ff3d1f6ae10d62249639c493d90add917bae Mon Sep 17 00:00:00 2001 From: Yann Hamdaoui Date: Fri, 24 Jan 2025 12:39:02 +0100 Subject: [PATCH] Copy paste some stuff in the AstResolver impl. TODO: adapt it --- core/src/cache.rs | 169 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 134 insertions(+), 35 deletions(-) diff --git a/core/src/cache.rs b/core/src/cache.rs index 302be5eac..65d5d4f3f 100644 --- a/core/src/cache.rs +++ b/core/src/cache.rs @@ -569,7 +569,7 @@ impl SourceCache { } let mut import_data = ImportData::new(); - let resolver = resolvers::AstResolver { + let resolver = AstResolver { alloc: &alloc, asts: &HashMap::new(), new_asts: Vec::new(), @@ -1803,45 +1803,144 @@ pub fn timestamp(path: impl AsRef) -> io::Result { fs::metadata(path.as_ref())?.modified() } -/// Provide mockup import resolvers for testing purpose. -pub mod resolvers { - use super::*; +/// As RFC007 is being rolled out, the typechecker now needs to operate on the new AST. We thus +/// need a structure that implements [AstImportResolver]. For borrowing reasons, this can't be all +/// of [Caches] or all of [ast_cache::AstCache], as we need to split the different things that are +/// borrowed mutably or immutably, or with different lifetimes. `AstResolver` is a structure that +/// borrows some parts of the cache during its lifetime and will retrieve alredy imported ASTs, or +/// register the newly imported ones in a separate vector, that can be then added back to the +/// original cache. They can't be added directly to the AST cache, once again, for borrowing and +/// lifetime reasons. +pub struct AstResolver<'ast, 'cache, 'input> { + /// The AST allocator used to parse new sources. + alloc: &'ast AstAlloc, + /// The AST cache before the start of import resolution. Because of technicalities of the + /// self-referential [super::AstCache], we can only take it as an immutable reference. Newly + /// imported ASTs are put in [Self::new_asts]. + asts: &'cache HashMap, ParseErrors)>, + /// Newly imported ASTs, to be appended to the AST cache after resolution. + new_asts: Vec<(FileId, Ast<'ast>)>, + /// The source cache where new sources will be stored. + sources: &'input mut SourceCache, + /// Direct and reverse dependencies of files (with respect to imports). + import_data: &'cache mut ImportData, +} - pub struct AstResolver<'ast, 'cache, 'input> { - /// The ast allocator used to parse new sources. - pub(super) alloc: &'ast AstAlloc, - /// The ast cache before the start of import resolution. Because of technicalities of the - /// self-referential [super::AstCache], we can only take it as an immutable reference. - /// Newly imported ASTs are put in [Self::new_asts]. - pub(super) asts: &'cache HashMap, ParseErrors)>, - /// Newly imported ASTs, to be appended to the AST cache after resolution. - pub(super) new_asts: Vec<(FileId, Ast<'ast>)>, - /// The source cache where new sources will be stored. - pub(super) sources: &'input mut SourceCache, - /// Direct and reverse dependencies of files (with respect to imports). - pub(super) import_data: &'cache mut ImportData, - } - - impl<'ast, 'cache, 'input> AstResolver<'ast, 'cache, 'input> { - pub(super) fn append_to_cache(self, asts: &mut HashMap, ParseErrors)>) { - asts.extend( - self.new_asts - .into_iter() - .map(|(id, ast)| (id, (ast, ParseErrors::default()))), - ); - } +impl<'ast, 'cache, 'input> AstResolver<'ast, 'cache, 'input> { + /// Consumes self and add the new file that were imported during the lifetime of this + /// resolver to the given AST cache. + fn append_to_cache(self, asts: &mut HashMap, ParseErrors)>) { + asts.extend( + self.new_asts + .into_iter() + .map(|(id, ast)| (id, (ast, ParseErrors::default()))), + ); } +} - impl<'ast, 'cache, 'input> AstImportResolver<'ast> for AstResolver<'ast, 'cache, 'input> { - fn resolve( - &mut self, - _import: &Import, - _parent: Option, - _pos: &TermPos, - ) -> Result<(ResolvedTerm, Ast<'ast>), ImportError> { - todo!() +impl<'ast, 'cache, 'input> AstImportResolver<'ast> for AstResolver<'ast, 'cache, 'input> { + fn resolve( + &mut self, + import: &Import, + parent: Option, + pos: &TermPos, + ) -> Result<(ResolvedTerm, Ast<'ast>), ImportError> { + //TODO[RFC007]: continue this, was just copy pasted now + + let (possible_parents, path, pkg_id, format) = match import { + Import::Path { path, format } => { + // `parent` is the file that did the import. We first look in its containing directory, followed by + // the directories in the import path. + let mut parent_path = parent + .and_then(|p| self.get_path(p)) + .map(PathBuf::from) + .unwrap_or_default(); + parent_path.pop(); + + ( + std::iter::once(parent_path) + .chain(self.sources.import_paths.iter().cloned()) + .collect(), + Path::new(path), + None, + *format, + ) + } + Import::Package { id } => { + let package_map = self + .sources + .package_map + .as_ref() + .ok_or(ImportError::NoPackageMap { pos: *pos })?; + let parent_path = parent + .and_then(|p| self.sources.packages.get(&p)) + .map(PathBuf::as_path); + let pkg_path = package_map.get(parent_path, *id, *pos)?; + ( + vec![pkg_path.to_owned()], + Path::new("main.ncl"), + Some(pkg_path.to_owned()), + // Packages are always in nickel format + InputFormat::Nickel, + ) + } + }; + + // Try to import from all possibilities, taking the first one that succeeds. + let (id_op, path_buf) = possible_parents + .iter() + .find_map(|parent| { + let mut path_buf = parent.clone(); + path_buf.push(path); + self.sources + .get_or_add_file(&path_buf, format) + .ok() + .map(|x| (x, path_buf)) + }) + .ok_or_else(|| { + let parents = possible_parents + .iter() + .map(|p| p.to_string_lossy()) + .collect::>(); + ImportError::IOError( + path.to_string_lossy().into_owned(), + format!("could not find import (looked in [{}])", parents.join(", ")), + *pos, + ) + })?; + + let (result, file_id) = match id_op { + CacheOp::Cached(id) => (ResolvedTerm::FromCache, id), + CacheOp::Done(id) => (ResolvedTerm::FromFile { path: path_buf }, id), + }; + + if let Some(parent) = parent { + self.import_data + .imports + .entry(parent) + .or_default() + .insert(file_id); + self.import_data + .rev_imports + .entry(file_id) + .or_default() + .insert(parent); } + + self.parse(file_id, format) + .map_err(|err| ImportError::ParseErrors(err, *pos))?; + + if let Some(pkg_id) = pkg_id { + self.sources.packages.insert(file_id, pkg_id); + } + + Ok((result, file_id)) } +} + +/// Provide mockup import resolvers for testing purpose. +pub mod resolvers { + use super::*; /// A dummy resolver that panics when asked to do something. Used to test code that contains no /// import.