summaryrefslogtreecommitdiffstats
path: root/site/main.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'site/main.mjs')
-rw-r--r--site/main.mjs156
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);