plum

#treesitter#compiler#wasm

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
- T15 --> T11
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/game.plum, plum-examples/game_scene.plum, plum-examples/scene.plum, 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
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-std/game/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
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