diff --git a/src/main.rs b/src/main.rs index 3dc3ed4..38918dc 100644 --- a/src/main.rs +++ b/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!( diff --git a/src/typechecker.rs b/src/typechecker.rs index 5c594ef..4ed2bbb 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -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, diff --git a/tests/control_flow.sui b/tests/control_flow.sui index af58199..391b8cd 100644 --- a/tests/control_flow.sui +++ b/tests/control_flow.sui @@ -5,7 +5,7 @@ fn main() -> int do else 0 - let i = 0; + let mut i = 0; while i < 5 i = i + 1; i