diff options
| author | Brian Cully <bjc@spork.org> | 2025-08-23 13:05:07 -0400 |
|---|---|---|
| committer | Brian Cully <bjc@spork.org> | 2025-08-23 13:05:07 -0400 |
| commit | 0753ea2b9e10b0f5030b05e84cd8490cc9ec7b34 (patch) | |
| tree | 78750e55c5839ffb9c703a3848b04718e8e11b61 | |
| parent | 8ce858ce7a03090b3b2a1310d17b1206f097637f (diff) | |
| download | automathon-0753ea2b9e10b0f5030b05e84cd8490cc9ec7b34.tar.gz automathon-0753ea2b9e10b0f5030b05e84cd8490cc9ec7b34.zip | |
highlight running code
| -rw-r--r-- | main.css | 4 | ||||
| -rw-r--r-- | main.mjs | 21 | ||||
| -rwxr-xr-x | src/lib.rs | 28 |
3 files changed, 47 insertions, 6 deletions
@@ -7,6 +7,10 @@ textarea { height: 25ex; } +#wordlist .ip { + background-color: yellow; +} + x-bytecode { display: block; } @@ -14,6 +14,12 @@ 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; +} + async function loaded() { console.debug('running init'); const mod = await init(); @@ -39,10 +45,25 @@ async function loaded() { if (res) { const wordlist = wordlistElts(interp.wordlist()); wordlist.forEach(elt => wordlistContainer.appendChild(elt)); + const sel = selectorForIP(0, 0); + document.querySelectorAll(sel).forEach(e => { + e.classList.add('ip') + }); } }; document.querySelector('#tick').onclick = e => { console.debug('tick clicked', e); + interp.tick(); + 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') + }); }; document.querySelector('#run').onclick = e => { console.debug('run clicked', e); @@ -10,6 +10,12 @@ pub struct ExportedInstructionPointer { pub offset: usize, } +impl From<&forth::interp::InstructionPointer> for ExportedInstructionPointer { + fn from(ip: &forth::interp::InstructionPointer) -> Self { + Self { word: ip.word, offset: ip.offset } + } +} + // wasm can't wrap Vec<Vec<String>>, so we need a custom type // - 23-aug-2025 #[wasm_bindgen] @@ -67,8 +73,12 @@ impl ExportedInterp { true } - pub fn tick(&mut self) { + pub fn tick(&mut self) -> Result<bool, String> { + 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))) } pub fn run(&mut self) -> Result<usize, String> { @@ -98,15 +108,21 @@ impl ExportedInterp { interp.wordlist.iter().map(|bc| ExportedByteCode::from_bc(bc)).collect() } + pub fn callstack(&self) -> Vec<ExportedInstructionPointer> { + let Some(interp) = &self.i else { + return vec![] + }; + info!("callstack: ‘{:?}’", interp.callstack); + interp.callstack.0.iter() + .map(|ip| ip.into()) + .collect() + } + pub fn ip(&self) -> ExportedInstructionPointer { let Some(interp) = self.i.as_ref() else { return ExportedInstructionPointer { word: 0, offset: 0 } }; - - ExportedInstructionPointer { - word: interp.ip.word, - offset: interp.ip.offset, - } + (&interp.ip).into() } } |
