Basic REPL in the web UI
This commit is contained in:
parent
e2354fb9ce
commit
182307a81f
8 changed files with 467 additions and 3 deletions
107
src/ui/REPL.tsx
Normal file
107
src/ui/REPL.tsx
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import { createSignal, Match, Switch } from 'solid-js';
|
||||
import { useProgram } from './ProgramProvider';
|
||||
import { eval_start } from 'src/lang/eval/evaluator';
|
||||
import { Value } from 'src/lang/eval/value';
|
||||
import { RuntimeError } from 'src/lang/eval/error';
|
||||
import { SourceText, sourceText } from 'src/lang/parser/source_text';
|
||||
import { ParseError, parseExpr } from 'src/lang/parser/parser';
|
||||
import { ShowParseError } from './ParseError';
|
||||
import { Val } from './Value';
|
||||
|
||||
namespace ReplResult {
|
||||
export type Idle =
|
||||
{ tag: "idle" }
|
||||
export type Success =
|
||||
{ tag: "success", value: Value }
|
||||
export type Parse_Error =
|
||||
{ tag: "parse_error", text: SourceText, err: ParseError }
|
||||
export type Runtime_Error =
|
||||
{ tag: "runtime_error", err: RuntimeError }
|
||||
}
|
||||
|
||||
type ReplResult =
|
||||
| ReplResult.Idle
|
||||
| ReplResult.Success
|
||||
| ReplResult.Parse_Error
|
||||
| ReplResult.Runtime_Error
|
||||
|
||||
|
||||
export function ExprREPL() {
|
||||
const program = useProgram();
|
||||
|
||||
const [input, setInput] = createSignal("");
|
||||
|
||||
const [result, setResult] = createSignal<ReplResult>({ tag: "idle" });
|
||||
|
||||
function runExecution() {
|
||||
const raw = input();
|
||||
if (input().trim() === "") {
|
||||
return;
|
||||
}
|
||||
const text = sourceText(raw);
|
||||
const parseResult = parseExpr(text);
|
||||
|
||||
if (parseResult.tag === "error") {
|
||||
setResult({ tag: "parse_error", text: text, err: parseResult.error });
|
||||
} else {
|
||||
const evalResult = eval_start(program, parseResult.value);
|
||||
if (evalResult.tag === "ok") {
|
||||
setResult({ tag: "success", value: evalResult.value });
|
||||
} else {
|
||||
setResult({ tag: "runtime_error", err: evalResult.error });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<textarea
|
||||
placeholder="+(3, 4)"
|
||||
rows="5"
|
||||
value={input()}
|
||||
onInput={(e) => setInput(e.currentTarget.value)}
|
||||
onKeyDown={(e) => {
|
||||
// Check for Enter + (Ctrl or Command for Mac)
|
||||
if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault(); // Stop the newline from being added
|
||||
runExecution();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<button onClick={runExecution}>Run</button>
|
||||
|
||||
<hr />
|
||||
|
||||
<div>
|
||||
<Switch>
|
||||
<Match when={result().tag === "idle"}>
|
||||
{ "" }
|
||||
</Match>
|
||||
<Match when={result().tag === "success" && result() as ReplResult.Success }>
|
||||
{(res) => (
|
||||
<article>
|
||||
<header>Result</header>
|
||||
<Val value={ res().value } />
|
||||
</article>
|
||||
)}
|
||||
</Match>
|
||||
<Match when={result().tag === "parse_error" && result() as ReplResult.Parse_Error }>
|
||||
{(res) => (
|
||||
<ShowParseError text={res().text} err={res().err} />
|
||||
)}
|
||||
</Match>
|
||||
<Match when={result().tag === "runtime_error" && result() as ReplResult.Runtime_Error }>
|
||||
{(res) => (
|
||||
<article style={{ "border-color": "var(--pico-del-color)" }}>
|
||||
<header style={{ color: "var(--pico-del-color)" }}>Runtime Error</header>
|
||||
<pre>{JSON.stringify(res().err, null, 2)}</pre>
|
||||
</article>
|
||||
)}
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue