Refactor to make language selection dynamic

This commit is contained in:
Nilay Majorwar
2021-12-18 17:15:22 +05:30
parent 2322ebe55d
commit bb9e85f422
9 changed files with 172 additions and 111 deletions

View File

@ -1,3 +1,4 @@
import { setupWorker } from "../setup-worker";
import { DocumentRange, LanguageEngine, StepExecutionResult } from "../types";
import { BFAstStep, BFInstruction, BFRS, BF_OP } from "./constants";
@ -178,4 +179,4 @@ class BrainfuckLanguageEngine implements LanguageEngine<BFRS> {
}
}
export default BrainfuckLanguageEngine;
setupWorker(new BrainfuckLanguageEngine());

View File

@ -1,3 +1,4 @@
import { setupWorker } from "../setup-worker";
import { LanguageEngine, StepExecutionResult } from "../types";
import { RS } from "./constants";
@ -120,4 +121,4 @@ class SampleLanguageEngine implements LanguageEngine<RS> {
};
}
export default SampleLanguageEngine;
setupWorker(new SampleLanguageEngine());

103
engines/setup-worker.ts Normal file
View File

@ -0,0 +1,103 @@
import ExecutionController from "./execution-controller";
import { LanguageEngine, StepExecutionResult } from "./types";
import {
WorkerAckType,
WorkerRequestData,
WorkerResponseData,
} from "./worker-constants";
/** 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 = () => {
postMessage(ackMessage("init"));
};
/**
* Reset the state of the controller and engine, to
* prepare for execution of a new program.
*/
const resetController = <RS>(controller: ExecutionController<RS>) => {
controller.resetState();
postMessage(ackMessage("reset"));
};
/**
* Load program code into the engine.
* @param code Code content of the program
*/
const prepare = <RS>(
controller: ExecutionController<RS>,
{ 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 = <RS>(
controller: ExecutionController<RS>,
points: number[]
) => {
controller.updateBreakpoints(points);
postMessage(ackMessage("bp-update"));
};
/**
* Execute the entire program loaded on engine,
* and return result of execution.
*/
const execute = <RS>(controller: ExecutionController<RS>, interval: number) => {
controller.executeAll({
interval,
onResult: (res) => postMessage(resultMessage(res)),
});
};
/** Trigger pause in program execution */
const pauseExecution = async <RS>(controller: ExecutionController<RS>) => {
await controller.pauseExecution();
postMessage(ackMessage("pause"));
};
/** Run a single execution step */
const executeStep = <RS>(controller: ExecutionController<RS>) => {
const result = controller.executeStep();
postMessage(resultMessage(result));
};
/**
* Create an execution controller worker script with the given engine.
* @param engine Language engine to create worker for
*/
export const setupWorker = <RS>(engine: LanguageEngine<RS>) => {
const controller = new ExecutionController(engine);
addEventListener("message", async (ev: MessageEvent<WorkerRequestData>) => {
if (ev.data.type === "Init") return initController();
if (ev.data.type === "Reset") return resetController(controller);
if (ev.data.type === "Prepare") return prepare(controller, ev.data.params);
if (ev.data.type === "Execute")
return execute(controller, ev.data.params.interval);
if (ev.data.type === "Pause") return await pauseExecution(controller);
if (ev.data.type === "ExecuteStep") return executeStep(controller);
if (ev.data.type === "UpdateBreakpoints")
return updateBreakpoints(controller, ev.data.params.points);
throw new Error("Invalid worker message type");
});
};

View File

@ -1,99 +0,0 @@
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");
});