summaryrefslogtreecommitdiff
path: root/src/toys/tabloid/js/playground.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/toys/tabloid/js/playground.js')
-rw-r--r--src/toys/tabloid/js/playground.js120
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");
+ }
+}