diff options
Diffstat (limited to 'site/main.mjs')
| -rw-r--r-- | site/main.mjs | 161 |
1 files changed, 84 insertions, 77 deletions
diff --git a/site/main.mjs b/site/main.mjs index b26fa4f..5358616 100644 --- a/site/main.mjs +++ b/site/main.mjs @@ -1,5 +1,3 @@ -import init, { make_vm } from './wasm/automathon.js'; - // simulation speed const TICKS_PER_SECOND = 12; const MS_PER_TICK = 1_000 / TICKS_PER_SECOND; @@ -28,12 +26,12 @@ 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++) { + bc.forEach((op, i) => { const opElt = document.createElement('x-op'); opElt.setAttribute('x-index', i); - opElt.textContent = bc.at(i); + opElt.textContent = op; bcElt.appendChild(opElt); - } + }) return bcElt; }) } @@ -45,12 +43,12 @@ function initWordlist() { }); } -function renderStack(vm) { +function renderStack(vmstack) { document.querySelectorAll(STACK_SELECTOR).forEach(e => { while (e.lastChild) { e.removeChild(e.lastChild); } - vm.stack().reverse() + vmstack.reverse() .forEach(datum => { const elt = document.createElement('li'); elt.textContent = datum; @@ -60,12 +58,12 @@ function renderStack(vm) { }); } -function renderCallStack(vm) { +function renderCallStack(vmcallstack) { document.querySelectorAll(CALLSTACK_SELECTOR).forEach(e => { while (e.lastChild) { e.removeChild(e.lastChild); } - vm.callstack().reverse() + vmcallstack.reverse() .forEach(datum => { const elt = document.createElement('li'); elt.textContent = `${datum.word}@${datum.offset}`; @@ -75,7 +73,7 @@ function renderCallStack(vm) { }); } -function renderVars(vm) { +function renderVars(vmvars) { document.querySelectorAll(VARS_SELECTOR).forEach(e => { while (e.lastChild) { e.removeChild(e.lastChild); @@ -87,7 +85,7 @@ function renderVars(vm) { e.appendChild(dt); const dd = document.createElement('dd'); - dd.textContent = vm[name](); + dd.textContent = vmvars[name]; e.appendChild(dd); }); }); @@ -110,16 +108,14 @@ function renderArena(robos, delta=0.0) { ctx.clearRect(0, 0, canvas.width, canvas.height); robos.forEach(robo => { - let heading = robo.vm.heading(); - let speed = robo.vm.speed(); - let [velx, vely] = [ - Math.cos(2 * Math.PI * heading / 360), - Math.sin(2 * Math.PI * heading / 360) - ].map(x => timeScale * speed * x); + const [velx, vely] = [ + robo.speedx, robo.speedy + ].map(x => timeScale * x); - renderRobo(ctx, robo.x, robo.y); - robo.x += velx; - robo.y += vely; + const x = robo.x + velx; + const y = robo.y + vely; + //console.debug('bjc render robo', robo, velx, vely, x, y) + renderRobo(ctx, x, y); }); } @@ -128,8 +124,8 @@ const highlight = new Highlight(highRange); CSS.highlights.set('exec', highlight); function renderTextHighlight(vm) { - const ip = vm.ip(); - const anno = vm.annotation_at(ip) + const ip = vm.ip; + const anno = vm.annos[ip.word][ip.offset]; const src = document.querySelector(SRC_SELECTOR); // this assumes the text node is the first child, maybe it isn't? @@ -137,23 +133,6 @@ function renderTextHighlight(vm) { highRange.setEnd(src.childNodes[0], anno.end); } -function tick(robo) { - if (!robo.vm.tick()) { - robo.vm.reset_ip(); - } - - const { word, offset } = robo.vm.ip(); - document.querySelectorAll(IP_SELECTOR).forEach(e => e.classList.remove('ip')); - const sel = selectorForIP(word, offset); - document.querySelectorAll(sel).forEach(e => { - e.classList.add('ip'); - }); - renderStack(robo.vm); - renderCallStack(robo.vm); - renderVars(robo.vm); - renderTextHighlight(robo.vm); -} - function loadForth(taintedPath) { // ascii only + ‘-’, ‘_’, ‘.’, and ‘/’, but no ‘../’ const path = @@ -176,13 +155,66 @@ function loadForth(taintedPath) { } async function loaded() { - console.debug('running init'); - const mod = await init(); - console.debug('init done', mod); + const roboWorker = new Worker('robo.mjs', { type: 'module' }); const robo = { + worker: roboWorker, + lastTick: undefined, x: 132, y: 25, - vm: make_vm() + heading: 0, + speed: 0, + speedx: 0, + speedy: 0, + }; + roboWorker.onmessage = e => { + const { kind, res, vm } = e.data; + switch (kind) { + case 'compile': + if (res) { + let wordlistContainer = document.querySelector(WORDLIST_SELECTOR); + console.debug('bjc new vm', vm); + const wordlist = wordlistElts(vm.wordlist); + wordlist.forEach(elt => wordlistContainer.appendChild(elt)); + initWordlist(); + renderStack(vm.stack); + renderCallStack(vm.callstack); + renderVars(vm.vars); + renderTextHighlight(vm); + + renderArena([robo]); + } + break; + case 'tick': + const { word, offset } = vm.ip; + document.querySelectorAll(IP_SELECTOR).forEach(e => e.classList.remove('ip')); + const sel = selectorForIP(word, offset); + document.querySelectorAll(sel).forEach(e => { + e.classList.add('ip'); + }); + renderStack(vm.stack); + renderCallStack(vm.callstack); + renderVars(vm.vars); + renderTextHighlight(vm); + + robo.lastTick = document.timeline.currentTime; + robo.heading = vm.vars.heading; + robo.speed = vm.vars.speed; + const [speedx, speedy] = [ + Math.cos(2 * Math.PI * robo.heading / 360), + Math.sin(2 * Math.PI * robo.heading / 360) + ].map(x => robo.speed * x); + robo.speedx = speedx; + robo.speedy = speedy; + robo.x += speedx; + robo.y += speedy; + renderArena([robo]); + break; + default: + console.error('invalid message from robo worker', e.data); + } + }; + roboWorker.onerror = e => { + console.error('error in roboWorker', e); }; document.querySelectorAll(SRC_SELECT_SELECTOR).forEach(async sel => { @@ -202,45 +234,19 @@ async function loaded() { // always add a newline until i decide what to do with the parser. const text = document.querySelector(SRC_SELECTOR).textContent + '\n'; console.debug('compiling', text); - const start = performance.now(); - const res = robo.vm.compile(text); - const end = performance.now(); - console.info(`compile took ${end-start} ms`); - if (res) { - const wordlist = wordlistElts(robo.vm.wordlist()); - wordlist.forEach(elt => wordlistContainer.appendChild(elt)); - initWordlist(); - renderStack(robo.vm); - renderCallStack(robo.vm); - renderVars(robo.vm); - renderTextHighlight(robo.vm); - renderArena([robo]); - } - }; - - document.querySelector(BENCH_BUTTON_SELECTOR).onclick = e => { - console.debug('bench clicked', e); - const start = performance.now(); - let tickCount = 0; - for (let i = 0; i < 1_000_000; i++) { - tickCount += robo.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', robo.vm.stack()); + roboWorker.postMessage({ kind: 'compile', text }); }; document.querySelector(TICK_BUTTON_SELECTOR).onclick = e => { console.debug('tick clicked', e); - tick(robo); - renderArena([robo], MS_PER_TICK); + roboWorker.postMessage({ kind: 'tick' }); }; let blinkenRun = false; document.querySelector(BLINKEN_BUTTON_SELECTOR).onclick = e => { console.debug('blinken clicked', e); - let lastTime; + let lastTime = 0; function r(t, manual=false) { if (!blinkenRun) { return; @@ -249,9 +255,11 @@ async function loaded() { window.requestAnimationFrame(r); } - const delta = (lastTime === undefined) ? 0 : t - lastTime; - lastTime = t; - if (delta > 0) { + // we often get called with the same time stamp many times + // in a row due to fingerprint-reduction tech. + if (t > lastTime) { + lastTime = t; + const delta = robo.lastTick ? t - robo.lastTick : 0; renderArena([robo], delta); } } @@ -269,8 +277,7 @@ async function loaded() { } setTimeout(onTimeout, MS_PER_TICK); - tick(robo); - r(document.timeline.currentTime, true); + roboWorker.postMessage({ kind: 'tick' }); } setTimeout(onTimeout); } |
