9c0e58d2dc57e8128b67b3295ebe37fbec366d6a
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', {credentials: 'include', headers:{'content-type':'text/plain'}}).then((re) => re.text()),
16 fetch('forth.wasm', {credentials: 'include', headers:{'content-type':'application/wasm'}}).then(re => re.arrayBuffer())
18 window.onload = () => {
19 /* Initialize Views */
20 let forthdiv
= document
.getElementById("forth")
21 if (forthdiv
=== null) {
22 forthdiv
= document
.createElement("div")
23 forthdiv
.setAttribute("style","height:100%;margin:auto;width:100%;max-width:640px;overflow-x:hidden;")
24 document
.body
.appendChild(forthdiv
)
26 const outframe
= document
.createElement("div")
27 outframe
.setAttribute("style", "background-color:black;padding-left:6px;padding-right:6px;color:chartreuse;height:268px;resize:vertical;display:flex;align-items:flex-end;flex-flow:row;")
28 const stackview
= document
.createElement("pre")
29 stackview
.setAttribute("style", "white-space:pre-wrap;flex:0 0 8%;")
30 outframe
.appendChild(stackview
)
31 const txtoutput
= document
.createElement("pre")
32 txtoutput
.setAttribute("style", "white-space:pre-wrap;max-height:256px;overflow-y:scroll;flex:1 0 342px;")
33 outframe
.appendChild(txtoutput
)
34 /* const rstackview = document.createElement("pre")
35 rstackview.setAttribute("style", "white-space:pre-wrap;flex:0 1 8%;")
36 outframe.appendChild(rstackview) */
37 const memview
= document
.createElement("pre")
38 memview
.setAttribute("style", "padding-left: 8px; white-space:pre-wrap;flex:0 0 88px;")
39 outframe
.appendChild(memview
)
40 const txtinput
= document
.createElement("textarea")
41 txtinput
.setAttribute("autofocus", "true")
42 txtinput
.setAttribute("wrap", "hard")
43 txtinput
.setAttribute("style", "resize:none;white-space:pre;margin-left:8%;width:75%;")
44 txtinput
.oninput
= () => txtinput
.setAttribute("rows", ((txtinput
.value
.match(/[\n]/g) || [1]).length
+ 1).toString());
46 forthdiv
.appendChild(outframe
)
47 forthdiv
.appendChild(txtinput
)
49 /* Initialize State */
54 const dictionary
= { EXECUTE
: 12 }
55 const doesDictionary
= {}
57 /* Environment functions */
59 print
: (string
) => txtoutput
.textContent
+= `\\\ => ${string} \n`
61 const wasmString
= (addr
, u
) =>
62 String
.fromCharCode
.apply(
64 new Uint16Array(wasmMem
.buffer
, addr
, u
>> 1)
66 const updateViews
= () => {
67 const base
= new DataView(wasmMem
.buffer
, 14348 /* base */, 4).getUint32(0,true)
68 stackview
.textContent
= simstack
.map((v
) => v
.toString(base
)).join('\n')
69 // rstackview.textContent = rstack.join('\n')
72 let here
= new DataView(wasmMem
.buffer
, 14340 /* here */, 4).getUint32(0,true)
73 memview
.textContent
= Array
.from(new Uint8Array(wasmMem
.buffer
, here
- maxBytes
, maxBytes
), (v
) => {
75 v
= ('0' + (v
& 0xFF).toString(16)).slice(-2)
79 return `${v}\n=> ${(here -maxBytes + cnt).toString(base)}\n`
84 outframe
.scrollTop
= outframe
.scrollHeight
89 txtinput
.addEventListener('keydown', (event
) => {
93 const endPos
= txtinput
.selectionStart
+ 1
95 txtinput
.value
.substring(0, txtinput
.selectionStart
) +
97 txtinput
.value
.substring(txtinput
.selectionEnd
, txtinput
.value
.length
)
98 txtinput
.setSelectionRange(endPos
, endPos
)
102 stdin
+= txtinput
.value
103 txtoutput
.textContent
+= txtinput
.value
104 if (!/\s/.test(txtinput
.value
.slice(-1)))
105 txtoutput
.textContent
+= " "
107 event
.preventDefault()
108 event
.stopPropagation()
111 txtoutput
.scrollTop
= txtoutput
.scrollHeight
121 read
: (writeAddr
, maxBytes
) => {
122 const maxChars
= maxBytes
>> 1
123 const bufView
= new Uint16Array(wasmMem
.buffer
, writeAddr
, maxChars
)
125 for (i
= 0; i
< maxChars
&& i
< stdin
.length
; i
++)
126 bufView
[i
] = stdin
.charCodeAt(i
)
127 stdin
= stdin
.substring(maxChars
)
130 write
: (readAddr
, maxBytes
) =>
131 output
.print(String
.fromCharCode
.apply(
133 new Uint16Array(wasmMem
.buffer
, readAddr
, maxBytes
>> 1)
140 pop
: () => simstack
.pop(),
141 push
: (val
) => simstack
.push(val
),
142 rinit
: () => rstack
.length
= 0,
143 rpop
: () => rstack
.pop(),
144 rpush
: (val
) => rstack
.push(val
),
145 sys_write
: (channel
, addr
, u
) => channels
[channel
] === undefined ? 0 : channels
[channel
].write(addr
, u
),
146 sys_read
: (channel
, addr
, u
) => channels
[channel
] === undefined ? 0 : channels
[channel
].read(addr
, u
),
147 sys_listen
: (reqAddr
, cbAddr
) => {
148 //TODO: call into the module to wasm fn "event" to push an event
149 //to forth, which pushes esi to the return stack and queues the
150 //callback function provided from listen. reqaddr could be
151 //"fetch", in which case no channel is used. otherwise very
152 //similar to sys_fetch.
154 sys_fetch
: (channel
, reqAddr
) => {
155 //TODO: map to fetch promise, write to channel buffer,
156 //javascript "fetch" as fallback, explicit handles for
157 //"textEntry" or any third party protocols like activitypub
158 console
.log(`fetch ${channel} ${reqAddr}`)
160 sys_echo
: (val
, base
) => output
.print(`${val.toString(base)} `),
161 sys_echochar
: (val
) => output
.print(String
.fromCharCode(val
)),
162 sys_reflect
: (addr
) => {
163 console
.log(`reflect: ${addr}: ${
164 new DataView(wasmMem.buffer, addr, 4)
168 vocab_get
: (addr
, u
) => dictionary
[wasmString(addr
, u
).toUpperCase()] || 0,
169 vocab_set
: (addr
, u
, v
) => dictionary
[wasmString(addr
, u
).toUpperCase()] = v
,
170 does_get
: (addr
, u
) => doesDictionary
[wasmString(addr
, u
).toUpperCase()] || 0,
171 does_set
: (addr
, u
, v
) => doesDictionary
[wasmString(addr
, u
).toUpperCase()] = v
,
172 is_whitespace
: (key
) => /\s/.test(String
.fromCharCode(key
)),
173 sys_stack
: () => console
.log(`[${simstack}][${rstack}]`),
174 sys_parsenum
: (addr
, u
, base
) => {
175 const answer
= Number
.parseInt(wasmString(addr
, u
), base
)
176 if (Number
.isNaN(answer
))
178 new DataView(wasmMem
.buffer
, addr
, 4).setUint32(0,answer
,true)
182 output
.print(Object
.getOwnPropertyNames(dictionary
).toString().split(',').join(' '))
186 initialize
.then((results
) => {
188 WebAssembly
.instantiate(results
[1], wasmImport
).then((module
) => {
189 wasmMem
= module
.instance
.exports
.memory
190 forth
= module
.instance
.exports
.main
191 console
.log('wasm loaded')