expr-eval-ex
v0.0.6
Published
Mathematical expression evaluator, extends from expr-eval
Downloads
155
Maintainers
Readme
JavaScript Expression Evaluator
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-ex
Basic Usage
var Parser = require('expr-eval-ex').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 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,
// The in operator is disabled by default in the current version
'in': true
}
});
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.variables();
min,x,y,z
js> expr.simplify({ y: 4, z: 5 }).variables();
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 | Left | Function call, property access ! | Left | Factorial ^ | Right | Exponentiation +, -, not, sqrt, etc. | Right | Unary prefix operators (see below for the full list) *, /, % | Left | Multiplication, division, remainder +, -, || | Left | Addition, subtraction, concatenation ==, !=, >=, <=, >, <, in | Left | Equals, not equals, etc. "in" means "is the left operand included in the right array operand?" (disabled by default) and | Left | Logical AND or | Left | Logical OR x ? y : z | Right | Ternary conditional (if x then y else z)
The in
operator is disabled by default in the current version. To use it,
construct a Parser
instance with operators.in
set to true
. For example:
var parser = new Parser({
operators: {
'in': true
}
});
// Now parser supports 'x in array' 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 (magnatude) 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) 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) 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 not x | Logical NOT operator round x | X, rounded to the nearest integer, using "gradeschool rounding" 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. if(c, a, b) | Function form of c ? a : b roundTo(x, n) | Rounds x to n places after the decimal point. isNull(val) | 判断对象是否为空(字符串不会剔除空白) isEmpty(val) | 判断对象是否为空(字符串会剔除空白后再判断)
Date functions
Here are some processing functions for dates.
支持的日期格式:
'YYYY-MM-DD', 'YYYY/MM/DD', 'YYYYMMDD', 'YYYY-MM-DD HH', 'YYYY/MM/DD HH', 'YYYYMMDD HH', 'YYYY-MM-DD HH:mm', 'YYYY/MM/DD HH:mm', 'YYYYMMDD HH:mm', 'YYYY-MM-DD HH:mm:ss', 'YYYY/MM/DD HH:mm:ss', 'YYYYMMDD HH:mm:ss', 'HH', 'HH:mm', 'HH:mm:ss'.
计算用时间单位符号
y:年 | Q:季 | M:月 | w:周 | d:天 | h:时 | m:分 | s:秒 | ms:毫秒 注意大小写
Function | Description :------------------------------ | :----------------------------------------------- now(format) | 获取当前时间。format为非必填的,内容可选择“支持的日期格式” format(date, format) | 格式化时间。format为非必填的,内容可选择“支持的日期格式” dateAdd(date, num, unit) | 日期增加数。unit非必填,不填则默认为'd'(天)(默认为num天),可选值请看“计算用时间单位符号”。例:dateAdd('2018-08-08 13', 2, 'h') dateSubtract(date, num, unit) | 日期减去数。unit非必填,不填则默认为'd'(天)(默认为num天),可选值请看“计算用时间单位符号”。例:dateSubtract('2018-08-08 13', 2, 'h') datesDiff(date1, date2, unit) | 两日期相差数。unit非必填,不填则默认为'd'(天)(默认为num天),可选值请看“计算用时间单位符号”。例:datesDiff('2018-08-08', '2018-08-06', 'h') datesDiff2(date1, date2, part_format, unit) | 两日期部分相差数。unit非必填,不填则默认为'd'(天)(默认为num天),可选值请看“计算用时间单位符号”。例:datesDiff2('2021-02-08 08:30', '2021-02-06 08:10', 'hh:mm', 'm') 结果为20 dateGet(date, unit) | 获取日期中的某个值(年份/月份/时分秒等),unit可选值请看“计算用时间单位符号”,其中天需要传D。例:dateGet('2018-08-08', 'D')
Tests
cd
to the project directory- Install development dependencies:
npm install
- Run the tests:
npm test