microcad_lang/resolve/
sources.rs1use derive_more::Deref;
7
8use crate::{parse::*, rc::*, resolve::*, src_ref::*, syntax::*};
9use std::collections::HashMap;
10
11#[derive(Default, Deref)]
19pub struct Sources {
20 externals: Externals,
22
23 by_hash: HashMap<u64, usize>,
24 by_path: HashMap<std::path::PathBuf, usize>,
25 by_name: HashMap<QualifiedName, usize>,
26
27 root: Rc<SourceFile>,
29
30 #[deref]
32 pub source_files: Vec<Rc<SourceFile>>,
33
34 search_paths: Vec<std::path::PathBuf>,
36}
37
38impl Sources {
39 pub fn load(
43 root: Rc<SourceFile>,
44 search_paths: &[impl AsRef<std::path::Path>],
45 ) -> ResolveResult<Self> {
46 let mut source_files = Vec::new();
47 let mut by_name = HashMap::new();
48 let mut by_hash = HashMap::new();
49 let mut by_path = HashMap::new();
50
51 by_hash.insert(root.hash, 0);
52 by_path.insert(root.filename(), 0);
53 by_name.insert(root.name.clone(), 0);
54 source_files.push(root.clone());
55
56 let externals = Externals::new(search_paths)?;
58
59 log::trace!("Externals:\n{externals}");
60
61 externals
63 .iter()
64 .try_for_each(|(name, path)| -> Result<(), ParseError> {
65 let source_file = SourceFile::load_with_name(path.clone(), name.clone())?;
66 let index = source_files.len();
67 by_hash.insert(source_file.hash, index);
68 by_path.insert(source_file.filename(), index);
69 by_name.insert(name.clone(), index);
70 source_files.push(source_file);
71 Ok(())
72 })?;
73
74 Ok(Self {
75 externals,
76 root,
77 source_files,
78 by_hash,
79 by_path,
80 by_name,
81 search_paths: search_paths
82 .iter()
83 .map(|path| {
84 path.as_ref()
85 .canonicalize()
86 .unwrap_or_else(|_| panic!("valid path: {}", path.as_ref().display()))
87 })
88 .collect(),
89 })
90 }
91
92 pub fn root(&self) -> Rc<SourceFile> {
94 self.root.clone()
95 }
96
97 pub fn insert(&mut self, source_file: Rc<SourceFile>) {
99 let hash = source_file.hash;
100 let path = source_file.filename();
101 let name = source_file.name.clone();
102
103 let index = if let Some(index) = self.by_path.get(&source_file.filename()).copied() {
105 self.by_hash.remove(&hash);
106 self.by_name.remove(&name);
107 self.by_path.remove(&path);
108 if self.root.filename() == path {
109 self.root = source_file.clone();
110 }
111 self.source_files[index] = source_file;
112
113 index
114 } else {
115 self.source_files.push(source_file.clone());
116 self.source_files.len() - 1
117 };
118
119 self.by_hash.insert(hash, index);
120 self.by_path.insert(path, index);
121 self.by_name.insert(name, index);
122 }
123
124 pub fn generate_name_from_path(
126 &self,
127 file_path: &std::path::Path,
128 ) -> ResolveResult<QualifiedName> {
129 if self.root.filename() == file_path {
131 return Ok(QualifiedName::from_id(self.root.id()));
132 }
133
134 let path = if let Some(path) = self
136 .search_paths
137 .iter()
138 .find_map(|path| file_path.strip_prefix(path).ok())
139 {
140 path.with_extension("")
141 }
142 else if let Some(root_dir) = self.root_dir() {
144 if let Ok(path) = file_path.strip_prefix(root_dir) {
145 path.with_extension("")
146 } else {
147 return Err(ResolveError::InvalidPath(file_path.to_path_buf()));
148 }
149 } else {
150 return Err(ResolveError::InvalidPath(file_path.to_path_buf()));
151 };
152
153 let path = if let Ok(path) = path.strip_prefix(".test") {
155 path.to_path_buf()
156 } else {
157 path
158 };
159
160 let path = if path
162 .iter()
163 .next_back()
164 .map(|s| s.to_string_lossy().to_string())
165 == Some("mod".into())
166 {
167 path.parent()
168 } else {
169 Some(path.as_path())
170 };
171
172 if let Some(path) = path {
174 Ok(path
175 .iter()
176 .map(|name| Identifier::no_ref(name.to_string_lossy().as_ref()))
177 .collect())
178 } else {
179 Err(ResolveError::InvalidPath(file_path.to_path_buf()))
180 }
181 }
182
183 pub fn get_by_src_ref(&self, referrer: &impl SrcReferrer) -> ResolveResult<Rc<SourceFile>> {
185 self.get_by_hash(referrer.src_ref().source_hash())
186 }
187
188 pub fn ref_str(&self, referrer: &impl SrcReferrer) -> String {
190 format!(
191 "{}:{}",
192 self.get_by_src_ref(referrer)
193 .expect("Source file not found")
194 .filename_as_str(),
195 referrer.src_ref(),
196 )
197 }
198
199 pub fn get_by_path(&self, path: &std::path::Path) -> ResolveResult<Rc<SourceFile>> {
201 let path = path.to_path_buf();
202 if let Some(index) = self.by_path.get(&path) {
203 Ok(self.source_files[*index].clone())
204 } else {
205 Err(ResolveError::FileNotFound(path))
206 }
207 }
208
209 pub fn get_name_by_hash(&self, hash: u64) -> ResolveResult<&QualifiedName> {
211 match self.get_by_hash(hash) {
212 Ok(file) => self.externals.get_name(&file.filename()),
213 Err(err) => Err(err),
214 }
215 }
216
217 pub fn get_code(&self, referrer: &impl SrcReferrer) -> ResolveResult<String> {
219 Ok(self
220 .get_by_src_ref(referrer)?
221 .get_code(&referrer.src_ref())
222 .to_string())
223 }
224
225 pub fn get_by_name(&self, name: &QualifiedName) -> ResolveResult<Rc<SourceFile>> {
227 if let Some(index) = self.by_name.get(name) {
228 Ok(self.source_files[*index].clone())
229 } else {
230 match self.externals.fetch_external(name) {
232 Ok((name, path)) => {
233 if self.get_by_path(&path).is_err() {
234 return Err(ResolveError::SymbolMustBeLoaded(name, path));
235 }
236 }
237 Err(ResolveError::ExternalSymbolNotFound(_)) => (),
238 Err(err) => return Err(err),
239 }
240 Err(ResolveError::SymbolNotFound(name.clone()))
241 }
242 }
243
244 fn name_from_index(&self, index: usize) -> Option<QualifiedName> {
245 self.by_name
246 .iter()
247 .find(|(_, i)| **i == index)
248 .map(|(name, _)| name.clone())
249 }
250
251 pub fn search_paths(&self) -> &Vec<std::path::PathBuf> {
253 &self.search_paths
254 }
255
256 fn root_dir(&self) -> Option<std::path::PathBuf> {
257 self.root.filename().parent().map(|p| p.to_path_buf())
258 }
259
260 pub fn load_mod_file(
262 &mut self,
263 parent_path: impl AsRef<std::path::Path>,
264 id: &Identifier,
265 ) -> ResolveResult<Rc<SourceFile>> {
266 log::trace!(
267 "loading file: {:?} [{id}]",
268 parent_path.as_ref().canonicalize().expect("invalid path")
269 );
270 let file_path = find_mod_file_by_id(parent_path, id)?;
271 let name = self.generate_name_from_path(&file_path)?;
272 let source_file = SourceFile::load_with_name(&file_path, name)?;
273 self.insert(source_file.clone());
274 Ok(source_file)
275 }
276
277 pub(super) fn update_file(
279 &mut self,
280 path: impl AsRef<std::path::Path>,
281 ) -> ResolveResult<ReplacedSourceFile> {
282 let path = path.as_ref().canonicalize()?.to_path_buf();
283 log::trace!("update_file: {path:?}");
284 if let Some(index) = self.by_path.get(&path).copied() {
285 let old = self.source_files[index].clone();
286 let name = old.name.clone();
287 let new = SourceFile::load_with_name(path, name)?;
288 self.insert(new.clone());
289 log::trace!("new sources:\n{self:?}");
290 Ok(ReplacedSourceFile { new, old })
291 } else {
292 Err(ResolveError::FileNotFound(path))
293 }
294 }
295}
296
297pub(super) struct ReplacedSourceFile {
298 pub(super) old: Rc<SourceFile>,
299 pub(super) new: Rc<SourceFile>,
300}
301
302pub trait GetSourceByHash {
304 fn get_by_hash(&self, hash: u64) -> ResolveResult<Rc<SourceFile>>;
306}
307
308impl GetSourceByHash for Sources {
309 fn get_by_hash(&self, hash: u64) -> ResolveResult<Rc<SourceFile>> {
311 if let Some(index) = self.by_hash.get(&hash) {
312 Ok(self.source_files[*index].clone())
313 } else if hash == 0 {
314 Err(ResolveError::NulHash)
315 } else {
316 Err(ResolveError::UnknownHash(hash))
317 }
318 }
319}
320
321impl std::fmt::Display for Sources {
322 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
323 for (index, source_file) in self.source_files.iter().enumerate() {
324 let filename = source_file.filename_as_str();
325 let name = self
326 .name_from_index(index)
327 .unwrap_or(QualifiedName::no_ref(vec![]));
328 let hash = source_file.hash;
329 writeln!(f, "[{index}] {name} {hash:#x} {filename}")?;
330 }
331 Ok(())
332 }
333}
334
335impl std::fmt::Debug for Sources {
336 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
337 for (index, source_file) in self.source_files.iter().enumerate() {
338 let filename = source_file.filename_as_str();
339 let name = self
340 .name_from_index(index)
341 .unwrap_or(QualifiedName::no_ref(vec![]));
342 let hash = source_file.hash;
343 writeln!(f, "[{index}] {name:?} {hash:#x} {filename}")?;
344 }
345 Ok(())
346 }
347}