259 lines
8.9 KiB
Python
259 lines
8.9 KiB
Python
import re
|
|
from collections import OrderedDict
|
|
|
|
|
|
class ExpressionCalculator:
|
|
def __init__(self):
|
|
self.expression = None
|
|
self.tokens = None
|
|
self.variables = None
|
|
self.ast = None
|
|
self.last_error = None
|
|
|
|
def _tokenize(self, expr):
|
|
"""将表达式字符串分解为标记列表"""
|
|
token_spec = [
|
|
("NUMBER", r"\d+(\.\d*)?"), # 整数或小数
|
|
("VARIABLE", r"@?[a-zA-Z_\u4e00-\u9fa5][\w.@\u4e00-\u9fa5]+"), # 支持带点的复合变量
|
|
("OPERATOR", r"[+\-*/()]"), # 运算符
|
|
("SKIP", r"\s+"), # 跳过空格
|
|
("MISMATCH", r"."), # 其他字符
|
|
]
|
|
|
|
tokens = []
|
|
pos = 0
|
|
while pos < len(expr):
|
|
match = None
|
|
for token_name, pattern in token_spec:
|
|
regex = re.compile(pattern)
|
|
match = regex.match(expr, pos)
|
|
if match:
|
|
value = match.group(0)
|
|
if token_name == "NUMBER":
|
|
tokens.append(("NUMBER", float(value)))
|
|
elif token_name == "VARIABLE":
|
|
tokens.append(("VARIABLE", value))
|
|
elif token_name == "OPERATOR":
|
|
tokens.append(("OPERATOR", value))
|
|
elif token_name == "SKIP":
|
|
pass # 跳过空格
|
|
else:
|
|
raise ValueError(f"非法字符: {value}")
|
|
pos = match.end()
|
|
break
|
|
if not match:
|
|
raise ValueError(f"无法解析的字符: {expr[pos]}")
|
|
return tokens
|
|
|
|
def _extract_variables(self):
|
|
"""提取表达式中的所有变量"""
|
|
variables = OrderedDict()
|
|
for token_type, value in self.tokens:
|
|
if token_type == "VARIABLE" and value not in variables:
|
|
variables[value] = None
|
|
return list(variables.keys())
|
|
|
|
def parse_expression(self, expression):
|
|
"""解析表达式并返回是否成功
|
|
|
|
Returns:
|
|
bool: 解析成功返回True,失败返回False
|
|
"""
|
|
|
|
self.expression = expression
|
|
self.last_error = None
|
|
try:
|
|
self.tokens = self._tokenize(expression)
|
|
self.variables = self._extract_variables()
|
|
self.ast = self._parse_expression()
|
|
return True
|
|
except Exception as e:
|
|
self.last_error = str(e)
|
|
return False
|
|
|
|
def get_last_error(self):
|
|
"""获取最后发生的错误信息"""
|
|
return self.last_error
|
|
|
|
def _parse_expression(self):
|
|
"""解析表达式并构建抽象语法树(AST)"""
|
|
self.current_token = 0
|
|
return self._parse_exp()
|
|
|
|
def _parse_exp(self):
|
|
"""解析加减表达式: exp -> term { ('+'|'-') term }"""
|
|
node = self._parse_term()
|
|
while self.current_token < len(self.tokens):
|
|
token_type, op = self.tokens[self.current_token]
|
|
if token_type == "OPERATOR" and op in ("+", "-"):
|
|
self.current_token += 1
|
|
right = self._parse_term()
|
|
node = ("BINOP", op, node, right)
|
|
else:
|
|
break
|
|
return node
|
|
|
|
def _parse_term(self):
|
|
"""解析乘除表达式: term -> factor { ('*'|'/') factor }"""
|
|
node = self._parse_factor()
|
|
while self.current_token < len(self.tokens):
|
|
token_type, op = self.tokens[self.current_token]
|
|
if token_type == "OPERATOR" and op in ("*", "/"):
|
|
self.current_token += 1
|
|
right = self._parse_factor()
|
|
node = ("BINOP", op, node, right)
|
|
else:
|
|
break
|
|
return node
|
|
|
|
def _parse_factor(self):
|
|
"""解析基本因子: factor -> NUMBER | VARIABLE | '(' exp ')' | ('+'|'-') factor"""
|
|
if self.current_token >= len(self.tokens):
|
|
raise ValueError("表达式不完整")
|
|
|
|
token_type, value = self.tokens[self.current_token]
|
|
|
|
# 处理括号表达式
|
|
if token_type == "OPERATOR" and value == "(":
|
|
self.current_token += 1
|
|
node = self._parse_exp()
|
|
if self.current_token >= len(self.tokens) or self.tokens[self.current_token][1] != ")":
|
|
raise ValueError("缺少右括号")
|
|
self.current_token += 1
|
|
return node
|
|
|
|
# 处理一元运算符
|
|
if token_type == "OPERATOR" and value in ("+", "-"):
|
|
self.current_token += 1
|
|
node = self.parse_factor()
|
|
return ("UNARYOP", value, node)
|
|
|
|
# 处理变量
|
|
if token_type == "VARIABLE":
|
|
self.current_token += 1
|
|
return ("VARIABLE", value)
|
|
|
|
# 处理数字
|
|
if token_type == "NUMBER":
|
|
self.current_token += 1
|
|
return ("NUMBER", value)
|
|
|
|
raise ValueError(f"语法错误: {value}")
|
|
|
|
def get_last_error(self):
|
|
"""获取最后一次解析错误信息
|
|
Returns:
|
|
str: 错误信息,如果没有错误则返回None
|
|
"""
|
|
return self.last_error
|
|
|
|
def evaluate(self, variables):
|
|
"""计算表达式的值
|
|
|
|
Returns:
|
|
tuple: (bool, float) - 第一个元素表示计算是否成功,第二个元素是计算结果(失败时为None)
|
|
"""
|
|
# 确保表达式已解析
|
|
if self.ast is None:
|
|
self.last_error = "表达式尚未解析"
|
|
return False, None
|
|
|
|
# 检查变量是否齐全
|
|
missing = [var for var in self.variables if var not in variables]
|
|
if missing:
|
|
self.last_error = f"缺少变量值: {', '.join(missing)}"
|
|
return False, None
|
|
|
|
# 执行计算
|
|
try:
|
|
result = self._evaluate_node(self.ast, variables)
|
|
self.last_error = None
|
|
return True, result
|
|
except Exception as e:
|
|
self.last_error = str(e)
|
|
return False, None
|
|
|
|
def _evaluate_node(self, node, variables):
|
|
"""递归计算AST节点的值"""
|
|
node_type = node[0]
|
|
|
|
if node_type == "NUMBER":
|
|
return node[1]
|
|
|
|
if node_type == "VARIABLE":
|
|
return variables[node[1]]
|
|
|
|
if node_type == "UNARYOP":
|
|
op, child = node[1], node[2]
|
|
value = self._evaluate_node(child, variables)
|
|
return value if op == "+" else -value
|
|
|
|
if node_type == "BINOP":
|
|
_, op, left, right = node
|
|
left_val = self._evaluate_node(left, variables)
|
|
right_val = self._evaluate_node(right, variables)
|
|
|
|
if op == "+":
|
|
return left_val + right_val
|
|
if op == "-":
|
|
return left_val - right_val
|
|
if op == "*":
|
|
return left_val * right_val
|
|
if op == "/":
|
|
if right_val == 0:
|
|
raise ValueError("除数不能为零")
|
|
return left_val / right_val
|
|
|
|
raise ValueError("未知节点类型")
|
|
|
|
def __str__(self):
|
|
"""返回表达式的字符串表示"""
|
|
return self.expression
|
|
|
|
|
|
# 测试代码
|
|
if __name__ == "__main__":
|
|
# 示例表达式
|
|
expr_str = "工程量.数量*工程量.人工费*工程量.人工系数*工程量.定额系数"
|
|
|
|
# 创建计算器实例
|
|
calculator = ExpressionCalculator()
|
|
parse_success = calculator.parse_expression(expr_str)
|
|
if not parse_success:
|
|
raise ValueError(f"解析表达式失败: {calculator.get_last_error()}")
|
|
success, result = calculator.evaluate()
|
|
if not success:
|
|
raise ValueError(f"计算表达式失败: {calculator.get_last_error()}")
|
|
|
|
# 获取变量列表
|
|
variables = calculator.variables
|
|
print("表达式中的变量:", variables)
|
|
|
|
# 为变量赋值
|
|
values = {"工程量.数量": 10, "工程量.人工费": 100, "工程量.人工系数": 1.5, "工程量.定额系数": 0.8}
|
|
|
|
# 计算结果
|
|
success, result = calculator.evaluate(values)
|
|
if success:
|
|
print(f"计算结果: {result}")
|
|
else:
|
|
print(f"计算失败: {calculator.get_last_error()}")
|
|
|
|
# 测试带括号和混合运算的表达式
|
|
complex_expr = "(工程量.数量 + 5) * (工程量.人工费 - 20) / 2"
|
|
complex_calc = ExpressionCalculator()
|
|
parse_success = complex_calc.parse_expression(complex_expr)
|
|
if not parse_success:
|
|
raise ValueError(f"解析复杂表达式失败: {complex_calc.get_last_error()}")
|
|
success, result = complex_calc.evaluate()
|
|
if not success:
|
|
raise ValueError(f"计算复杂表达式失败: {complex_calc.get_last_error()}")
|
|
print("\n复杂表达式:", complex_expr)
|
|
print("变量列表:", complex_calc.variables)
|
|
|
|
success, complex_result = complex_calc.evaluate({"工程量.数量": 15, "工程量.人工费": 120})
|
|
if success:
|
|
print(f"计算结果: {complex_result}")
|
|
else:
|
|
print(f"计算失败: {complex_calc.get_last_error()}")
|