From dda0bf6fb952bd4945651527f693709f5a2a2e8c Mon Sep 17 00:00:00 2001 From: Steve Dunham Date: Sat, 23 Nov 2024 10:21:09 -0800 Subject: [PATCH] playground: add ability to run code --- playground/samples/Hello.newt | 43 +++++++++++++++++++++++ playground/samples/frame.html | 29 ++++++++++++++++ playground/src/main.ts | 65 ++++++++++++++++++++++++++++++----- 3 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 playground/samples/Hello.newt create mode 100644 playground/samples/frame.html diff --git a/playground/samples/Hello.newt b/playground/samples/Hello.newt new file mode 100644 index 0000000..5899e4f --- /dev/null +++ b/playground/samples/Hello.newt @@ -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" diff --git a/playground/samples/frame.html b/playground/samples/frame.html new file mode 100644 index 0000000..9556a3c --- /dev/null +++ b/playground/samples/frame.html @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/playground/src/main.ts b/playground/src/main.ts index 7b4b703..b64961a 100644 --- a/playground/src/main.ts +++ b/playground/src/main.ts @@ -10,11 +10,31 @@ monaco.languages.setMonarchTokensProvider("newt", newtTokens); monaco.languages.setLanguageConfiguration("newt", newtConfig); 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) { 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) => { state.output.value = ev.data.output; state.javascript.value = ev.data.javascript; @@ -30,6 +50,7 @@ self.MonacoEnvironment = { const state = { output: signal(""), javascript: signal(""), + messages: signal([]), editor: signal(null), }; @@ -110,19 +131,35 @@ function Result() { 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 JAVASCRIPT = "JS"; +const CONSOLE = "Console"; function Tabs() { - const [selected, setSelected] = useState(RESULTS); - + const [selected, setSelected] = useState(localStorage.tab ?? RESULTS); const Tab = (label: string) => { - let onClick = () => setSelected(label); + let onClick = () => { + setSelected(label); + localStorage.tab = label + } let className = "tab"; if (label == selected) className += " selected"; return h("div", { className, onClick }, label); }; + useEffect(() => { + if (state.messages.value) setSelected(CONSOLE) + }, [state.messages.value]) + let body; switch (selected) { case RESULTS: @@ -131,6 +168,9 @@ function Tabs() { case JAVASCRIPT: body = h(JavaScript, {}); break; + case CONSOLE: + body = h(Console, {}); + break; default: body = h("div", {}); } @@ -138,13 +178,14 @@ function Tabs() { return h( "div", { 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) ); } const SAMPLES = [ "Tour.newt", + "Hello.newt", "DSL.newt", "Tree.newt", "Reasoning.newt", @@ -180,12 +221,18 @@ function EditWrap({vertical, toggle}: {vertical: boolean, toggle: () => void}) { h("option", { value: "" }, "choose sample"), options ), - h('div', {style: {flex: '1 1'}}), - h('button', {onClick: toggle}, - h('svg', {width:20, height: 20}, - h('path',{d,fill:'none',stroke:'black'}) - ) + h("div", { style: { flex: "1 1" } }), + h("button", { onClick: runOutput + }, "⏵"), + 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 })) );