Files
KG_generation/equipment_calculation/expressioncalculator.py
T
2025-08-22 18:13:09 +08:00

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()}")