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