1pub mod prelude;
6
7use seq_map::SeqMap;
8use std::collections::HashSet;
9use std::path::PathBuf;
10use std::{env, io};
11use swamp_script_ast::prelude::*;
12use swamp_script_ast::Function;
13use swamp_script_parser::{AstParser, ParseError};
14use swamp_script_source_map::{FileId, SourceMap};
15use tracing::{debug, trace};
16
17pub struct ParseRoot {
18 pub base_path: PathBuf,
19}
20
21#[derive(Debug)]
22pub enum ParseRootError {
23 IoError(std::io::Error),
24 ParseError(ParseError),
25}
26
27impl From<std::io::Error> for ParseRootError {
28 fn from(err: std::io::Error) -> Self {
29 Self::IoError(err)
30 }
31}
32
33impl From<ParseError> for ParseRootError {
34 fn from(value: ParseError) -> Self {
35 Self::ParseError(value)
36 }
37}
38
39#[derive(Debug)]
40pub struct ParseModule {
41 pub ast_module: swamp_script_ast::Module,
42 pub file_id: FileId,
43}
44
45impl ParseModule {
46 pub fn declare_external_function(
48 &mut self,
49 parameters: Vec<Parameter>,
50 return_type: Option<Type>,
51 ) {
52 let fake_identifier = Node::default();
53
54 let signature = FunctionDeclaration {
55 name: fake_identifier.clone(),
56 params: parameters,
57 self_parameter: None,
58 return_type,
59 };
60 let external_signature = Function::External(signature);
61
62 self.ast_module.definitions.insert(
63 0, Definition::FunctionDef(external_signature),
65 );
66 }
67}
68
69#[derive(Debug)]
70pub struct RelativePath(pub String);
71
72impl ParseRoot {
73 pub fn new(base_path: PathBuf) -> Self {
74 Self { base_path }
75 }
76
77 pub fn parse(&self, contents: String, file_id: FileId) -> Result<ParseModule, ParseRootError> {
78 let parser = AstParser {};
79
80 let ast_program = parser.parse_module(&*contents)?;
81
82 Ok(ParseModule {
83 ast_module: ast_program,
84 file_id,
85 })
86 }
87}
88
89#[derive(Clone)]
90#[allow(unused)]
91pub struct ModuleInfo {
92 path: Vec<String>,
93 imports: Vec<Vec<String>>,
94 parsed: bool,
95 analyzed: bool,
96}
97
98pub struct DependencyParser {
99 pub import_scanned_modules: SeqMap<Vec<String>, ModuleInfo>,
100 already_parsed_modules: SeqMap<Vec<String>, ParseModule>,
101 pub already_resolved_modules: HashSet<Vec<String>>,
102}
103
104impl Default for DependencyParser {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110impl DependencyParser {
111 pub fn new() -> Self {
112 Self {
113 import_scanned_modules: SeqMap::new(),
114 already_parsed_modules: SeqMap::new(),
115 already_resolved_modules: HashSet::new(),
116 }
117 }
118
119 pub fn add_resolved_module(&mut self, module_path: Vec<String>) {
120 self.already_resolved_modules.insert(module_path);
121 }
122
123 pub fn add_ast_module(&mut self, module_path: Vec<String>, parsed_module: ParseModule) {
124 debug!(
125 "Adding ast module parsed outside of graph resolver {:?}",
126 module_path
127 );
128 self.already_parsed_modules
129 .insert(module_path, parsed_module)
130 .expect("can not add parsed module")
131 }
132}
133
134#[derive(Debug)]
135pub enum DependencyError {
136 CircularDependency(Vec<String>),
137 ParseRootError(ParseRootError),
138 IoError(io::Error),
139}
140
141impl From<ParseRootError> for DependencyError {
142 fn from(err: ParseRootError) -> Self {
143 Self::ParseRootError(err)
144 }
145}
146
147impl From<io::Error> for DependencyError {
148 fn from(value: io::Error) -> Self {
149 Self::IoError(value)
150 }
151}
152
153fn get_all_import_paths(parsed_module: &ParseModule) -> Vec<Vec<String>> {
154 let mut imports = vec![];
155
156 for def in parsed_module.ast_module.definitions() {
157 match def {
158 Definition::Use(import) => imports.push(import.assigned_path.clone()),
159 _ => continue,
160 }
161 }
162
163 imports
164}
165
166impl DependencyParser {
167 pub fn parse_all_dependant_modules(
168 &mut self,
169 parse_root: ParseRoot,
170 module_path: &[String],
171 source_map: &mut SourceMap,
172 ) -> Result<(), DependencyError> {
173 let mut to_parse = vec![module_path.to_vec()];
174
175 while let Some(path) = to_parse.pop() {
176 let module_path_vec = &path.clone();
177 if self.import_scanned_modules.contains_key(module_path_vec) {
178 continue;
179 }
180
181 let parsed_module_to_scan =
182 if let Some(parsed_module) = self.already_parsed_modules.get(module_path_vec) {
183 parsed_module
184 } else {
185 if self.already_resolved_modules.contains(module_path_vec) {
186 continue;
187 } else {
188 let (file_id, script) =
189 source_map.read_file_relative(module_path_vec.join("/").as_ref())?;
190 let parse_module = parse_root.parse(script, file_id)?;
191
192 self.already_parsed_modules
193 .insert(path.clone(), parse_module)
194 .expect("TODO: panic message");
195
196 self.already_parsed_modules
197 .get(&path.clone())
198 .expect("we just inserted it")
199 }
200 };
201
202 let imports = get_all_import_paths(parsed_module_to_scan);
203
204 let filtered_imports: Vec<Vec<String>> = imports
205 .into_iter()
206 .filter(|import| !self.already_resolved_modules.contains(import))
207 .collect();
208
209 self.import_scanned_modules
210 .insert(
211 path.clone(),
212 ModuleInfo {
213 path: path.clone(),
214 imports: filtered_imports.clone(),
215 parsed: false,
216 analyzed: false,
217 },
218 )
219 .expect("TODO: panic message");
220
221 to_parse.extend(filtered_imports.clone());
222 }
223 Ok(())
224 }
225
226 pub fn get_parsed_module(&self, path: &[String]) -> Option<&ParseModule> {
227 self.already_parsed_modules.get(&path.to_vec())
228 }
229
230 pub fn get_parsed_module_mut(&mut self, path: &[String]) -> Option<&mut ParseModule> {
231 self.already_parsed_modules.get_mut(&path.to_vec())
232 }
233
234 pub(crate) fn get_analysis_order(&self) -> Result<Vec<Vec<String>>, DependencyError> {
235 let mut order = Vec::new();
236 let mut visited = HashSet::new();
237 let mut temp_visited = HashSet::new();
238
239 fn visit(
240 graph: &DependencyParser,
241 path: &[String],
242 visited: &mut HashSet<Vec<String>>,
243 temp_visited: &mut HashSet<Vec<String>>,
244 order: &mut Vec<Vec<String>>,
245 ) -> Result<(), DependencyError> {
246 if temp_visited.contains(path) {
247 return Err(DependencyError::CircularDependency(Vec::from(path)));
248 }
249
250 if visited.contains(path) {
251 return Ok(());
252 }
253
254 temp_visited.insert(Vec::from(path));
255
256 if let Some(module) = graph.import_scanned_modules.get(&path.to_vec()) {
257 for import in &module.imports {
258 visit(graph, import, visited, temp_visited, order)?;
259 }
260 }
261
262 order.push(Vec::from(path));
263 visited.insert(Vec::from(path));
264
265 temp_visited.remove(path);
266
267 Ok(())
268 }
269
270 for path in self.import_scanned_modules.keys() {
271 if !visited.contains(path) {
272 visit(self, path, &mut visited, &mut temp_visited, &mut order)?;
273 }
274 }
275
276 Ok(order)
277 }
278}
279
280fn get_current_dir() -> Result<PathBuf, std::io::Error> {
281 let path = env::current_dir()?;
282
283 Ok(path)
286}
287
288#[derive(Debug)]
289pub enum DepLoaderError {
290 DependencyError(DependencyError),
291}
292
293impl From<DependencyError> for DepLoaderError {
294 fn from(e: DependencyError) -> Self {
295 Self::DependencyError(e)
296 }
297}
298
299pub fn parse_dependant_modules_and_resolve(
300 base_path: PathBuf,
301 module_path: Vec<String>,
302 dependency_parser: &mut DependencyParser,
303 source_map: &mut SourceMap,
304) -> Result<Vec<Vec<String>>, DepLoaderError> {
305 debug!(current_directory=?get_current_dir().expect("failed to get current directory"), "current directory");
306 let parse_root = ParseRoot::new(base_path);
307
308 dependency_parser.parse_all_dependant_modules(parse_root, &module_path, source_map)?;
309
310 let module_paths_in_order = dependency_parser.get_analysis_order()?;
311
312 Ok(module_paths_in_order)
313}
314
315pub fn create_parsed_modules(
316 script: &str,
317 source_map: &mut SourceMap,
318 root_path: PathBuf,
319) -> Result<DependencyParser, ParseError> {
320 let parser = AstParser {};
321
322 let file_id = source_map.add_manual_no_id(&*root_path, script);
323 let ast_module_result = parser.parse_module(script);
324 if let Err(some) = ast_module_result {
325 return Err(some);
326 }
327 let ast_module = ast_module_result.unwrap();
328 trace!("ast_module:\n{:?}", ast_module);
329
330 let parse_module = ParseModule {
331 ast_module,
332 file_id,
333 };
334
335 let mut graph = DependencyParser::new();
336 let root = vec!["test".to_string()];
337 graph.add_ast_module(root, parse_module);
338
339 debug!("root path is {root_path:?}");
340
341 Ok(graph)
342}