summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--index.html47
-rw-r--r--main.css52
-rw-r--r--main.mjs63
-rwxr-xr-xsrc/lib.rs6
4 files changed, 118 insertions, 50 deletions
diff --git a/index.html b/index.html
index 7e2d4c6..8b96298 100644
--- a/index.html
+++ b/index.html
@@ -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>
diff --git a/main.css b/main.css
index 7a032cc..960db69 100644
--- a/main.css
+++ b/main.css
@@ -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;
+}
diff --git a/main.mjs b/main.mjs
index f8c9267..dcd2ec7 100644
--- a/main.mjs
+++ b/main.mjs
@@ -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;
}
diff --git a/src/lib.rs b/src/lib.rs
index f3c9a89..850070a 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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()