@@ -163,19 +163,49 @@ function findBinaryFunction(tokens: ExprElement[], fn: string) {
163163 }
164164}
165165
166- // Some minuses are initially parsed as unary functions (i.e. the string "- b" is parsed an `ExprFunction` with
167- // `.fn` = "−" and an `ExprIdentifier` "b" as its single argument.)
168- // If any unary minus function is preceded by another token, we need to merge the pair of tokens into a binary minus
169- // function.
170- function findBinaryMinusFunctions ( tokens : ExprElement [ ] ) {
166+
167+ /**
168+ * Merge any tokens with a subtraction into a single term. Subtraction can be in the form of ['a', '-', 'b'], where the
169+ * token is an operator. Alternatively, due to previously parsing unary minus, it can be in the form of ['a', {function
170+ * "-" args: "b"}]. This function merges both cases into a single term.
171+ * */
172+ function findBinarySubtractionFunctions ( tokens : ExprElement [ ] ) {
171173 for ( let i = 1 ; i < tokens . length ; i ++ ) {
172174 const token = tokens [ i ] ;
173- if ( token instanceof ExprFunction && token . fn === '−' ) {
175+
176+ // This is the case when we have something like ["a", "-", "b"].
177+ if ( isOperator ( token , '- −' ) ) {
178+ const a = tokens [ i - 1 ] ;
179+ const b = tokens [ i + 1 ] ;
180+
181+ if ( a instanceof ExprOperator ) {
182+ throw ExprError . consecutiveOperators ( a . o , token . o ) ;
183+ }
184+ if ( b instanceof ExprOperator ) {
185+ throw ExprError . consecutiveOperators ( token . o , b . o ) ;
186+ }
187+
188+ const args = [ removeBrackets ( a ) , removeBrackets ( b ) ] ;
189+ tokens . splice ( i - 1 , 3 , new ExprFunction ( '−' , args ) ) ;
190+ i -= 2 ;
191+ }
192+
193+ // This is the case when have already parsed subtraction ExprFunctions somewhere in the expression, not preceded by
194+ // a number. For example, we may have something like [ExprIdentifier: 'x', {function '-' args: 'b'}] or ['a', '+',
195+ // {function: '-', args: ['b']}].
196+ if ( token instanceof ExprFunction && token . fn === '−' && token . args . length === 1 ) {
174197 const a = tokens [ i - 1 ] ;
175198 const b = token . args [ 0 ] ;
176199
177200 const args = [ removeBrackets ( a ) , removeBrackets ( b ) ] ;
178- tokens . splice ( i - 1 , 2 , new ExprFunction ( '−' , args ) ) ;
201+ if ( a instanceof ExprOperator ) {
202+ // This can happen if we have a '+' before a '-'. Here, we have something like ['a', '+', {function '-' args:
203+ // 'b'}]. In this case, we merge to something of the form ['a', {function '+' args: [{function '-',
204+ // args['b'] }] }].
205+ tokens . splice ( i - 1 , 2 , new ExprFunction ( a . o , [ token ] ) ) ;
206+ } else {
207+ tokens . splice ( i - 1 , 2 , new ExprFunction ( '−' , args ) ) ;
208+ }
179209 i -= 1 ;
180210 }
181211 }
@@ -340,21 +370,18 @@ export function collapseTerm(tokens: ExprElement[]): ExprElement {
340370 // Treat ± as a minus.
341371 if ( isOperator ( tokens [ i ] , '− ±' ) ) {
342372 if ( tokens [ i - 1 ] instanceof ExprNumber ) continue ;
343-
344373 tokens . splice ( i , 2 , new ExprFunction ( '−' , [ tokens [ i + 1 ] ] ) ) ;
345374 }
346375 }
347376
348377 // Match multiplication operators.
349378 tokens = findAssociativeFunction ( tokens , '× * ·' , true ) ;
350379
351- findBinaryFunction ( tokens , '- −' ) ;
380+ findBinarySubtractionFunctions ( tokens ) ;
352381
353382 // Match + operators.
354383 if ( isOperator ( tokens [ 0 ] , '+' ) ) tokens = tokens . slice ( 1 ) ;
355- tokens = findAssociativeFunction ( tokens , '+' ) ;
356-
357- findBinaryMinusFunctions ( tokens ) ;
384+ tokens = findAssociativeFunction ( tokens , '+' , true ) ;
358385
359386 if ( tokens . length > 1 ) throw ExprError . invalidExpression ( ) ;
360387 return tokens [ 0 ] ;
0 commit comments