WebAssembly and JavaScript
The web integration runs the existing C++ DIPL parser and inspector, and the PUQ conversion command, in a browser through WebAssembly. Its JavaScript module provides string and bundled-project parsing, value descriptions, schema inspection, validation, override reevaluation, and unit conversion. It has no browser UI dependency. The JavaScript module is built from this repository and is not currently published as an npm package.
Build it with Emscripten’s emcmake and a C++17-capable toolchain:
export EM_CACHE="$PWD/build-web/cache"
emcmake cmake -S bindings/web -B build-web -G Ninja
cmake --build build-web -j4
node bindings/web/tests/smoke.mjs
Emscripten also requires Python 3.10 or newer. Put that python3 first on
PATH if your operating system provides an older default. EM_CACHE
keeps the compiler cache in a writable project build directory.
The build writes bindings/web/dist/dipl_web.mjs and
bindings/web/dist/dipl_web.wasm. When serving these files to a browser,
serve the JavaScript and WASM from the same origin and use a server that sends
application/wasm for the .wasm file.
The module initializes asynchronously. Evaluated values are returned as DIPL text, including units separately, so callers do not lose integer precision or array type information through an implicit JavaScript conversion:
import { loadDIPL } from "./bindings/web/index.mjs";
const dipl = await loadDIPL();
const source = `distance float = 24 m
duration float = 8 s
speed float = ({?distance} / {?duration}) m/s`;
const model = dipl.parse(source);
console.log(model.paths());
console.log(model.describe("speed").valueText);
console.log(model.describeMany(["distance", "duration", "speed"]));
const candidate = model.withOverride("distance = 32 m");
console.log(candidate.describe("speed").valueText);
console.log(dipl.validate(source, "missing = 1"));
candidate.dispose();
model.dispose();
withOverride evaluates the original source with a replacement override
body and returns a new model. It does not mutate the original. This uses the
same parser and validation rules as native DIPL. schemas() exposes the
schema definition and application hierarchy for building navigation or forms.
describeMany(paths, maxValueElements) returns one description per requested
path, in the same order, through a single WebAssembly call. It uses the model’s
already evaluated environment; each result has the same fields and value limits
as describe(). Pass the paths you need to avoid serializing the full model.
See Inspecting environments for the related C++ inspection API.
For a model split across several sources, pass text-backed registrations to
parseProject. This calls the core C++ project registration method, which
processes units, named schema bodies, existing overrides, and ordered code in
the same phases as a DIPfile. Code and override name fields are source
identities used in diagnostics; schema and unit name fields are their DIPL
names. Schema bodies and override bodies have no $schema or $override
wrapper. A website can fetch or generate this object without providing a
filesystem to the parser:
const project = {
units: [{ name: "custom_length", text: "m" }],
schemas: [{ name: "settings", text: "distance float = 24 custom_length\nduration float = 8 s" }],
code: [
{ name: "model.dip", text: "simulation : settings" },
{ name: "derived.dip", text: "speed float = ({?simulation.distance} / {?simulation.duration}) m/s" },
],
overrides: [],
};
const model = dipl.parseProject(project);
const next = model.withOverride("simulation.distance = 32 custom_length");
console.log(next.describe("speed").valueText);
console.log(dipl.validateProject(project, "unknown = 1"));
next.dispose();
model.dispose();
withOverride replaces the previous interactive override body and leaves
project.overrides intact. DIPL rejects two overrides of the same path. If the
project input already contains an override for the path being edited, replace
that entry in project.overrides and call parseProject again; the web
binding retains the core parser’s duplicate-override rule.
parseProject remains useful for projects assembled in memory. For a DIPfile
with local files, package it with the SNT CLI instead. The package command
uses the native C++ project loader to collect files opened during evaluation,
including sources[] entries and raw .dipt tables. It rejects missing
files and paths outside the DIPfile directory.
snt dip package --project path/to/DIPfile --output project.bundle.json
The result is a static JSON object containing format, entry and
files. Its paths are relative to the DIPfile directory. A site may fetch
this asset explicitly, then load it without further network access:
const bundle = await fetch("./project.bundle.json").then((response) => response.json());
const model = dipl.parseBundle(bundle);
console.log(model.describe("measurements.mass"));
const candidate = model.withOverride("scale = 4");
console.log(candidate.describe("result").valueText);
console.log(dipl.validateBundle(bundle, "scale = 4"));
candidate.dispose();
model.dispose();
The browser calls the same C++ DIPfile registration and table parsers as native
SNT. A missing bundled file or a path escaping the bundle is an error; the
runtime does not fetch it. $source paths derived from values are collected
when packaging the original project. If a later override selects a different
file, pass --include relative/path to snt dip package or the override
will fail clearly. Run
the package command from the project directory when its $source paths are
relative to the working directory. DIPH5 persistence, reports, adapters, and
DIPL source serialization are separate features.
PUQ conversion
loadPUQ exposes the existing C++ PUQConvert command to JavaScript.
It accepts the same expression, target units, and optional unit-system and
physical-quantity context as snt puq convert:
import { loadPUQ } from "./bindings/web/index.mjs";
const puq = await loadPUQ();
console.log(puq.convert("35*eV", "J"));
console.log(puq.convert("12*statA", "A", {
inputSystem: "ESU", outputSystem: "SI", outputQuantity: "I",
}));
console.log(puq.systems());
The PUQ conversion page includes a live browser converter using this interface. The documentation build compiles the WebAssembly module and copies it into Sphinx’s static assets.