168 lines
4.6 KiB
JavaScript
168 lines
4.6 KiB
JavaScript
|
|
window.apfl_playground = (() => {
|
||
|
|
let instances = new Map();
|
||
|
|
|
||
|
|
function playground_write(handle, error, s) {
|
||
|
|
let inst = instances.get(handle);
|
||
|
|
if (inst) {
|
||
|
|
inst.write(error, s);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
window.playground_write = playground_write;
|
||
|
|
|
||
|
|
let repl_new_for_playground = undefined;
|
||
|
|
let repl_run = undefined;
|
||
|
|
let repl_destroy = undefined;
|
||
|
|
|
||
|
|
function Playground() {
|
||
|
|
let h = repl_new_for_playground();
|
||
|
|
if (!h) {
|
||
|
|
throw new Error("Could not init playground");
|
||
|
|
}
|
||
|
|
|
||
|
|
this.echoSource = true;
|
||
|
|
|
||
|
|
this._h = h;
|
||
|
|
this._onOutput = null;
|
||
|
|
this._onError = null;
|
||
|
|
this._lastResult = Playground.REPL_OK;
|
||
|
|
instances.set(h, this);
|
||
|
|
}
|
||
|
|
|
||
|
|
Playground.REPL_OK = 0;
|
||
|
|
Playground.REPL_MORE_INPUT = 1;
|
||
|
|
Playground.REPL_ERR = 2;
|
||
|
|
Playground.REPL_FATAL = 3;
|
||
|
|
|
||
|
|
Playground.prototype.destroy = function () {
|
||
|
|
repl_destroy(this._h);
|
||
|
|
instances.delete(this._h);
|
||
|
|
};
|
||
|
|
|
||
|
|
Playground.prototype.onOutput = function (f) {
|
||
|
|
this._onOutput = f;
|
||
|
|
};
|
||
|
|
|
||
|
|
Playground.prototype.onError = function (f) {
|
||
|
|
this._onError = f;
|
||
|
|
};
|
||
|
|
|
||
|
|
Playground.prototype.write = function (error, s) {
|
||
|
|
let writer = error ? this._onError : this._onOutput;
|
||
|
|
if (writer) {
|
||
|
|
writer(s);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Playground.prototype.getPrompt = function () {
|
||
|
|
return this._lastResult === Playground.REPL_MORE_INPUT ? "..." : ">";
|
||
|
|
};
|
||
|
|
|
||
|
|
Playground.prototype.runCode = function (source) {
|
||
|
|
source += "";
|
||
|
|
if (this.echoSource) {
|
||
|
|
this.write(false, `${this.getPrompt()} ${source}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (this._lastResult = repl_run(this._h, source));
|
||
|
|
};
|
||
|
|
|
||
|
|
Playground.prototype.interactive = function (parent) {
|
||
|
|
const elem = (name, inner) => {
|
||
|
|
let out = document.createElement(name);
|
||
|
|
if (inner) {
|
||
|
|
inner(out);
|
||
|
|
}
|
||
|
|
return out;
|
||
|
|
};
|
||
|
|
|
||
|
|
let output;
|
||
|
|
let form;
|
||
|
|
let prompt;
|
||
|
|
let input;
|
||
|
|
|
||
|
|
parent.appendChild(elem("DIV", outer => {
|
||
|
|
outer.classList = "apfl_playground";
|
||
|
|
outer.appendChild(output = elem("PRE", output => {
|
||
|
|
output.classList = "apfl_playground_output";
|
||
|
|
}))
|
||
|
|
outer.appendChild(form = elem("FORM", form => {
|
||
|
|
form.appendChild(prompt = elem("SPAN", prompt => {
|
||
|
|
prompt.className = "apfl_playground_prompt";
|
||
|
|
prompt.innerText = this.getPrompt();
|
||
|
|
}));
|
||
|
|
form.appendChild(input = elem("INPUT", input => {
|
||
|
|
input.type = "text";
|
||
|
|
input.className = "apfl_playground_input";
|
||
|
|
input.placeholder = "code ...";
|
||
|
|
}));
|
||
|
|
}))
|
||
|
|
}));
|
||
|
|
|
||
|
|
form.onsubmit = (e) => {
|
||
|
|
e.preventDefault();
|
||
|
|
let code = input.value;
|
||
|
|
input.value = "";
|
||
|
|
|
||
|
|
if (!code) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
this.runCode(code + "\n");
|
||
|
|
|
||
|
|
prompt.innerText = this.getPrompt();
|
||
|
|
};
|
||
|
|
|
||
|
|
const write = (error, s) => {
|
||
|
|
var span = elem("SPAN");
|
||
|
|
if (error) {
|
||
|
|
span.classList.add("error");
|
||
|
|
}
|
||
|
|
var lines = s.split("\n");
|
||
|
|
var scroll = false;
|
||
|
|
for (var i = 0 ; i < lines.length; i++) {
|
||
|
|
if (i > 0) {
|
||
|
|
span.appendChild(elem("BR"));
|
||
|
|
scroll = true;
|
||
|
|
}
|
||
|
|
var line = lines[i];
|
||
|
|
if (line != "") {
|
||
|
|
span.appendChild(document.createTextNode(lines[i]));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
output.append(span);
|
||
|
|
if (scroll) {
|
||
|
|
output.scrollTop = output.scrollHeight - output.clientHeight;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
this.onOutput(s => write(false, s));
|
||
|
|
this.onError(s => write(true, s));
|
||
|
|
};
|
||
|
|
|
||
|
|
let ok = false
|
||
|
|
let onModLoaded = [];
|
||
|
|
|
||
|
|
return async function () {
|
||
|
|
if (ok) {
|
||
|
|
return Playground;
|
||
|
|
}
|
||
|
|
|
||
|
|
let p = new Promise(resolve => {
|
||
|
|
onModLoaded.push(resolve);
|
||
|
|
});
|
||
|
|
|
||
|
|
WasmApflPlayground().then(instance => {
|
||
|
|
repl_new_for_playground = instance.cwrap("repl_new_for_playground", "number", []);
|
||
|
|
repl_run = instance.cwrap("repl_run", "number", ["number", "string"]);
|
||
|
|
repl_destroy = instance.cwrap("repl_destroy", "void", ["number"]);
|
||
|
|
|
||
|
|
ok = true;
|
||
|
|
|
||
|
|
onModLoaded.forEach(l => l(Playground));
|
||
|
|
});
|
||
|
|
|
||
|
|
return await p;
|
||
|
|
};
|
||
|
|
})();
|