function forth(print = console.log) { const s = []; // the stack const popNum = () => Number(s.pop()); const fs = { '/' : () => { 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()) }, '=' : () => { s.push(popNum() === popNum()) }, 'then' : () => {/*Doing nothing skips the token*/}, 'if' : (initialIdx, tokens) => { if (!s.pop()) { let localIdx = initialIdx; while (tokens[localIdx] && tokens[localIdx] !== 'then') {localIdx++} return localIdx - initialIdx; } }, 'not' : () => { s.push(!s.pop()) }, '.' : () => { print(s.pop()) }, 'peek' : () => { print(s[s.length - 1]) }, 'dup' : () => { s.push(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 => new Promise( resolve => { exec(t.split(/[ \n\t]+/).filter(x => x !== '')); resolve(); } ); } /* root tag to add to page:
* */ if (typeof window !== 'undefined') { // browser UI 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 { height: 100%; margin-top: 0px; overflow: clip; margin: 0px; } #forthlinelabel { padding-top:1px; max-width: 3%; } #forthform { margin-bottom: 0px; } `; document.head.appendChild(style); forthroot.innerHTML = `

      
` forthouter.onclick = () => forthline.focus(); const print = x => { forthresults.innerHTML = x + '\n' + forthresults.innerHTML; }; const m = forth(print); forthform.onsubmit = () => { const input = forthline.value; if (input !== '') { m(input).then(() => print(`> ${input}`)); forthline.value = ''; } return false; } } else { // UI if NodeJS 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) } })(); }