diff options
Diffstat (limited to 'site/main.mjs')
| -rw-r--r-- | site/main.mjs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/site/main.mjs b/site/main.mjs new file mode 100644 index 0000000..3add1fe --- /dev/null +++ b/site/main.mjs @@ -0,0 +1,156 @@ +import init, { make_vm } from './wasm/automathon.js'; + +function wordlistElts(wordlist) { + return wordlist.map((bc, i) => { + const bcElt = document.createElement('x-bytecode'); + bcElt.setAttribute('x-index', i); + for (let i = 0; i < bc.len(); i++) { + const opElt = document.createElement('x-op'); + opElt.setAttribute('x-index', i); + opElt.textContent = bc.at(i); + bcElt.appendChild(opElt); + } + return bcElt; + }) +} + +function selectorForIP(word, offset) { + return `#wordlist x-bytecode[x-index='${word}'] x-op[x-index='${offset}']`; +} + +function initWordlist() { + const sel = selectorForIP(0, 0); + document.querySelectorAll(sel).forEach(e => { + e.classList.add('ip') + }); +} + +function renderStack(vm) { + document.querySelectorAll('#stack').forEach(e => { + while (e.lastChild) { + e.removeChild(e.lastChild); + } + vm.stack().reverse() + .forEach(datum => { + const elt = document.createElement('li'); + elt.textContent = datum; + e.appendChild(elt); + return elt; + }); + }); +} + +function renderCallStack(vm) { + document.querySelectorAll('#callstack').forEach(e => { + while (e.lastChild) { + e.removeChild(e.lastChild); + } + vm.callstack().reverse() + .forEach(datum => { + const elt = document.createElement('li'); + elt.textContent = `${datum.word}@${datum.offset}`; + e.appendChild(elt); + return elt; + }); + }); +} + +const highRange = new Range(); +const highlight = new Highlight(highRange); +CSS.highlights.set('exec', highlight); + +function renderTextHighlight(vm) { + const ip = vm.ip(); + const anno = vm.annotation_at(ip) + const src = document.querySelector('#src'); + + // this assumes the text node is the first child, maybe it isn't? + highRange.setStart(src.childNodes[0], anno.start); + highRange.setEnd(src.childNodes[0], anno.end); +} + +function tick(vm) { + if (!vm.tick()) { + vm.reset_ip(); + } + + const { word, offset } = vm.ip(); + document.querySelectorAll('#wordlist .ip').forEach(e => e.classList.remove('ip')); + const sel = selectorForIP(word, offset); + document.querySelectorAll(sel).forEach(e => { + e.classList.add('ip'); + }); + renderStack(vm); + renderCallStack(vm); + renderTextHighlight(vm); +} + +async function loaded() { + console.debug('running init'); + const mod = await init(); + console.debug('init done', mod); + const vm = make_vm(); + + document.querySelector('#compile').onclick = e => { + console.debug('compile clicked', e); + + let wordlistContainer = document.querySelector('#wordlist'); + while (wordlistContainer.lastChild) { + console.debug('removing child', wordlistContainer.lastChild) + wordlistContainer.removeChild(wordlistContainer.lastChild); + } + + // always add a newline until i decide what to do with the parser. + const text = document.querySelector('#src').textContent + '\n'; + console.debug('compiling', text); + const start = performance.now(); + const res = vm.compile(text); + const end = performance.now(); + console.info(`compile took ${end-start} ms`); + if (res) { + const wordlist = wordlistElts(vm.wordlist()); + wordlist.forEach(elt => wordlistContainer.appendChild(elt)); + initWordlist(); + renderStack(vm); + renderCallStack(vm); + renderTextHighlight(vm); + } + }; + document.querySelector('#tick').onclick = e => { + console.debug('tick clicked', e); + tick(vm); + }; + document.querySelector('#bench').onclick = e => { + console.debug('bench clicked', e); + const start = performance.now(); + let tickCount = 0; + for (let i = 0; i < 1_000_000; i++) { + tickCount += vm.run(); + } + const end = performance.now(); + console.info(`run took ${end-start} ms for ${tickCount} ticks (${(end-start)/tickCount * 1_000_000} ns/tick).`); + console.info('result', vm.stack()); + }; + + let blinkenRun = false; + document.querySelector('#blinken').onclick = e => { + console.debug('blinken clicked', e); + blinkenRun = !blinkenRun; + if (blinkenRun) { + e.target.textContent = 'haltenblinken'; + } else { + e.target.textContent = 'blinken'; + } + const frameRate = 6; + const onTimeout = _ => { + if (blinkenRun) { + tick(vm); + setTimeout(onTimeout, 1_000 / frameRate); + } + } + setTimeout(onTimeout); + } + window.vm = vm; +} + +document.addEventListener('DOMContentLoaded', loaded); |
