diff options
| author | Brian Cully <bjc@spork.org> | 2025-08-23 13:40:37 -0400 |
|---|---|---|
| committer | Brian Cully <bjc@spork.org> | 2025-08-23 21:26:06 -0400 |
| commit | 365312b14723503424a601a49827c191af82ad7a (patch) | |
| tree | 5e37a01320fa77b6a19d7f0f0b7d44ec28e38a07 | |
| parent | 39042a3476ffbdc79d1e49a69cb5103628eb7265 (diff) | |
| download | automathon-365312b14723503424a601a49827c191af82ad7a.tar.gz automathon-365312b14723503424a601a49827c191af82ad7a.zip | |
begginnenblinken
| -rw-r--r-- | index.html | 47 | ||||
| -rw-r--r-- | main.css | 52 | ||||
| -rw-r--r-- | main.mjs | 63 | ||||
| -rwxr-xr-x | src/lib.rs | 6 |
4 files changed, 118 insertions, 50 deletions
@@ -7,7 +7,11 @@ <body> <h1>automathon</h1> - <textarea> + <div id='editor'> + <div id='code'> + <button id='compile'>compile</button> + <br> + <textarea id='src'> : fac dup 1 > if @@ -15,22 +19,31 @@ then ; 5 fac - </textarea> - <button id='compile'>compile</button> - <button id='tick'>tick</button> - <button id='bench'>bench</button> - <fieldset> - <legend>stack</legend> - <ol id='stack'></ol> - </fieldset> - <fieldset> - <legend>call stack</legend> - <ol id='callstack'></ol> - </fieldset> - <fieldset> - <legend>word list</legend> - <div id='wordlist'></div> - </fieldset> +drop</textarea> + </div> + <div id='state-container'> + <div id='state'> + <div class='controls'> + <button id='tick'>tick</button> + <button id='bench'>bench</button> + <button id='blinken'>blinken</button> + </div> + <fieldset class='wordlist'> + <legend>word list</legend> + <div id='wordlist'></div> + </fieldset> + <fieldset class='stack'> + <legend>stack</legend> + <ol id='stack'></ol> + </fieldset> + <fieldset class='callstack'> + <legend>call stack</legend> + <ol id='callstack'></ol> + </fieldset> + </div> + </div> + </div> + <br> <canvas id='arena' width='512' height='512'>no canvas!</canvas> <script src='./main.mjs' type='module'></script> </body> @@ -1,10 +1,50 @@ -canvas { - border: 1px solid greenyellow; +html { + box-sizing: border-box; +} +*, *::before, *::after { + box-sizing: inherit; } -textarea { - width: 25em; +#editor { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-gap: 1em; +} + +#code { + position: relative; + grid-row: 1; +} + +#code textarea { + width: 100%; height: 25ex; + margin-top: 1ex; + padding: 1em; +} + +#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 { @@ -28,3 +68,7 @@ x-op { padding: 5px; border: 1px solid grey; } + +canvas { + border: 1px solid greenyellow; +} @@ -15,9 +15,7 @@ function wordlistElts(wordlist) { } function selectorForIP(word, offset) { - const sel = `#wordlist x-bytecode[x-index='${word}'] x-op[x-index='${offset}']`; - console.debug('using sel', sel); - return sel; + return `#wordlist x-bytecode[x-index='${word}'] x-op[x-index='${offset}']`; } function initWordlist() { @@ -34,10 +32,8 @@ function renderStack(interp) { } interp.stack().reverse() .forEach(datum => { - console.debug('creating elt for', datum); const elt = document.createElement('li'); elt.textContent = datum; - console.debug('elt', elt); e.appendChild(elt); return elt; }); @@ -51,16 +47,29 @@ function renderCallStack(interp) { } interp.callstack().reverse() .forEach(datum => { - console.debug('creating elt for', datum); const elt = document.createElement('li'); elt.textContent = `${datum.word}@${datum.offset}`; - console.debug('elt', elt); e.appendChild(elt); return elt; }); }); } +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); +} + async function loaded() { console.debug('running init'); const mod = await init(); @@ -75,7 +84,6 @@ async function loaded() { console.debug('removing child', wordlistContainer.lastChild) wordlistContainer.removeChild(wordlistContainer.lastChild); } - console.debug('container has firstchild', wordlistContainer.firstChild) // always add a newline until i decide what to do with the parser. const text = document.querySelector('textarea').value + '\n'; @@ -87,25 +95,13 @@ async function loaded() { const wordlist = wordlistElts(interp.wordlist()); wordlist.forEach(elt => wordlistContainer.appendChild(elt)); initWordlist(); + renderStack(interp); + renderCallStack(interp); } }; document.querySelector('#tick').onclick = e => { console.debug('tick clicked', e); - if (!interp.tick()) { - interp.reset_ip(); - } - console.info('callstack', interp.callstack()); - console.info('stack', interp.stack()); - - const { word, offset } = interp.ip(); - console.info('ip', word, offset); - 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); + tick(interp); }; document.querySelector('#bench').onclick = e => { console.debug('bench clicked', e); @@ -115,9 +111,28 @@ async function loaded() { tickCount += interp.run(); } const end = performance.now(); - console.info(`run took ${end-start} ms for ${tickCount} ticks (${(end-start)/tickCount * 1000000} µs/tick).`); + 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; } @@ -1,4 +1,4 @@ -use log::{Level, debug, error, info}; +use log::{Level, error, info}; use console_log; use wasm_bindgen::prelude::*; @@ -67,7 +67,6 @@ impl ExportedInterp { error!("couldn't parse program text: {:?}", e); return false } - debug!("wordlist: {:?}", &p.wordlist); let interp = forth::interp::Interp::new(p.wordlist); let _ = self.i.insert(interp); true @@ -77,7 +76,6 @@ impl ExportedInterp { let Some(interp) = &mut self.i else { return Err("no interpreter".to_string()) }; - info!("executing single instruction"); interp.tick().or_else(|err| Err(format!("runtime error: {:?}", err))) } @@ -104,7 +102,6 @@ impl ExportedInterp { return vec![] }; - info!("wordlist: ‘{:?}’", interp.wordlist); interp.wordlist.iter().map(|bc| ExportedByteCode::from_bc(bc)).collect() } @@ -112,7 +109,6 @@ impl ExportedInterp { let Some(interp) = &self.i else { return vec![] }; - info!("callstack: ‘{:?}’", interp.callstack); interp.callstack.0.iter() .map(|ip| ip.into()) .collect() |
