mathjs: Indicating expression location for error
When evaluating with multiple expressions and one of them has an error:
math.evaluate(["1+1","2+"])
The error is:
SyntaxError: Unexpected end of expression (char 3)
I would like to know which is the expression with the error:
SyntaxError: Unexpected end of expression (expr 2, char 3)
About this issue
- Original URL
- State: open
- Created 3 years ago
- Comments: 15 (6 by maintainers)
Well, that is an interesting question. The code internally is throwing errors. So the usual behavior would be for those errors to propagate outside of mathjs – I mean, that’s what “throwing an error” is all about. So while it’s absolutely possible and desirable to improve the data in an error, imagine the client that can’t afford for there to be any errors and absolutely wants to catch the first one before executing any further? So once that first exception is thrown and not caught within the mathjs package, there is no opportunity to continue evaluating the remaining expressions.
Hence, I think that all mathjs could do is offer an ‘evaluateAll’ convenience method that catches errors for you around every expression – basically recreate the loop that Jos shows in https://github.com/josdejong/mathjs/issues/2318#issuecomment-925861448 but in a standardized mathjs method – or possibly (?) add an options argument to
evaluate()
that would take say ‘{catch: true}’ to tell evaluate to wrap each evaluation in a try and if it catches one, return the error object instead of the result. I think @josdejong would have to weigh in on any of these possibilities.Thanks so much for your interest in this issue, @zishiwu123. The point in parse.js that you have identified has to do with looping over the clauses in the object syntax
{x: 1, y: 2}
rather than the different expressions in a call of evaluate on an array of expressions.The loop you are looking for – actually there are two instances – are the deepmaps in src/expression/function/evaluate.js, near the end where it deals with Array or Matrix arguments. There is where you’d have to capture the index information and create a way that an eventual error could access it.
Finally, my personal suggestion is that the expression index annotation should only appear when the error occurs in one of these array or matrix calls, so that the errors from single-expression calls are not cluttered with index information that doesn’t add anything – when you evaluate a single expression, there really is no “index”.
Yes, I think the consensus here is to extend the “state” of parsing to allow to record which expression was being evaluated in an array call to evaluate (but not burden the message in an ordinary single-expression call). A PR to that end would, I believe be welcome.
I think it would be useful to indicate in which expression the error was found like described by @zishiwu123.
Maybe if the expression has multiple lines the error could indicate the line and character. This might not be as important because it could also be extracted and converted by the user.
It seems reasonable to me not to implement
evaluateAll
but just improve the exception thrown to know where the error was found.Research The error message is generated by: https://github.com/josdejong/mathjs/blob/4f83b786ea420b4095073dc97f3321d1dd768147/src/expression/parse.js#L1769-L1775
Here are the contents of the
state
object passed intocreateSyntaxError()
for the first example @dvd101x posted:Proposed Solution The
state
object tracks the current evaluated expression"2+"
, which is where the error occurs. But it doesn’t track the index of the current expression (let’s call itexpressionIndex
) being evaluated from the array of expressions["1+1","2+"]
that was passed intomath.evaluate
. If we want to knowexpressionIndex
, I think we need to add it as an additional property to thestate
object and increment it every time this line inparseObject()
is executed: https://github.com/josdejong/mathjs/blob/4f83b786ea420b4095073dc97f3321d1dd768147/src/expression/parse.js#L1660The existing
index
property in thestate
object tracks the index of the current character in the current expression. This is different from the proposedexpressionIndex
property.Questions Besides occurring in
parseObject()
, the check for conditionstate.token === ','
occurs 5 other times, all inparse.js
. I am not very familiar with the code base but I would like to help out with this issue. So far my questions are:parseObject()
need to be applied [in the same way / in a different way /not at all] in the other 5 occurrences of the conditionstate.token === ','
?Yes indeed, thanks for sharing a clear example David👍
You can evaluate the expressions one by one: