better typechecker errors
This commit is contained in:
parent
dde1da8656
commit
a62432bfbb
3 changed files with 108 additions and 5 deletions
55
src/main.rs
55
src/main.rs
|
|
@ -95,6 +95,56 @@ fn format_parse_error(source: &str, error: &suicmez::parser::ParseError) -> Stri
|
|||
format!("Parse error: {} (at byte {})", error.message, error.span.start)
|
||||
}
|
||||
|
||||
fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> String {
|
||||
// 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() {
|
||||
let line_start = current_pos;
|
||||
let line_end = current_pos + line.len();
|
||||
|
||||
// Check if the error span intersects with this line
|
||||
if error.span.start < line_end && error.span.end > line_start {
|
||||
let mut result = String::new();
|
||||
|
||||
// Print the error message
|
||||
result.push_str(&format!("Type error: {}\n", error.kind));
|
||||
|
||||
// Print the line number and content
|
||||
result.push_str(&format!("{} | {}\n", line_idx + 1, line));
|
||||
|
||||
// Calculate column positions within the line
|
||||
let line_start_col = error.span.start.saturating_sub(line_start);
|
||||
let line_end_col = (error.span.end - line_start).min(line.len());
|
||||
|
||||
// Print spaces and squiggly line for the span
|
||||
result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len())));
|
||||
for _ in 0..line_start_col {
|
||||
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
|
||||
}
|
||||
|
||||
// Fallback if we can't find the line
|
||||
format!("Type error: {} (at byte {})", error.kind, error.span.start)
|
||||
}
|
||||
|
||||
fn run_file(filename: &str) -> Result<(), String> {
|
||||
// Read the source file
|
||||
let source = fs::read_to_string(filename)
|
||||
|
|
@ -134,10 +184,7 @@ fn run_file(filename: &str) -> Result<(), String> {
|
|||
// Typecheck the AST
|
||||
let mut typechecker = TypeChecker::new();
|
||||
let typed_nodes = typechecker.typecheck_program(&lowered_nodes).map_err(|e| {
|
||||
format!(
|
||||
"Type error at {}:{}: {:?}",
|
||||
e.span.file, e.span.start, e.kind
|
||||
)
|
||||
format_type_error(&source, &e)
|
||||
})?;
|
||||
|
||||
println!(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// src/typechecker.rs
|
||||
use crate::ast::*;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Type {
|
||||
|
|
@ -86,6 +87,61 @@ pub enum TypeErrorKind {
|
|||
Other(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for TypeErrorKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TypeErrorKind::TypeMismatch(expected, actual) => {
|
||||
write!(f, "Type mismatch: expected {}, found {}", expected.to_string(), actual.to_string())
|
||||
}
|
||||
TypeErrorKind::UndefinedVariable(name) => {
|
||||
write!(f, "Undefined variable '{}'", name)
|
||||
}
|
||||
TypeErrorKind::UndefinedType(name) => {
|
||||
write!(f, "Undefined type '{}'", name)
|
||||
}
|
||||
TypeErrorKind::UndefinedFunction(name) => {
|
||||
write!(f, "Undefined function '{}'", name)
|
||||
}
|
||||
TypeErrorKind::UndefinedField(field, ty) => {
|
||||
write!(f, "Undefined field '{}' on type {}", field, ty.to_string())
|
||||
}
|
||||
TypeErrorKind::UndefinedVariant(enum_name, variant) => {
|
||||
write!(f, "Undefined variant '{}' in enum '{}'", variant, enum_name)
|
||||
}
|
||||
TypeErrorKind::ArityMismatch(expected, actual) => {
|
||||
write!(f, "Function expects {} arguments, but {} were provided", expected, actual)
|
||||
}
|
||||
TypeErrorKind::NotAFunction(ty) => {
|
||||
write!(f, "Expected a function, but found {}", ty.to_string())
|
||||
}
|
||||
TypeErrorKind::NotAnArray(ty) => {
|
||||
write!(f, "Expected an array, but found {}", ty.to_string())
|
||||
}
|
||||
TypeErrorKind::NotAStruct(ty) => {
|
||||
write!(f, "Expected a struct, but found {}", ty.to_string())
|
||||
}
|
||||
TypeErrorKind::NotAnEnum(ty) => {
|
||||
write!(f, "Expected an enum, but found {}", ty.to_string())
|
||||
}
|
||||
TypeErrorKind::InvalidCast(from, to) => {
|
||||
write!(f, "Invalid cast from {} to {}", from.to_string(), to.to_string())
|
||||
}
|
||||
TypeErrorKind::InvalidPattern(msg) => {
|
||||
write!(f, "Invalid pattern: {}", msg)
|
||||
}
|
||||
TypeErrorKind::MutableityError(msg) => {
|
||||
write!(f, "Mutability error: {}", msg)
|
||||
}
|
||||
TypeErrorKind::LinearityError(msg) => {
|
||||
write!(f, "Linearity error: {}", msg)
|
||||
}
|
||||
TypeErrorKind::Other(msg) => {
|
||||
write!(f, "{}", msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct VarInfo {
|
||||
ty: Type,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ fn main() -> int do
|
|||
else
|
||||
0
|
||||
|
||||
let i = 0;
|
||||
let mut i = 0;
|
||||
while i < 5
|
||||
i = i + 1;
|
||||
i
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue