Parse expressions like sin^2 (x^3) and do computer algebra on them — symbolic
differentiation and integration, numeric and symbolic equality testing,
simplification, assumptions, matrices, ODEs, and more.
This repository is the monorepo root (private; package name
math-expressions-workspace). The library itself is implemented in Rust and
shipped to JavaScript through wasm. The former JavaScript implementation has been
ported to Rust; its source and test suite are preserved out-of-tree under
tmp/js-legacy/ (git-ignored) for reference — see History.
Deployed to GitHub Pages on every push to main:
- Playground — https://doenet.github.io/math-expressions/ — run the Rust (WASM) engine and the canonical JS library side by side.
- Rust API docs — https://doenet.github.io/math-expressions/docs/ —
cargo docfor the core crate.
Everything lives under packages/:
The pure-Rust library: parsing (text ↔ ast, latex ↔ ast), equality
(numeric + finite-field + exact + structural), normalization / simplify / expand,
differentiation, symbolic + certified integration, matrices / eigenvalues, ODE
solving, assumptions, factoring, and an arbitrary-precision engine. Tests and the
JS-derived differential fixtures live in tests/; scripts/ regenerates the
fixtures from the legacy JS oracle. No JavaScript — a pure library crate.
The single place the core is compiled to WebAssembly and adapted for JS. Co-locates two language trees (the Doenet layout):
src-rust/(+Cargo.toml) — themath-expressions-wasmwasm-bindgencrate: a thin adapter over the core's public API.src-js/— TypeScript bindings, principally the AST → math.js bridge for fast numeric graphing (Doenet + jsxgraph), plus the shared wasm handle types.build-wasm.sh— the canonical wasm build script that themath-expressions-js-compatpackage's ownbuild-wasm.shdelegates to; other packages call it with a target (nodejsfor synchronous Node use,webfor the browser).tests/is a Vitest end-to-end suite that loads the browser build (ESM +initSync) and exercises every subsystem.
The directory is math-expressions-js-compat, but its package.json name is
math-expressions — this is the npm package (v3, 3.0.0-alpha1), a drop-in
replacement for the original math-expressions JS API (me.fromText(...).equals(...)),
implemented in TypeScript over the wasm core — no math of its own. lib/ is the
compat layer (mirrors the old lib/** module paths); spec/ is the legacy
Vitest suite converted to TypeScript and run against the drop-in. (Not all
legacy behavior is ported yet — see
JS_TEST_COVERAGE_AUDIT.md.)
A Vite/React app that runs the Rust (wasm) engine side-by-side with the
original published JS library (pulled in via the math-expressions-canonical
npm alias → math-expressions@2.0.0-alpha94) so their outputs can be compared.
Suggested development happens inside the dev container: open the repository in
VS Code and choose Reopen in Container for the correct Node.js and Rust
toolchains (including the wasm32 target and a pinned wasm-bindgen-cli).
# Core Rust library
cargo test --release # some corpus tests are slow in debug
# wasm bindings — browser build + end-to-end suite
cd packages/math-expressions-rs-wasm && npm run build:wasm && npm test
# math-expressions drop-in (dir: math-expressions-js-compat) — legacy JS suite (TS)
cd packages/math-expressions-js-compat && npm run build:wasm && npm testCI (.github/workflows/ci.yml) runs the Rust build/test, the playground build,
the wasm end-to-end suite, and the drop-in suite. The Rust suite includes
differential corpora checked against the original JS/mathjs behavior
(packages/math-expressions-rs/tests/fixtures/).
The core crate's modules were regrouped by concern.
packages/math-expressions-rs/src/lib.rs documents the resulting facade tiers;
the table below translates the old names, which the historical design notes in
active-plans/ still use.
| was | is now |
|---|---|
norm/ |
normalize/ |
exact.rs |
eval_exact/ |
precise/ |
eval_numeric/certified_digits/ |
eval/ |
eval_numeric/complex.rs |
functions/ |
special_functions/ |
output/ |
print/ |
diff.rs, integrate/ |
calculus/diff.rs, calculus/integrate/ |
factor.rs, ratform.rs, rootof.rs |
polynomials/{factor,ratform,rootof}.rs |
poly/, upoly.rs |
polynomials/{multivariate,univariate}.rs |
numeric.rs, ode.rs |
mathjs_compat/{dense_f64,ode}.rs |
expr.rs, sym.rs, js_tree.rs |
expr/{tree,sym,serde}.rs |
js_match.rs |
math-expressions-rs-wasm/src-rust/js_match.rs |
pm.rs |
ops/pm.rs |
matrix/kernels.rs |
matrix/elimination.rs |
ops.rs, num.rs, assumptions/mod.rs and functions/mod.rs were also split
into submodules; each new barrel module's //! docs list its parts. expr.rs
additionally split off expr/visit.rs, which now owns
norm::syntactic::map_children as crate::expr::map_children.
Inside eval_numeric/certified_digits/, the old precise/ files were renamed
too: precise/mod.rs → pipeline.rs, precise/tier0.rs → float_bounds.rs,
precise/complex.rs → cfix.rs.
Two types were renamed along the way: Tri → MaybeBool (the three-valued
Option<bool> alias in assumptions), and numeric::EigenPair →
mathjs_compat::NumericEigenPair (the symbolic matrix::EigenPair keeps its
name). flatten_tree, unflatten_left, unflatten_right and match_template
are no longer part of the core crate's public API — they moved to the wasm crate
with js_match.rs.
The pre-port JavaScript library (lib/) and its Vitest suite (spec/) now live
under tmp/js-legacy/ (git-ignored, kept on disk). The JS→Rust mapping is
documented in active-plans/:
JS_RUST_DIFF.md,
JS_RUST_TEST_DIVERGENCES.md,
WHATS_LEFT.md, and
JS_TEST_COVERAGE_AUDIT.md.
Math-expressions is dual-licensed under GPLv3 and under Apache Version 2.0.