playground: add ability to run code

This commit is contained in:
2024-11-23 10:21:09 -08:00
parent affae1fecf
commit dda0bf6fb9
3 changed files with 128 additions and 9 deletions

View File

@@ -0,0 +1,43 @@
module Main
-- Monad
class Monad (m : U U) where
bind : {a b} m a (a m b) m b
pure : {a} a m a
infixl 1 _>>=_ _>>_
_>>=_ : {m} {{Monad m}} {a b} -> (m a) -> (a -> m b) -> m b
ma >>= amb = bind ma amb
_>>_ : {m} {{Monad m}} {a b} -> m a -> m b -> m b
ma >> mb = mb
-- I don't want to use an empty type because it would be a proof of void
ptype World
data IORes : U -> U where
MkIORes : {a : U} -> a -> World -> IORes a
IO : U -> U
IO a = World -> IORes a
data Unit : U where
MkUnit : Unit
instance Monad IO where
bind ma mab = \ w => case ma w of
MkIORes a w => mab a w
pure a = \ w => MkIORes a w
ptype String
pfunc putStrLn : String -> IO Unit := "(s) => (w) => {
console.log(s)
return MkIORes(Unit,MkUnit,w)
}"
main : IO Unit
main = do
putStrLn "hello, world"

View File

@@ -0,0 +1,29 @@
<html>
<head>
<script>
realLog = console.log
messages = []
console.log = (...args) => {
messages.push(args.join(' '))
realLog(...args)
}
window.addEventListener('message', (ev) => {
realLog('got', ev)
let {cmd, src} = ev.data
if (cmd === 'exec') {
try {
eval(src)
} catch (e) {
console.log(e)
}
}
window.parent.postMessage({messages}, '*')
messages = []
})
realLog('IFRAME INITIALIZED')
</script>
</head>
<body>
</body>
</html>

View File

@@ -10,11 +10,31 @@ monaco.languages.setMonarchTokensProvider("newt", newtTokens);
monaco.languages.setLanguageConfiguration("newt", newtConfig); monaco.languages.setLanguageConfiguration("newt", newtConfig);
const newtWorker = new Worker("worker.js"); //new URL("worker.js", import.meta.url)) const newtWorker = new Worker("worker.js"); //new URL("worker.js", import.meta.url))
const iframe = document.createElement("iframe");
iframe.src = "frame.html";
iframe.style.display = "none";
document.body.appendChild(iframe);
function run(src: string) { function run(src: string) {
newtWorker.postMessage({ src }); newtWorker.postMessage({ src });
} }
function runOutput() {
const src = state.javascript.value
console.log("RUN", iframe.contentWindow);
try {
iframe.contentWindow?.postMessage({ cmd: "exec", src }, "*");
} catch (e) {
console.error(e);
}
}
window.onmessage = (ev) => {
console.log("window got", ev.data);
if (ev.data.messages)
state.messages.value = ev.data.messages;
};
newtWorker.onmessage = (ev) => { newtWorker.onmessage = (ev) => {
state.output.value = ev.data.output; state.output.value = ev.data.output;
state.javascript.value = ev.data.javascript; state.javascript.value = ev.data.javascript;
@@ -30,6 +50,7 @@ self.MonacoEnvironment = {
const state = { const state = {
output: signal(""), output: signal(""),
javascript: signal(""), javascript: signal(""),
messages: signal<string[]>([]),
editor: signal<monaco.editor.IStandaloneCodeEditor | null>(null), editor: signal<monaco.editor.IStandaloneCodeEditor | null>(null),
}; };
@@ -110,19 +131,35 @@ function Result() {
return h("div", { id: "result" }, text); return h("div", { id: "result" }, text);
} }
function Console() {
const messages = state.messages.value ?? []
return h(
"div",
{ id: "console" },
messages.map((msg) => h("div", { className: "message" }, msg))
);
}
const RESULTS = "Output"; const RESULTS = "Output";
const JAVASCRIPT = "JS"; const JAVASCRIPT = "JS";
const CONSOLE = "Console";
function Tabs() { function Tabs() {
const [selected, setSelected] = useState(RESULTS); const [selected, setSelected] = useState(localStorage.tab ?? RESULTS);
const Tab = (label: string) => { const Tab = (label: string) => {
let onClick = () => setSelected(label); let onClick = () => {
setSelected(label);
localStorage.tab = label
}
let className = "tab"; let className = "tab";
if (label == selected) className += " selected"; if (label == selected) className += " selected";
return h("div", { className, onClick }, label); return h("div", { className, onClick }, label);
}; };
useEffect(() => {
if (state.messages.value) setSelected(CONSOLE)
}, [state.messages.value])
let body; let body;
switch (selected) { switch (selected) {
case RESULTS: case RESULTS:
@@ -131,6 +168,9 @@ function Tabs() {
case JAVASCRIPT: case JAVASCRIPT:
body = h(JavaScript, {}); body = h(JavaScript, {});
break; break;
case CONSOLE:
body = h(Console, {});
break;
default: default:
body = h("div", {}); body = h("div", {});
} }
@@ -138,13 +178,14 @@ function Tabs() {
return h( return h(
"div", "div",
{ className: "tabPanel right" }, { className: "tabPanel right" },
h("div", { className: "tabBar" }, Tab(RESULTS), Tab(JAVASCRIPT)), h("div", { className: "tabBar" }, Tab(RESULTS), Tab(JAVASCRIPT), Tab(CONSOLE)),
h("div", { className: "tabBody" }, body) h("div", { className: "tabBody" }, body)
); );
} }
const SAMPLES = [ const SAMPLES = [
"Tour.newt", "Tour.newt",
"Hello.newt",
"DSL.newt", "DSL.newt",
"Tree.newt", "Tree.newt",
"Reasoning.newt", "Reasoning.newt",
@@ -180,12 +221,18 @@ function EditWrap({vertical, toggle}: {vertical: boolean, toggle: () => void}) {
h("option", { value: "" }, "choose sample"), h("option", { value: "" }, "choose sample"),
options options
), ),
h('div', {style: {flex: '1 1'}}), h("div", { style: { flex: "1 1" } }),
h('button', {onClick: toggle}, h("button", { onClick: runOutput
h('svg', {width:20, height: 20}, }, "⏵"),
h('path',{d,fill:'none',stroke:'black'}) h(
) "button",
{ onClick: toggle },
h(
"svg",
{ width: 20, height: 20 },
h("path", { d, fill: "none", stroke: "black" })
) )
)
), ),
h("div", { className: "tabBody" }, h(Editor, { initialValue: value })) h("div", { className: "tabBody" }, h(Editor, { initialValue: value }))
); );