aboutsummaryrefslogtreecommitdiff
path: root/static/index.js
blob: cff780591ad313504d6eaa3feaa183117a4cda4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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) {
    console.error(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);