use crate::lexer::tokens::Token;
use std::collections::VecDeque;
use thiserror::Error;
#[derive(Debug, Clone)]
pub struct ErrorContext {
pub file_path: Option<String>,
pub source_code: Option<String>,
pub call_stack: VecDeque<String>,
pub variable_scope: Vec<String>,
pub suggestions: Vec<String>,
}
impl ErrorContext {
pub fn new() -> Self {
Self {
file_path: None,
source_code: None,
call_stack: VecDeque::new(),
variable_scope: Vec::new(),
suggestions: Vec::new(),
}
}
pub fn with_file_path(mut self, file_path: String) -> Self {
self.file_path = Some(file_path);
self
}
pub fn with_source_code(mut self, source_code: String) -> Self {
self.source_code = Some(source_code);
self
}
pub fn add_call_stack(mut self, function: String) -> Self {
self.call_stack.push_front(function);
self
}
pub fn add_variable_scope(mut self, variable: String) -> Self {
self.variable_scope.push(variable);
self
}
pub fn add_suggestion(mut self, suggestion: String) -> Self {
self.suggestions.push(suggestion);
self
}
pub fn with_suggestions(mut self, suggestions: Vec<String>) -> Self {
self.suggestions = suggestions;
self
}
}
impl Default for ErrorContext {
fn default() -> Self {
Self::new()
}
}
#[derive(Error, Debug, Clone)]
pub enum ParserError {
#[error("Syntax Error: Unexpected token '{token}' at line {line}, column {column}. Expected one of: {expected}")]
UnexpectedToken {
token: String,
expected: String,
line: usize,
column: usize,
context: ErrorContext,
},
#[error("Syntax Error: Unexpected end of file. Expected: {expected}")]
UnexpectedEOF {
expected: String,
context: ErrorContext,
},
#[error("Attribute Error: Invalid attribute '{attribute}' at line {line}")]
InvalidAttribute {
attribute: String,
line: usize,
context: ErrorContext,
},
#[error("Syntax Error: Missing closing brace for function '{function}' at line {line}")]
MissingClosingBrace {
function: String,
line: usize,
context: ErrorContext,
},
#[error("Function Error: Invalid function call '{function}' at line {line}: {reason}")]
InvalidFunctionCall {
function: String,
reason: String,
line: usize,
context: ErrorContext,
},
#[error("Type Error: Type mismatch at line {line}: expected {expected}, got {got}")]
TypeMismatch {
expected: String,
got: String,
line: usize,
context: ErrorContext,
},
#[error("Semantic Error: {message} at line {line}")]
SemanticError {
message: String,
line: usize,
context: ErrorContext,
},
#[error("Recovery Error: Failed to recover from previous error")]
RecoveryError {
original_error: Box<ParserError>,
context: ErrorContext,
},
}
impl ParserError {
pub fn unexpected_token(token: &Token, expected: &[&str], line: usize, column: usize) -> Self {
let expected_str = expected.join(", ");
let token_str = format!("{:?}", token);
ParserError::UnexpectedToken {
token: token_str,
expected: expected_str,
line,
column,
context: ErrorContext::new(),
}
}
pub fn unexpected_eof(expected: &str) -> Self {
ParserError::UnexpectedEOF {
expected: expected.to_string(),
context: ErrorContext::new(),
}
}
pub fn invalid_attribute(attribute: &str, line: usize) -> Self {
ParserError::InvalidAttribute {
attribute: attribute.to_string(),
line,
context: ErrorContext::new(),
}
}
pub fn missing_closing_brace(function: &str, line: usize) -> Self {
ParserError::MissingClosingBrace {
function: function.to_string(),
line,
context: ErrorContext::new(),
}
}
pub fn invalid_function_call(function: &str, reason: &str, line: usize) -> Self {
ParserError::InvalidFunctionCall {
function: function.to_string(),
reason: reason.to_string(),
line,
context: ErrorContext::new(),
}
}
pub fn type_mismatch(expected: &str, got: &str, line: usize) -> Self {
ParserError::TypeMismatch {
expected: expected.to_string(),
got: got.to_string(),
line,
context: ErrorContext::new(),
}
}
pub fn semantic_error(message: &str, line: usize) -> Self {
ParserError::SemanticError {
message: message.to_string(),
line,
context: ErrorContext::new(),
}
}
pub fn with_context(mut self, context: ErrorContext) -> Self {
match &mut self {
ParserError::UnexpectedToken { context: ctx, .. } => *ctx = context,
ParserError::UnexpectedEOF { context: ctx, .. } => *ctx = context,
ParserError::InvalidAttribute { context: ctx, .. } => *ctx = context,
ParserError::MissingClosingBrace { context: ctx, .. } => *ctx = context,
ParserError::InvalidFunctionCall { context: ctx, .. } => *ctx = context,
ParserError::TypeMismatch { context: ctx, .. } => *ctx = context,
ParserError::SemanticError { context: ctx, .. } => *ctx = context,
ParserError::RecoveryError { context: ctx, .. } => *ctx = context,
}
self
}
pub fn line_number(&self) -> Option<usize> {
match self {
ParserError::UnexpectedToken { line, .. } => Some(*line),
ParserError::InvalidAttribute { line, .. } => Some(*line),
ParserError::MissingClosingBrace { line, .. } => Some(*line),
ParserError::InvalidFunctionCall { line, .. } => Some(*line),
ParserError::TypeMismatch { line, .. } => Some(*line),
ParserError::SemanticError { line, .. } => Some(*line),
_ => None,
}
}
pub fn column_number(&self) -> Option<usize> {
match self {
ParserError::UnexpectedToken { column, .. } => Some(*column),
_ => None,
}
}
pub fn context(&self) -> &ErrorContext {
match self {
ParserError::UnexpectedToken { context, .. } => context,
ParserError::UnexpectedEOF { context, .. } => context,
ParserError::InvalidAttribute { context, .. } => context,
ParserError::MissingClosingBrace { context, .. } => context,
ParserError::InvalidFunctionCall { context, .. } => context,
ParserError::TypeMismatch { context, .. } => context,
ParserError::SemanticError { context, .. } => context,
ParserError::RecoveryError { context, .. } => context,
}
}
pub fn generate_suggestions(&self) -> Vec<String> {
match self {
ParserError::UnexpectedToken { expected, .. } => vec![
format!("Check if you meant one of: {}", expected),
"Make sure all parentheses and braces are properly closed".to_string(),
"Verify that all keywords are spelled correctly".to_string(),
],
ParserError::MissingClosingBrace { .. } => vec![
"Add a closing brace '}' to match the opening brace".to_string(),
"Check for nested braces and ensure they're properly paired".to_string(),
],
ParserError::InvalidFunctionCall { .. } => vec![
"Check that the function name is correct".to_string(),
"Verify that all required arguments are provided".to_string(),
"Ensure function arguments match the expected types".to_string(),
],
ParserError::RecoveryError { original_error, .. } => {
original_error.generate_suggestions()
}
_ => vec![
"Review the syntax and ensure it follows the language specification".to_string(),
],
}
}
pub fn format_with_source(&self) -> String {
let mut output = String::new();
if let Some(path) = &self.context().file_path {
if !path.is_empty() {
output.push_str(&format!(" --> {}\n", path));
}
}
output.push_str(&format!("{}\n", self));
if let Some(line_num) = self.line_number() {
if let Some(source) = &self.context().source_code {
let lines: Vec<&str> = source.lines().collect();
if line_num > 0 && line_num <= lines.len() {
let line_content = lines[line_num - 1];
let prefix = format!(" --> Line {}: ", line_num);
output.push_str(&format!("{}{}\n", prefix, line_content));
if let Some(col) = self.column_number() {
let pad = prefix.len() + col.saturating_sub(1);
let pointer = " ".repeat(pad) + "^";
output.push_str(&format!(" {}\n", pointer));
}
}
}
}
if !self.context().call_stack.is_empty() {
output.push_str("\nCall Stack:\n");
for (i, call) in self.context().call_stack.iter().enumerate() {
output.push_str(&format!(" {}: {}\n", i, call));
}
}
if !self.context().suggestions.is_empty() {
output.push_str("\nSuggestions:\n");
for suggestion in &self.context().suggestions {
output.push_str(&format!(" • {}\n", suggestion));
}
}
output
}
}
pub trait ErrorRecovery {
fn recover_from_error(&mut self, error: &ParserError) -> Result<(), ParserError>;
fn skip_to_synchronization_point(&mut self) -> bool;
fn insert_missing_token(&mut self, token: Token) -> bool;
fn generate_suggestions(&self, error: &ParserError) -> Vec<String>;
}
#[derive(Debug, Clone, PartialEq)]
pub enum ErrorSeverity {
Warning,
Error,
Fatal,
}
pub trait ErrorReporter {
fn report_error(&mut self, error: ParserError);
fn report_warning(&mut self, message: String, line: usize);
fn has_errors(&self) -> bool;
fn get_errors(&self) -> Vec<ParserError>;
fn clear_errors(&mut self);
}
pub struct SimpleErrorReporter {
errors: Vec<ParserError>,
warnings: Vec<(String, usize)>,
}
impl SimpleErrorReporter {
pub fn new() -> Self {
Self {
errors: Vec::new(),
warnings: Vec::new(),
}
}
}
impl Default for SimpleErrorReporter {
fn default() -> Self {
Self::new()
}
}
impl ErrorReporter for SimpleErrorReporter {
fn report_error(&mut self, error: ParserError) {
self.errors.push(error);
}
fn report_warning(&mut self, message: String, line: usize) {
self.warnings.push((message, line));
}
fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
fn get_errors(&self) -> Vec<ParserError> {
self.errors.clone()
}
fn clear_errors(&mut self) {
self.errors.clear();
self.warnings.clear();
}
}
impl ErrorRecovery for crate::parser::Parser {
fn recover_from_error(&mut self, error: &ParserError) -> Result<(), ParserError> {
if self.skip_to_synchronization_point() {
Ok(())
} else {
Err(ParserError::RecoveryError {
original_error: Box::new(error.clone()),
context: ErrorContext::new(),
})
}
}
fn skip_to_synchronization_point(&mut self) -> bool {
let start = match self.recovery_skip_from.take() {
Some(p) => p,
None => return false,
};
self.skip_to_sync_point_from(start)
}
fn insert_missing_token(&mut self, _token: Token) -> bool {
true
}
fn generate_suggestions(&self, error: &ParserError) -> Vec<String> {
error.generate_suggestions()
}
}