better errrors

This commit is contained in:
Masashi 2025-12-17 20:48:59 +05:30
commit c4c2cccbcf
2 changed files with 77 additions and 54 deletions

View file

@ -27,6 +27,7 @@ pub enum ImportError {
file: String, file: String,
message: String, message: String,
span: (usize, usize), span: (usize, usize),
source: String,
}, },
CircularDependency { CircularDependency {
files: Vec<String>, files: Vec<String>,
@ -56,6 +57,7 @@ impl std::fmt::Display for ImportError {
file, file,
message, message,
span, span,
source: _,
} => { } => {
write!( write!(
f, f,
@ -170,12 +172,14 @@ impl ImportResolver {
fn parse_tokens( fn parse_tokens(
tokens: Vec<(Token, logos::Span)>, tokens: Vec<(Token, logos::Span)>,
filename: String, filename: String,
source: String,
) -> Result<Vec<ASTNode>, ImportError> { ) -> Result<Vec<ASTNode>, ImportError> {
let mut parser = Parser::new(filename.clone(), tokens); let mut parser = Parser::new(filename.clone(), tokens);
parser.parse().map_err(|e| ImportError::ParseError { parser.parse().map_err(|e| ImportError::ParseError {
file: filename, file: filename,
message: e.message.clone(), message: e.message.clone(),
span: (e.span.start, e.span.end), span: (e.span.start, e.span.end),
source,
}) })
} }
@ -199,7 +203,7 @@ impl ImportResolver {
// Parse the file // Parse the file
println!(" [parsing] {}", filename); println!(" [parsing] {}", filename);
let tokens = Self::lex_source(&source, filename)?; let tokens = Self::lex_source(&source, filename)?;
let nodes = Self::parse_tokens(tokens, filename.to_string())?; let nodes = Self::parse_tokens(tokens, filename.to_string(), source.clone())?;
// Cache the result // Cache the result
self.parse_cache.insert( self.parse_cache.insert(

View file

@ -64,63 +64,73 @@ fn run_test_suite() {
} }
} }
fn format_parse_error(source: &str, error: &suicmez::parser::ParseError) -> String { fn format_import_error(error: &suicmez::import_resolver::ImportError) -> String {
// Find the line containing the error match error {
let lines: Vec<&str> = source.lines().collect(); suicmez::import_resolver::ImportError::ParseError {
let mut current_pos = 0; file,
message,
span,
source,
} => {
// Find the line containing the error
let lines: Vec<&str> = source.lines().collect();
let mut current_pos = 0;
for (line_idx, line) in lines.iter().enumerate() { for (line_idx, line) in lines.iter().enumerate() {
let line_start = current_pos; let line_start = current_pos;
let line_end = current_pos + line.len(); let line_end = current_pos + line.len();
// Check if the error span intersects with this line // Check if the error span intersects with this line
if error.span.start < line_end && error.span.end > line_start { if span.0 < line_end && span.1 > line_start {
let mut result = String::new(); let mut result = String::new();
// Print the error message // Print the error message
result.push_str(&format!("Parse error: {}\n", error.message)); result.push_str(&format!("Parse error in {}: {}\n", file, message));
// Print the line number and content // Print the line number and content
result.push_str(&format!("{} | {}\n", line_idx + 1, line)); result.push_str(&format!("{} | {}\n", line_idx + 1, line));
// Calculate column positions within the line // Calculate column positions within the line
let line_start_col = error.span.start.saturating_sub(line_start); let line_start_col = span.0.saturating_sub(line_start);
let line_end_col = (error.span.end - line_start).min(line.len()); let line_end_col = (span.1 - line_start).min(line.len());
// Print spaces and squiggly line for the span // Print spaces and squiggly line for the span
result.push_str(&format!( result.push_str(&format!(
"{} | ", "{} | ",
" ".repeat((line_idx + 1).to_string().len()) " ".repeat((line_idx + 1).to_string().len())
)); ));
for _ in 0..line_start_col { for _ in 0..line_start_col {
result.push(' '); result.push(' ');
}
for _ in line_start_col..line_end_col {
result.push('~');
}
result.push('\n');
// Print caret at the start position
result.push_str(&format!(
"{} | ",
" ".repeat((line_idx + 1).to_string().len())
));
for _ in 0..line_start_col {
result.push(' ');
}
result.push('^');
return result;
}
current_pos = line_end + 1; // +1 for the newline character
} }
for _ in line_start_col..line_end_col {
result.push('~');
}
result.push('\n');
// Print caret at the start position // Fallback if we can't find the line
result.push_str(&format!( format!(
"{} | ", "Parse error in {}: {} (at byte {})",
" ".repeat((line_idx + 1).to_string().len()) file, message, span.0
)); )
for _ in 0..line_start_col {
result.push(' ');
}
result.push('^');
return result;
} }
_ => format!("Import error: {}", error),
current_pos = line_end + 1; // +1 for the newline character
} }
// Fallback if we can't find the line
format!(
"Parse error: {} (at byte {})",
error.message, error.span.start
)
} }
fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> String { fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> String {
@ -185,7 +195,16 @@ fn run_file(filename: &str, debug: bool) -> Result<(), String> {
let mut resolver = ImportResolver::new(); let mut resolver = ImportResolver::new();
let ast_nodes = resolver let ast_nodes = resolver
.resolve(filename) .resolve(filename)
.map_err(|e| format!("Import resolution error: {}", e))?; .map_err(|e| {
if matches!(
e,
suicmez::import_resolver::ImportError::ParseError { .. }
) {
format_import_error(&e)
} else {
format!("Import resolution error: {}", e)
}
})?;
println!( println!(
"Import resolution complete! {} total nodes loaded", "Import resolution complete! {} total nodes loaded",