diff options
Diffstat (limited to 'static/index.js')
-rw-r--r-- | static/index.js | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/static/index.js b/static/index.js new file mode 100644 index 0000000..d460c61 --- /dev/null +++ b/static/index.js @@ -0,0 +1,124 @@ +import CodeFlask from "https://cdn.jsdelivr.net/npm/codeflask@1.4.1/+esm"; +import initGleamCompiler from "./compiler.js"; +import stdlib from "./stdlib.js"; + +const output = document.querySelector("#output"); +const initialCode = document.querySelector("#code").textContent; + +const prismGrammar = { + comment: { + pattern: /\/\/.*/, + greedy: true, + }, + function: /([a-z_][a-z0-9_]+)(?=\()/, + keyword: + /\b(use|case|if|external|fn|import|let|assert|try|pub|type|opaque|const|todo|as)\b/, + symbol: { + pattern: /([A-Z][A-Za-z0-9_]+)/, + greedy: true, + }, + operator: { + pattern: + /(<<|>>|<-|->|\|>|<>|\.\.|<=\.?|>=\.?|==\.?|!=\.?|<\.?|>\.?|&&|\|\||\+\.?|-\.?|\/\.?|\*\.?|%\.?|=)/, + greedy: true, + }, + string: { + pattern: /"(?:\\(?:\r\n|[\s\S])|(?!")[^\\\r\n])*"/, + greedy: true, + }, + module: { + pattern: /([a-z][a-z0-9_]*)\./, + inside: { + punctuation: /\./, + }, + alias: "keyword", + }, + punctuation: /[.\\:,{}()]/, + number: + /\b(?:0b[0-1]+|0o[0-7]+|[[:digit:]][[:digit:]_]*(\\.[[:digit:]]*)?|0x[[:xdigit:]]+)\b/, +}; + +// Monkey patch console.log to keep a copy of the output +let logged = ""; +const log = console.log; +console.log = (...args) => { + log(...args); + logged += args.map((e) => `${e}`).join(" ") + "\n"; +}; + +function resetLogCapture() { + logged = ""; +} + +async function compileEval(project, code) { + try { + project.writeModule("main", code); + project.compilePackage("javascript"); + const js = project.readCompiledJavaScript("main"); + const main = await loadProgram(js); + resetLogCapture(); + if (main) main(); + replaceOutput(logged, "log"); + } catch (error) { + replaceOutput(error.toString(), "error"); + } + for (const warning of project.takeWarnings()) { + appendOutput(warning, "warning"); + } +} + +async function loadProgram(js) { + const url = new URL(import.meta.url); + url.pathname = ""; + url.hash = ""; + url.search = ""; + const href = url.toString(); + const js1 = js.replaceAll( + /from\s+"\.\/(.+)"/g, + `from "${href}precompiled/$1"` + ); + const js2 = btoa(unescape(encodeURIComponent(js1))); + const module = await import("data:text/javascript;base64," + js2); + return module.main; +} + +function clearOutput() { + while (output.firstChild) { + output.removeChild(output.firstChild); + } +} + +function replaceOutput(content, className) { + clearOutput(); + appendOutput(content, className); +} + +function appendOutput(content, className) { + const element = document.createElement("pre"); + element.textContent = content; + element.className = className; + output.appendChild(element); +} + +const editor = new CodeFlask("#editor-target", { + language: "gleam", +}); +editor.addLanguage("gleam", prismGrammar); +editor.updateCode(initialCode); + +const compiler = await initGleamCompiler(); +const project = compiler.newProject(); +for (const [name, code] of Object.entries(stdlib)) { + project.writeModule(name, code); +} + +function debounce(fn, delay) { + let timer = null; + return (...args) => { + clearTimeout(timer); + timer = setTimeout(() => fn(...args), delay); + }; +} + +editor.onUpdate(debounce((code) => compileEval(project, code), 200)); +compileEval(project, initialCode); |