plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
0f66a6c
— Peter John
2026-11-04T12:24:33+05:30
docs: plan example consolidation and games/assets move (T25)
docs/decisions/2026-11-language-and-scene-decisions.md
CHANGED
|
@@ -17,6 +17,8 @@ A record of the design decisions taken in the 2026-11-03/04 sessions, with the r
|
|
|
17
17
|
- `plum-std/game/functional/`: Picture, Anim, Ui (state/view).
|
|
18
18
|
- `plum-examples/games/imperative/`, `games/pure/` and `games/functional/` for their example games (the mutable games moved too, 2026-11-04). PureScene is imperative under an ownership discipline (`act(own n)` edits a retained tree in place); only the functional component style is functional. `plum game` finds assets in the nearest parent `assets/` folder.
|
|
19
19
|
|
|
20
|
+
- **Examples (user, 2026-11-04):** examples consolidated from 34 files to about 20 topical ones (every test kept), the mutable `game_loop` moved to `games/imperative/`, and the shared game assets moved to `plum-examples/games/assets/`.
|
|
21
|
+
|
|
20
22
|
## 3. Pure scene on enums
|
|
21
23
|
|
|
22
24
|
- **Decision (user):** the pure scene graph models sum types with enums, not a 14-field trait embedded by records.
|
docs/plans/2026-11-03-language-restrictions.md
CHANGED
|
@@ -54,6 +54,8 @@ flowchart LR
|
|
|
54
54
|
T15[delete own]
|
|
55
55
|
T16[imperative games]
|
|
56
56
|
T17[imperative std]
|
|
57
|
+
T23[component game]
|
|
58
|
+
T25[consolidate examples]
|
|
57
59
|
%% taskgraph
|
|
58
60
|
T1 --> T2
|
|
59
61
|
T2 --> T3
|
|
@@ -68,7 +70,9 @@ flowchart LR
|
|
|
68
70
|
T14 --> T9
|
|
69
71
|
T9 --> T10
|
|
70
72
|
T10 --> T15
|
|
73
|
+
T15 --> T25
|
|
71
|
-
|
|
74
|
+
T25 --> T11
|
|
75
|
+
T23 --> T9
|
|
72
76
|
T16 --> T17
|
|
73
77
|
T17 --> T6
|
|
74
78
|
```
|
|
@@ -108,7 +112,7 @@ Package: compiler; categories: deep, state.
|
|
|
108
112
|
**Test code:** `fusion_runs_before_union_lowering` (explain note for a union-member pipeline); plum-wasm-codegen/tests/fusion_alloc_test.rs unchanged and green; LOC removed from drive/simplify reported.
|
|
109
113
|
|
|
110
114
|
### T3 fusion candidates must be expression-bodied · Complexity 3 (O-1) · Est. context 40k
|
|
111
|
-
**Owns:** plum-core/src/drive.rs, plum-core/tests/drive_test.rs, plum-std/List.plum, plum-std/game/functional/Picture.plum
|
|
115
|
+
**Owns:** plum-core/src/drive.rs, plum-core/tests/drive_test.rs, plum-std/List.plum, plum-std/game/functional/Picture.plum, plum-wasm-codegen/tests/fusion_alloc_test.rs
|
|
112
116
|
**Components:** 1
|
|
113
117
|
**LOC owned:** 90
|
|
114
118
|
**Tests:** fast
|
|
@@ -174,7 +178,7 @@ e.g. `fun view(s: State) -> SceneNode = ...` -> pure; `fun render(io: Io, n: Sce
|
|
|
174
178
|
**Test code:** plum-std green; `grep -rn "extern fun" plum-std` only in Io.plum.
|
|
175
179
|
|
|
176
180
|
### T13 migrate examples, games, fixtures, host tests to `io` · Complexity 4 (O-1) · Est. context 55k
|
|
177
|
-
**Owns:** plum-examples, plum-game/web-test/fixtures, plum-game/tests/host_test.rs, README.md, plum-std/Io.plum, plum-checker/src/lib.rs
|
|
181
|
+
**Owns:** plum-examples, plum-game/web-test/fixtures, plum-game/tests/host_test.rs, README.md, plum-std/Io.plum, plum-checker/src/lib.rs, plum-lsp/src/main.rs, plum-lsp/src/completion.rs, plum-std/game/Game.plum, plum-std/Os.plum, plum-std/Time.plum, plum-std/Random.plum, plum-std/Http.plum, plum-game/web-test/tests/web_check.rs
|
|
178
182
|
**Components:** 2
|
|
179
183
|
**LOC owned:** 100
|
|
180
184
|
**Tests:** slow
|
|
@@ -191,7 +195,7 @@ e.g. `fun view(s: State) -> SceneNode = ...` -> pure; `fun render(io: Io, n: Sce
|
|
|
191
195
|
Package: compiler + std; categories: state, deep.
|
|
192
196
|
|
|
193
197
|
### T7 `mut` fields grammar and the immutability rule · Complexity 5 (O-1) · Est. context 50k
|
|
194
|
-
**Owns:** plum-tooling/tree-sitter-plum/grammar.js, plum-tooling/tree-sitter-plum/src, plum-tooling/tree-sitter-plum/test/corpus/mut_fields.txt, plum-core/src/ast.rs, plum-core/src/parser.rs, plum-checker/src/lib.rs, plum-checker/tests/mutability_test.rs
|
|
198
|
+
**Owns:** plum-tooling/tree-sitter-plum/grammar.js, plum-tooling/tree-sitter-plum/src, plum-tooling/tree-sitter-plum/test/corpus/mut_fields.txt, plum-core/src/ast.rs, plum-core/src/parser.rs, plum-checker/src/lib.rs, plum-checker/tests/mutability_test.rs, plum-core/src/embeds.rs, plum-core/src/traits.rs, plum-core/tests/parser_test.rs, plum-checker/src/traits.rs
|
|
195
199
|
**Components:** 3
|
|
196
200
|
**LOC owned:** 150
|
|
197
201
|
**Tests:** fast
|
|
@@ -215,7 +219,7 @@ Package: compiler + std; categories: state, deep.
|
|
|
215
219
|
**Test code:** `plum test plum-std` green without `--allow-mutation`.
|
|
216
220
|
|
|
217
221
|
### T14 migrate examples and fixtures; drop the escape hatch · Complexity 4 (O-1) · Est. context 50k
|
|
218
|
-
**Owns:** plum-examples, plum-game/web-test/fixtures, plum-checker/src/lib.rs
|
|
222
|
+
**Owns:** plum-examples, plum-game/web-test/fixtures, plum-checker/src/lib.rs, plum-game/tests/fixtures, plum-std/game/imperative/Scene.plum (all of plum-examples except games/functional/game_state_view.plum, which T23 owns)
|
|
219
223
|
**Components:** 2
|
|
220
224
|
**LOC owned:** 100
|
|
221
225
|
**Tests:** slow
|
|
@@ -275,7 +279,7 @@ Package: compiler + std; categories: state, deep.
|
|
|
275
279
|
**Test code:** README snippets parse (plum-lsp test readmeSnippetsHaveNoSyntaxDiagnostics); benchmark table.
|
|
276
280
|
|
|
277
281
|
### T16 imperative games folder (user, 2026-11-04) · Complexity 2 (O-1) · Est. context 30k
|
|
278
|
-
**Owns:** plum-examples/
|
|
282
|
+
**Owns:** plum-examples/games/imperative, plum-game/tests/host_test.rs, plum-game/web-test/tests/web_check.rs, plum-game/web-test/BENCHMARK.md, README.md
|
|
279
283
|
**Components:** 1
|
|
280
284
|
**LOC owned:** 40
|
|
281
285
|
**Tests:** slow
|
|
@@ -287,7 +291,7 @@ Package: compiler + std; categories: state, deep.
|
|
|
287
291
|
**Test code:** `run_tests --fast plum-game/tests/host_test.rs plum-game/web-test/tests/web_check.rs plum-examples` green.
|
|
288
292
|
|
|
289
293
|
### T17 std imperative folder (user, 2026-11-04) · Complexity 2 (O-1) · Est. context 30k
|
|
290
|
-
**Owns:** plum-std/game/imperative/Scene.plum, plum-
|
|
294
|
+
**Owns:** plum-std/game/imperative/Scene.plum, plum-examples/games/imperative, plum-examples/traits.plum, plum-examples/trait_scene.plum, plum-game/web-test/fixtures/stress_scene.plum, plum-game/web-test/fixtures/stress_world.plum, README.md, plum-game/web-test/README.md, plum-game/web-test/BENCHMARK.md, plum-lsp/src/main.rs, plum-lsp/src/completion.rs
|
|
291
295
|
**Components:** 1
|
|
292
296
|
**LOC owned:** 30
|
|
293
297
|
**Tests:** slow
|
|
@@ -298,6 +302,106 @@ Package: compiler + std; categories: state, deep.
|
|
|
298
302
|
**Review Focus:** protocol: host + browser checks green.
|
|
299
303
|
**Test code:** run_tests --fast plum-std, plum-examples, plum-game/tests/host_test.rs, plum-game/web-test/tests/web_check.rs green.
|
|
300
304
|
|
|
305
|
+
### T18 stale tests after T1/T2 (finding F-T1T2-2) · Complexity 1 (O-1) · Est. context 20k
|
|
306
|
+
**Owns:** plum-checker/tests/checker_tests.rs, plum-wasm-codegen/tests/reuse_e2e_test.rs
|
|
307
|
+
**Components:** 1
|
|
308
|
+
**LOC owned:** 30
|
|
309
|
+
**Tests:** fast
|
|
310
|
+
**Model:** sonnet
|
|
311
|
+
**Est. time:** 1.5 min
|
|
312
|
+
**Consumes:** none
|
|
313
|
+
**Produces:** checker_tests expectations follow T1's casing messages and `$cN` copy names; reuse A/B runs with fusion off plus `pipeline_marks_bump_reuse`
|
|
314
|
+
**Review Focus:** none.
|
|
315
|
+
**Test code:** both files green.
|
|
316
|
+
**Steps:** done by the F-T1T2-2 fixer.
|
|
317
|
+
|
|
318
|
+
### T19 field-less records in the grammar (gap found by T4) · Complexity 3 (O-1) · Est. context 40k
|
|
319
|
+
**Owns:** plum-tooling/tree-sitter-plum/grammar.js, plum-tooling/tree-sitter-plum/src, plum-tooling/tree-sitter-plum/test/corpus/records.txt, plum-core/src/parser.rs, plum-core/tests/parser_test.rs, plum-std/Io.plum, plum-std/game/Game.plum, plum-std/Os.plum, plum-std/Time.plum, plum-std/Random.plum, plum-std/Http.plum
|
|
320
|
+
**Components:** 2
|
|
321
|
+
**LOC owned:** 60
|
|
322
|
+
**Tests:** fast
|
|
323
|
+
**Model:** sonnet
|
|
324
|
+
**Est. time:** 3 min
|
|
325
|
+
**Consumes:** none
|
|
326
|
+
**Produces:** `record Io` (and `record X()`) parse as records with no fields, constructed as `Io()`; plum-std/Io.plum drops the `host: Int` marker field and std uses `Io()`
|
|
327
|
+
**Review Focus:** none beyond grammar regressions (every std/example file still parses; regenerate with --disable-optimizations).
|
|
328
|
+
**Test code:** `field_less_record_parses`; corpus records.txt; plum-std, plum-examples, io_capability_test green.
|
|
329
|
+
|
|
330
|
+
### T20 Random without a hidden Io (finding from T13) · Complexity 4 (O-1) · Est. context 45k
|
|
331
|
+
**Owns:** plum-std/Random.plum, plum-std/Slice.plum, plum-std/Uuid.plum, plum-std/Io.plum, plum-examples, plum-game/web-test/fixtures, plum-std/Os.plum, plum-game/tests/fixtures
|
|
332
|
+
**Components:** 1
|
|
333
|
+
**LOC owned:** 100
|
|
334
|
+
**Tests:** fast
|
|
335
|
+
**Model:** sonnet
|
|
336
|
+
**Est. time:** 4 min
|
|
337
|
+
**Consumes:** none
|
|
338
|
+
**Produces:** std `Random` is a pure seeded value (`Random.seeded(seed: Int) -> Random`, methods returning the value plus the next `Random`, or a mutable-free equivalent); wall-clock seeding only via `io.randomSeed()` (an Io method); `Slice.pick/shuffle` and `Uuid.v4` take a `Random` (or `io`) explicitly; no `Io()` constructed anywhere in std except inside Io.plum
|
|
339
|
+
**Review Focus:** state: determinism (same seed, same sequence).
|
|
340
|
+
**Test code:** `same_seed_same_sequence`, `uuid_from_seeded_random`; plum-std, plum-examples green; `grep -n "Io()" plum-std` only in Io.plum.
|
|
341
|
+
|
|
342
|
+
### T21 test files touched by T13 · Complexity 1 (O-1) · Est. context 20k
|
|
343
|
+
**Owns:** plum-game/tests/fixtures, plum-checker/tests/io_capability_test.rs, plum-wasm-codegen/tests/examples_test.rs
|
|
344
|
+
**Components:** 1
|
|
345
|
+
**LOC owned:** 30
|
|
346
|
+
**Tests:** fast
|
|
347
|
+
**Model:** sonnet
|
|
348
|
+
**Est. time:** 1.5 min
|
|
349
|
+
**Consumes:** none
|
|
350
|
+
**Produces:** host fixtures in the io style; io_capability_test without the removed allowRawIo option; examples_test stubs for io.plum's imports
|
|
351
|
+
**Review Focus:** none.
|
|
352
|
+
**Test code:** host_test, io_capability_test, examples_test green.
|
|
353
|
+
**Steps:** done by T13.
|
|
354
|
+
|
|
355
|
+
### T22 game hosts provide the random seed (gap found by T20) · Complexity 2 (O-1) · Est. context 35k
|
|
356
|
+
**Owns:** plum-game/src/host.rs, plum-game/src/api.rs, plum-game/web/plum_bridge.js, plum-game/tests/host_test.rs, plum-examples/games/imperative, plum-examples/games/pure/game_pure_scene.plum, plum-examples/games/functional/game_state_view.plum
|
|
357
|
+
**Components:** 1
|
|
358
|
+
**LOC owned:** 40
|
|
359
|
+
**Tests:** slow
|
|
360
|
+
**Model:** sonnet
|
|
361
|
+
**Est. time:** 3 min
|
|
362
|
+
**Consumes:** none
|
|
363
|
+
**Produces:** native (wasmtime) and web (JS bridge) game hosts provide the `rawRandomHalf` import; game `init(io)` seeds its Random from `io.randomSeed()` instead of the fixed 2024 (tests keep their own fixed seeds)
|
|
364
|
+
**Review Focus:** protocol: host ABI; tests deterministic.
|
|
365
|
+
**Test code:** host_test and web_check green; a host test that a game calling io.randomSeed() loads and runs frames.
|
|
366
|
+
|
|
367
|
+
### T23 functional game in the component style (user, 2026-11-04) · Complexity 5 (O-1) · Est. context 55k
|
|
368
|
+
**Owns:** plum-examples/games/functional/game_state_view.plum
|
|
369
|
+
**Components:** 1
|
|
370
|
+
**LOC owned:** 180
|
|
371
|
+
**Tests:** fast
|
|
372
|
+
**Model:** sonnet
|
|
373
|
+
**Est. time:** 5 min
|
|
374
|
+
**Consumes:** none
|
|
375
|
+
**Produces:** per-kind records (Ship, Orb, Mine, Star) with `update(self, ..) -> Self` and `render(self, ..) -> SceneNode`; `State` with `update(self, msg)` and `render(self)`; `List(...)` and std List methods instead of Cons chains and mapLink/lenLink/snoc/replaceNth/nthOr/toNodes; a `Random` value in State (Random.seeded, Rolled) instead of hand-threaded seeds and OrbsMade/MinesMade/Collected; expression-bodied update/render (bindings + one tail expression, no return); no field assignment on non-mut fields (spreads only); host `draw(io, w) = Picture.draw(io, w.state.render())`
|
|
376
|
+
**Review Focus:** state: rules, keys, scoring unchanged; fusion still complete.
|
|
377
|
+
**Test code:** every existing game test keeps name and values; plum-wasm-codegen/tests/fusion_alloc_test.rs green (view->draw fused, no SceneNode allocation on fused fns); host_test + web_check state_view green.
|
|
378
|
+
|
|
379
|
+
### T24 synthetic writes + stale tests (finding F-T14-1) · Complexity 3 (O-1) · Est. context 40k
|
|
380
|
+
**Owns:** plum-core/src, plum-checker/src/lib.rs, plum-checker/tests, plum-core/tests, plum-wasm-codegen/tests/codegen_tests.rs, plum-game/tests/host_test.rs, plum-game/tests/fixtures, plum-cli/tests, README.md
|
|
381
|
+
**Components:** 2
|
|
382
|
+
**LOC owned:** 120
|
|
383
|
+
**Tests:** slow
|
|
384
|
+
**Model:** sonnet
|
|
385
|
+
**Est. time:** 4 min
|
|
386
|
+
**Consumes:** none
|
|
387
|
+
**Produces:** `ast::Assign.isSynthetic` (TRMC's hole-filling write sets it; exempt from the immutability rule); stale test programs updated to `io` / `mut`; README snippets parse again (plum-lsp `readmeSnippetsHaveNoSyntaxDiagnostics`)
|
|
388
|
+
**Review Focus:** state: user writes never exempt.
|
|
389
|
+
**Test code:** `trmc_tail_write_not_rejected`, `user_tail_write_still_rejected`; full suite green.
|
|
390
|
+
**Steps:** done by the F-T14-1 fixer, plus the README snippet fix.
|
|
391
|
+
|
|
392
|
+
### T25 consolidate examples and move game assets (user, 2026-11-04) · Complexity 3 (O-1) · Est. context 45k
|
|
393
|
+
**Owns:** plum-examples, plum-game/tests/host_test.rs, plum-game/web-test/tests/web_check.rs, plum-game/web-test/README.md, plum-wasm-codegen/tests/examples_test.rs, plum-lsp/src/main.rs, README.md
|
|
394
|
+
**Components:** 1
|
|
395
|
+
**LOC owned:** 120
|
|
396
|
+
**Tests:** slow
|
|
397
|
+
**Model:** sonnet
|
|
398
|
+
**Est. time:** 5 min
|
|
399
|
+
**Consumes:** none
|
|
400
|
+
**Produces:** `git mv plum-examples/assets plum-examples/games/assets`; merged examples (git mv + concatenate, one file per topic, tests keep names and values): basics (basics+io+testing+functions), numbers (numbers+operators+constants), control_flow (control_flow+loops), recursion (tail_calls+cons_list+own_list remnants), structs_records (+digit_names+annotated_locals), arrays (typed_arrays+struct_arrays), sequences (forms+seq), traits (+trait_scene), errors (error_propagation+optionals); game_loop -> games/imperative/; unions, struct_union_members, unboxed_variants untouched (next plan); every path reference updated (README, host/web tests, examples_test, LSP tests); name clashes when merging resolved by renaming helpers, never tests
|
|
401
|
+
**Review Focus:** state: test count per topic unchanged (report before/after totals).
|
|
402
|
+
**Test code:** plum-examples total test count equal before and after; host_test + web_check + examples_test green; assets still resolve.
|
|
403
|
+
**Steps:** commit the moves on the session branch before checkpointing (checkpoint can't follow renames).
|
|
404
|
+
|
|
301
405
|
## Review Focus
|
|
302
406
|
Per package lines above.
|
|
303
407
|
|