diff --git a/src/common/cse-machine/README.md b/src/common/cse-machine/README.md new file mode 100644 index 0000000..3b297d2 --- /dev/null +++ b/src/common/cse-machine/README.md @@ -0,0 +1,120 @@ +

CSE Machine

+ +

A language-agnostic protocol for the Source Academy CSE (Control–Stash–Environment) machine visualizer

+ +

+ + +

+ +## Features +- Language-agnostic snapshot protocol — any evaluator (js-slang, py-slang, …) can feed the same CSE machine visualization +- Structured-clone-safe types: everything that crosses the channel is plain JSON, so it survives `MessageChannel` / web-worker boundaries + +## Installation +```bash +yarn add @sourceacademy/common-cse-machine +# OR +npm i @sourceacademy/common-cse-machine +# OR +pnpm add @sourceacademy/common-cse-machine +``` + +## Structure +This package ([`@sourceacademy/common-cse-machine`](https://github.com/source-academy/plugins/tree/main/src/common/cse-machine)) contains the shared constants and types required by both the runner-side and web-side plugin implementations. + +These include: +- The IDs and channel name the two plugins use to find each other (`RUNNER_ID`, `WEB_ID`, `CSE_CHANNEL`, `CSE_DIRECTORY_ID`) +- The snapshot types that represent a complete evaluation step (`CseSnapshot`, `CseSnapshotMessage`) +- The serialized sub-types for each part of the machine (`CseSerializedInstruction`, `CseSerializedValue`, `CseSerializedEnvFrame`, `CseSerializedBinding`) + +## Usage +Import types from `@sourceacademy/common-cse-machine` when implementing a language-specific serializer: + +```ts +import type { + CseSnapshot, + CseSerializedValue, + CseSerializedInstruction, + CseSerializedEnvFrame, +} from '@sourceacademy/common-cse-machine'; + +function buildSnapshot(stepIndex: number): CseSnapshot { + return { + stepIndex, + control: [ { displayText: "call f" } ], + stash: [ { displayValue: "42", label: "number" } ], + environments: [ { id: "e0", name: "global", parentId: null, bindings: [], isActive: true } ], + currentLine: 3, + }; +} +``` + +## Protocol Types + +### Constants +| Name | Value | Description | +|------|-------|-------------| +| `CSE_CHANNEL` | `"__cse"` | The channel the runner and host plugins communicate over | +| `RUNNER_ID` | `"__runner_cse"` | ID of the runner-side plugin | +| `WEB_ID` | `"__web_cse"` | ID of the web/host-side plugin | +| `CSE_DIRECTORY_ID` | `"cse-machine"` | Plugin directory lookup key | +| `CSE_MESSAGE_TYPE_SNAPSHOTS` | `"snapshots"` | Discriminator for `CseSnapshotMessage` | + +### `CseSnapshot` +A complete snapshot of the CSE machine at a single evaluation step. Arrays are ordered top-first (i.e. top of control/stash is index 0). + +| Field | Type | Description | +|-------|------|-------------| +| `stepIndex` | `number` | 0-based index of this step within the run | +| `control` | `CseSerializedInstruction[]` | Control items, top first | +| `stash` | `CseSerializedValue[]` | Stash values, top first | +| `environments` | `CseSerializedEnvFrame[]` | All live environment frames at this step | +| `currentLine` | `number \| undefined` | 1-based source line of the most recently evaluated node | + +### `CseSerializedValue` +A single value on the stash or bound in an environment frame. + +| Field | Type | Description | +|-------|------|-------------| +| `displayValue` | `string` | Pre-rendered string shown in the visualizer | +| `label` | `string` | Coarse type tag, e.g. `"number"`, `"closure"`, `"list"` | +| `tag` | `string \| undefined` | Optional fine-grained tag for the visualizer | +| `metadata` | `unknown \| undefined` | Language-specific extras (e.g. closure frame id, array element refs) | + +### `CseSerializedInstruction` +A single item on the control stack. + +| Field | Type | Description | +|-------|------|-------------| +| `displayText` | `string` | Pre-rendered text shown on the control stack | +| `tag` | `string \| undefined` | Optional fine-grained tag for the visualizer | +| `metadata` | `unknown \| undefined` | Typed info used for animation dispatch (e.g. `instrType`, `numOfArgs`, `startLine`) | + +### `CseSerializedEnvFrame` +A single environment frame. + +| Field | Type | Description | +|-------|------|-------------| +| `id` | `string` | Stable unique id within a run | +| `name` | `string` | Display name (e.g. `"global"`, function name) | +| `parentId` | `string \| null` | Lexical parent frame id, or `null` for the root | +| `closureFrameId` | `string \| undefined` | For a closure's frame: the id of the frame it was defined in | +| `bindings` | `CseSerializedBinding[]` | Name → value bindings in this frame | +| `heapObjects` | `CseSerializedValue[] \| undefined` | Anonymous heap objects (closures/arrays) not bound to any name | +| `isActive` | `boolean` | Whether this is the currently-active (innermost) frame | +| `isOnCallStack` | `boolean \| undefined` | Whether this frame is currently on the call stack | + +### `CseSerializedBinding` +A single name → value binding within a frame. + +| Field | Type | Description | +|-------|------|-------------| +| `name` | `string` | The bound name | +| `value` | `CseSerializedValue` | The bound value | +| `isConst` | `boolean \| undefined` | Whether the binding is a constant (e.g. `const` in Source) | + +## Further reading +- To send snapshots from an evaluator, use [`@sourceacademy/runner-cse-machine`](https://github.com/source-academy/plugins/tree/main/src/runner/cse-machine) +- To receive snapshots in a host app, use [`@sourceacademy/web-cse-machine`](https://github.com/source-academy/plugins/tree/main/src/web/cse-machine) +- The [plugins wiki](https://github.com/source-academy/plugins/wiki) covers how Conductor plugins communicate diff --git a/src/runner/cse-machine/README.md b/src/runner/cse-machine/README.md new file mode 100644 index 0000000..4202aa3 --- /dev/null +++ b/src/runner/cse-machine/README.md @@ -0,0 +1,60 @@ +

CSE Machine

+ +

Runner-side plugin for the Source Academy CSE machine — sends evaluation snapshots to the host

+ +

+ + +

+ +## Features +- Transports a complete run's worth of CSE snapshots from the evaluator (worker) to the host app (browser) over a Conductor channel +- Language-agnostic: any evaluator can use it as long as it serializes its state into `CseSnapshot`s + +## Installation +```bash +yarn add @sourceacademy/runner-cse-machine +# OR +npm i @sourceacademy/runner-cse-machine +# OR +pnpm add @sourceacademy/runner-cse-machine +``` + +## Structure +This package ([`@sourceacademy/runner-cse-machine`](https://github.com/source-academy/plugins/tree/main/src/runner/cse-machine)) contains the `CseMachinePlugin` class — a Conductor runner plugin that an evaluator registers and calls after each run to forward snapshots to the host. + +The plugin owns only the transport. Serializing a language's control/stash/environment into [`CseSnapshot`](https://github.com/source-academy/plugins/tree/main/src/common/cse-machine/README.md#csesnapshot)s is the evaluator's responsibility and stays in the evaluator repo. + +### API Reference +| Name | Description | +|------|-------------| +| `sendSnapshots(snapshots: CseSnapshot[]): void` | Send the full batch of snapshots for a completed run to the host plugin. Call this once per run after all steps have been collected. | + +## Usage +After installation, import `CseMachinePlugin` and register it with the Conductor evaluator. After each run, collect your language-specific snapshots, serialize them into [`CseSnapshot`](https://github.com/source-academy/plugins/tree/main/src/common/cse-machine/README.md#csesnapshot)s, and call `sendSnapshots`. + +```ts +import { CseMachinePlugin } from '@sourceacademy/runner-cse-machine'; +import type { CseSnapshot } from '@sourceacademy/common-cse-machine'; + +// Register the plugin when setting up the evaluator +const cseMachinePlugin = conductorContext.getPlugin(CseMachinePlugin); + +// After a run completes, serialize each step into a CseSnapshot +const snapshots: CseSnapshot[] = steps.map((step, i) => ({ + stepIndex: i, + control: step.control.map(serializeControlItem), + stash: step.stash.map(serializeValue), + environments: serializeEnvChain(step.environments), + currentLine: step.currentNode?.line, +})); + +cseMachinePlugin.sendSnapshots(snapshots); +``` + +For a full example of how to serialize a language's control/stash/environment into `CseSnapshot`s, see the [py-slang `PyCseMachinePlugin`](https://github.com/source-academy/py-slang/blob/main/src/conductor/plugins/PyCseMachinePlugin.ts). + +## Further reading +- For the shared protocol types and IDs, see [`@sourceacademy/common-cse-machine`](https://github.com/source-academy/plugins/tree/main/src/common/cse-machine) +- For the host-side plugin that renders snapshots, see [`@sourceacademy/web-cse-machine`](https://github.com/source-academy/plugins/tree/main/src/web/cse-machine) +- The [plugins wiki](https://github.com/source-academy/plugins/wiki) covers how Conductor plugins communicate diff --git a/src/web/cse-machine/README.md b/src/web/cse-machine/README.md new file mode 100644 index 0000000..fc5d959 --- /dev/null +++ b/src/web/cse-machine/README.md @@ -0,0 +1,63 @@ +

CSE Machine

+ +

Web/host-side plugin for the Source Academy CSE machine — receives evaluation snapshots for rendering

+ +

+ + +

+ +## Features +- Subscribes to the CSE channel and delivers snapshot batches to the host app's visualization layer +- Language-agnostic: works with any evaluator that sends `CseSnapshot`s via the runner plugin +- Re-exports all protocol types from `@sourceacademy/common-cse-machine` so host apps need only one import + +## Installation +In production, use the [plugin directory](https://github.com/source-academy/plugin-directory) hosted on [GitHub Pages](https://source-academy.github.io/plugins/directory.json). It contains a list of all plugins available in this monorepo. Your host app should be able to load plugins from the plugin directory (the Source Academy frontend does). + +For locally hosting the plugin repository, run `yarn build` at the root of the repository. Then serve the plugin directory with: +```bash +yarn dlx serve --cors dist/ -p 1915 +``` +The plugin directory will be available at `http://localhost:1915/directory.json`. + +To install the package directly: +```bash +yarn add @sourceacademy/web-cse-machine +# OR +npm i @sourceacademy/web-cse-machine +# OR +pnpm add @sourceacademy/web-cse-machine +``` + +## Structure +This package ([`@sourceacademy/web-cse-machine`](https://github.com/source-academy/plugins/tree/main/src/web/cse-machine)) contains the `CseMachineHostPlugin` abstract class — a Conductor host plugin that subscribes to the [`CSE_CHANNEL`](https://github.com/source-academy/plugins/tree/main/src/common/cse-machine/README.md#constants) and delivers received [`CseSnapshot`](https://github.com/source-academy/plugins/tree/main/src/common/cse-machine/README.md#csesnapshot) batches to the host app. + +The plugin owns only the transport/receipt. Wiring snapshots into the actual visualization layer (adapter + renderer) is the host app's responsibility and stays in the host repo, because it is tightly coupled to the host's existing CSE machine UI. + +### API Reference +| Name | Description | +|------|-------------| +| `abstract receiveSnapshots(snapshots: CseSnapshot[]): void` | Called by the plugin each time a batch of snapshots arrives. Implement this in your host app to wire the snapshots into your visualization layer. | + +## Usage +Extend `CseMachineHostPlugin` and implement `receiveSnapshots` to connect incoming snapshots to your rendering layer: + +```ts +import { CseMachineHostPlugin } from '@sourceacademy/web-cse-machine'; +import type { CseSnapshot } from '@sourceacademy/web-cse-machine'; // re-exported from common + +export class MyCseMachinePlugin extends CseMachineHostPlugin { + receiveSnapshots(snapshots: CseSnapshot[]): void { + // Hand snapshots to your visualization layer, e.g.: + cseMachineStore.setSnapshots(snapshots); + } +} +``` + +For a full example of how the Source Academy frontend wires this into its CSE machine UI, see the [frontend source](https://github.com/source-academy/frontend). + +## Further reading +- For the shared protocol types and IDs, see [`@sourceacademy/common-cse-machine`](https://github.com/source-academy/plugins/tree/main/src/common/cse-machine) +- For the runner-side plugin that sends snapshots, see [`@sourceacademy/runner-cse-machine`](https://github.com/source-academy/plugins/tree/main/src/runner/cse-machine) +- The [plugins wiki](https://github.com/source-academy/plugins/wiki) covers how Conductor plugins communicate