1pub mod prelude;
6use dirs::home_dir;
7use seq_map::SeqMap;
8use source_map_cache::{FileId, SourceMap};
9use std::collections::HashSet;
10use std::io::ErrorKind;
11use std::path::{Path, PathBuf};
12use std::{env, io};
13use swamp_ast::Function;
14use swamp_ast::prelude::*;
15use swamp_parser::{AstParser, SpecificError};
16use time_dilation::ScopedTimer;
17
18pub struct ParseRoot;
19
20#[derive(Debug)]
21pub enum ParseRootError {
22 ParserError(ParserError),
23}
24
25#[derive(Debug)]
26pub struct ParsedAstModule {
27 pub ast_module: swamp_ast::Module,
28 pub file_id: FileId,
29}
30
31impl ParsedAstModule {
32 pub fn declare_external_function(
34 &mut self,
35 parameters: Vec<Parameter>,
36 return_type: Option<Type>,
37 ) {
38 let fake_identifier = Node::default();
39
40 let signature = FunctionDeclaration {
41 name: fake_identifier,
42 params: parameters,
43 self_parameter: None,
44 return_type,
45 generic_variables: vec![],
46 };
47 let external_signature = Function::External(signature);
48
49 self.ast_module.definitions.insert(
50 0, Definition::FunctionDef(external_signature),
52 );
53 }
54}
55
56#[derive(Debug)]
57pub struct RelativePath(pub String);
58
59#[derive(Debug)]
60pub struct ParserError {
61 pub node: Node,
62 pub specific: SpecificError,
63 pub file_id: FileId,
64}
65
66impl Default for ParseRoot {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl ParseRoot {
73 #[must_use]
74 pub const fn new() -> Self {
75 Self {}
76 }
77
78 pub fn parse(
79 &self,
80 contents: String,
81 file_id: FileId,
82 ) -> Result<ParsedAstModule, ParseRootError> {
83 let ast_program = AstParser.parse_module(&contents).map_err(|err| {
84 let new_err = ParserError {
85 node: Node { span: err.span },
86 specific: err.specific,
87 file_id,
88 };
89 ParseRootError::ParserError(new_err)
90 })?;
91
92 Ok(ParsedAstModule {
93 ast_module: ast_program,
94 file_id,
95 })
96 }
97}
98
99#[derive(Clone)]
100#[allow(unused)]
101pub struct ModuleInfo {
102 path: Vec<String>,
103 imports: Vec<Vec<String>>,
104 uses: Vec<Vec<String>>,
105 parsed: bool,
106 analyzed: bool,
107}
108
109pub struct DependencyParser {
110 pub import_scanned_modules: SeqMap<Vec<String>, ModuleInfo>,
111 already_parsed_modules: SeqMap<Vec<String>, ParsedAstModule>,
112 pub already_resolved_modules: HashSet<Vec<String>>,
113}
114
115impl Default for DependencyParser {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121impl DependencyParser {
122 pub fn new() -> Self {
123 Self {
124 import_scanned_modules: SeqMap::new(),
125 already_parsed_modules: SeqMap::new(),
126 already_resolved_modules: HashSet::new(),
127 }
128 }
129
130 pub fn add_resolved_module(&mut self, module_path: Vec<String>) {
131 self.already_resolved_modules.insert(module_path);
132 }
133
134 pub fn add_ast_module(&mut self, module_path: Vec<String>, parsed_module: ParsedAstModule) {
135 self.already_parsed_modules
136 .insert(module_path, parsed_module)
137 .expect("can not add parsed module")
138 }
139}
140
141#[derive(Debug)]
142pub enum DependencyError {
143 CircularDependency(Vec<String>),
144 ParseRootError(ParseRootError),
145 ReadFileError(io::Error),
146}
147
148impl From<ParseRootError> for DependencyError {
149 fn from(err: ParseRootError) -> Self {
150 Self::ParseRootError(err)
151 }
152}
153
154pub const LOCAL_ROOT_PACKAGE_PATH: &str = "crate";
155
156pub fn get_all_local_paths(
157 source_map: &SourceMap,
158 parsed_module: &ParsedAstModule,
159) -> (Vec<Vec<String>>, Vec<Vec<String>>) {
160 let mut imports = vec![];
161 let mut uses = vec![];
162
163 for def in parsed_module.ast_module.definitions() {
164 match def {
165 Definition::Mod(import) => {
166 let mut sections = Vec::new();
167 sections.push(LOCAL_ROOT_PACKAGE_PATH.to_string());
168 for section_node in &import.module_path.0 {
169 let import_path = source_map
170 .get_span_source(
171 parsed_module.file_id,
172 section_node.span.offset as usize,
173 section_node.span.length.into(),
174 )
175 .to_string();
176 sections.push(import_path);
177 }
178
179 imports.push(sections);
180 }
181
182 Definition::Use(import) => {
183 let mut sections = Vec::new();
184 for section_node in &import.module_path.0 {
185 let import_path = source_map
186 .get_span_source(
187 parsed_module.file_id,
188 section_node.span.offset as usize,
189 section_node.span.length.into(),
190 )
191 .to_string();
192 sections.push(import_path);
193 }
194 uses.push(sections);
195 }
196 _ => continue,
197 }
198 }
199
200 (imports, uses)
201}
202
203#[must_use]
204pub fn module_path_to_relative_swamp_file(module_path_vec: &[String]) -> PathBuf {
205 let mut path_buf = PathBuf::new();
206
207 let orig_len = module_path_vec.len();
208
209 let converted_path = if module_path_vec[0] == "crate" {
210 &module_path_vec[1..]
211 } else {
212 module_path_vec
213 };
214
215 path_buf.push(converted_path.join("/"));
216 if orig_len == 1 {
217 path_buf.push("lib"); }
219
220 path_buf.set_extension("swamp");
221
222 path_buf
223}
224
225#[must_use]
226pub fn module_path_to_relative_swamp_file_string(module_path_vec: &[String]) -> String {
227 module_path_to_relative_swamp_file(module_path_vec)
228 .to_str()
229 .unwrap()
230 .into()
231}
232
233#[must_use]
234pub fn mount_name_from_path(path: &[String]) -> &str {
235 if path[0] == "crate" {
236 "crate"
237 } else {
238 "registry"
239 }
240}
241
242pub fn parse_single_module(
245 source_map: &mut SourceMap,
246 module_path: &[String],
247) -> Result<ParsedAstModule, DependencyError> {
248 let debug = format!("parse module {module_path:?}");
249 let _parse_module_timer = ScopedTimer::new(&debug);
250
251 let mount_name = mount_name_from_path(module_path);
252
253 let (file_id, script) = source_map
254 .read_file_relative(
255 mount_name,
256 &module_path_to_relative_swamp_file_string(module_path),
257 )
258 .map_err(DependencyError::ReadFileError)?;
259
260 let parse_module = ParseRoot.parse(script, file_id)?;
261
262 Ok(parse_module)
263}
264
265impl DependencyParser {
266 pub fn parse_local_modules(
267 &mut self,
268 module_path: &[String],
269 source_map: &mut SourceMap,
270 ) -> Result<(), DependencyError> {
271 let mut to_parse = vec![module_path.to_vec()];
272
273 while let Some(path) = to_parse.pop() {
274 let module_path_vec = &path.clone();
275 if self.import_scanned_modules.contains_key(module_path_vec) {
276 continue;
277 }
278
279 let parsed_module_to_scan =
280 if let Some(parsed_module) = self.already_parsed_modules.get(module_path_vec) {
281 parsed_module
282 } else if self.already_resolved_modules.contains(module_path_vec) {
283 continue;
284 } else if path == ["core"] {
285 continue;
286 } else {
287 let parsed_ast_module = parse_single_module(source_map, &path)?;
288
289 self.already_parsed_modules
290 .insert(path.clone(), parsed_ast_module)
291 .expect("TODO: panic message");
292
293 self.already_parsed_modules
294 .get(&path.clone())
295 .expect("we just inserted it")
296 };
297
298 let (imports, uses) = get_all_local_paths(source_map, parsed_module_to_scan);
299 let filtered_imports: Vec<Vec<String>> = imports
300 .into_iter()
301 .filter(|import| !self.already_resolved_modules.contains(import))
302 .collect();
303
304 let filtered_uses: Vec<Vec<String>> = uses
305 .into_iter()
306 .filter(|import| !self.already_resolved_modules.contains(import))
307 .collect();
308
309 self.import_scanned_modules
310 .insert(
311 path.clone(),
312 ModuleInfo {
313 path: path.clone(),
314 imports: filtered_imports.clone(),
315 uses: filtered_uses.clone(),
316 parsed: false,
317 analyzed: false,
318 },
319 )
320 .expect("TODO: panic message");
321
322 to_parse.extend(filtered_imports.clone());
323
324 to_parse.extend(filtered_uses.clone());
325 }
326 Ok(())
327 }
328
329 #[must_use]
330 pub fn get_parsed_module(&self, path: &[String]) -> Option<&ParsedAstModule> {
331 self.already_parsed_modules.get(&path.to_vec())
332 }
333
334 pub fn get_parsed_module_mut(&mut self, path: &[String]) -> Option<&mut ParsedAstModule> {
335 self.already_parsed_modules.get_mut(&path.to_vec())
336 }
337
338 pub(crate) fn get_analysis_order(&self) -> Result<Vec<Vec<String>>, DependencyError> {
339 let mut order = Vec::new();
340 let mut visited = HashSet::new();
341 let mut temp_visited = HashSet::new();
342
343 fn visit(
344 graph: &DependencyParser,
345 path: &[String],
346 visited: &mut HashSet<Vec<String>>,
347 temp_visited: &mut HashSet<Vec<String>>,
348 order: &mut Vec<Vec<String>>,
349 ) -> Result<(), DependencyError> {
350 if temp_visited.contains(path) {
351 return Err(DependencyError::CircularDependency(Vec::from(path)));
352 }
353
354 if visited.contains(path) {
355 return Ok(());
356 }
357
358 temp_visited.insert(Vec::from(path));
359
360 if let Some(module) = graph.import_scanned_modules.get(&path.to_vec()) {
361 for import in &module.uses {
362 visit(graph, import, visited, temp_visited, order)?;
363 }
364 for import in &module.imports {
365 visit(graph, import, visited, temp_visited, order)?;
366 }
367 }
368
369 order.push(Vec::from(path));
370 visited.insert(Vec::from(path));
371
372 temp_visited.remove(path);
373
374 Ok(())
375 }
376
377 for path in self.import_scanned_modules.keys() {
378 if !visited.contains(path) {
379 visit(self, path, &mut visited, &mut temp_visited, &mut order)?;
380 }
381 }
382
383 Ok(order)
384 }
385}
386
387#[derive(Debug)]
388pub enum DepLoaderError {
389 DependencyError(DependencyError),
390}
391
392impl From<DependencyError> for DepLoaderError {
393 fn from(e: DependencyError) -> Self {
394 Self::DependencyError(e)
395 }
396}
397
398pub fn os_home_relative_path(project_name: &str) -> io::Result<PathBuf> {
401 home_dir().map_or_else(
402 || {
403 Err(io::Error::new(
404 io::ErrorKind::Other,
405 "Could not determine home directory",
406 ))
407 },
408 |home_path| {
409 let mut path = home_path;
410 path.push(format!(".{project_name}"));
411 Ok(path)
412 },
413 )
414}
415
416pub fn path_from_environment_variable() -> io::Result<PathBuf> {
417 env::var("SWAMP_HOME").as_ref().map_or_else(
418 |_| Err(io::Error::new(ErrorKind::InvalidData, "missing SWAMP_HOME")),
419 |string_value| Ok(Path::new(string_value).to_path_buf()),
420 )
421}
422pub fn swamp_home() -> io::Result<PathBuf> {
423 path_from_environment_variable().map_or_else(|_| os_home_relative_path("swamp"), Ok)
424}
425
426pub fn swamp_registry_path() -> io::Result<PathBuf> {
429 let mut swamp_home = swamp_home()?;
430 swamp_home.push("packages");
431 Ok(swamp_home)
432}
433
434pub fn parse_local_modules_and_get_order(
435 module_path: &[String],
436 dependency_parser: &mut DependencyParser,
437 source_map: &mut SourceMap,
438) -> Result<Vec<Vec<String>>, DepLoaderError> {
439 dependency_parser.parse_local_modules(&module_path, source_map)?;
440
441 let module_paths_in_order = dependency_parser.get_analysis_order()?;
442
443 Ok(module_paths_in_order)
444}