Skip to content

Commit

Permalink
Release 0.5.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
RubenVerg committed Aug 2, 2024
1 parent 59a343a commit b317fe8
Show file tree
Hide file tree
Showing 9 changed files with 498 additions and 9 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ TinyAPL (read like *tiny apple*) is a tiny APL dialect and interpreter in Haskel
* Part 5: [Array Notation and Reductions](https://blog.rubenverg.com/tinyapl_5_array_notation_reductions)
* Part 6: [Tests, Docs, Each](https://blog.rubenverg.com/tinyapl_6_tests_docs_each)
* Part 7: [Quads, Key, Index](https://blog.rubenverg.com/tinyapl_7_quads_key_index)
* Part 8: [All About Rank, and a Web Interface](https://blog.rubenverg.com/tinyapl_8_rank_web)

Documentation is available [here](https://tinyapl.rubenverg.com)
Documentation is available [here](https://tinyapl.rubenverg.com) and you can run the latest interpreter [here](https://tinyapl.rubenverg.com/run/latest)

## Features

Expand Down
122 changes: 122 additions & 0 deletions docs/interpreters/0.5.0/ghc_wasm_jsffi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// This file implements the JavaScript runtime logic for Haskell
// modules that use JSFFI. It is not an ESM module, but the template
// of one; the post-linker script will copy all contents into a new
// ESM module.

// Manage a mapping from unique 32-bit ids to actual JavaScript
// values.
class JSValManager {
#lastk = 0;
#kv = new Map();

constructor() {}

// Maybe just bump this.#lastk? For 64-bit ids that's sufficient,
// but better safe than sorry in the 32-bit case.
#allocKey() {
let k = this.#lastk;
while (true) {
if (!this.#kv.has(k)) {
this.#lastk = k;
return k;
}
k = (k + 1) | 0;
}
}

newJSVal(v) {
const k = this.#allocKey();
this.#kv.set(k, v);
return k;
}

// A separate has() call to ensure we can store undefined as a value
// too. Also, unconditionally check this since the check is cheap
// anyway, if the check fails then there's a use-after-free to be
// fixed.
getJSVal(k) {
if (!this.#kv.has(k)) {
throw new WebAssembly.RuntimeError(`getJSVal(${k})`);
}
return this.#kv.get(k);
}

// Check for double free as well.
freeJSVal(k) {
if (!this.#kv.delete(k)) {
throw new WebAssembly.RuntimeError(`freeJSVal(${k})`);
}
}
}

// A simple & fast setImmediate() implementation for browsers. It's
// not a drop-in replacement for node.js setImmediate() because:
// 1. There's no clearImmediate(), and setImmediate() doesn't return
// anything
// 2. There's no guarantee that callbacks scheduled by setImmediate()
// are executed in the same order (in fact it's the opposite lol),
// but you are never supposed to rely on this assumption anyway
class SetImmediate {
#fs = [];
#mc = new MessageChannel();

constructor() {
this.#mc.port1.addEventListener("message", () => {
this.#fs.pop()();
});
this.#mc.port1.start();
}

setImmediate(cb, ...args) {
this.#fs.push(() => cb(...args));
this.#mc.port2.postMessage(undefined);
}
}

// The actual setImmediate() to be used. This is a ESM module top
// level binding and doesn't pollute the globalThis namespace.
let setImmediate;
if (globalThis.setImmediate) {
// node.js, bun
setImmediate = globalThis.setImmediate;
} else {
try {
// deno
setImmediate = (await import("node:timers")).setImmediate;
} catch {
// browsers
const sm = new SetImmediate();
setImmediate = (cb, ...args) => sm.setImmediate(cb, ...args);
}
}

export default (__exports) => {
const __ghc_wasm_jsffi_jsval_manager = new JSValManager();
const __ghc_wasm_jsffi_finalization_registry = new FinalizationRegistry(sp => __exports.rts_freeStablePtr(sp));
return {
newJSVal: (v) => __ghc_wasm_jsffi_jsval_manager.newJSVal(v),
getJSVal: (k) => __ghc_wasm_jsffi_jsval_manager.getJSVal(k),
freeJSVal: (k) => __ghc_wasm_jsffi_jsval_manager.freeJSVal(k),
scheduleWork: () => setImmediate(__exports.rts_schedulerLoop),
ZC25ZCtinyaplzm0zi5zi0zi0zminplacezmtinyaplzmjsZCMainZC: ($1,$2) => {return [$1, $2];},
ZC26ZCtinyaplzm0zi5zi0zi0zminplacezmtinyaplzmjsZCMainZC: async ($1,$2) => {await $1($2);},
ZC27ZCtinyaplzm0zi5zi0zi0zminplacezmtinyaplzmjsZCMainZC: async ($1) => {return await $1();},
ZC28ZCtinyaplzm0zi5zi0zi0zminplacezmtinyaplzmjsZCMainZC: ($1) => {return $1;},
ZC29ZCtinyaplzm0zi5zi0zi0zminplacezmtinyaplzmjsZCMainZC: ($1) => {return $1;},
ZC30ZCtinyaplzm0zi5zi0zi0zminplacezmtinyaplzmjsZCMainZC: ($1,$2) => {const a = $2.slice(); a.unshift($1); return a;},
ZC31ZCtinyaplzm0zi5zi0zi0zminplacezmtinyaplzmjsZCMainZC: () => {return [];},
ZC0ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: ($1,$2) => ($1.reject(new WebAssembly.RuntimeError($2))),
ZC16ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: ($1,$2) => ($1.resolve($2)),
ZC18ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: ($1,$2) => ($1.resolve($2)),
ZC19ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: ($1) => ($1.resolve()),
ZC20ZCghczminternalZCGHCziInternalziWasmziPrimziExportsZC: () => {let res, rej; const p = new Promise((resolve, reject) => { res = resolve; rej = reject; }); p.resolve = res; p.reject = rej; return p;},
ZC17ZCghczminternalZCGHCziInternalziWasmziPrimziImportsZC: ($1,$2) => ($1.then(res => __exports.rts_promiseResolveJSVal($2, res), err => __exports.rts_promiseReject($2, err))),
ZC18ZCghczminternalZCGHCziInternalziWasmziPrimziImportsZC: ($1,$2) => ($1.then(() => __exports.rts_promiseResolveUnit($2), err => __exports.rts_promiseReject($2, err))),
ZC0ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1) => (`${$1.stack ? $1.stack : $1}`),
ZC1ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1,$2) => ((new TextDecoder('utf-8', {fatal: true})).decode(new Uint8Array(__exports.memory.buffer, $1, $2))),
ZC2ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1,$2,$3) => ((new TextEncoder()).encodeInto($1, new Uint8Array(__exports.memory.buffer, $2, $3)).written),
ZC3ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1) => ($1.length),
ZC4ZCghczminternalZCGHCziInternalziWasmziPrimziTypesZC: ($1) => {if (!__ghc_wasm_jsffi_finalization_registry.unregister($1)) { throw new WebAssembly.RuntimeError('js_callback_unregister'); }},
ZC0ZCghczminternalZCGHCziInternalziWasmziPrimziConcziInternalZC: async ($1) => (new Promise(res => setTimeout(res, $1 / 1000))),
};
};
81 changes: 81 additions & 0 deletions docs/interpreters/0.5.0/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<!doctype html>
<head>
<meta charset="utf-8">
<title>TinyAPL Interpreter</title>
<link rel="icon" type="img/svg+xml" href="/logo.svg">
<link rel="stylesheet" href="/style.css">
<style>
#buttons button {
font-family: var(--font-mono);
}

#output {
height: 90%;
font-family: var(--font-mono);
}

#input, #highlighted {
width: 97.5%;
font-family: var(--font-mono);
margin: 2.5px;
padding: 2.5px;
height: 1.25em;
grid-row: 1;
grid-column: 1;
overflow-x: auto;
white-space: nowrap;
font-size: 1rem;
border: 1px solid black;
}

#input {
z-index: 1;
color: transparent;
background-color: transparent;
caret-color: black;
resize: none;
}

#highlighted {
z-index: 0;
}

#button {
width: auto;
font-family: var(--font-mono);
}

#input-wrapper {
display: inline-grid;
width: 90%;
}

.code {
color: grey;
}

.error {
background: #ffcccc;
color: red;
}

.result {
color: black;
}

.quad {
color: dimgrey;
}
</style>
</head>
<body>
<div id="buttons"></div>
<pre id="output"></pre>
<div id="input-wrapper">
<textarea id="input" spellcheck="false" cols="1"></textarea>
<pre id="highlighted" aria-hidden="true"></pre>
</div>
<button id="button" type="button">Run</button>
<pre id="test"></pre>
<script type="module" src="./index.js"></script>
</body>
Loading

0 comments on commit b317fe8

Please sign in to comment.