c05d444d08215e8daf4bc804fbab3454fd29830e
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 = () => {
19 let forthdiv = document.getElementById("forth")
20 if (forthdiv === null) {
21 forthdiv = document.createElement("div")
22 forthdiv.setAttribute("style","height:400px;")
23 document.body.appendChild(forthdiv)
25 const outframe = document.createElement("div")
26 outframe.setAttribute("style", "overflow-y:hidden;height:40%;")
27 const txtoutput = document.createElement("pre")
28 txtoutput.setAttribute("style", "white-space:pre-wrap;")
29 const txtinput = document.createElement("textarea")
30 let txtinputrows = "1"
31 txtinput.setAttribute("autofocus", "true")
32 txtinput.setAttribute("cols", "60")
33 txtinput.setAttribute("rows", txtinputrows)
34 outframe.appendChild(txtoutput)
35 forthdiv.appendChild(outframe)
36 forthdiv.appendChild(txtinput)
38 print: (string) => txtoutput.textContent += string
45 txtinput
.addEventListener('keydown', (event
) => {
49 txtinput
.value
+= '\n'
50 txtinputrows
= (Number
.parseInt(txtinputrows
, 10) + 1).toString()
51 txtinput
.setAttribute("rows", txtinputrows
)
54 stdin
+= txtinput
.value
55 txtoutput
.textContent
+= txtinput
.value
+ '\n'
58 txtinput
.setAttribute("rows", txtinputrows
)
59 event
.preventDefault()
60 event
.stopPropagation()
62 txtoutput
.textContent
+= " _ok.\n"
63 outframe
.scrollTop
= outframe
.scrollHeight
;
67 if (txtinput
.value
.charCodeAt(txtinput
.value
.length
- 1) === 10) {
68 txtinputrows
= (Number
.parseInt(txtinputrows
, 10) - 1).toString()
69 txtinput
.setAttribute("rows", txtinputrows
)
76 /* document.addEventListener('keydown', (event) => {
77 //console.log(`keydown: ${event.key}`)
78 if (event.key != "F5") {
79 event.preventDefault()
80 event.stopPropagation()
83 txtoutput.textContent += '\n'
87 outframe.scrollTop = outframe.scrollHeight;
88 txtoutput.textContent += '\n'
91 stdin = stdin.substring(0, stdin.length - 1)
92 txtoutput.textContent = txtoutput.textContent.substring(0, txtoutput.textContent.length - 1)
95 if (event.key.length == 1) {
96 const input = event.ctrlKey ? ` \\\^${event.key} ` : event.key
106 read
: (writeAddr
, maxBytes
) => {
107 const maxChars
= maxBytes
>> 1
108 const bufView
= new Uint16Array(wasmMem
.buffer
, writeAddr
, maxChars
)
110 for (i
= 0; i
< maxChars
&& i
< stdin
.length
; i
++)
111 bufView
[i
] = stdin
.charCodeAt(i
)
112 stdin
= stdin
.substring(maxChars
)
115 write
: (readAddr
, maxBytes
) =>
116 output
.print(String
.fromCharCode
.apply(
118 new Uint16Array(wasmMem
.buffer
, readAddr
, maxBytes
>> 1)
167 'EXECUTE-MODE': 16680,
173 pop
: () => simstack
.pop(),
174 push
: (val
) => simstack
.push(val
),
175 rinit
: () => rstack
.length
= 0,
176 rpop
: () => rstack
.pop(),
177 rpush
: (val
) => rstack
.push(val
),
178 sys_write
: (channel
, addr
, u
) => channels
[channel
] === undefined ? 0 : channels
[channel
].write(addr
, u
),
179 sys_read
: (channel
, addr
, u
) => channels
[channel
] === undefined ? 0 : channels
[channel
].read(addr
, u
),
180 sys_listen
: (reqAddr
, cbAddr
) => {
181 //TODO: call into the module to wasm fn "event" to push an event
182 //to forth, which pushes esi to the return stack and queues the
183 //callback function provided from listen. reqaddr could be
184 //"fetch", in which case no channel is used. otherwise very
185 //similar to sys_fetch.
187 sys_fetch
: (channel
, reqAddr
) => {
188 //TODO: map to fetch promise, write to channel buffer,
189 //javascript "fetch" as fallback, explicit handles for
190 //"textEntry" or any third party protocols like activitypub
191 console
.log(`fetch ${channel} ${reqAddr}`)
193 sys_echo
: (val
) => output
.print(`${val} `),
194 sys_echochar
: (val
) => output
.print(String
.fromCharCode(val
)),
195 sys_reflect
: (addr
) => {
196 console
.log(`reflect: ${addr}: ${
197 new DataView(wasmMem.buffer, addr, 4)
201 vocab_get
: (addr
, u
) => {
202 const word
= String
.fromCharCode
.apply(
204 new Uint16Array(wasmMem
.buffer
, addr
, u
>> 1)
206 const answer
= dictionary
[word
.toUpperCase()]
207 if (answer
=== undefined)
211 vocab_set
: (addr
, u
, num
) => {
212 const word
= String
.fromCharCode
.apply(
214 new Uint16Array(wasmMem
.buffer
, addr
, u
>> 1)
216 dictionary
[word
.toUpperCase()] = num
219 is_whitespace
: (key
) => /\s/.test(String
.fromCharCode(key
)),
220 sys_stack
: () => console
.log(`[${simstack}]`),
221 sys_parsenum
: (addr
, u
, base
) => {
222 const word
= String
.fromCharCode
.apply(
224 new Uint16Array(wasmMem
.buffer
, addr
, u
>> 1)
226 const answer
= Number
.parseInt(word
, base
)
227 if (Number
.isNaN(answer
))
229 new DataView(wasmMem
.buffer
, addr
, 4).setUint32(0,answer
,true)
233 output
.print(Object
.getOwnPropertyNames(dictionary
).toString().split(',').join(' '))
237 initialize
.then((results
) => {
239 WebAssembly
.instantiate(results
[1], wasmImport
).then((module
) => {
240 wasmMem
= module
.instance
.exports
.memory
241 forth
= module
.instance
.exports
.main
242 console
.log('wasm loaded')