esolang/ui/Mainframe.tsx
Nilay Majorwar 01ba292b9f Implement basic execution system and UI
This is a rather large commit that includes all of the following:
- React UI with code editor, runtime renderer and input-output panes
- Language providers for a sample language and Brainfuck
- Implementation of code execution in a web worker
- All-at-once unabortable execution of program fully functional
2021-12-14 22:30:41 +05:30

79 lines
2.8 KiB
TypeScript

import React from "react";
import { CodeEditor, CodeEditorRef } from "../ui/code-editor";
import { InputEditor, InputEditorRef } from "../ui/input-editor";
import { MainLayout } from "../ui/MainLayout";
import { useExecController } from "../ui/use-exec-controller";
import { DocumentRange, LanguageProvider } from "../engines/types";
import SampleLangProvider from "../engines/sample-lang";
import BrainfuckProvider from "../engines/brainfuck";
import { OutputViewer } from "../ui/output-viewer";
export const Mainframe = () => {
const codeEditorRef = React.useRef<CodeEditorRef>(null);
const inputEditorRef = React.useRef<InputEditorRef>(null);
// const providerRef = React.useRef<LanguageProvider<any>>(SampleLangProvider);
const providerRef = React.useRef<LanguageProvider<any>>(BrainfuckProvider);
const execController = useExecController();
// UI states used in execution time
const [rendererState, setRendererState] = React.useState<any>(null);
const [output, setOutput] = React.useState<string | null>(null);
const [codeHighlights, setCodeHighlights] = React.useState<
DocumentRange | undefined
>();
const testDrive = React.useCallback(async () => {
console.info("=== RUNNING TEST DRIVE ===");
// Check that controller is ready to execute
const readyStates = ["empty", "ready", "done"];
if (!readyStates.includes(execController.state)) {
console.error(`Controller not ready: state is ${execController.state}`);
return;
}
// Prepare for execution
setOutput("");
await execController.resetState();
await execController.prepare(
codeEditorRef.current!.getValue(),
inputEditorRef.current!.getValue()
);
// Begin execution
await execController.executeAll((result) => {
setRendererState(result.rendererState);
setCodeHighlights(result.nextStepLocation || undefined);
setOutput((o) => (o || "") + (result.output || ""));
}, 20);
}, [execController.state]);
React.useEffect(() => {
const handler = (ev: KeyboardEvent) => {
if (!(ev.ctrlKey && ev.code === "KeyY")) return;
testDrive();
};
document.addEventListener("keydown", handler);
return () => document.removeEventListener("keydown", handler);
}, [testDrive]);
return (
<MainLayout
renderEditor={() => (
<CodeEditor
ref={codeEditorRef}
languageId="brainfuck"
highlights={codeHighlights}
defaultValue={providerRef.current.sampleProgram}
tokensProvider={providerRef.current.editorTokensProvider}
/>
)}
renderRenderer={() => (
<providerRef.current.Renderer state={rendererState} />
)}
renderInput={() => <InputEditor ref={inputEditorRef} />}
renderOutput={() => <OutputViewer value={output} />}
/>
);
};