- 
          
 - 
                Notifications
    
You must be signed in to change notification settings  - Fork 1.3k
 
Description
Describe the bug
Prior to #3547, mathjs could parse conditional expressions in which the true-expr happened to be a floating-point number beginning with a decimal point. Now such expressions may throw an error.
To Reproduce
math.evaluate('true?.3:.7') -- throws "SyntaxError: Unexpected operator ?. (char 7)". It previously returned the number .3.
Discussion
The error is occurring because parseConditional first attempts to parseLogicalOr, as that element might be the condition-expr of a conditional. In that attempt, parseLogicalOr eventually descends down to parseSymbol, which succeeds on true (producing a ConstantNode of the boolean true value), and then it attempts to parse any accessors to that symbol, at which point it eagerly consumes the ?. as an optional chaining operator, not realizing it could also be the start of a conditional followed by a true-expr that happens to begin with a dot, and the parse fails when the next token (3) is not anything that can currently legally follow the optional chaining operator.
Possible remediations?
Currently, with property names not allowed to start with a digit, parseAccessors could conceivably, rather than throwing an unexpected ?. operator error when it does not recognize the next token, re-tokenize the input as a ? token followed by a . (does the parser currently have any way to push a different tokenization of the same characters back onto the stream to be parsed??). It isn't enough just to save state and unwind to just before the ?. was read and return whatever was parsed before the ?. was encountered, since then it will just continue to be tokenized as ?. and parseConditional won't see the ? token it needs to begin a conditional.
So that would suggest that to fix this regression, ?. needs to be removed as a delimiter, so that it will always be parsed as just a ? token, and then parseAccessors has to deal with looking at the ? token and then checking for a . after that, and possibly revert to the state before the ? was examined if no parse as optional chaining is forthcoming.
However, I know there has also been an effort to allow numeric property accessors foo.3. If that is allowed, then I fear there is no way to support all of conditional expressions and optional chaining and ranges, because then foo?.3:7 would become truly ambiguous between foo ? (.3) : 7 that conditionally chooses between the values .3 and 7 based on the truthiness/falsyness of foo, on the one hand, and (foo?.3) : 7 that optionally chains to get the value of the property 3 on the entity foo if it is defined, and then takes  the range from that value to 7, on the other hand.
I haven't really considered whether there is any other sort of valid expression besides a decimal number that can start with a . that might create ambiguities with the new optional chaining operator vs. conditional.
I am at a loss how mathjs might proceed. The logical possibilities that come to mind include:
- Abandon numeric property access
 - Abandon the optional chaining operator
 - Require true-exprs of conditional expressions not to start with a 
., so that they would require a 0, whitespace or parentheses if you want the middle expression to be something like.3. But this path feels unsatisfactory because numeric property names are uncommon, so in factfoo?.3:7seems much more likely to be intended as a conditional than as a optional-chained property access being used to compute the first item in a Range. So I don't think that it should silently be parsed as the optional-chaining and range; maybe it would have to be detected and throw an ambiguous expression error. - Require  that optional chaining that does form the beginning of a range be parenthesized, and silently parse 
foo?.3:7as a conditional. This would be a bit tricky, again because of having to split the optional chaining operator into two tokens so that the conditional can consume just the?. And it also seems to be at odds with, generally speaking, the conditional operator being low-precedence/loosely binding and accessors being very high-precedence/tightly binding. On the other hand,foo?.3:7used to work as a conditional, and it seems reasonable that its meaning shouldn't change just because we would now like to allow optional chaining... 
So frankly I don't see any good alternative. Presumably Jos will devise some way to proceed; I look forward to his insight.