use thiserror::Error;
#[derive(Debug, Error, Clone, PartialEq)]
pub enum ParseError {
#[error("语法错误:位置 {pos},{message}")]
Syntax { pos: usize, message: String },
#[error("未知函数:{name}")]
UnknownFunction { name: String },
#[error("参数数量错误:期望 {expected},实际 {actual}")]
ArgumentCount { expected: usize, actual: usize },
#[error("无效的数值格式:{value}")]
InvalidNumber { value: String },
#[error("无效的变量名:{name}")]
InvalidVariable { name: String },
#[error("括号不匹配:位置 {pos}")]
UnmatchedParenthesis { pos: usize },
#[error("意外的输入结束")]
UnexpectedEndOfInput,
#[error("意外的字符:位置 {pos},字符 '{ch}'")]
UnexpectedCharacter { pos: usize, ch: char },
#[error("空表达式")]
EmptyExpression,
}
impl ParseError {
pub fn syntax(pos: usize, message: impl Into<String>) -> Self {
ParseError::Syntax {
pos,
message: message.into(),
}
}
pub fn unknown_function(name: impl Into<String>) -> Self {
ParseError::UnknownFunction {
name: name.into(),
}
}
pub fn argument_count(expected: usize, actual: usize) -> Self {
ParseError::ArgumentCount { expected, actual }
}
pub fn invalid_number(value: impl Into<String>) -> Self {
ParseError::InvalidNumber {
value: value.into(),
}
}
pub fn invalid_variable(name: impl Into<String>) -> Self {
ParseError::InvalidVariable {
name: name.into(),
}
}
pub fn unmatched_parenthesis(pos: usize) -> Self {
ParseError::UnmatchedParenthesis { pos }
}
pub fn unexpected_character(pos: usize, ch: char) -> Self {
ParseError::UnexpectedCharacter { pos, ch }
}
pub fn position(&self) -> Option<usize> {
match self {
ParseError::Syntax { pos, .. } => Some(*pos),
ParseError::UnmatchedParenthesis { pos } => Some(*pos),
ParseError::UnexpectedCharacter { pos, .. } => Some(*pos),
_ => None,
}
}
pub fn user_friendly_message(&self) -> String {
match self {
ParseError::Syntax { pos, message } => {
format!("语法错误(位置 {}):{}", pos + 1, message)
}
ParseError::UnknownFunction { name } => {
format!("未知函数 '{}'。您是否想要使用:sin, cos, tan, exp, ln, sqrt?", name)
}
ParseError::ArgumentCount { expected, actual } => {
format!("函数参数数量错误:期望 {} 个参数,但提供了 {} 个", expected, actual)
}
ParseError::InvalidNumber { value } => {
format!("无效的数值格式 '{}'。请检查数值是否正确输入", value)
}
ParseError::InvalidVariable { name } => {
format!("无效的变量名 '{}'。变量名应以字母开头,只包含字母、数字和下划线", name)
}
ParseError::UnmatchedParenthesis { pos } => {
format!("括号不匹配(位置 {})。请检查是否有未闭合的括号", pos + 1)
}
ParseError::UnexpectedEndOfInput => {
"表达式不完整。请检查是否缺少操作数或运算符".to_string()
}
ParseError::UnexpectedCharacter { pos, ch } => {
format!("意外的字符 '{}' (位置 {})。请检查输入是否正确", ch, pos + 1)
}
ParseError::EmptyExpression => {
"表达式为空。请输入一个有效的数学表达式".to_string()
}
}
}
pub fn suggestions(&self) -> Vec<String> {
match self {
ParseError::UnknownFunction { name } => {
let mut suggestions = Vec::new();
let common_functions = [
"sin", "cos", "tan", "asin", "acos", "atan",
"sinh", "cosh", "tanh", "exp", "ln", "log",
"sqrt", "abs", "factorial", "gamma"
];
for func in &common_functions {
if levenshtein_distance(name, func) <= 2 {
suggestions.push(format!("您是否想要使用 '{}'?", func));
}
}
if suggestions.is_empty() {
suggestions.push("请检查函数名是否正确拼写".to_string());
suggestions.push("查看支持的函数列表:sin, cos, tan, exp, ln, sqrt 等".to_string());
}
suggestions
}
ParseError::InvalidNumber { value } => {
vec![
"请检查数值格式是否正确".to_string(),
"支持的格式:整数(123)、小数(3.14)、科学记数法(1.23e-4)".to_string(),
format!("如果 '{}' 是变量名,请确保它不以数字开头", value),
]
}
ParseError::UnmatchedParenthesis { .. } => {
vec![
"检查每个左括号 '(' 是否有对应的右括号 ')'".to_string(),
"检查每个右括号 ')' 是否有对应的左括号 '('".to_string(),
"使用文本编辑器的括号匹配功能来检查括号配对".to_string(),
]
}
ParseError::Syntax { .. } => {
vec![
"检查运算符是否正确使用".to_string(),
"确保每个运算符都有适当的操作数".to_string(),
"检查是否有连续的运算符(如 '++' 或 '--')".to_string(),
]
}
ParseError::ArgumentCount { expected, .. } => {
vec![
format!("该函数需要 {} 个参数", expected),
"检查函数调用中的逗号分隔符".to_string(),
"确保每个参数都是有效的表达式".to_string(),
]
}
_ => {
vec![
"请检查表达式的语法是否正确".to_string(),
"参考文档了解正确的语法格式".to_string(),
]
}
}
}
pub fn format_with_context(&self, input: &str) -> String {
let mut result = String::new();
result.push_str(&format!("错误:{}\n", self.user_friendly_message()));
if let Some(pos) = self.position() {
if pos < input.len() {
result.push_str(&format!("输入:{}\n", input));
result.push_str(&format!("位置:{}{}\n", " ".repeat(pos + 3), "^"));
}
}
let suggestions = self.suggestions();
if !suggestions.is_empty() {
result.push_str("\n建议:\n");
for (i, suggestion) in suggestions.iter().enumerate() {
result.push_str(&format!(" {}. {}\n", i + 1, suggestion));
}
}
result
}
}
fn levenshtein_distance(s1: &str, s2: &str) -> usize {
let len1 = s1.len();
let len2 = s2.len();
if len1 == 0 {
return len2;
}
if len2 == 0 {
return len1;
}
let mut matrix = vec![vec![0; len2 + 1]; len1 + 1];
for i in 0..=len1 {
matrix[i][0] = i;
}
for j in 0..=len2 {
matrix[0][j] = j;
}
let s1_chars: Vec<char> = s1.chars().collect();
let s2_chars: Vec<char> = s2.chars().collect();
for i in 1..=len1 {
for j in 1..=len2 {
let cost = if s1_chars[i - 1] == s2_chars[j - 1] { 0 } else { 1 };
matrix[i][j] = std::cmp::min(
std::cmp::min(
matrix[i - 1][j] + 1, matrix[i][j - 1] + 1 ),
matrix[i - 1][j - 1] + cost );
}
}
matrix[len1][len2]
}