use std::{ cell::RefCell, collections::HashMap, path::{Path, PathBuf}, rc::Rc, sync::Arc, }; use rowan::ast::AstNode; use crate::{lst_parser::syntax_kind::SyntaxKind, SyntaxNode}; use super::{ error::Error, nodes::{self, Mod, ModBody, ModName, Root}, source_file::SourceFile, Loc, }; pub struct ModuleTree { modules: Rc>>, } #[derive(Clone)] struct ModuleParsingContext { files: Rc>>, errors: Rc>>, proj_dir: Rc, cur_path: Vec, cur_file: PathBuf, } impl ModuleParsingContext { fn new(entry_path: &Path) -> Self { let proj_dir = entry_path.parent().unwrap().to_owned(); Self { files: Rc::new(RefCell::new(HashMap::new())), errors: Rc::new(RefCell::new(Vec::new())), proj_dir: Rc::new(entry_path.to_owned()), cur_path: Vec::new(), cur_file: entry_path.to_owned(), } } fn entry_path(&self) -> PathBuf { self.proj_dir.to_path_buf() } fn make_mod_file_path(&self, mod_path: &[String], mod_name: &str) -> PathBuf { let mut entry_path = self.entry_path(); for el in mod_path { entry_path.push(format!("{el}/")); } entry_path.push(format!("{mod_name}.owo")); entry_path } fn push_cur_path(&mut self, name_to_add: String) { self.cur_path.push(name_to_add); } fn set_cur_file(&mut self, new: PathBuf) { self.cur_file = new; } fn push_errs(&self, errs: Vec) { self.errors.borrow_mut().extend(errs); } fn parse_child_modules( &self, node: &SyntaxNode, name: Option, ) -> HashMap> { let mut ctx = self.clone(); if let Some(name) = name { ctx.push_cur_path(name) } node.children() .filter_map(Mod::cast) .filter_map(|m| match Module::parse_mod(m, self.clone()) { Ok(module) => Some((module.name(), Rc::new(module))), Err(error) => { self.errors.borrow_mut().push(error); None } }) .collect::>>() } fn dissolve(self) -> (HashMap, Vec) { let Self { files, errors, .. } = self; (files.take(), errors.take()) } } impl ModuleTree { pub fn parse_from_main( path: &PathBuf, main_file: &SourceFile, ) -> (Self, HashMap, Vec) { let entry_path = path.parent().unwrap().to_owned(); let ctx = ModuleParsingContext::new(&entry_path); let modules = Rc::new(ctx.parse_child_modules(main_file.tree().syntax(), None)); let (files, errors) = ctx.dissolve(); (Self { modules }, files, errors) } pub fn print_tree(&self, lst: &Root) { let name = "main"; print_tree(&name, self.modules.clone(), 0) } } pub struct Module { path: Vec, name: String, kind: ModuleKind, children: Rc>>, parent: Option>, } impl Module { fn parse_mod(module: Mod, ctx: ModuleParsingContext) -> Result { let children = module .syntax() .children() // .map(|n| n.kind()) .collect::>(); if children.len() == 1 { let name = &children[0]; assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); return Self::parse_file_mod( name.text().to_string(), ctx.clone(), Loc::new(ctx.cur_file.clone(), &module), ); } else if children.len() == 2 { let name = &children[0]; assert_eq!(name.kind(), SyntaxKind::MODULE_NAME); let body = &children[1]; assert_eq!(body.kind(), SyntaxKind::MODULE_BODY); return Ok(Self::parse_inline_mod(module, ctx.clone())); } todo!() } fn parse_file_mod( name: String, mut ctx: ModuleParsingContext, decl: Loc, ) -> Result { ctx.set_cur_file(ctx.make_mod_file_path(&ctx.cur_path, &name)); let source = match std::fs::read_to_string(&ctx.cur_file) { Ok(f) => f, Err(e) => return Err(Error::FailedToOpenFileMod(ctx.cur_file, e)), }; let (source_file, file_errors) = SourceFile::parse_from(&ctx.cur_file, source); let old_path = ctx.cur_path.clone(); ctx.push_cur_path(name.clone()); ctx.push_errs(file_errors); let children = Rc::new(ctx.parse_child_modules(source_file.tree().syntax(), Some(name.clone()))); ctx.files .borrow_mut() .insert(ctx.cur_file.clone(), source_file); Ok(Self { path: old_path, name, kind: ModuleKind::File { declaration: decl, file_id: ctx.cur_file.clone(), }, children, parent: None, }) } fn parse_inline_mod(module: Mod, mut ctx: ModuleParsingContext) -> Self { let mut children = module.syntax().children().collect::>(); let body = ModBody::cast(children.pop().unwrap()).unwrap(); let name = ModName::cast(children.pop().unwrap()) .unwrap() .syntax() .text() .to_string(); let old_path = ctx.cur_path.clone(); ctx.push_cur_path(name.clone()); let children = Rc::new(ctx.parse_child_modules(body.syntax(), Some(name.clone()))); Self { name, kind: ModuleKind::Inline(Loc::new(ctx.cur_file.to_owned(), &module)), children, parent: None, path: old_path, } } pub fn name(&self) -> String { // self.name.to_node(lst.syntax()).syntax().text().to_string() self.name.clone() } } fn print_tree(name: &str, children: Rc>>, level: u32) { const INDENT_STR: &str = " "; for _ in 0..level { print!("{}", INDENT_STR); } print!("{name}\n"); // for (name, module) in children.iter() { // ; // } children .iter() .for_each(|(name, m)| print_tree(name, m.children.clone(), level + 1)) } enum ModuleKind { Inline(Loc), File { declaration: Loc, file_id: PathBuf, }, }