zouyu
2025-11-12 1bef995d9a6cc817bea0318a2a33ea013fe69deb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import Big from "big.js";
 
/**
 * 安全计算器(支持 + - * / 和括号)
 * @param {string} expr 数学表达式,如 "(0.1 + 0.2) * 3"
 * @returns {string} 计算结果字符串
 */
export function bigEval(expr) {
  console.log(expr)
  // 步骤1:词法分析
  const tokens = tokenize(expr);
  // 步骤2:转换为逆波兰表达式(后缀表达式)
  const rpn = shuntingYard(tokens);
  // 步骤3:执行计算
  return evaluateRPN(rpn);
}
 
// 词法分析器
function tokenize(expr) {
  const tokens = [];
  let numBuffer = "";
  let prevToken = null;
 
  const flushNumber = () => {
    if (numBuffer) {
      // 新增:验证数字格式(支持负数、小数)
      if (!/^-?(\d+(\.\d*)?|\.\d+)$/.test(numBuffer)) {
        throw new Error(`无效数字格式: ${numBuffer}`);
      }
      if (numBuffer.startsWith(".")) numBuffer = "0" + numBuffer;
      tokens.push({ type: "number", value: numBuffer });
      numBuffer = "";
    }
  };
 
  for (let i = 0; i < expr.length; i++) {
    const c = expr[i];
    if (/\d|\./.test(c)) {
      numBuffer += c;
    } else if ("+-*/()".includes(c)) {
      // 修改:增加 numBuffer 为空的判断,确保负号只能作为数字前缀(关键修复)
      if (c === "-" && numBuffer === "" && (
          !prevToken ||
          (prevToken.type === "operator" && prevToken.value !== ")")
      )) {
        numBuffer += c;
      } else {
        flushNumber();
        tokens.push({ type: "operator", value: c });
      }
      prevToken = tokens[tokens.length - 1];
    } else if (c !== " ") {
      throw new Error(`非法字符: ${c}`);
    }
  }
 
  flushNumber();
  return tokens;
}
 
// 调度场算法生成逆波兰表达式
function shuntingYard(tokens) {
  const output = [];
  const stack = [];
  const precedence = { "+": 1, "-": 1, "*": 2, "/": 2 };
 
  for (const token of tokens) {
    if (token.type === "number") {
      output.push(token.value);
    } else if (token.value === "(") {
      stack.push(token.value);
    } else if (token.value === ")") {
      while (stack.length && stack[stack.length - 1] !== "(") {
        output.push(stack.pop());
      }
      stack.pop(); // 弹出左括号
    } else {
      while (
        stack.length &&
        stack[stack.length - 1] !== "(" &&
        precedence[stack[stack.length - 1]] >= precedence[token.value]
      ) {
        output.push(stack.pop());
      }
      stack.push(token.value);
    }
  }
 
  while (stack.length) output.push(stack.pop());
  return output;
}
 
// 执行逆波兰表达式计算
function evaluateRPN(rpn) {
  const stack = [];
 
  for (const token of rpn) {
    if (/[-]?\d|\./.test(token)) {
      stack.push(new Big(token));
    } else {
      const b = stack.pop();
      const a = stack.pop();
 
      switch (token) {
        case "+": stack.push(a.plus(b)); break;
        case "-": stack.push(a.minus(b)); break;
        case "*": stack.push(a.times(b)); break;
        case "/":
          if (b.eq(0)) throw new Error("除数不能为零");
          stack.push(a.div(b));
          break;
        default: throw new Error(`未知运算符: ${token}`);
      }
    }
  }
 
  return stack.pop().toString();
}