From 411f2735bffc42d11a3b1b3447bbb1603e48a3eb Mon Sep 17 00:00:00 2001 From: Brian Cully Date: Sun, 24 Aug 2025 15:46:22 -0400 Subject: move html stuff into ‘site’ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- Makefile | 4 +- index.html | 50 ------------------ main.css | 80 ----------------------------- main.mjs | 156 -------------------------------------------------------- site/index.html | 50 ++++++++++++++++++ site/main.css | 80 +++++++++++++++++++++++++++++ site/main.mjs | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 289 insertions(+), 289 deletions(-) delete mode 100644 index.html delete mode 100644 main.css delete mode 100644 main.mjs create mode 100644 site/index.html create mode 100644 site/main.css create mode 100644 site/main.mjs diff --git a/.gitignore b/.gitignore index 54954ce..ce39741 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /target -/pkg +/site/wasm diff --git a/Makefile b/Makefile index f37f0ac..08d33b2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # the --target is currently necessary or firefox will return # "disallowed mime type" -bjc 7-aug-2025 build: - wasm-pack build --target web + wasm-pack build --target web --out-dir site/wasm serve: build - python -m http.server 8118 + (cd site && python -m http.server 8118) diff --git a/index.html b/index.html deleted file mode 100644 index 77c2bdc..0000000 --- a/index.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - automathon - - - - -

automathon

- -
-
- -
-
: fac - dup - 1 > if - dup 1 - fac * - then - ; -5 fac -drop
-
- -
-
-
- - - -
-
- word list -
-
-
- stack -
    -
    -
    - call stack -
      -
      -
      -
      -
      -
      - - - diff --git a/main.css b/main.css deleted file mode 100644 index 9f7381d..0000000 --- a/main.css +++ /dev/null @@ -1,80 +0,0 @@ -html { - box-sizing: border-box; -} -*, *::before, *::after { - box-sizing: inherit; -} - -#editor { - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-gap: 1em; -} - -#code { - position: relative; - grid-row: 1; -} - -#code #src { - width: 100%; - height: 25ex; - margin-top: 1ex; - padding: 1em; - border: 2px inset lightgray; - white-space: pre-wrap; -} - -::highlight(exec) { - background-color: yellowgreen; -} - -#compile { - position: absolute; - right: 0; -} - -#state-container { - grid-row: 1; -} - -#state { - display: grid; - grid-template-columns: repeat(2, 1fr); -} - -#state .controls { - grid-row: 1; - grid-column: 1 / span 2; -} - -#state .wordlist { - grid-row: 2; - grid-column: 1 / span 2; -} - -#wordlist .ip { - background-color: yellow; -} - -x-bytecode { - display: block; -} - -x-bytecode::before { - display: inline-block; - width: 1em; - content: attr(x-index); - text-align: right; - padding-right: 0.4em; -} - -x-op { - display: inline-block; - padding: 5px; - border: 1px solid grey; -} - -canvas { - border: 1px solid greenyellow; -} diff --git a/main.mjs b/main.mjs deleted file mode 100644 index 9e3e037..0000000 --- a/main.mjs +++ /dev/null @@ -1,156 +0,0 @@ -import init, { make_vm } 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(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); diff --git a/site/index.html b/site/index.html new file mode 100644 index 0000000..77c2bdc --- /dev/null +++ b/site/index.html @@ -0,0 +1,50 @@ + + + + automathon + + + + +

      automathon

      + +
      +
      + +
      +
      : fac + dup + 1 > if + dup 1 - fac * + then + ; +5 fac +drop
      +
      + +
      +
      +
      + + + +
      +
      + word list +
      +
      +
      + stack +
        +
        +
        + call stack +
          +
          +
          +
          +
          +
          + + + diff --git a/site/main.css b/site/main.css new file mode 100644 index 0000000..9f7381d --- /dev/null +++ b/site/main.css @@ -0,0 +1,80 @@ +html { + box-sizing: border-box; +} +*, *::before, *::after { + box-sizing: inherit; +} + +#editor { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-gap: 1em; +} + +#code { + position: relative; + grid-row: 1; +} + +#code #src { + width: 100%; + height: 25ex; + margin-top: 1ex; + padding: 1em; + border: 2px inset lightgray; + white-space: pre-wrap; +} + +::highlight(exec) { + background-color: yellowgreen; +} + +#compile { + position: absolute; + right: 0; +} + +#state-container { + grid-row: 1; +} + +#state { + display: grid; + grid-template-columns: repeat(2, 1fr); +} + +#state .controls { + grid-row: 1; + grid-column: 1 / span 2; +} + +#state .wordlist { + grid-row: 2; + grid-column: 1 / span 2; +} + +#wordlist .ip { + background-color: yellow; +} + +x-bytecode { + display: block; +} + +x-bytecode::before { + display: inline-block; + width: 1em; + content: attr(x-index); + text-align: right; + padding-right: 0.4em; +} + +x-op { + display: inline-block; + padding: 5px; + border: 1px solid grey; +} + +canvas { + border: 1px solid greenyellow; +} 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); -- cgit v1.3