Skip to content

The playground

The whole compiler, running in a browser tab, with five views of the same programme side by side.

make -C examples web-serve

Then http://localhost:8000/. Nothing is uploaded, nothing is stored, and there is no OCaml on the far end.

What it shows

The five views are the five the command line has, and the source pane is beside them.

Run is what the programme prints. The status line reports whether both back ends agree, which is the differential test of the back ends running on whatever you typed: the programme is executed twice, once by walking the tree and once through the emitted Python.

Types is the signature inferred, as ocamlc -i would print it. A type variable written '_weak1 rather than 'a has not been generalized, which is the value restriction.

Names is the scope tree, one per namespace. Reading it answers the questions people actually get stuck on: which x does this x refer to, does this recursive call resolve, does that parameter escape the function.

Python is what the second back end emits, and Printed is your source read back out of the tree, which is how you check the grouping is what you meant.

Two help pages sit beside it, linked from the header: one on using the playground, and a language reference.

How it is built

Pyodide is CPython compiled to WebAssembly. Both packages here are pure Python with no dependencies, so the toolchain is one zip and one call:

await py.unpackArchive(await bundle.arrayBuffer(), "zip", { extractDir: HOME });
analyse = py.runPython("from ocaml.pipeline import analyse\nanalyse");

web/build.py zips src/astero, src/ocaml and the forty-one corpus programmes straight from the working tree. 166 KB, nothing vendored, so what the page runs is what the tests run.

The whole Python side is one function. ocaml.pipeline.analyse(source) runs every stage and returns what each produced, so the page is a view over one dictionary and the JavaScript stays dumb. python -m ocaml prints one of its keys; the page renders all five. Neither repeats the sequence.

The reference page is generated

language.html is a card covering the whole subset: definitions, types, expressions, patterns, operators, keywords, the standard library, and the deliberate divergences from real OCaml.

Three of its tables are read off the compiler when the page is built:

table read from
operators, with precedence and associativity front/parser.py's LEVELS
keywords front/lexer.py's KEYWORDS
every standard-library name, with its type middle/prelude.py's PRELUDE_TYPES

So adding a function to the prelude puts it in the documentation, with its type, without anybody editing the page. A test asserts the generated page contains all three tables in full, so the derivation cannot quietly stop.

The prose lives in language.template.html, which is in the repository; language.html is a build product, like the bundle.

Deploying it

The site is the contents of web/ after make -C examples web. Any static host will serve it: there is no server side, and the only thing fetched from elsewhere is Pyodide itself, from a CDN, pinned to one version in a constant at the top of app.js.

If it does not load, the status line says why. The two usual causes are that the CDN has moved on, in which case bump PYODIDE_VERSION, and that the bundle has not been built, in which case run make -C examples web.