diff options
Diffstat (limited to 'src/toys/tabloid/js/playground.js')
| -rw-r--r-- | src/toys/tabloid/js/playground.js | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/toys/tabloid/js/playground.js b/src/toys/tabloid/js/playground.js new file mode 100644 index 0000000..d6ff71a --- /dev/null +++ b/src/toys/tabloid/js/playground.js @@ -0,0 +1,120 @@ +import { tokenize, Parser, Environment } from "./tabloid.js"; +import { SAMPLE_PROGRAMS } from "./samples.js"; + +class TabloidRunner { + run(code) { + const stdout = []; + const stderr = []; + + if (!code) { + stderr.push("No code to execute."); + return { stdout, stderr }; + } + + try { + const tokens = tokenize(code); + const nodes = new Parser(tokens).parse(); + const env = new Environment({ + print: (msg) => stdout.push(String(msg)), + input: (promptText) => window.prompt(promptText) ?? "" + }); + env.run(nodes); + } catch (error) { + stderr.push(error.message || error.toString()); + } + + return { stdout, stderr }; + } +} + +export class TabloidPlayground { + constructor() { + this.runner = new TabloidRunner(); + this.programLookup = new Map(SAMPLE_PROGRAMS.map((program) => [program.id, program])); + this.activeProgramId = this.defaultProgramId = SAMPLE_PROGRAMS[0].id; + + this.stdoutElement = document.getElementById("stdout-content"); + this.errorElement = document.getElementById("error-content"); + this.programSelect = document.getElementById("program-select"); + this.runButton = document.getElementById("run-button"); + this.clearButton = document.getElementById("clear-button"); + + this.editor = adelieEditor.init("#code-editor", { + language: "tabloid" + }); + + this.init(); + } + + init() { + this.populateProgramSelect(); + this.registerEvents(); + this.loadProgram(this.activeProgramId); + } + + populateProgramSelect() { + if (!this.programSelect) { + return; + } + + this.programSelect.innerHTML = ""; + SAMPLE_PROGRAMS.forEach((program) => { + const option = document.createElement("option"); + option.value = program.id; + option.textContent = program.label; + this.programSelect.appendChild(option); + }); + + this.programSelect.value = this.activeProgramId; + } + + registerEvents() { + this.runButton.addEventListener("click", () => this.runCode()); + this.clearButton.addEventListener("click", () => this.resetOutput()); + + this.programSelect.addEventListener("change", () => { + this.loadProgram(this.programSelect.value); + }); + + document.addEventListener("keydown", (event) => { + if (event.ctrlKey && event.key === "Enter") { + event.preventDefault(); + this.runCode(); + } + }); + } + + setEditorContent(content) { + const length = this.editor.state.doc.toString().length; + this.editor.dispatch({ + changes: { from: 0, to: length, insert: content } + }); + } + + loadProgram(programId = this.defaultProgramId) { + const selectedProgram = this.programLookup.get(programId); + if (!selectedProgram) { + return; + } + + this.activeProgramId = selectedProgram.id; + this.setEditorContent(selectedProgram.code); + this.resetOutput(); + + if (this.programSelect) { + this.programSelect.value = this.activeProgramId; + } + } + + resetOutput() { + this.stdoutElement.textContent = ""; + this.errorElement.textContent = ""; + } + + runCode() { + const code = this.editor.state.doc.toString(); + const result = this.runner.run(code); + this.stdoutElement.textContent = result.stdout.join("\n"); + this.errorElement.textContent = result.stderr.join("\n"); + } +} |
