R0 / S1 Standalone WebAssembly Demo

Counter: 0

Application logic: R0 → WebAssembly
Browser plumbing: JavaScript (handwritten, no Emscripten JS runtime)

This is the same R0 application as the W1.1 demo, but the WebAssembly module is built -nostdlib with a 22 KB glon.wasm, a handwritten glon.js, and no virtual filesystem: the source below is inlined in the page and passed to glon_load at runtime.

R0 / GLON application

counter: 0

increment: [
    counter: + counter 1
    web-set-int 1 counter
]
Show low-level RAW/S1 host adapter

RAW is R0’s trapdoor to the tiny Forth-like S1 substrate. Application code normally stays above this layer.

web-set-int: raw 2 [
    LIT 16 DIV
    >R
    LIT 16 DIV
    LIT 1000000 MUL
    R> ADD
    PRINT
    ARITY 0 EXIT
]
Show handwritten JavaScript bootloader

This bootloader contains no counter state or increment logic; it only instantiates glon.wasm, supplies the two host imports, and forwards clicks / DOM updates.

/* standalone/glon.js -- the handwritten JavaScript half of the standalone
 * WebAssembly boundary.
 *
 * This file REPLACES the generated Emscripten runtime from W1 (web/demo.js):
 * it instantiates the raw glon.wasm (built with `-nostdlib`, no Emscripten JS
 * runtime, no virtual filesystem) and supplies only the two host imports the
 * C side declares.  JavaScript responsibilities remain strictly limited to:
 *
 *   - host_print:  capture the host's plain text output (console.log);
 *   - host_set_text: decode a numeric DOM handle and write the value into the
 *     element tagged with data-glon-id="<handle>";
 *   - feed the R0/GLON source (inlined as <script type="application/glon">)
 *     into glon_load once at startup;
 *   - forward clicks on [data-glon-click="<word>"] to glon_call.
 *
 * No counter state or application logic lives here.
 */
(function () {
  "use strict";

  var ex = null;                 /* WebAssembly instance exports */
  var dec = new TextDecoder();
  var enc = new TextEncoder();

  /* The WASM memory can grow (ALLOW_MEMORY_GROWTH); always take a fresh view. */
  function view() {
    return new Uint8Array(ex.memory.buffer);
  }

  var imports = {
    env: {
      host_print: function (ptr, len) {
        var bytes = view().subarray(ptr, ptr + len);
        console.log("[glon]", dec.decode(bytes));
      },
      host_set_text: function (handle, value) {
        var el = document.querySelector('[data-glon-id="' + handle + '"]');
        if (el) el.textContent = String(value);
      }
    }
  };

  /* copy a JS string into WASM memory; returns [ptr, byteLength] */
  function alloc(str) {
    var bytes = enc.encode(str);
    var p = ex.glon_alloc(bytes.length);
    view().set(bytes, p);
    return [p, bytes.length];
  }

  function boot() {
    if (ex.glon_init() !== 0) {
      console.error("glon.js: glon_init failed");
      return;
    }

    var src = document.querySelector('script[type="application/glon"]');
    if (!src) {
      console.error('glon.js: no <script type="application/glon"> found');
      return;
    }

    var pair = alloc(src.textContent);
    var rc = ex.glon_load(pair[0], pair[1]);
    if (rc !== 0) {
      console.error("glon.js: glon_load failed (rc=" + rc + ")");
      return;
    }

    document.querySelectorAll("[data-glon-click]").forEach(function (btn) {
      var name = btn.getAttribute("data-glon-click");
      btn.addEventListener("click", function () {
        var p2 = alloc(name);
        ex.glon_call(p2[0], p2[1]);
      });
    });
  }

  function ready(result) {
    ex = result.instance.exports;
    boot();
  }

  function fail(err) {
    console.error("glon.js: failed to load glon.wasm", err);
  }

  function loadBytes(bytes) {
    WebAssembly.instantiate(bytes, imports).then(ready).catch(fail);
  }

  /* instantiateStreaming requires the correct application/wasm MIME type and
   * an http(s) origin; fall back to a plain fetch + instantiate otherwise. */
  if (typeof WebAssembly.instantiateStreaming === "function" && window.fetch) {
    WebAssembly.instantiateStreaming(fetch("glon.wasm"), imports)
      .then(ready)
      .catch(function () {
        fetch("glon.wasm")
          .then(function (resp) { return resp.arrayBuffer(); })
          .then(loadBytes)
          .catch(fail);
      });
  } else if (window.fetch) {
    fetch("glon.wasm")
      .then(function (resp) { return resp.arrayBuffer(); })
      .then(loadBytes)
      .catch(fail);
  } else {
    fail(new Error("no fetch / WebAssembly support"));
  }
})();