plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


7aa0d3b — Peter John 2026-11-03T20:34:45+05:30
docs(plans): pure scene enums: game kinds stay records in a union enum
docs/plans/2026-11-03-pure-scene-enums.review.md CHANGED
@@ -17,10 +17,13 @@ This plan moves all of it to enums (user decision 2026-11-03: pure scene only; s
17
17
  - `Kind[K]` is an inline-variant enum: `Group(children)`, `Sprite(texture region)`, `Label(text, size)`, `Button(text, textures, pressed, over)` and `Custom(body: K)`.
18
18
  - `n.x` stays a plain field read, and there's no 14-field header to repeat.
19
19
  - Narrowing back to a kind is a `match`. That fixes phase A's known gap: a trait value from the tree couldn't be narrowed back to its record.
20
- - **The game brings its own kinds.**
21
- - `game_pure_scene` defines `enum Body = Panel(..) | Ship(..) | Orb(..) | Mine(..) | Star(..)`, with only each kind's own fields (clock, velocity, phase, texture...), and builds a `Node[Body]` tree.
22
- - Per-kind behaviour moves from overrides into two plain functions the game passes in: `act(node, dt) -> node` (drift, spin, animation clocks) and `draw(node, matrix, tint)`.
23
- - `SceneTree.update(root, dt, act)` and `PureScene.drawTree(root, draw)` call them for `Custom` nodes. `Group`, `Sprite`, `Label` and `Button` are handled by std itself.
20
+ - **The game brings its own kinds, as records (user decision 2026-11-03).**
21
+ - `game_pure_scene` keeps `Panel`, `Ship`, `Orb`, `Mine` and `Star` as plain records. Each holds only its own fields (clock, velocity, phase, texture...) and its own `act` and `draw` methods. They no longer embed a trait or repeat the 14-field header.
22
+ - The records are joined by a union enum, `enum Body = Panel | Ship | Orb | Mine | Star`. A call like `b.act(node, dt)` on a `Body` dispatches to the right record with no `match` (checked by a probe).
23
+ - The game builds a `Node[Body]` tree.
24
+ - std finds the body in a `Custom(body)` node and calls two functions the game passes in, giving each the body and the node: `act(body, node, dt)` and `draw(body, node, matrix, tint)`.
25
+ - In the game these are one-liners: `fun actBody(b: Body, n: Node[Body], dt: Float) = b.act(n, dt)`. A body's method can move the node (`n.x = ...`) and change its own fields in place.
26
+ - `SceneTree.update(root, dt, actBody)` and `PureScene.drawTree(root, drawBody)` call them for `Custom` nodes. `Group`, `Sprite`, `Label` and `Button` are handled by std itself.
24
27
  - Closure specialisation inlines the passed functions, so there's no indirect call per node.
25
28
  - **Hit-testing and input are unchanged in behaviour.**
26
29
  - A `Custom` node hits on its box, like today's default.
@@ -36,7 +39,7 @@ This plan moves all of it to enums (user decision 2026-11-03: pure scene only; s
36
39
  ## What you will get
37
40
  - `PureScene`, `SceneTree`, `SceneInput` and `Actions` with no `trait`, no `override` and no union-of-records enums.
38
41
  - `game_pure_scene.plum` and the `stress_pure_scene` web fixture on `Node[Body]`.
39
- - The game reads like `game_state_view`: kinds are an enum and behaviour is a `match`.
42
+ - Each kind is a small record with its own methods, joined by a union enum `Body`.
40
43
  - Same rules, keys, assets, sounds and scoring.
41
44
  - README's pure-scene section and the std doc comments updated to the new shape.
42
45
 
@@ -46,14 +49,14 @@ This plan moves all of it to enums (user decision 2026-11-03: pure scene only; s
46
49
  | Actions on enums | `Action` record + `ActionKind` enum; in-place stepping kept; golden-value tests unchanged in values | M |
47
50
  | PureScene on enums | `Node[K]`, `Kind[K]`, builders (`group`, `sprite`, `label`, `button`, `custom`), drawing, hit test, Button's pointer handling | M |
48
51
  | SceneTree and SceneInput | update/find/hit/stage over `Node[K]` with the game's `act` (and `draw` passed through drawing) | M |
49
- | Orb Run on Node[Body] | `game_pure_scene.plum`: `enum Body`, `actBody`, `drawBody`; tests kept by name and value | M |
52
+ | Orb Run on Node[Body] | `game_pure_scene.plum`: kind records with `act`/`draw` methods, `enum Body = Panel \| Ship \| ...`, one-line `actBody`/`drawBody`; tests kept by name and value | M |
50
53
  | Fixture, host checks, docs | `stress_pure_scene.plum`, host parity and draw-count tests, README | S |
51
54
  | Benchmark | Headed Chromium run against the mutable scene, same bar as before | S |
52
55
 
53
56
  ## How it fits together
54
57
  ```mermaid
55
58
  flowchart LR
56
- G["game: enum Body, actBody, drawBody"] --> T["SceneTree.update(root, dt, actBody)"]
59
+ G["game: records Ship, Orb, ... + enum Body, actBody, drawBody"] --> T["SceneTree.update(root, dt, actBody)"]
57
60
  G --> D["PureScene.drawTree(root, drawBody)"]
58
61
  T --> N["own record Node[K] + enum Kind[K]"]
59
62
  D --> N
@@ -84,7 +87,7 @@ flowchart LR
84
87
  - The supercompiler (next plan).
85
88
 
86
89
  ## How we will know it works
87
- - `grep` finds no `trait`, `override` or record-union `enum X = A | B` in the four std modules or `game_pure_scene.plum`.
90
+ - `grep` finds no `trait` or `override` in the four std modules or `game_pure_scene.plum`, and no record-union enum in the std modules (the game's `Body` union is intended).
88
91
  - Actions golden tests and every `game_pure_scene` test pass, with the same names and values.
89
92
  - Host parity and draw-count tests pass: the pure scene draws the same count as the mutable scene. The `pureScene*` browser check passes.
90
93
  - Full suites stay green: Plum std/examples (632+) and the Rust crates.