use thiserror::Error;
use crate::lexer::{LexerError, Token};
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum LpParseError {
#[error("Invalid number format '{value}' at position {position}")]
InvalidNumber {
value: String,
position: usize,
},
#[error("Missing required section: {section}")]
MissingSection {
section: String,
},
#[error("Invalid bounds for variable '{variable}': {details}")]
InvalidBounds {
variable: String,
details: String,
},
#[error("Validation error: {message}")]
ValidationError {
message: String,
},
#[error("Parse error at position {position}: {message}")]
ParseError {
position: usize,
message: String,
},
#[error("File I/O error: {message}")]
IoError {
message: String,
},
}
impl LpParseError {
pub fn invalid_number(value: impl Into<String>, position: usize) -> Self {
Self::InvalidNumber { value: value.into(), position }
}
pub fn missing_section(section: impl Into<String>) -> Self {
Self::MissingSection { section: section.into() }
}
pub fn invalid_bounds(variable: impl Into<String>, details: impl Into<String>) -> Self {
Self::InvalidBounds { variable: variable.into(), details: details.into() }
}
pub fn validation_error(message: impl Into<String>) -> Self {
Self::ValidationError { message: message.into() }
}
pub fn parse_error(position: usize, message: impl Into<String>) -> Self {
Self::ParseError { position, message: message.into() }
}
pub fn io_error(message: impl Into<String>) -> Self {
Self::IoError { message: message.into() }
}
}
impl<'input> From<lalrpop_util::ParseError<usize, Token<'input>, LexerError>> for LpParseError {
fn from(err: lalrpop_util::ParseError<usize, Token<'input>, LexerError>) -> Self {
match err {
lalrpop_util::ParseError::InvalidToken { location } => Self::parse_error(location, "Invalid token"),
lalrpop_util::ParseError::UnrecognizedEof { location, expected } => {
let expected_str = if expected.is_empty() { String::new() } else { format!(", expected one of: {}", expected.join(", ")) };
Self::parse_error(location, format!("Unexpected end of input{expected_str}"))
}
lalrpop_util::ParseError::UnrecognizedToken { token: (start, tok, _), expected } => {
let expected_str = if expected.is_empty() { String::new() } else { format!(", expected one of: {}", expected.join(", ")) };
Self::parse_error(start, format!("Unexpected token {tok:?}{expected_str}"))
}
lalrpop_util::ParseError::ExtraToken { token: (start, tok, _) } => Self::parse_error(start, format!("Extra token {tok:?}")),
lalrpop_util::ParseError::User { error } => {
let message = error.message.clone().unwrap_or_else(|| "Lexer error".to_string());
Self::parse_error(error.position, message)
}
}
}
}
pub type LpResult<T> = Result<T, LpParseError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let err = LpParseError::invalid_bounds("x", "lower exceeds upper");
assert_eq!(err.to_string(), "Invalid bounds for variable 'x': lower exceeds upper");
}
}