Add Elm-style error diagnostics
Implement beautiful, informative error messages inspired by Elm: - Rich diagnostic rendering with source code snippets - Colored output with proper underlines showing error locations - Categorized error titles (Type Mismatch, Unknown Name, etc.) - Contextual hints and suggestions for common errors - Support for type errors, runtime errors, and parse errors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
//! Parser for the Lux language
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::diagnostics::{Diagnostic, Severity};
|
||||
use crate::lexer::{LexError, Lexer, Token, TokenKind};
|
||||
use std::fmt;
|
||||
|
||||
@@ -21,6 +22,63 @@ impl fmt::Display for ParseError {
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
/// Convert to a rich diagnostic for Elm-style error display
|
||||
pub fn to_diagnostic(&self) -> Diagnostic {
|
||||
let (title, hints) = categorize_parse_error(&self.message);
|
||||
|
||||
Diagnostic {
|
||||
severity: Severity::Error,
|
||||
title,
|
||||
message: self.message.clone(),
|
||||
span: self.span,
|
||||
hints,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Categorize parse errors to provide better titles and hints
|
||||
fn categorize_parse_error(message: &str) -> (String, Vec<String>) {
|
||||
let message_lower = message.to_lowercase();
|
||||
|
||||
if message_lower.contains("unexpected") && message_lower.contains("expected") {
|
||||
(
|
||||
"Unexpected Token".to_string(),
|
||||
vec!["Check for missing or misplaced punctuation.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("expected") && message_lower.contains("expression") {
|
||||
(
|
||||
"Missing Expression".to_string(),
|
||||
vec!["An expression was expected here.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("expected") && message_lower.contains(":") {
|
||||
(
|
||||
"Missing Type Annotation".to_string(),
|
||||
vec!["A type annotation is required here.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("unclosed") || message_lower.contains("unterminated") {
|
||||
(
|
||||
"Unclosed Delimiter".to_string(),
|
||||
vec![
|
||||
"Check for matching opening and closing brackets.".to_string(),
|
||||
"Make sure all strings are properly closed with quotes.".to_string(),
|
||||
],
|
||||
)
|
||||
} else if message_lower.contains("invalid") {
|
||||
(
|
||||
"Invalid Syntax".to_string(),
|
||||
vec!["Check the syntax of this construct.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("identifier") {
|
||||
(
|
||||
"Invalid Identifier".to_string(),
|
||||
vec!["Identifiers must start with a letter and contain only letters, numbers, and underscores.".to_string()],
|
||||
)
|
||||
} else {
|
||||
("Parse Error".to_string(), vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LexError> for ParseError {
|
||||
fn from(err: LexError) -> Self {
|
||||
ParseError {
|
||||
|
||||
Reference in New Issue
Block a user