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