esolang/engines/worker.ts
2021-12-16 23:05:22 +05:30

100 lines
2.7 KiB
TypeScript

import BrainfuckLanguageEngine from "./brainfuck/engine";
import ExecutionController from "./execution-controller";
import { StepExecutionResult } from "./types";
import {
WorkerAckType,
WorkerRequestData,
WorkerResponseData,
} from "./worker-constants";
let _controller: ExecutionController<any> | null = null;
/** Create a worker response for update acknowledgement */
const ackMessage = <RS>(state: WorkerAckType): WorkerResponseData<RS> => ({
type: "ack",
data: state,
});
/** Create a worker response for execution result */
const resultMessage = <RS>(
result: StepExecutionResult<RS>
): WorkerResponseData<RS> => ({
type: "result",
data: result,
});
/**
* Initialize the execution controller.
*/
const initController = () => {
const engine = new BrainfuckLanguageEngine();
_controller = new ExecutionController(engine);
postMessage(ackMessage("init"));
};
/**
* Reset the state of the controller and engine, to
* prepare for execution of a new program.
*/
const resetController = () => {
_controller!.resetState();
postMessage(ackMessage("reset"));
};
/**
* Load program code into the engine.
* @param code Code content of the program
*/
const prepare = ({ code, input }: { code: string; input: string }) => {
_controller!.prepare(code, input);
postMessage(ackMessage("prepare"));
};
/**
* Update debugging breakpoints
* @param points List of line numbers having breakpoints
*/
const updateBreakpoints = (points: number[]) => {
_controller!.updateBreakpoints(points);
postMessage(ackMessage("bp-update"));
};
/**
* Execute the entire program loaded on engine,
* and return result of execution.
*/
const execute = (interval: number) => {
_controller!.executeAll({
interval,
onResult: (res) => postMessage(resultMessage(res)),
});
};
/**
* Trigger pause in program execution
*/
const pauseExecution = async () => {
await _controller!.pauseExecution();
postMessage(ackMessage("pause"));
};
/**
* Run a single execution step
*/
const executeStep = () => {
const result = _controller!.executeStep();
postMessage(resultMessage(result));
};
addEventListener("message", async (ev: MessageEvent<WorkerRequestData>) => {
if (ev.data.type === "Init") return initController();
if (ev.data.type === "Reset") return resetController();
if (ev.data.type === "Prepare") return prepare(ev.data.params);
if (ev.data.type === "Execute") return execute(ev.data.params.interval);
if (ev.data.type === "Pause") return await pauseExecution();
if (ev.data.type === "ExecuteStep") return executeStep();
if (ev.data.type === "UpdateBreakpoints")
return updateBreakpoints(ev.data.params.points);
throw new Error("Invalid worker message type");
});