145 lines
4.0 KiB
Python
145 lines
4.0 KiB
Python
|
|
"""公式解析异常定义。
|
|||
|
|
|
|||
|
|
提供清晰的错误信息,帮助用户快速定位公式解析问题。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import difflib
|
|||
|
|
from typing import List, Optional
|
|||
|
|
|
|||
|
|
|
|||
|
|
class FormulaParseError(Exception):
|
|||
|
|
"""公式解析错误基类。
|
|||
|
|
|
|||
|
|
Attributes:
|
|||
|
|
expr: 原始表达式字符串
|
|||
|
|
lineno: 错误所在行号(从1开始)
|
|||
|
|
col_offset: 错误所在列号(从0开始)
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
message: str,
|
|||
|
|
expr: Optional[str] = None,
|
|||
|
|
lineno: Optional[int] = None,
|
|||
|
|
col_offset: Optional[int] = None,
|
|||
|
|
):
|
|||
|
|
self.expr = expr
|
|||
|
|
self.lineno = lineno
|
|||
|
|
self.col_offset = col_offset
|
|||
|
|
|
|||
|
|
# 构建详细错误信息
|
|||
|
|
full_message = self._format_message(message)
|
|||
|
|
super().__init__(full_message)
|
|||
|
|
|
|||
|
|
def _format_message(self, message: str) -> str:
|
|||
|
|
"""格式化错误信息,包含位置指示器。"""
|
|||
|
|
lines = [f"FormulaParseError: {message}"]
|
|||
|
|
|
|||
|
|
if self.expr:
|
|||
|
|
lines.append(f" 公式: {self.expr}")
|
|||
|
|
|
|||
|
|
# 添加错误位置指示器
|
|||
|
|
if self.col_offset is not None and self.lineno is not None:
|
|||
|
|
# 计算错误行在表达式中的起始位置
|
|||
|
|
expr_lines = self.expr.split("\n")
|
|||
|
|
if 1 <= self.lineno <= len(expr_lines):
|
|||
|
|
error_line = expr_lines[self.lineno - 1]
|
|||
|
|
lines.append(f" {error_line}")
|
|||
|
|
# 添加指向错误位置的箭头
|
|||
|
|
pointer = " " * (self.col_offset + 7) + "^--- 此处出错"
|
|||
|
|
lines.append(pointer)
|
|||
|
|
|
|||
|
|
return "\n".join(lines)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UnknownFunctionError(FormulaParseError):
|
|||
|
|
"""未知函数错误。
|
|||
|
|
|
|||
|
|
当表达式中使用了未注册的函数时抛出。
|
|||
|
|
|
|||
|
|
Attributes:
|
|||
|
|
func_name: 未知的函数名
|
|||
|
|
available: 可用函数列表
|
|||
|
|
suggestions: 模糊匹配建议列表
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
func_name: str,
|
|||
|
|
available: List[str],
|
|||
|
|
expr: Optional[str] = None,
|
|||
|
|
lineno: Optional[int] = None,
|
|||
|
|
col_offset: Optional[int] = None,
|
|||
|
|
):
|
|||
|
|
self.func_name = func_name
|
|||
|
|
self.available = available
|
|||
|
|
|
|||
|
|
# 使用 difflib 获取模糊匹配建议
|
|||
|
|
self.suggestions = difflib.get_close_matches(
|
|||
|
|
func_name, available, n=3, cutoff=0.5
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 构建错误信息
|
|||
|
|
if self.suggestions:
|
|||
|
|
suggestion_str = ", ".join(f"'{s}'" for s in self.suggestions)
|
|||
|
|
hint_msg = f"你是不是想找: {suggestion_str}?"
|
|||
|
|
else:
|
|||
|
|
# 只显示前10个可用函数
|
|||
|
|
available_preview = ", ".join(available[:10])
|
|||
|
|
if len(available) > 10:
|
|||
|
|
available_preview += f", ... 等共 {len(available)} 个函数"
|
|||
|
|
hint_msg = f"可用函数预览: {available_preview}"
|
|||
|
|
|
|||
|
|
msg = f"未知函数 '{func_name}'。{hint_msg}"
|
|||
|
|
|
|||
|
|
super().__init__(
|
|||
|
|
message=msg,
|
|||
|
|
expr=expr,
|
|||
|
|
lineno=lineno,
|
|||
|
|
col_offset=col_offset,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class InvalidSyntaxError(FormulaParseError):
|
|||
|
|
"""语法错误。
|
|||
|
|
|
|||
|
|
当表达式语法不正确或不支持时抛出。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UnsupportedOperatorError(InvalidSyntaxError):
|
|||
|
|
"""不支持的运算符错误。
|
|||
|
|
|
|||
|
|
当使用了不支持的运算符时抛出(如位运算、矩阵运算等)。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
class EmptyExpressionError(FormulaParseError):
|
|||
|
|
"""空表达式错误。"""
|
|||
|
|
|
|||
|
|
def __init__(self):
|
|||
|
|
super().__init__("表达式不能为空或只包含空白字符")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RegistryError(Exception):
|
|||
|
|
"""注册表错误基类。"""
|
|||
|
|
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
class DuplicateFunctionError(RegistryError):
|
|||
|
|
"""函数重复注册错误。
|
|||
|
|
|
|||
|
|
当尝试注册已存在的函数且未设置 force=True 时抛出。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(self, func_name: str):
|
|||
|
|
self.func_name = func_name
|
|||
|
|
super().__init__(
|
|||
|
|
f"函数 '{func_name}' 已存在。使用 force=True 覆盖,或选择其他名称。"
|
|||
|
|
)
|