expr-eval vs mathjs
Evaluating Mathematical Expressions in JavaScript Applications
expr-evalmathjsSimilar Packages:

Evaluating Mathematical Expressions in JavaScript Applications

expr-eval and mathjs are both JavaScript libraries designed to parse and evaluate mathematical expressions from strings, but they target different use cases and complexity levels. expr-eval is a lightweight, focused library that safely evaluates basic arithmetic expressions with support for variables, functions, and operators. It prioritizes simplicity, security, and minimal bundle size. mathjs, by contrast, is a comprehensive mathematics library that includes an expression parser alongside extensive capabilities for symbolic computation, matrices, units, complex numbers, and statistical operations. It aims to be a full-featured math environment similar to MATLAB or Mathematica, suitable for scientific and engineering applications.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
expr-eval01,324-676 years agoMIT
mathjs015,0029.41 MB17618 days agoApache-2.0

Evaluating Math Expressions: expr-eval vs mathjs Compared

Both expr-eval and mathjs let you turn strings like '2 + 3 * x' into executable code, but they serve very different needs. One is a lean calculator; the other is a full math engine. Let’s see how they stack up in real development scenarios.

🔒 Security and Sandboxing: Safe Evaluation Matters

expr-eval was built from the ground up to avoid eval() and prevent arbitrary code execution. It parses expressions into an AST and evaluates them in a controlled sandbox.

// expr-eval: Safe by default
import { Parser } from 'expr-eval';

const parser = new Parser();
const expr = parser.parse('sqrt(x^2 + y^2)');
const result = expr.evaluate({ x: 3, y: 4 }); // returns 5
// No access to global objects like window, document, or Function

mathjs also avoids eval() in its expression parser and provides a configurable scope, but because it exposes a broader API surface (including import and dynamic function creation in some contexts), extra care is needed when evaluating untrusted input.

// mathjs: Safe when used correctly
import { evaluate } from 'mathjs';

// Pass only allowed variables/functions in scope
const scope = { x: 3, y: 4 };
const result = evaluate('sqrt(x^2 + y^2)', scope); // returns 5
// Avoid passing user input directly without sanitizing scope

💡 Key point: Both are safe if you control the scope and don’t expose dangerous globals—but expr-eval’s smaller surface area makes accidental exposure less likely.

🧮 Supported Math Features: Basic vs Advanced

expr-eval supports standard arithmetic (+ - * / ^ %), parentheses, variables, and a fixed set of built-in functions like sin, cos, log, abs, etc. You can add custom functions, but it doesn’t handle matrices, units, or symbolic math.

// expr-eval: Basic math only
const expr = parser.parse('log(100) + sin(pi/2)');
console.log(expr.evaluate()); // ~2 + 1 = 3

mathjs includes all of the above plus:

  • Matrix operations ([[1,2],[3,4]] * [5,6])
  • Unit arithmetic (5 cm + 2 inch)
  • Complex numbers (2 + 3i)
  • Symbolic simplification (simplify('x*2 + x')'3 * x')
  • Big number support
// mathjs: Advanced features
import { evaluate, simplify } from 'mathjs';

// Units
console.log(evaluate('5 cm + 2 inch').toString()); // '10.08 cm'

// Matrices
console.log(evaluate('[[1,2],[3,4]] * [5,6]')._data); // [17, 39]

// Symbolic
console.log(simplify('x*2 + x').toString()); // '3 * x'

If your app needs any of these advanced features, expr-eval simply can’t help—you’ll need mathjs.

⚙️ Custom Functions and Variables

Both libraries let you inject custom logic.

expr-eval allows you to define functions and constants during evaluation:

// expr-eval: Custom function
const expr = parser.parse('discount(price, 0.1)');
const result = expr.evaluate({
  price: 100,
  discount: (p, rate) => p * (1 - rate)
}); // returns 90

mathjs supports custom functions via the scope object or by extending the parser:

// mathjs: Custom function
const scope = {
  price: 100,
  discount: (p, rate) => p * (1 - rate)
};
const result = evaluate('discount(price, 0.1)', scope); // returns 90

The experience is similar—but note that mathjs functions must conform to its internal type system if you want full compatibility with units or matrices.

📦 Bundle Size and Dependencies

expr-eval is tiny (~5 KB minified) and has zero dependencies. It’s ideal for frontend apps where every kilobyte counts.

mathjs is much larger (can be >500 KB if you import everything), though it supports tree-shaking. You can import only the evaluate function to reduce size:

// mathjs: Tree-shakable import
import { evaluate } from 'mathjs';
// Still larger than expr-eval, but manageable for basic use

If you’re building a mobile web app or a performance-sensitive dashboard, expr-eval’s footprint is a major advantage.

🛠️ Error Handling and Debugging

Both provide clear error messages for malformed expressions.

expr-eval throws descriptive errors:

try {
  parser.parse('2 + * 3');
} catch (e) {
  console.error(e.message); // "Parse error at position 4"
}

mathjs does the same:

try {
  evaluate('2 + * 3');
} catch (e) {
  console.error(e.message); // "SyntaxError: Unexpected token *"
}

No significant difference here—both are developer-friendly.

🌐 Real-World Use Cases

Case 1: Financial Calculator in a Form

You’re building a loan calculator where users enter 'principal * rate / 12'.

  • Best choice: expr-eval
  • Why? Lightweight, secure, and handles variables like principal and rate perfectly.

Case 2: Engineering Simulation Tool

You need to evaluate formulas involving units (N*m), matrices (stress tensors), and complex impedance.

  • Best choice: mathjs
  • Why? Only mathjs supports units, matrices, and complex numbers out of the box.

Case 3: Spreadsheet-Like Formula Engine

Users write 'SUM(A1:A10) * TAX_RATE'.

  • Best choice: mathjs (if you need SUM, AVERAGE, etc.)
  • But: expr-eval can work if you implement SUM yourself as a custom function.

📊 Summary Table

Featureexpr-evalmathjs
Bundle size~5 KB (tiny)100+ KB (large, but tree-shakable)
Security modelStrict sandboxSafe with controlled scope
Basic arithmetic✅ Yes✅ Yes
Matrices❌ No✅ Yes
Units (kg, m, s)❌ No✅ Yes
Complex numbers❌ No✅ Yes
Symbolic math❌ No✅ Yes (simplify, derivative)
Custom functions✅ Yes✅ Yes
DependenciesNoneOptional (but core is self-contained)

💡 Final Recommendation

  • Use expr-eval when you need a small, secure, no-frills expression evaluator for basic math with variables—perfect for forms, config logic, or lightweight calculators.

  • Use mathjs when your app requires advanced mathematical capabilities beyond arithmetic, or when you anticipate needing matrices, units, or symbolic operations down the road.

Don’t over-engineer: if you’re only doing 'price * quantity * tax', expr-eval gets the job done cleanly. But if you’re building the next Wolfram Alpha competitor, mathjs is your foundation.

How to Choose: expr-eval vs mathjs

  • expr-eval:

    Choose expr-eval if you need a small, secure, and straightforward way to evaluate user-provided arithmetic expressions—such as in form calculators, configuration logic, or simple formula fields. It avoids eval(), supports custom variables and functions, and has no external dependencies, making it ideal for frontend applications where bundle size and security are critical.

  • mathjs:

    Choose mathjs if your application requires advanced mathematical operations beyond basic arithmetic—like matrix algebra, unit conversions, symbolic simplification, or complex number support—and you also need expression parsing. It’s well-suited for scientific tools, engineering dashboards, or educational apps where rich math functionality justifies the larger footprint.

README for expr-eval

JavaScript Expression Evaluator

npm CDNJS version Build Status

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 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. Expressions 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

OperatorAssociativityDescription
(...)NoneGrouping
f(), x.y, a[i]LeftFunction call, property access, array indexing
!LeftFactorial
^RightExponentiation
+, -, not, sqrt, etc.RightUnary prefix operators (see below for the full list)
*, /, %LeftMultiplication, division, remainder
+, -, ||LeftAddition, subtraction, array/list concatenation
==, !=, >=, <=, >, <, inLeftEquals, not equals, etc. "in" means "is the left operand included in the right array operand?"
andLeftLogical AND
orLeftLogical OR
x ? y : zRightTernary conditional (if x then y else z)
=RightVariable assignment
;LeftExpression 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.

OperatorDescription
-xNegation
+xUnary 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 xAbsolute value (magnitude) of x
acos xArc cosine of x (in radians)
acosh xHyperbolic arc cosine of x (in radians)
asin xArc sine of x (in radians)
asinh xHyperbolic arc sine of x (in radians)
atan xArc tangent of x (in radians)
atanh xHyperbolic arc tangent of x (in radians)
cbrt xCube root of x
ceil xCeiling of x — the smallest integer that’s >= x
cos xCosine of x (x is in radians)
cosh xHyperbolic cosine of x (x is in radians)
exp xe^x (exponential/antilogarithm function with base e)
expm1 xe^x - 1
floor xFloor of x — the largest integer that’s <= x
length xString length of x
ln xNatural logarithm of x
log xNatural logarithm of x (synonym for ln, not base-10)
log10 xBase-10 logarithm of x
log2 xBase-2 logarithm of x
log1p xNatural logarithm of (1 + x)
not xLogical NOT operator
round xX, rounded to the nearest integer, using "grade-school rounding"
sign xSign of x (-1, 0, or 1 for negative, zero, or positive respectively)
sin xSine of x (x is in radians)
sinh xHyperbolic sine of x (x is in radians)
sqrt xSquare root of x. Result is NaN (Not a Number) if x is negative.
tan xTangent of x (x is in radians)
tanh xHyperbolic tangent of x (x is in radians)
trunc xIntegral 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.

FunctionDescription
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:

ConstantDescription
EThe value of Math.E from your JavaScript runtime
PIThe value of Math.PI from your JavaScript runtime
trueLogical true value
falseLogical 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