2022-04-17 09:28:53 +02:00
|
|
|
import wasmModule from "../waforth.wat";
|
2022-04-14 21:09:05 +02:00
|
|
|
|
2019-03-10 10:37:01 +01:00
|
|
|
const isSafari =
|
|
|
|
typeof navigator != "undefined" &&
|
|
|
|
/^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2022-04-14 23:00:45 +02:00
|
|
|
// eslint-disable-next-line no-unused-vars
|
2022-04-14 21:46:06 +02:00
|
|
|
const arrayToBase64 =
|
2022-04-14 23:00:45 +02:00
|
|
|
typeof Buffer === "undefined"
|
2022-04-14 21:46:06 +02:00
|
|
|
? function arrayToBase64(bytes) {
|
2019-03-10 14:32:41 +01:00
|
|
|
var binary = "";
|
|
|
|
var len = bytes.byteLength;
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
|
|
binary += String.fromCharCode(bytes[i]);
|
|
|
|
}
|
|
|
|
return window.btoa(binary);
|
2022-04-14 21:46:06 +02:00
|
|
|
}
|
2022-04-14 23:00:45 +02:00
|
|
|
: function arrayToBase64(s) {
|
2022-04-14 21:46:06 +02:00
|
|
|
return Buffer.from(s).toString("base64");
|
2019-03-10 14:32:41 +01:00
|
|
|
};
|
2022-04-14 21:46:06 +02:00
|
|
|
|
|
|
|
class WAForth {
|
|
|
|
constructor() {}
|
2019-03-10 10:37:01 +01:00
|
|
|
|
2019-11-08 16:19:11 +01:00
|
|
|
start() {
|
2018-05-12 21:09:19 +02:00
|
|
|
let table;
|
2018-05-27 21:49:22 +02:00
|
|
|
let memory;
|
2018-06-03 15:17:38 +02:00
|
|
|
const buffer = (this.buffer = []);
|
2018-05-12 21:09:19 +02:00
|
|
|
|
2022-04-14 21:09:05 +02:00
|
|
|
return WebAssembly.instantiate(wasmModule, {
|
2018-05-29 13:06:24 +02:00
|
|
|
shell: {
|
|
|
|
////////////////////////////////////////
|
|
|
|
// I/O
|
|
|
|
////////////////////////////////////////
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2018-05-29 13:06:24 +02:00
|
|
|
emit: this.onEmit,
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2019-03-12 09:04:39 +01:00
|
|
|
getc: () => {
|
2018-06-03 15:17:38 +02:00
|
|
|
if (buffer.length === 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return buffer.pop();
|
|
|
|
},
|
|
|
|
|
2022-04-13 17:02:46 +02:00
|
|
|
debug: (d) => {
|
2019-03-14 16:32:27 +01:00
|
|
|
console.log("DEBUG: ", d, String.fromCharCode(d));
|
2018-05-29 13:06:24 +02:00
|
|
|
},
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2019-03-12 09:22:33 +01:00
|
|
|
key: () => {
|
|
|
|
let c;
|
|
|
|
while (c == null || c == "") {
|
|
|
|
c = window.prompt("Enter character");
|
|
|
|
}
|
|
|
|
return c.charCodeAt(0);
|
|
|
|
},
|
|
|
|
|
|
|
|
accept: (p, n) => {
|
|
|
|
const input = (window.prompt("Enter text") || "").substr(0, n);
|
|
|
|
const target = new Uint8Array(memory.buffer, p, input.length);
|
|
|
|
for (let i = 0; i < input.length; ++i) {
|
|
|
|
target[i] = input.charCodeAt(i);
|
|
|
|
}
|
|
|
|
console.log("ACCEPT", p, n, input.length);
|
|
|
|
return input.length;
|
|
|
|
},
|
|
|
|
|
2018-05-29 13:06:24 +02:00
|
|
|
////////////////////////////////////////
|
|
|
|
// Loader
|
|
|
|
////////////////////////////////////////
|
2018-05-13 17:07:54 +02:00
|
|
|
|
2018-05-29 13:06:24 +02:00
|
|
|
load: (offset, length, index) => {
|
|
|
|
let data = new Uint8Array(
|
|
|
|
this.core.exports.memory.buffer,
|
|
|
|
offset,
|
|
|
|
length
|
|
|
|
);
|
|
|
|
if (isSafari) {
|
|
|
|
// On Safari, using the original Uint8Array triggers a bug.
|
|
|
|
// Taking an element-by-element copy of the data first.
|
|
|
|
let dataCopy = [];
|
|
|
|
for (let i = 0; i < length; ++i) {
|
|
|
|
dataCopy.push(data[i]);
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
2018-05-29 13:06:24 +02:00
|
|
|
data = new Uint8Array(dataCopy);
|
|
|
|
}
|
|
|
|
if (index >= table.length) {
|
|
|
|
table.grow(table.length); // Double size
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
2022-04-14 21:46:06 +02:00
|
|
|
// console.log("Load", index, arrayToBase64(data));
|
2018-05-29 13:06:24 +02:00
|
|
|
var module = new WebAssembly.Module(data);
|
|
|
|
new WebAssembly.Instance(module, {
|
2022-04-13 17:02:46 +02:00
|
|
|
env: { table, memory, tos: -1 },
|
2018-05-29 13:06:24 +02:00
|
|
|
});
|
2022-04-13 17:02:46 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}).then((instance) => {
|
2018-05-29 13:06:24 +02:00
|
|
|
this.core = instance.instance;
|
|
|
|
table = this.core.exports.table;
|
|
|
|
memory = this.core.exports.memory;
|
|
|
|
});
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
read(s) {
|
|
|
|
const data = new TextEncoder().encode(s);
|
2018-06-03 15:17:38 +02:00
|
|
|
for (let i = data.length - 1; i >= 0; --i) {
|
|
|
|
this.buffer.push(data[i]);
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
|
|
|
}
|
2018-05-13 17:07:54 +02:00
|
|
|
|
|
|
|
run(s) {
|
|
|
|
this.read(s);
|
2019-03-12 14:26:38 +01:00
|
|
|
try {
|
|
|
|
return this.core.exports.interpret();
|
|
|
|
} catch (e) {
|
|
|
|
// Exceptions thrown from the core means QUIT or ABORT is called, or an error
|
|
|
|
// has occurred. Assume what has been done has been done, and ignore here.
|
|
|
|
}
|
2018-05-13 17:07:54 +02:00
|
|
|
}
|
2018-05-12 21:09:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export default WAForth;
|