summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--site/bench.mjs13
-rw-r--r--site/index.html2
-rw-r--r--site/main.mjs161
-rw-r--r--site/robo.mjs73
4 files changed, 171 insertions, 78 deletions
diff --git a/site/bench.mjs b/site/bench.mjs
new file mode 100644
index 0000000..ea88f6b
--- /dev/null
+++ b/site/bench.mjs
@@ -0,0 +1,13 @@
+// todo: put this on a different page just for benching
+
+// 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());
+// };
diff --git a/site/index.html b/site/index.html
index 635cc3c..dac9b4f 100644
--- a/site/index.html
+++ b/site/index.html
@@ -16,7 +16,7 @@
</canvas>
<div class='controls'>
- <button id='bench'>bench</button>
+ <!-- <button id='bench'>bench</button> -->
<button id='tick'>tick</button>
<button id='blinken'>blinken</button>
</div> <!-- controls -->
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);
}
diff --git a/site/robo.mjs b/site/robo.mjs
new file mode 100644
index 0000000..e6db244
--- /dev/null
+++ b/site/robo.mjs
@@ -0,0 +1,73 @@
+import init, { make_vm } from './wasm/automathon.js';
+
+let vm;
+
+function vmData(vm) {
+ const wordlist = vm.wordlist().map(bc => {
+ let res = [];
+ for (let i = 0; i < bc.len(); i++) {
+ res.push(bc.at(i))
+ }
+ return res;
+ });
+ const callstack = vm.callstack().map(e => { return { word: e.word, offset: e.offset }; });
+ const vars = Object.fromEntries(['out', 'heading', 'speed', 'doppler'].map(name => [name, vm[name]()]));
+ const ip = {word: vm.ip().word, offset: vm.ip().offset};
+ const annos = vm.annotations().map(word_annos => {
+ let res = [];
+ for (let i = 0; i < word_annos.len(); i++) {
+ const anno = word_annos.at(i);
+ res.push({ start: anno.start, end: anno.end })
+ }
+ return res;
+ });
+ return {
+ wordlist,
+ stack: vm.stack(),
+ callstack,
+ vars,
+ ip,
+ annos,
+ }
+}
+
+function compile(text) {
+ const start = performance.now();
+ const res = vm.compile(text);
+ const end = performance.now();
+ console.info(`compile took ${end-start} ms`);
+ postMessage({ kind: 'compile', res, vm: vmData(vm) });
+}
+
+function tick() {
+ if (!vm.tick()) {
+ vm.reset_ip();
+ }
+ postMessage({ kind: 'tick', vm: vmData(vm) });
+}
+
+let mod;
+async function messageHandler(e) {
+ if (mod === undefined) {
+ console.debug('worker running init');
+ mod = await init();
+ console.debug('worker init done');
+ vm = make_vm();
+ console.debug('worker vm made');
+ }
+ const { kind } = e.data;
+ switch (kind) {
+ case 'compile':
+ compile(e.data.text);
+ break;
+ case 'tick':
+ tick();
+ break;
+ default:
+ console.error('invalid message to robo worker', e.data);
+ postMessage({ kind: 'error', error: 'badmsg' });
+ }
+}
+
+console.debug('loading robo worker');
+addEventListener('message', messageHandler);