JavaScript自定义类似eval函数:连续减号操作bug求助
Hey there! It sounds like you're making great progress on building your custom expression evaluator in JavaScript—nice work getting most operations running smoothly! The issue with consecutive '-' operators is a super common pitfall when parsing expressions, since '-' can act as both binary subtraction (like 5 - 3) and unary negation (like -3 or 5--3). Let's break down how to fix this.
Why the Bug Happens
Most basic parsers treat '-' as a single type of operator, but in reality, its role depends on context:
- If '-' comes right after an operator, at the start of the string, or after an opening parenthesis
(, it's a unary operator (negates the value that follows). - Otherwise, it's a binary operator (subtracts the right operand from the left).
If your current code doesn't make this distinction, it'll see something like "5--3" as two back-to-back binary subtraction operators—with no operand in between—leading to errors or wrong calculations.
Step-by-Step Fix
Let's adjust your tokenization and parsing logic to handle unary negation properly:
1. Improve Tokenization to Identify Unary Minuses
First, update your tokenizer to mark '-' as a unary operator when it appears in the right context:
function tokenize(expr) { const tokens = []; let numBuffer = ''; for (let i = 0; i < expr.length; i++) { const char = expr[i]; // Build numbers (including decimals) if (/\d|\./.test(char)) { numBuffer += char; } else { // Push the completed number token if we have one if (numBuffer) { tokens.push({ type: 'number', value: parseFloat(numBuffer) }); numBuffer = ''; } // Check if this '-' is a unary operator if (char === '-' && ( i === 0 || tokens.length === 0 || ['+', '-', '*', '/', '('].includes(tokens.at(-1).type) )) { tokens.push({ type: 'unary-minus', value: '-' }); } else { // Treat as regular binary operator tokens.push({ type: 'operator', value: char }); } } } // Push the final number token if any if (numBuffer) { tokens.push({ type: 'number', value: parseFloat(numBuffer) }); } return tokens; }
2. Update Parsing to Handle Unary Negation
Next, modify your expression evaluation logic to give unary minus higher precedence than binary subtraction (just like native JavaScript does). A recursive descent parser works well here—add a function to handle unary operations first:
function evaluateUnary(tokens, currentIndex) { const token = tokens[currentIndex]; // Handle unary minus: negate the next expression if (token.type === 'unary-minus') { const { value: innerValue, newIndex } = evaluateUnary(tokens, currentIndex + 1); return { value: -innerValue, newIndex }; } // Handle numbers or parenthesized expressions if (token.type === 'number') { return { value: token.value, newIndex: currentIndex + 1 }; } if (token.value === '(') { const { value: innerValue, newIndex } = evaluateExpression(tokens, currentIndex + 1); // Skip the closing parenthesis return { value: innerValue, newIndex: newIndex + 1 }; } throw new Error(`Unexpected token at position ${currentIndex}`); } // Your existing evaluateExpression function would call evaluateUnary first, // then handle binary operators like +, -, *, /
Test Edge Cases
Make sure to validate these scenarios to ensure the fix works:
"5--3"should evaluate to8(equivalent to5 - (-3))"--10"should evaluate to10"-5 + 7"should evaluate to2"3 * -4"should evaluate to-12
内容的提问来源于stack exchange,提问作者Raj

