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