1 /* This program is free software: you can redistribute it and/or modify
2 it under the terms of the GNU General Public License as published by
3 the Free Software Foundation, either version 3 of the License, or
4 (at your option) any later version.
6 This program is distributed in the hope that it will be useful,
7 but WITHOUT ANY WARRANTY; without even the implied warranty of
8 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 GNU General Public License for more details.
11 You should have received a copy of the GNU General Public License
12 along with this program. If not, see <http://www.gnu.org/licenses/>. */
14 const initialize = Promise.all([
15 fetch('forth.forth').then((re) => re.text()),
16 fetch('forth.wasm').then(re => re.arrayBuffer())
18 window.onload = () => {
22 let forthdiv = document.getElementById("forth")
23 if (forthdiv === null) {
24 forthdiv = document.createElement("div")
25 forthdiv.setAttribute("style","height:100%;margin:auto;width:80%;")
26 document.body.appendChild(forthdiv)
28 const outframe = document.createElement("div")
29 outframe.setAttribute("style", "background-color:black;padding-left:6px;padding-right:6px;color:chartreuse;height:256px;resize:vertical;display:flex;align-items:flex-end;")
30 const stackview = document.createElement("pre")
31 stackview.setAttribute("style", "white-space:pre-wrap;flex:0 1 8%;")
32 outframe.appendChild(stackview)
33 const txtoutput = document.createElement("pre")
34 txtoutput.setAttribute("style", "white-space:pre-wrap;overflow-y:scroll;flex:1 0 342px;")
35 outframe.appendChild(txtoutput)
36 const rstackview = document.createElement("pre")
37 rstackview.setAttribute("style", "white-space:pre-wrap;flex:0 1 8%;")
38 outframe.appendChild(rstackview)
39 const memview = document.createElement("pre")
40 memview.setAttribute("style", "white-space:pre-wrap;flex:0 0 356px;")
41 outframe.appendChild(memview)
42 const txtinput = document.createElement("textarea")
43 txtinput.setAttribute("autofocus", "true")
44 txtinput.setAttribute("rows", "1")
45 txtinput.setAttribute("style", "white-space:pre;margin-left:8%;width:60%;")
46 txtinput.oninput = () => txtinput.rows = (txtinput.value.match(/[\n]/g) || [1]).length;
47 forthdiv.appendChild(outframe)
48 forthdiv.appendChild(txtinput)
52 print: (string) => txtoutput.textContent += `\\\ => ${string} \n`
54 const updateViews = () => {
55 stackview.textContent = simstack.join('\n')
56 rstackview.textContent = rstack.join('\n')
59 let here = new DataView(wasmMem.buffer, 14340 /* here */, 4).getUint32(0,true)
60 memview
.textContent
= Array
.from(new Uint8Array(wasmMem
.buffer
, here
- maxBytes
, maxBytes
), (v
) => {
62 v
= ('0' + (v
& 0xFF).toString(16)).slice(-2)
71 outframe
.scrollTop
= outframe
.scrollHeight
75 txtinput
.addEventListener('keydown', (event
) => {
79 txtinput
.value
+= '\n'
83 stdin
+= txtinput
.value
84 txtoutput
.textContent
+= txtinput
.value
85 if (!/\s/.test(txtinput
.value
.slice(-1)))
86 txtoutput
.textContent
+= " "
88 event
.preventDefault()
89 event
.stopPropagation()
101 read
: (writeAddr
, maxBytes
) => {
102 const maxChars
= maxBytes
>> 1
103 const bufView
= new Uint16Array(wasmMem
.buffer
, writeAddr
, maxChars
)
105 for (i
= 0; i
< maxChars
&& i
< stdin
.length
; i
++)
106 bufView
[i
] = stdin
.charCodeAt(i
)
107 stdin
= stdin
.substring(maxChars
)
110 write
: (readAddr
, maxBytes
) =>
111 output
.print(String
.fromCharCode
.apply(
113 new Uint16Array(wasmMem
.buffer
, readAddr
, maxBytes
>> 1)
160 'EXECUTE-MODE': 16680,
166 pop
: () => simstack
.pop(),
167 push
: (val
) => simstack
.push(val
),
168 rinit
: () => rstack
.length
= 0,
169 rpop
: () => rstack
.pop(),
170 rpush
: (val
) => rstack
.push(val
),
171 sys_write
: (channel
, addr
, u
) => channels
[channel
] === undefined ? 0 : channels
[channel
].write(addr
, u
),
172 sys_read
: (channel
, addr
, u
) => channels
[channel
] === undefined ? 0 : channels
[channel
].read(addr
, u
),
173 sys_listen
: (reqAddr
, cbAddr
) => {
174 //TODO: call into the module to wasm fn "event" to push an event
175 //to forth, which pushes esi to the return stack and queues the
176 //callback function provided from listen. reqaddr could be
177 //"fetch", in which case no channel is used. otherwise very
178 //similar to sys_fetch.
180 sys_fetch
: (channel
, reqAddr
) => {
181 //TODO: map to fetch promise, write to channel buffer,
182 //javascript "fetch" as fallback, explicit handles for
183 //"textEntry" or any third party protocols like activitypub
184 console
.log(`fetch ${channel} ${reqAddr}`)
186 sys_echo
: (val
) => output
.print(`${val} `),
187 sys_echochar
: (val
) => output
.print(String
.fromCharCode(val
)),
188 sys_reflect
: (addr
) => {
189 console
.log(`reflect: ${addr}: ${
190 new DataView(wasmMem.buffer, addr, 4)
194 vocab_get
: (addr
, u
) => {
195 const word
= String
.fromCharCode
.apply(
197 new Uint16Array(wasmMem
.buffer
, addr
, u
>> 1)
199 const answer
= dictionary
[word
.toUpperCase()]
200 if (answer
=== undefined)
204 vocab_set
: (addr
, u
, num
) => {
205 const word
= String
.fromCharCode
.apply(
207 new Uint16Array(wasmMem
.buffer
, addr
, u
>> 1)
209 dictionary
[word
.toUpperCase()] = num
212 is_whitespace
: (key
) => /\s/.test(String
.fromCharCode(key
)),
213 sys_stack
: () => console
.log(`[${simstack}]`),
214 sys_parsenum
: (addr
, u
, base
) => {
215 const word
= String
.fromCharCode
.apply(
217 new Uint16Array(wasmMem
.buffer
, addr
, u
>> 1)
219 const answer
= Number
.parseInt(word
, base
)
220 if (Number
.isNaN(answer
))
222 new DataView(wasmMem
.buffer
, addr
, 4).setUint32(0,answer
,true)
226 output
.print(Object
.getOwnPropertyNames(dictionary
).toString().split(',').join(' '))
230 initialize
.then((results
) => {
232 WebAssembly
.instantiate(results
[1], wasmImport
).then((module
) => {
233 wasmMem
= module
.instance
.exports
.memory
234 forth
= module
.instance
.exports
.main
235 console
.log('wasm loaded')