use super::ast::SourceLocation;
#[derive(Debug, Clone, thiserror::Error)]
pub enum ParseError {
#[error("Unexpected token at {location}: expected {expected}, found {found}")]
UnexpectedToken {
expected: String,
found: String,
location: SourceLocation,
},
#[error("Unexpected end of file at {location}")]
UnexpectedEof {
location: SourceLocation,
},
#[error("Invalid directive '{directive}' at {location}")]
InvalidDirective {
directive: String,
location: SourceLocation,
},
#[error("Invalid instruction at {location}: {message}")]
InvalidInstruction {
message: String,
location: SourceLocation,
},
#[error("Invalid operand '{operand}' at {location}")]
InvalidOperand {
operand: String,
location: SourceLocation,
},
#[error("Invalid type '{ty}' at {location}")]
InvalidType {
ty: String,
location: SourceLocation,
},
#[error("Duplicate label '{label}' at {location}")]
DuplicateLabel {
label: String,
location: SourceLocation,
},
#[error("Undefined label '{label}' at {location}")]
UndefinedLabel {
label: String,
location: SourceLocation,
},
#[error("Lexer error at {location}: {message}")]
LexerError {
message: String,
location: SourceLocation,
},
}
impl ParseError {
pub fn location(&self) -> &SourceLocation {
match self {
ParseError::UnexpectedToken { location, .. } => location,
ParseError::UnexpectedEof { location, .. } => location,
ParseError::InvalidDirective { location, .. } => location,
ParseError::InvalidInstruction { location, .. } => location,
ParseError::InvalidOperand { location, .. } => location,
ParseError::InvalidType { location, .. } => location,
ParseError::DuplicateLabel { location, .. } => location,
ParseError::UndefinedLabel { location, .. } => location,
ParseError::LexerError { location, .. } => location,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = ParseError::UnexpectedToken {
expected: "identifier".into(),
found: "number".into(),
location: SourceLocation {
line: 10,
column: 5,
file: None,
},
};
let msg = format!("{}", err);
assert!(msg.contains("Unexpected token"));
assert!(msg.contains("10:5"));
}
#[test]
fn test_error_location() {
let loc = SourceLocation {
line: 42,
column: 10,
file: None,
};
let err = ParseError::InvalidDirective {
directive: ".foo".into(),
location: loc.clone(),
};
assert_eq!(err.location().line, 42);
assert_eq!(err.location().column, 10);
}
}