silentmatt / expr-eval

Mathematical expression evaluator in JavaScript
http://silentmatt.com/javascript-expression-evaluator/
MIT License
1.18k stars 239 forks source link

Evaluate expression with strings #244

Open mabregana5 opened 3 years ago

mabregana5 commented 3 years ago

Is there a way to evaluate an expression and concatenate strings in between?

For example:

let expr = "'' + (datediff('mm', a, b) - datediff('yy', a, b) * 12) + ' years'"; let params = { a: moment(), b: moment({ y: 2020, m: 1, d: 1 }) }; let value = parser.parse(expr).evaluate(params)

Expected: value == "[dateValue] years"

Actual: value == NaN

We have a form of expression evaluation in our web app, but we are using this package within our more recent PWA. The example expression in expr is one way we can add strings and have substituted values in the formulas.

value is assigned NaN, but if you remove the strings, it is instead assigned with an appropriate value.

Originally posted by @mabregana5 in https://github.com/silentmatt/expr-eval/discussions/240#discussioncomment-259136

silentmatt commented 3 years ago

The expression language (unlike javascript) uses || for string concatenation (borrowed from SQL). + will always try to convert its operands to numbers first, which is why you're getting NaN.

let expr = "'' || (datediff('mm', a, b) - datediff('yy', a, b) * 12) || ' years'";
let params = { a: moment(), b: moment({ y: 2020, m: 1, d: 1 }) };
let value = parser.parse(expr).evaluate(params)
mabregana5 commented 3 years ago

Thanks for the reply @silentmatt.

I was wondering if you had any suggestions in the ability to substitute the '+' operator with the double pipe '||' in sub-expressions that involve string concatenation?

Or a way to override the '+' operator for custom logic, as opposed to adding a custom function?

silentmatt commented 3 years ago

Substituting the operator would be tricky, since the expressions don't store any type information, but you can override the + operator implementation by replacing the parser.binaryOps['+'] function with your own. If you want JavaScript's behavior, it can be as simple as:

let parser = new Parser();
parser.binaryOps['+'] = (a, b) => a + b;