1use std::path::{Path, PathBuf};
2
3use crate::ast::TopLevel;
4use crate::lexer::Lexer;
5use crate::parser::Parser;
6
7pub fn parse_source(source: &str) -> Result<Vec<TopLevel>, String> {
8 let mut lexer = Lexer::new(source);
9 let tokens = lexer.tokenize().map_err(|e| e.to_string())?;
10 let mut parser = Parser::new(tokens);
11 parser.parse().map_err(|e| e.to_string())
12}
13
14pub fn find_module_file(name: &str, module_root: &str) -> Option<PathBuf> {
15 let root = Path::new(module_root);
16 let parts: Vec<&str> = name.split('.').filter(|s| !s.is_empty()).collect();
17 if parts.is_empty() {
18 return None;
19 }
20
21 let lower_rel = format!(
22 "{}.av",
23 parts
24 .iter()
25 .map(|p| p.to_lowercase())
26 .collect::<Vec<_>>()
27 .join("/")
28 );
29 let exact_rel = format!("{}.av", parts.join("/"));
30
31 let lower = root.join(&lower_rel);
32 if lower.exists() {
33 return Some(lower);
34 }
35
36 let exact = root.join(&exact_rel);
37 if exact.exists() {
38 return Some(exact);
39 }
40
41 None
42}
43
44pub fn canonicalize_path(path: &Path) -> PathBuf {
45 std::fs::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
46}