X-Git-Url: https://git.kengrimes.com/?p=watForth.git;a=blobdiff_plain;f=forth.js;fp=forth.js;h=0844bd078e0a046e833e81ca146ba681ee2d5116;hp=3dbfc2b78daef434efcb2b2c8e97ebd61927e25a;hb=7946a78988064b7ec67e8d27763a962cebb29515;hpb=fb7946e17777ee389e3eaf742e6ff87a5d832710 diff --git a/forth.js b/forth.js index 3dbfc2b..0844bd0 100644 --- a/forth.js +++ b/forth.js @@ -17,44 +17,19 @@ const output = { print: (string) => {}, updateViews: () => {} } -const channels = [] -let wasmMem, forth - -/* Init */ -Promise.all([ - fetch('forth.forth', {credentials: 'include', headers:{'content-type':'text/plain'}}).then((re) => re.text()), - fetch('forth.wasm', {credentials: 'include', headers:{'content-type':'application/wasm'}}).then(re => re.arrayBuffer()) -]).then((results) => { - WebAssembly.instantiate(results[1], wasmImport).then((module) => { - wasmMem = module.instance.exports.memory.buffer - forth = (chno, data) => { - if (typeof chno !== "number") { - data = chno - chno = 0 - } - channels[chno].writeEnv(data) - if (chno === 0) - output.print(data) - module.instance.exports.main(chno) - output.updateViews() - } - console.log('wasm loaded') - forth(results[0]) - }) -}) +let wasmMem, wasmMain /* Environment functions */ -const wasmString = (addr, u) => - String.fromCharCode.apply( - null, - new Uint16Array(wasmMem, addr, u >> 1) - ) +const bufString = (arrayBuf, addr, u) => + String.fromCharCode.apply(null, new Uint16Array(arrayBuf, addr, u >> 1)) +const wasmString = (addr, u) => bufString(wasmMem, addr, u) +const wasmBase = () => new DataView(wasmMem, 14348, 4).getUint32(0,true) const strToArrayBuffer = (str) => { - const buf = new ArrayBuffer(str.length << 1); - const bufView = new Uint16Array(buf); + const buf = new ArrayBuffer(str.length << 1) + const bufView = new Uint16Array(buf) for (let i = 0, strLen = str.length; i < strLen; i++) - bufView[i] = str.charCodeAt(i); - return buf; + bufView[i] = str.charCodeAt(i) + return buf } const bufJoin = (buf1, buf2) => { const newBuf = new Uint8Array(buf1.byteLength + buf2.byteLength) @@ -64,8 +39,16 @@ const bufJoin = (buf1, buf2) => { } /* I/O Channel Drivers */ +const channels = [] +const getChannel = (chno) => { + const channel = channels[chno] + if (!channel) + throw new Error(`invalid channel access ${chno}`) + return channel +} class Channel { constructor(opt) { + opt = opt || Object.create(null) opt.chno = channels.indexOf(undefined) if (opt.chno === -1) opt.chno = channels.length @@ -75,57 +58,21 @@ class Channel { channels[this.chno] = this return this } - read(writeAddr, maxBytes) { - const wasmView = new Uint8Array(wasmMem, writeAddr, maxBytes) - const bufBytes = Math.min(maxBytes, this.buffer.byteLength) - const bufView = new Uint8Array(this.buffer, 0, bufBytes) - wasmView.set(bufView, 0) + read(writeArray, writeAddr, writeMax) { + const bufBytes = Math.min(writeMax, this.buffer.byteLength) + new Uint8Array(writeArray).set(new Uint8Array(this.buffer, 0, bufBytes), writeAddr) this.buffer = this.buffer.slice(bufBytes) return bufBytes } - write(readAddr, maxBytes) { - const newBuf = new Uint8Array(this.buffer.byteLength + maxBytes) - newBuf.set(new Uint8Array(this.buffer), 0) - newBuf.set(new Uint8Array(wasmMem, readAddr, maxBytes), this.buffer.byteLength) - this.buffer = newBuf + write(readArray, readAddr, readSize) { + this.buffer = bufJoin(this.buffer, new Uint8Array(readArray, readAddr, readSize)) + wasmMain(this.chno) + output.updateViews() } - writeEnv(data) { - switch (typeof data) { - case "string": - this.buffer = bufJoin(this.buffer, strToArrayBuffer(data)) - break - case "object" : - if (data instanceof ArrayBuffer) - this.buffer = bufJoin(this.buffer, data) - else - this.buffer = bufJoin(this.buffer, strToArrayBuffer(JSON.stringify(data))) - break - case "number" : - const buf = new ArrayBuffer(4) - new DataView(buf, 0, 4).setInt32(data) - this.buffer = bufJoin(this.buffer, buf) - break - default : - console.error(`environment wrote unhandled object: ${data}`) - return - } + send(readArray, readAddr, readSize) { + this.write(readArray, readAddr, readSize) } } -/* 0 STDIN, 1 STDOUT, 2 STDERR */ -new Channel({ - write(readAddr, maxBytes) { - super.write(readAddr, maxBytes) - output.print(wasmString(readAddr, maxBytes)) - } -}) -new Channel({ - read(writeAddr, maxBytes) { return 0 }, - write(readAddr, maxBytes) { output.print(`\\\ => ${wasmString(readAddr, maxBytes)}\n`) } -}) -new Channel({ - read(writeAddr, maxBytes) { return 0 }, - write(readAddr, maxBytes) { console.error(wasmString(readAddr, maxBytes)) } -}) /* System runtime */ const simstack = [] @@ -137,11 +84,12 @@ const wasmImport = { pop: () => simstack.pop(), push: (val) => simstack.push(val), rinit: () => rstack.length = 0, - rpop: () => rstack.length ? rstack.pop() : 16388, + rpop: () => rstack.pop(), rpush: (val) => rstack.push(val), - sys_write: (chno, addr, u) => channels[chno] === undefined ? 0 : channels[chno].write(addr, u), - sys_read: (chno, addr, u) => channels[chno] === undefined ? 0 : channels[chno].read(addr, u), - sys_listen: (chno, cbAddr, addr, u) => { //sys_connect? + sys_write: (chno, addr, u) => getChannel(chno).write(wasmMem, addr, u), + sys_read: (chno, addr, u) => getChannel(chno).read(wasmMem, addr, u), + sys_send: (chno, addr, u) => getChannel(chno).send(wasmMem, addr, u), + sys_connect: (addr, u) => { //TODO: call into the module to wasm fn "event" to push an event //to forth, which pushes esi to the return stack and queues the //callback function provided from listen. reqaddr could be @@ -153,33 +101,35 @@ const wasmImport = { //other listen types: mouse, keyboard, touch, main_loop (10ms interval), wrtc, etc //fetch is different because it offers no "write" interface, doesn't repeat }, - sys_fetch: (cbAddr, addr, u) => { + sys_fetch: (chno, addr, u) => { const str = wasmString(addr, u) - console.log(`fetching: ${str}`) + console.log(`fetching: ${str} || ${addr} ${u}`) const args = JSON.parse(wasmString(addr, u)) console.log(args) const url = args.url delete args.url - const channel = new Channel() + const channel = channels[chno] + if (!channel) { + console.error(`invalid channel fetch: ${chno}`) + return -1 + } fetch(url, args).then((re) => { if (args.headers === undefined || args.headers['content-type'] === undefined || args.headers['content-type'].toLowerCase().indexOf('text/plain') === 0 ) - re.text().then((txt) => forth(channel.chno, txt)) + re.text().then((txt) => channel.write(strToArrayBuffer(txt), 0, txt.length << 1)) else { const reader = new FileReader() - reader.onload = (evt) => forth(channel.chno, evt.target.result) + reader.onload = (evt) => channel.write(evt.target.result, 0, evt.target.result.byteLength) re.blob().then((blob) => reader.readAsArrayBuffer(blob)) } }).catch(console.error) - //TODO: map to fetch promise, write to channel buffer, - //javascript "fetch" as fallback, explicit handles for - //"textEntry" or any third party protocols like activitypub - return channel.chno + return 0 }, - sys_close: (chno) => delete channels[chno], - sys_echo: (val, base) => output.print(`\\\ => ${val.toString(base)} `), - sys_echochar: (val) => output.print(String.fromCharCode(val)), + sys_open: () => new Channel().chno, + sys_close: (chno) => chno < 3 ? 0 : delete channels[chno], + sys_echo: (val) => output.print(`\\\ => ${val.toString(wasmBase())}\n`), + sys_log: (addr, u) => console.log(`=> ${wasmString(addr, u)}`), sys_reflect: (addr) => { console.log(`reflect: ${addr}: ${ new DataView(wasmMem, addr, 4) @@ -192,8 +142,8 @@ const wasmImport = { does_set: (addr, u, v) => doesDictionary[wasmString(addr, u).toUpperCase()] = v, is_whitespace: (key) => /\s/.test(String.fromCharCode(key)), sys_stack: () => console.log(`[${simstack}][${rstack}]`), - sys_parsenum: (addr, u, base) => { - const answer = Number.parseInt(wasmString(addr, u), base) + sys_parsenum: (addr, u) => { + const answer = Number.parseInt(wasmString(addr, u), wasmBase()) if (Number.isNaN(answer)) return -1 new DataView(wasmMem, addr, 4).setUint32(0,answer,true) @@ -205,6 +155,32 @@ const wasmImport = { } } +/* Initialization */ +/* 0 STDIN, 1 STDOUT, 2 STDERR */ +new Channel({ + send(readArray, readAddr, readSize) { output.print(bufString(readArray, readAddr, readSize)) } +}) +new Channel({ + write(readArray, readAddr, readSize) { output.print(bufString(readArray, readAddr, readSize)) }, + send(readArray, readAddr, readSize) { output.print(`\n\\\ => ${bufString(readArray, readAddr, readSize)}\n`) } +}) +new Channel({ + write(readArray, readAddr, readSize) { console.error(bufString(readArray, readAddr, readSize)) } +}) + +/* Fetch wasm file, and initial forth file */ +Promise.all([ + fetch('forth.forth', {credentials: 'include', headers:{'content-type':'text/plain'}}).then((re) => re.text()), + fetch('forth.wasm', {credentials: 'include', headers:{'content-type':'application/wasm'}}).then(re => re.arrayBuffer()) +]).then((results) => { + WebAssembly.instantiate(results[1], wasmImport).then((module) => { + wasmMem = module.instance.exports.memory.buffer + wasmMain = module.instance.exports.main + console.log('wasm loaded') + getChannel(0).write(strToArrayBuffer(results[0])) + }) +}) + /* View Logic */ window.onload = () => { let forthdiv = document.getElementById("forth") @@ -250,7 +226,7 @@ window.onload = () => { txtinput.value += " " event.preventDefault() event.stopPropagation() - forth(txtinput.value) + getChannel(0).write(strToArrayBuffer(txtinput.value)) txtinput.value = "" } break @@ -265,7 +241,7 @@ window.onload = () => { /* Set up output functions */ output.print = (string) => txtoutput.textContent += string, output.updateViews = () => { - const base = new DataView(wasmMem, 14348 /* base */, 4).getUint32(0,true) + const base = wasmBase() stackview.textContent = simstack.map((v) => v.toString(base)).join('\n') // rstackview.textContent = rstack.join('\n') let cnt = 0;