Skip to content

Commit 7370e75

Browse files
committed
Update negative parsing
1 parent 2f92642 commit 7370e75

File tree

1 file changed

+13
-11
lines changed

1 file changed

+13
-11
lines changed

src/parser.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,19 +163,15 @@ function findBinaryFunction(tokens: ExprElement[], fn: string) {
163163
}
164164
}
165165

166-
// When all minuses have been parsed as functions (i.e. an `ExprFunction` with a single argument), we find binary
167-
// functions by looking for a minus function preceded by a number or variable.
168-
function findBinaryLeadingMinus(tokens: ExprElement[]) {
166+
// Some minuses have been parsed as functions (i.e. an `ExprFunction` with a single argument).
167+
// If they are preceded by something, we need to treat the expression as a unary minus.
168+
function findBinaryMinusFunctions(tokens: ExprElement[]) {
169169
for (let i = 1; i < tokens.length; i++) {
170170
const token = tokens[i];
171171
if (token instanceof ExprFunction && token.fn === '−') {
172172
const a = tokens[i - 1];
173173
const b = token.args[0];
174174

175-
if (a instanceof ExprOperator) {
176-
throw ExprError.consecutiveOperators(a.o, token.fn);
177-
}
178-
179175
const args = [removeBrackets(a), removeBrackets(b)];
180176
tokens.splice(i - 1, 2, new ExprFunction('−', args));
181177
i -= 1;
@@ -334,24 +330,30 @@ export function collapseTerm(tokens: ExprElement[]): ExprElement {
334330
}
335331
}
336332

337-
// Replace all operator minuses with functions. Each function takes only one argument, the next token in sequence.
338-
for (let i = 0; i < tokens.length - 1; i++) {
333+
// Replace all operator minuses, not preceded by numbers, with functions. Each function takes only one argument,
334+
// the next token in sequence.
335+
// Move backwards to correctly handle nested expressions. For example, " - - a" should be parsed as function "−" with
336+
// argument [function "-" with argument ["a"]].
337+
for (let i = tokens.length - 1; i >= 0; i--) {
339338
// Treat ± as a minus.
340339
if (isOperator(tokens[i], '− ±')) {
340+
if (tokens[i - 1] instanceof ExprNumber) continue;
341+
341342
tokens.splice(i, 2, new ExprFunction('−', [tokens[i + 1]]));
342343
}
343344
}
344345

345346
// Match multiplication operators.
346347
tokens = findAssociativeFunction(tokens, '× * ·', true);
347348

348-
// Match - operators, nested within the replaced minus functions.
349-
findBinaryLeadingMinus(tokens);
349+
findBinaryFunction(tokens, '- −');
350350

351351
// Match + operators.
352352
if (isOperator(tokens[0], '+')) tokens = tokens.slice(1);
353353
tokens = findAssociativeFunction(tokens, '+');
354354

355+
findBinaryMinusFunctions(tokens);
356+
355357
if (tokens.length > 1) throw ExprError.invalidExpression();
356358
return tokens[0];
357359
}

0 commit comments

Comments
 (0)