diff options
author | dan <[email protected]> | 2023-05-19 22:35:13 -0400 |
---|---|---|
committer | dan <[email protected]> | 2023-05-19 22:35:13 -0400 |
commit | d656fc34943304e6806033009923a0c20b798be5 (patch) | |
tree | 9c52a3783d1e171ed970aa743b73359a59c68072 /forth.js | |
download | forth-d656fc34943304e6806033009923a0c20b798be5.tar.gz forth-d656fc34943304e6806033009923a0c20b798be5.tar.bz2 forth-d656fc34943304e6806033009923a0c20b798be5.zip |
init
Diffstat (limited to 'forth.js')
-rw-r--r-- | forth.js | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/forth.js b/forth.js new file mode 100644 index 0000000..83397c6 --- /dev/null +++ b/forth.js @@ -0,0 +1,134 @@ +function forth(print = console.log) { + const s = []; // the stack + const popNum = () => Number(s.pop()); + + const fs = { + ' ' : () => {}, + '\n' : () => {}, + '\t' : () => {}, + '/' : () => { + const a2 = popNum(); + const a1 = popNum(); + s.push(a1 / a2); + }, + '-' : () => { + const a2 = popNum(); + const a1 = popNum(); + s.push(a1 - a2); + }, + '+' : () => { s.push(popNum() + popNum()) }, + '*' : () => { s.push(popNum() * popNum()) }, + '.' : () => print(s.pop()), + 'peek' : () => print(s[s.length - 1]), + ':' : (initialIdx, tokens) => { + let localIdx = initialIdx + 1; + const fname = tokens[localIdx++]; + const fnContent = []; + for (;tokens[localIdx] !== ';'; localIdx++) { + fnContent.push(tokens[localIdx]); + } + fs[fname] = () => { + exec(fnContent); + } + return localIdx - initialIdx; // numItemsSkipped + }, + 's' : () => print(JSON.stringify(s)), + 'sum' : () => { + let total = 0; + while (s.length > 0) { + total += popNum(); + } + s.push(total); + }, + '(' : (initialIdx, tokens) => { + let localIdx = initialIdx + 1; + while (tokens[localIdx] !== ')') { localIdx++ } + return localIdx - initialIdx; + } + } + + function exec(tokens) { + for (let tokenIdx = 0; tokenIdx < tokens.length; tokenIdx++) { + const token = tokens[tokenIdx]; + if (fs[token]) { + const numItemsSkipped = fs[token](tokenIdx, tokens); + if (Number.isInteger(numItemsSkipped)) { + tokenIdx += numItemsSkipped; + } + } + else { + s.push(token); + } + } + } + return t => exec(t.split(/[ \n\t]+/)); +} + + +if (window) { + const style = document.createElement('style') + style.innerHTML = ` + #forthroot * { + background-color: #42456f; + color: white; + font-family: monospace; + font-size: 1.2rem; + box-sizing: border-box; + } + #forthouter { + cursor: text; + border: solid thin black; + } + #forthline { + width: 97%; + border: none; + } + #forthline:focus { + outline: none; + } + #forthresultsbox { + height: 90vh; + } + #forthresults { + margin-top: 0px; + } + #forthlinelabel { + padding-top:1px; + max-width: 3%; + } + `; + document.head.appendChild(style); + forthroot.innerHTML = ` + <div id="forthouter"> + <form id="forthform"> + <label id="forthlinelabel" for="forthline" position="left">></label> + <input id="forthline" type="text" value=""> + </form> + <div id='forthresultsbox'> + <pre id="forthresults"></pre> + </div> + </div>` + forthouter.onclick = () => forthline.focus(); + const print = x => { forthresults.innerHTML = x + '\n' + forthresults.innerHTML; }; + const m = forth(print); + forthform.onsubmit = () => { + print(`> ${forthline.value}`); + if (forthline.value !== '') { + m(forthline.value); + forthline.value = ''; + } + return false; + } + +} else { + const readline = require('readline'); + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + const prompt = (query) => new Promise((resolve) => rl.question(query, resolve)); + const m = forth(); + (async () => { + while (true) { + const input = await prompt("> "); + m(input) + } + })(); +} |