smartcal
v1.2.0
Published
Ultra-high-performance mathematical expression engine with JIT compilation (~2.5M+ ops/s), CSP-safe VM mode, Pratt parser, and zero dependencies. Evaluate dynamic formulas safely without eval().
Maintainers
Readme
Why SmartCal?
Most mathematical expression evaluators in JavaScript suffer from critical flaws: dangerous eval() usage, inefficient parsing pipelines, and absence of true compilation.
SmartCal v1.1 solves these problems with a cutting-edge compiler architecture:
| Criteria | Traditional Evaluators | SmartCal Engine |
| :--- | :--- | :--- |
| Security | eval() — injection vulnerabilities, CSP-blocked | Zero-eval JIT + CSP-Safe VM |
| Parsing | Shunting-Yard — struggles with nested ternaries | Pratt Parser O(N) — linear, unambiguous |
| Performance | Re-parses text on every evaluation | Compile once, evaluate at ~2.5M+ ops/s |
| Nested Formulas | Recursive re-parsing, exponential degradation | Memoized topological DAG resolution |
| Environment | Requires unsafe-eval | 100% CSP-Safe mode available |
Quick Start
Prerequisites
Installation
bun add smartcal
# or
npm install smartcalBasic Evaluation
import SmartCal from 'smartcal';
// Arithmetic with operator precedence
console.log(SmartCal('2 + 3 * 4')); // 14
console.log(SmartCal('2 ^ 3 ^ 2')); // 512 (right-associative)
// With variables
const bmi = SmartCal('weight / (height ^ 2)', { weight: 70, height: 1.75 }); // 22.86
// Ternary expressions
console.log(SmartCal('score >= 80 ? "A" : "B"', { score: 85 })); // "A"
// Unicode support
console.log(SmartCal('café + quantité', { café: 2.5, quantité: 4 })); // 6.5High-Performance Precompiled Evaluation
import { compile } from 'smartcal';
// Compile once
const taxCalc = compile('income > 50000 ? (income - 50000) * 0.30 + 5000 : income * 0.10');
// Evaluate many times
console.log(taxCalc.evaluate({ income: 75000 })); // 12500
console.log(taxCalc.evaluate({ income: 30000 })); // 3000Execution Modes
const jit = compile('price * quantity', { mode: 'jit' }); // Maximum performance
const vm = compile('price * quantity', { mode: 'vm' }); // CSP-safe
const auto = compile('price * quantity', { mode: 'auto' }); // AdaptiveStrict Mode
By default, a variable absent from data silently evaluates to 0.
Enable strict mode to throw a VariableNotFoundError instead:
import SmartCal, { compile, VariableNotFoundError } from 'smartcal';
// SmartCal()
try {
SmartCal('price + tax', { price: 100 }, { strict: true });
} catch (e) {
if (e instanceof VariableNotFoundError) {
console.error(`Missing variable: "${e.variable}"`); // "tax"
}
}
// compile()
const expr = compile('price + tax', { strict: true });
expr.evaluate({ price: 100, tax: 5 }); // 105
expr.evaluate({ price: 100 }); // throws VariableNotFoundError("tax")
extractVariableslets you validate data before evaluation:import { extractVariables } from 'smartcal'; const needed = extractVariables('price * (1 - discount)'); // ['price', 'discount'] const missing = needed.filter(v => !(v in data)); if (missing.length) throw new Error(`Missing: ${missing.join(', ')}`);
Expression Validation
import { isValidExpression } from 'smartcal';
isValidExpression('price * (1 - discount)'); // true
isValidExpression('price * '); // falsePerformance
Detailed benchmarks are auto-generated and always up-to-date in the documentation:
Quick highlights (JIT Mode):
| Scenario | Throughput | Latency | | :--- | :--- | :--- | | Simple Arithmetic | ~2.4M ops/s | ~0.4 µs | | Nested Ternaries | ~1.1M ops/s | ~0.9 µs | | Boolean Logic XXL | ~900K ops/s | ~1.1 µs | | Polynomial (11 Variables) | ~280K ops/s | ~3.5 µs |
[!TIP] Run benchmarks locally:
bun run bench
Advanced Usage
Sub-Formulas & DAG (f_*)
import SmartCal, { compile } from 'smartcal';
const f_subtotal = compile('price * quantity * (1 - discount)');
const f_tax = compile('f_subtotal * taxRate');
const f_total = compile('f_subtotal + f_tax + shipping');
console.log(f_total.evaluate({
price: 50, quantity: 2, discount: 0.10,
taxRate: 0.20, shipping: 5,
f_subtotal, f_tax,
})); // 113Custom Functions
import { FunctionRegistry, compile } from 'smartcal';
FunctionRegistry.register('clamp', (val, min, max) =>
Math.min(Math.max(val, min), max)
);
const speedLimit = compile('clamp(speed, 0, 130)');
console.log(speedLimit.evaluate({ speed: 150 })); // 130
// Built-in: abs, sqrt, round, floor, ceil, min, max, sin, cos, tan, log, exp
const hypotenuse = compile('sqrt(a ^ 2 + b ^ 2)');
console.log(hypotenuse.evaluate({ a: 3, b: 4 })); // 5Documentation
Full documentation is available at nxhermane.github.io/smartcal (available in English and French).
Operator Precedence
| Priority | Operators | Description |
|----------|-----------|-------------|
| 1 | ( ) | Parentheses |
| 2 | ^ | Exponentiation (right-associative) |
| 3 | *, /, % | Multiplicative |
| 4 | +, - | Additive |
| 5 | <, <=, >, >= | Comparison |
| 6 | ==, != | Equality |
| 7 | && | Logical AND |
| 8 | \|\| | Logical OR |
| 9 | ? : | Ternary (right-associative) |
API Reference
// Direct evaluation
SmartCal(expression: string, data?: DataType, options?: SmartCalOptions): number | string
// Validate syntax
isValidExpression(expression: string): boolean
// Compile for reuse
compile(expression: string, options?: CompileOptions): CompiledExpression
// extract variables
extractVariables(expression: string): string[]Error Types
| Error | Description |
|-------|-------------|
| ScanError | Unknown character or unclosed string |
| ParseError | Invalid token sequence |
| FormulaResolutionError | Circular dependency in f_* formulas |
| JITError | JIT compilation blocked by CSP |
| VMError | Undefined operation in VM mode |
| IncorrectSyntaxError | Legacy syntax error |
| InvalidFormulaError | Empty formula |
| VariableNotFoundError | Variable not found in strict mode |
Contribution
Want to help improve SmartCal? See CONTRIBUTING.md for development setup, commit guidelines, and the pull request process.
