File size: 15,417 Bytes
c211499
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
JavaScript Expression Evaluator
===============================

[![npm](https://img.shields.io/npm/v/expr-eval.svg?maxAge=3600)](https://www.npmjs.com/package/expr-eval)
[![CDNJS version](https://img.shields.io/cdnjs/v/expr-eval.svg?maxAge=3600)](https://cdnjs.com/libraries/expr-eval)
[![Build Status](https://travis-ci.org/silentmatt/expr-eval.svg?branch=master)](https://travis-ci.org/silentmatt/expr-eval)

Description
-------------------------------------

Parses and evaluates mathematical expressions. It's a safer and more
math-oriented alternative to using JavaScript’s `eval` function for mathematical
expressions.

It has built-in support for common math operators and functions. Additionally,
you can add your own JavaScript functions. Expressions can be evaluated
directly, or compiled into native JavaScript functions.

Installation
-------------------------------------

    npm install expr-eval

Basic Usage
-------------------------------------

    var Parser = require('expr-eval').Parser;

    var parser = new Parser();
    var expr = parser.parse('2 * x + 1');
    console.log(expr.evaluate({ x: 3 })); // 7

    // or
    Parser.evaluate('6 * x', { x: 7 }) // 42

Documentation
-------------------------------------

* [Parser](#parser)
    - [Parser()](#parser-1)
    - [parse(expression: string)](#parseexpression-string)
    - [Parser.parse(expression: string)](#parserparseexpression-string)
    - [Parser.evaluate(expression: string, variables?: object)](#parserevaluateexpression-string-variables-object)
* [Expression](#expression)
    - [evaluate(variables?: object)](#evaluatevariables-object)
    - [substitute(variable: string, expression: Expression | string | number)](#substitutevariable-string-expression-expression--string--number)
    - [simplify(variables: object)](#simplifyvariables-object)
    - [variables(options?: object)](#variablesoptions-object)
    - [symbols(options?: object)](#symbolsoptions-object)
    - [toString()](#tostring)
    - [toJSFunction(parameters: array | string, variables?: object)](#tojsfunctionparameters-array--string-variables-object)
* [Expression Syntax](#expression-syntax)
    - [Operator Precedence](#operator-precedence)
    - [Unary operators](#unary-operators)
    - [Array literals](#array-literals)
    - [Pre-defined functions](#pre-defined-functions)
    - [Custom JavaScript functions](#custom-javascript-functions)
    - [Constants](#constants)

### Parser ###

Parser is the main class in the library. It has as single `parse` method, and
"static" methods for parsing and evaluating expressions.

#### Parser()

Constructs a new `Parser` instance.

The constructor takes an optional `options` parameter that allows you to enable or disable operators.

For example, the following will create a `Parser` that does not allow comparison or logical operators, but does allow `in`:

    var parser = new Parser({
      operators: {
        // These default to true, but are included to be explicit
        add: true,
        concatenate: true,
        conditional: true,
        divide: true,
        factorial: true,
        multiply: true,
        power: true,
        remainder: true,
        subtract: true,

        // Disable and, or, not, <, ==, !=, etc.
        logical: false,
        comparison: false,

        // Disable 'in' and = operators
        'in': false,
        assignment: false
      }
    });

#### parse(expression: string)

Convert a mathematical expression into an `Expression` object.

#### Parser.parse(expression: string)

Static equivalent of `new Parser().parse(expression)`.

#### Parser.evaluate(expression: string, variables?: object)

Parse and immediately evaluate an expression using the values and functions from
the `variables` object.

Parser.evaluate(expr, vars) is equivalent to calling
Parser.parse(expr).evaluate(vars).

### Expression ###

`Parser.parse(str)` returns an `Expression` object. `Expression`s are similar to
JavaScript functions, i.e. they can be "called" with variables bound to
passed-in values. In fact, they can even be converted into JavaScript
functions.

#### evaluate(variables?: object)

Evaluate the expression, with variables bound to the values in {variables}. Each
variable in the expression is bound to the corresponding member of the
`variables` object. If there are unbound variables, `evaluate` will throw an
exception.

    js> expr = Parser.parse("2 ^ x");
    (2^x)
    js> expr.evaluate({ x: 3 });
    8

#### substitute(variable: string, expression: Expression | string | number)

Create a new `Expression` with the specified variable replaced with another
expression. This is similar to function composition. If `expression` is a string
or number, it will be parsed into an `Expression`.

    js> expr = Parser.parse("2 * x + 1");
    ((2*x)+1)
    js> expr.substitute("x", "4 * x");
    ((2*(4*x))+1)
    js> expr2.evaluate({ x: 3 });
    25

#### simplify(variables: object)

Simplify constant sub-expressions and replace variable references with literal
values. This is basically a partial evaluation, that does as much of the
calculation as it can with the provided variables. Function calls are not
evaluated (except the built-in operator functions), since they may not be
deterministic.

Simplify is pretty simple. For example, it doesn’t know that addition and
multiplication are associative, so `((2*(4*x))+1)` from the previous example
cannot be simplified unless you provide a value for x. `2*4*x+1` can however,
because it’s parsed as `(((2*4)*x)+1)`, so the `(2*4)` sub-expression will be
replaced with "8", resulting in `((8*x)+1)`.

    js> expr = Parser.parse("x * (y * atan(1))").simplify({ y: 4 });
    (x*3.141592653589793)
    js> expr.evaluate({ x: 2 });
    6.283185307179586

#### variables(options?: object)

Get an array of the unbound variables in the expression.

    js> expr = Parser.parse("x * (y * atan(1))");
    (x*(y*atan(1)))
    js> expr.variables();
    x,y
    js> expr.simplify({ y: 4 }).variables();
    x

By default, `variables` will return "top-level" objects, so for example, `Parser.parse(x.y.z).variables()` returns `['x']`. If you want to get the whole chain of object members, you can call it with `{ withMembers: true }`. So `Parser.parse(x.y.z).variables({ withMembers: true })` would return `['x.y.z']`.

#### symbols(options?: object)

Get an array of variables, including any built-in functions used in the
expression.

    js> expr = Parser.parse("min(x, y, z)");
    (min(x, y, z))
    js> expr.symbols();
    min,x,y,z
    js> expr.simplify({ y: 4, z: 5 }).symbols();
    min,x

Like `variables`, `symbols` accepts an option argument `{ withMembers: true }` to include object members.

#### toString()

Convert the expression to a string. `toString()` surrounds every sub-expression
with parentheses (except literal values, variables, and function calls), so
it’s useful for debugging precedence errors.

#### toJSFunction(parameters: array | string, variables?: object)

Convert an `Expression` object into a callable JavaScript function. `parameters`
is an array of parameter names, or a string, with the names separated by commas.

If the optional `variables` argument is provided, the expression will be
simplified with variables bound to the supplied values.

    js> expr = Parser.parse("x + y + z");
    ((x + y) + z)
    js> f = expr.toJSFunction("x,y,z");
    [Function] // function (x, y, z) { return x + y + z; };
    js> f(1, 2, 3)
    6
    js> f = expr.toJSFunction("y,z", { x: 100 });
    [Function] // function (y, z) { return 100 + y + z; };
    js> f(2, 3)
    105

### Expression Syntax ###

The parser accepts a pretty basic grammar. It's similar to normal JavaScript
expressions, but is more math-oriented. For example, the `^` operator is
exponentiation, not xor.

#### Operator Precedence

Operator                 | Associativity | Description
:----------------------- | :------------ | :----------
(...)                    | None          | Grouping
f(), x.y, a[i]           | Left          | Function call, property access, array indexing
!                        | Left          | Factorial
^                        | Right         | Exponentiation
+, -, not, sqrt, etc.    | Right         | Unary prefix operators (see below for the full list)
\*, /, %                 | Left          | Multiplication, division, remainder
+, -, \|\|               | Left          | Addition, subtraction, array/list concatenation
==, !=, >=, <=, >, <, in | Left          | Equals, not equals, etc. "in" means "is the left operand included in the right array operand?"
and                      | Left          | Logical AND
or                       | Left          | Logical OR
x ? y : z                | Right         | Ternary conditional (if x then y else z)
=                        | Right         | Variable assignment
;                        | Left          | Expression separator

    var parser = new Parser({
      operators: {
        'in': true,
        'assignment': true
      }
    });
    // Now parser supports 'x in array' and 'y = 2*x' expressions

#### Unary operators

The parser has several built-in "functions" that are actually unary operators.
The primary difference between these and functions are that they can only accept
exactly one argument, and parentheses are optional. With parentheses, they have
the same precedence as function calls, but without parentheses, they keep their
normal precedence (just below `^`). For example, `sin(x)^2` is equivalent to
`(sin x)^2`, and `sin x^2` is equivalent to `sin(x^2)`.

The unary `+` and `-` operators are an exception, and always have their normal
precedence.

Operator | Description
:------- | :----------
-x       | Negation
+x       | Unary plus. This converts it's operand to a number, but has no other effect.
x!       | Factorial (x * (x-1) * (x-2) * … * 2 * 1). gamma(x + 1) for non-integers.
abs x    | Absolute value (magnitude) of x
acos x   | Arc cosine of x (in radians)
acosh x  | Hyperbolic arc cosine of x (in radians)
asin x   | Arc sine of x (in radians)
asinh x  | Hyperbolic arc sine of x (in radians)
atan x   | Arc tangent of x (in radians)
atanh x  | Hyperbolic arc tangent of x (in radians)
cbrt x   | Cube root of x
ceil x   | Ceiling of x — the smallest integer that’s >= x
cos x    | Cosine of x (x is in radians)
cosh x   | Hyperbolic cosine of x (x is in radians)
exp x    | e^x (exponential/antilogarithm function with base e)
expm1 x  | e^x - 1
floor x  | Floor of x — the largest integer that’s <= x
length x | String length of x
ln x     | Natural logarithm of x
log x    | Natural logarithm of x (synonym for ln, not base-10)
log10 x  | Base-10 logarithm of x
log2 x   | Base-2 logarithm of x
log1p x  | Natural logarithm of (1 + x)
not x    | Logical NOT operator
round x  | X, rounded to the nearest integer, using "grade-school rounding"
sign x   | Sign of x (-1, 0, or 1 for negative, zero, or positive respectively)
sin x    | Sine of x (x is in radians)
sinh x   | Hyperbolic sine of x (x is in radians)
sqrt x   | Square root of x. Result is NaN (Not a Number) if x is negative.
tan x    | Tangent of x (x is in radians)
tanh x   | Hyperbolic tangent of x (x is in radians)
trunc x  | Integral part of a X, looks like floor(x) unless for negative number

#### Pre-defined functions

Besides the "operator" functions, there are several pre-defined functions. You
can provide your own, by binding variables to normal JavaScript functions.
These are not evaluated by simplify.

Function      | Description
:------------ | :----------
random(n)     | Get a random number in the range [0, n). If n is zero, or not provided, it defaults to 1.
fac(n)        | n! (factorial of n: "n * (n-1) * (n-2) * … * 2 * 1") Deprecated. Use the ! operator instead.
min(a,b,…)    | Get the smallest (minimum) number in the list.
max(a,b,…)    | Get the largest (maximum) number in the list.
hypot(a,b)    | Hypotenuse, i.e. the square root of the sum of squares of its arguments.
pyt(a, b)     | Alias for hypot.
pow(x, y)     | Equivalent to x^y. For consistency with JavaScript's Math object.
atan2(y, x)   | Arc tangent of x/y. i.e. the angle between (0, 0) and (x, y) in radians.
roundTo(x, n) | Rounds x to n places after the decimal point.
map(f, a)     | Array map: Pass each element of `a` the function `f`, and return an array of the results.
fold(f, y, a) | Array fold: Fold/reduce array `a` into a single value, `y` by setting `y = f(y, x, index)` for each element `x` of the array.
filter(f, a)  | Array filter: Return an array containing only the values from `a` where `f(x, index)` is `true`.
indexOf(x, a) | Return the first index of string or array `a` matching the value `x`, or `-1` if not found.
join(sep, a)  | Concatenate the elements of `a`, separated by `sep`.
if(c, a, b)   | Function form of c ? a : b. Note: This always evaluates both `a` and `b`, regardless of whether `c` is `true` or not. Use `c ? a : b` instead if there are side effects, or if evaluating the branches could be expensive.

#### Array literals

Arrays can be created by including the elements inside square `[]` brackets, separated by commas. For example:

    [ 1, 2, 3, 2+2, 10/2, 3! ]

#### Function definitions

You can define functions using the syntax `name(params) = expression`. When it's evaluated, the name will be added to the passed in scope as a function. You can call it later in the expression, or make it available to other expressions by re-using the same scope object. Functions can support multiple parameters, separated by commas.

Examples:

    square(x) = x*x
    add(a, b) = a + b
    factorial(x) = x < 2 ? 1 : x * factorial(x - 1)

#### Custom JavaScript functions

If you need additional functions that aren't supported out of the box, you can easily add them in your own code. Instances of the `Parser` class have a property called `functions` that's simply an object with all the functions that are in scope. You can add, replace, or delete any of the properties to customize what's available in the expressions. For example:

    var parser = new Parser();

    // Add a new function
    parser.functions.customAddFunction = function (arg1, arg2) {
      return arg1 + arg2;
    };

    // Remove the factorial function
    delete parser.functions.fac;

    parser.evaluate('customAddFunction(2, 4) == 6'); // true
    //parser.evaluate('fac(3)'); // This will fail

#### Constants

The parser also includes a number of pre-defined constants that can be used in expressions. These are shown
in the table below:

Constant     | Description
:----------- | :----------
E            | The value of `Math.E` from your JavaScript runtime
PI           | The value of `Math.PI` from your JavaScript runtime
true         | Logical `true` value
false        | Logical `false` value

Pre-defined constants are stored in `parser.consts`. You can make changes to this property to customise the
constants available to your expressions. For example:

    var parser = new Parser();
    parser.consts.R = 1.234;

    console.log(parser.parse('A+B/R').toString());  // ((A + B) / 1.234)

To disable the pre-defined constants, you can replace or delete `parser.consts`:

    var parser = new Parser();
    parser.consts = {};


### Tests ###

1. `cd` to the project directory
2. Install development dependencies: `npm install`
3. Run the tests: `npm test`