Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pascal in the browser

Running Pascal fully client-side — no server, no upload, nothing leaving the page.

This repository holds two things:

  • packages/pascal-wasm — the publishable package @live-codes/pascal-wasm: the Free Pascal pas2js compiler compiled to WebAssembly, with a small API for compiling Pascal in a page or a Web Worker.
  • poc/ — a playground that uses the package, to prove the whole path works end to end.

Quick start

npm start          # serves the repository

Then open the printed URL (http://localhost:8080/poc/). The page loads the compiler in a Web Worker, compiles whatever is in the editor and runs the result in a sandboxed frame. Ctrl+Enter runs.

poc/classic-worker.html is the same thing in a non-module worker, consuming the IIFE bundle with importScripts().

npm test           # the package's test suite

The package

import { compile } from '@live-codes/pascal-wasm';

const { js, diagnostics } = await compile('begin Writeln(42) end.');

It works in browsers, in module and classic Web Workers and in Node, needs no build step, and resolves its assets relative to itself so it can be imported straight from a CDN. A minified IIFE build (dist/pascal-wasm.iife.min.js, ~27 kB) is shipped for non-module workers and plain <script> tags, exposing the same API on a global named pascalWasm.

See the package README for the full API — createCompiler, compile, parseDiagnostics, configure, compiler flags, extra units, sourceMap, hosting the assets yourself, and worker usage.

What a compiled program can reach

Because pas2js emits JavaScript rather than wasm, a compiled program is an ordinary page citizen. Each of these was verified running in a browser (see poc/examples/):

Capability Status How
Read / modify the DOM native uses webdocument.createElement, innerHTML, window.innerWidth
Call host-provided JS native procedure hostLog(const msg: String); external name 'hostLog';
stdout native Write/Writeln reach console.log; SetWriteCallBack redirects them
stderr / errors native uncaught exceptions carry $classname, fMessage and a JS stack, delivered via rtl.onUncaughtException
Exit code native ExitCode := 3 is the RTL's rtl.exitcode, returned by rtl.run()
stdin not in the RTL there is no Read/Readln — input has to be host-supplied

So none of stdout, errors or the exit code need mocking; only input does, because the pas2js RTL has no Read/Readln at all. window.prompt covers the synchronous case; anything non-blocking would need SharedArrayBuffer + Atomics.wait (and cross-origin isolation) or a Worker, since a synchronous read cannot await a message.

Source maps

The compiler emits source maps, which is what turns the JavaScript-level error stack into a line in the user's source. From the compiler's own -h:

-Jm                 generate source maps
-Jminclude          include the Pascal sources in the map
-Jmbasedir=<dir>    write source file names relative to <dir>
-Jmsourceroot=<x>   sourceRoot prefix for source file names
-Jmabsolute         store absolute filenames
-Jmxssiheader-      drop the XSSI ")]}'" prefix (it is on by default)

Two things only show up once you try it, and both are handled for you by sourceMap: true:

  • -Jm on its own fails: source names must be expressible relative to the map's folder, and the RTL lives outside it. Hence -Jmbasedir=/.
  • Maps start with the XSSI guard )]}', which is not valid JSON. Hence -Jmxssiheader-.

How it works

The pas2js compiler is built for wasm32-wasi, so the package supplies an in-memory filesystem and a WASI implementation (browser_wasi_shim, vendored) and runs the compiler as an ordinary WASI program. Compiling a program takes roughly 20–500 ms once the binary is loaded, and the compiled module is reused across calls.

Three details are easy to trip over, all reproduced from the official FPC browser compiler demo:

  1. The compiler resolves argv[0] to find its own directory and aborts with EFOpenError if that path does not exist, so a /bin/pas2js stub and a /pas2js path both have to exist.
  2. -n is needed so it does not look for a pas2js.cfg.
  3. Instantiation must be asynchronous: Chrome refuses a synchronous new WebAssembly.Instance for buffers over 8 MB, and this binary is ~9 MB.

Licensing

LGPL-2.1-or-later, matching pas2js, which is itself LGPL-2.1 — this package redistributes its compiler binary and runtime library. The bundled WASI shim is MIT OR Apache-2.0.

THIRD-PARTY-NOTICES.md records versions, licenses, the provenance and SHA-256 of the compiler binary, and where to find the corresponding source.

Note that the bundled pas2js.wasm comes from the deployment of the official Free Pascal demo rather than a tagged release (the official release archives contain no WebAssembly at all, and the demo tracks trunk — it reports 3.3.1 while the latest release is 3.2.0). Building the compiler from source with FPC and demo/webcompiler is the route to a version-pinned binary.

Layout

packages/pascal-wasm/     the published package
  src/                    ESM glue: assets, compiler, config, public API
  src/vendor/             vendored WASI shim
  scripts/build.mjs       builds the classic-script bundle
  dist/                   pascal-wasm.iife.min.js (built, committed)
  assets/                 pas2js.wasm and the Pascal RTL sources
  types/                  TypeScript declarations
  test/                   test suite (npm test)
poc/                      the playground
  index.html, app.js      UI, and running the compiled program
  compiler-worker.js      compiles in a module worker
  classic-worker.html     the same via the IIFE bundle in a classic worker
  examples/               sample programs
tools/serve.mjs           static file server for local development

Publishing

The package is ready for npm publish --access public from packages/pascal-wasm. Once published it is immediately importable from a CDN:

import { compile } from 'https://cdn.jsdelivr.net/npm/@live-codes/pascal-wasm@0.1.0/src/index.js';

Add a repository field to packages/pascal-wasm/package.json first — it was left out because the remote for this checkout is unknown.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages