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