import parser from "./parser.js"; import { compileProgram } from "./compiler.js"; const SAMPLE_PROGRAM = `// THIS PROGRAM COMPUTES X1 + X2 // Y <- X1 [ A1 ] IF X1 != 0 GOTO A2 GOTO B1 [ A2 ] X1 <- X1 - 1 Y <- Y + 1 GOTO A1 // Z1 <- X2 [ B1 ] IF X2 != 0 GOTO B2 GOTO C1 [ B2 ] X2 <- X2 - 1 Z1 <- Z1 + 1 GOTO B1 // Y <- Y + Z1 [ C1 ] IF Z1 != 0 GOTO C2 GOTO E1 [ C2 ] Z1 <- Z1 - 1 Y <- Y + 1 GOTO C1`; const STATUS_VARIANTS = { success: "text-success", error: "text-error", info: "muted" }; export class GodelPlayground { constructor() { this.elements = { compileBtn: document.getElementById("compile-btn"), copyBtn: document.getElementById("copy-btn"), evalBtn: document.getElementById("eval-btn"), computeNumberBtn: document.getElementById("compute-godel-btn"), compileStatus: document.getElementById("compile-status"), evalStatus: document.getElementById("eval-status"), godelSequence: document.getElementById("godel-sequence"), godelNumber: document.getElementById("godel-number") }; this.sourceEditor = null; this.compiledEditor = null; this.worker = null; this.latestSequence = []; this.init(); } init() { this.setupEditors(); this.bindEvents(); this.hydrateFromParams(); if (!this.getSource().trim()) { this.setSource(SAMPLE_PROGRAM); } this.compileSource(); } setupEditors() { this.sourceEditor = adelieEditor.init("#source-editor", { language: "javascript" }); this.compiledEditor = adelieEditor.init("#compiled-editor", { language: "javascript" }); this.setSource(SAMPLE_PROGRAM); } bindEvents() { this.elements.compileBtn.addEventListener("click", () => this.compileSource()); this.elements.evalBtn.addEventListener("click", () => this.evaluateCompiled()); this.elements.copyBtn.addEventListener("click", () => this.copyShareLink()); this.elements.computeNumberBtn.addEventListener("click", () => this.computeGodelNumber()); document.addEventListener("keydown", (event) => { if (event.ctrlKey && event.key === "Enter") { event.preventDefault(); this.compileSource(); } }); } hydrateFromParams() { const params = new URLSearchParams(window.location.search); const encoded = params.get("instructions"); if (encoded) { try { const decoded = atob(encoded); this.setSource(decoded); } catch (error) { console.warn("Failed to decode instructions from URL", error); } } } getSource() { return this.sourceEditor.state.doc.toString(); } setSource(content) { const docLength = this.sourceEditor.state.doc.toString().length; this.sourceEditor.dispatch({ changes: { from: 0, to: docLength, insert: content } }); } getCompiled() { return this.compiledEditor.state.doc.toString(); } setCompiled(content) { const docLength = this.compiledEditor.state.doc.toString().length; this.compiledEditor.dispatch({ changes: { from: 0, to: docLength, insert: content } }); } prepareSource(source) { return source.replace(/\/\/.*$/gm, "").trim(); } compileSource() { const raw = this.getSource(); const prepared = this.prepareSource(raw); if (!prepared) { this.setStatus("compile", "Provide some source to compile", "error"); return; } try { const ast = parser.parse(prepared); const { js, godelSequence } = compileProgram(ast); this.latestSequence = godelSequence; this.setCompiled(js); this.renderSequence(godelSequence); this.setStatus( "compile", `Compiled ${godelSequence.length} instruction${godelSequence.length === 1 ? "" : "s"}.`, "success" ); this.elements.computeNumberBtn.disabled = godelSequence.length === 0; } catch (error) { this.latestSequence = []; this.renderSequence([]); this.elements.computeNumberBtn.disabled = true; this.setStatus("compile", error.message || "Failed to compile program", "error"); } } evaluateCompiled() { const js = this.getCompiled(); if (!js.trim()) { this.setStatus("eval", "Compile a program first", "error"); return; } try { const result = (0, eval)(js); this.setStatus( "eval", `Result: ${typeof result === "undefined" ? "(no return)" : result}`, "success" ); } catch (error) { this.setStatus("eval", error.message || "Failed to evaluate program", "error"); } } renderSequence(sequence) { if (!sequence.length) { this.elements.godelSequence.textContent = "Compile to view the Gödel sequence."; this.elements.godelNumber.textContent = ""; return; } this.elements.godelSequence.textContent = `[${sequence.join(", ")}]`; this.elements.godelNumber.textContent = ""; } copyShareLink() { const data = btoa(this.getSource()); const url = `${window.location.href.split("?")[0]}?instructions=${data}`; navigator.clipboard.writeText(url) .then(() => alert("Shareable link copied to clipboard")) .catch(() => alert("Failed to copy link")); } computeGodelNumber() { if (!this.latestSequence.length) { this.setStatus("compile", "Compile a program to produce its Gödel sequence", "error"); return; } this.elements.godelNumber.textContent = "Working..."; this.elements.computeNumberBtn.disabled = true; const worker = this.getWorker(); worker.onmessage = (event) => { this.elements.godelNumber.textContent = event.data; this.elements.computeNumberBtn.disabled = false; }; worker.onerror = () => { this.elements.godelNumber.textContent = "Failed to compute Gödel number"; this.elements.computeNumberBtn.disabled = false; }; worker.postMessage(this.latestSequence); } getWorker() { if (!this.worker) { const workerUrl = new URL("./godel-worker.js", import.meta.url); this.worker = new Worker(workerUrl, { type: "module" }); } return this.worker; } setStatus(kind, message, variant = "info") { const element = kind === "compile" ? this.elements.compileStatus : this.elements.evalStatus; element.textContent = message; element.className = `status-text ${STATUS_VARIANTS[variant] || ""}`; } }