import init, { make_interp } from './pkg/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(interp) { document.querySelectorAll('#stack').forEach(e => { while (e.lastChild) { e.removeChild(e.lastChild); } interp.stack().reverse() .forEach(datum => { const elt = document.createElement('li'); elt.textContent = datum; e.appendChild(elt); return elt; }); }); } function renderCallStack(interp) { document.querySelectorAll('#callstack').forEach(e => { while (e.lastChild) { e.removeChild(e.lastChild); } interp.callstack().reverse() .forEach(datum => { const elt = document.createElement('li'); elt.textContent = `${datum.word}@${datum.offset}`; e.appendChild(elt); return elt; }); }); } function renderTextHighlight(interp) { const ip = interp.ip(); const anno = interp.annotation_at(ip) const src = document.querySelector('#src') const text = src.textContent; // split textcontent into 3 spans: pre-highlight, highlight, // post-highlight const pre = document.createElement('span'); pre.textContent = text.substring(0, anno.start); const high = document.createElement('span'); high.classList.add('exec'); high.textContent = text.substring(anno.start, anno.end); const post = document.createElement('span'); post.textContent = text.substring(anno.end); while (src.lastChild) { src.removeChild(src.lastChild) } src.appendChild(pre); src.appendChild(high); src.appendChild(post); } function tick(interp) { if (!interp.tick()) { interp.reset_ip(); } const { word, offset } = interp.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(interp); renderCallStack(interp); renderTextHighlight(interp); } async function loaded() { console.debug('running init'); const mod = await init(); console.debug('init done', mod); const interp = make_interp(); 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 = interp.compile(text); const end = performance.now(); console.info(`compile took ${end-start} ms`); if (res) { const wordlist = wordlistElts(interp.wordlist()); wordlist.forEach(elt => wordlistContainer.appendChild(elt)); initWordlist(); renderStack(interp); renderCallStack(interp); renderTextHighlight(interp); } }; document.querySelector('#tick').onclick = e => { console.debug('tick clicked', e); tick(interp); }; 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 += interp.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', interp.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(interp); setTimeout(onTimeout, 1_000 / frameRate); } } setTimeout(onTimeout); } window.interp = interp; } document.addEventListener('DOMContentLoaded', loaded);