plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
4299cfb
— Peter John
2026-11-04T23:01:45+05:30
docs: language simplification run B plan
docs/plans/2026-11-04-language-simplification-b.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Language simplification, run B: one product type, `struct`
|
|
2
|
+
|
|
3
|
+
**Review plan:** docs/plans/2026-11-04-language-simplification.review.md
|
|
4
|
+
**Goal:** `record` and `struct` become one immutable product type `struct`. The compiler decides each struct's layout in one place (flat value vs boxed heap struct); `record` disappears from the grammar.
|
|
5
|
+
**Architecture:** tree-sitter grammar, plum-core parser/AST/lowering (embeds, ctors, traits), plum-checker, plum-wasm-codegen, plum-std, plum-examples.
|
|
6
|
+
**Tech stack:** Rust 2021, tree-sitter, Plum.
|
|
7
|
+
**Detail:** contracts
|
|
8
|
+
**Target-min:** 35
|
|
9
|
+
|
|
10
|
+
## Global constraints
|
|
11
|
+
- Agents cannot run `cargo test` (hook): `cargo build --workspace --tests`, run binaries in `target/debug/deps` (newest by mtime). Own scratchpad subdir + CARGO_TARGET_DIR.
|
|
12
|
+
- Grammar edits: `tree-sitter generate --disable-optimizations`; update corpus.
|
|
13
|
+
- Gate for every task: all workspace test binaries pass; `plum test plum-std plum-examples` 65/65 files green (669 tests at start).
|
|
14
|
+
- Never run `plum format` on real files. Don't commit. Checkpoint Owns globs match ONE directory level; list nested dirs explicitly.
|
|
15
|
+
- Rulings (review-plan defaults, user may change): layout rule lives in ONE function — a struct is flat (today's `struct` layout) when it has no embedding, no `mut` field, is not recursive and all fields are flat-able (primitives or other flat structs); otherwise boxed (today's `record` layout). `--explain` prints each struct's layout. `==` keeps today's meaning per layout (field-wise for flat, identity for boxed) until the deferred deep-`==` item. `mut` fields stay (FBIP removes them later). Embedding and traits are untouched in this run (run C).
|
|
16
|
+
|
|
17
|
+
## Task graph
|
|
18
|
+
```mermaid
|
|
19
|
+
flowchart LR
|
|
20
|
+
T1[grammar accepts struct for both]
|
|
21
|
+
T2[one AST kind + layout fn]
|
|
22
|
+
T3[codegen uses layout fn]
|
|
23
|
+
T4[rename record to struct]
|
|
24
|
+
T5[remove record keyword]
|
|
25
|
+
T6[verify run B]
|
|
26
|
+
%% taskgraph
|
|
27
|
+
T1 --> T2
|
|
28
|
+
T2 --> T3
|
|
29
|
+
T3 --> T4
|
|
30
|
+
T4 --> T5
|
|
31
|
+
T5 --> T6
|
|
32
|
+
```
|
|
33
|
+
Ruling: sequential - each step changes the same AST kind the next depends on - cost: no parallelism.
|
|
34
|
+
|
|
35
|
+
## Package A: compiler
|
|
36
|
+
**Probe entry:** run=plum test plum-std plum-examples
|
|
37
|
+
|
|
38
|
+
### T1 grammar accepts struct for both · Complexity 3 · Est. context 35k
|
|
39
|
+
**Owns:** plum-tooling/tree-sitter-plum/grammar.js, plum-tooling/tree-sitter-plum/src/*.c, plum-tooling/tree-sitter-plum/src/*.json, plum-tooling/tree-sitter-plum/test/corpus/*.txt, plum-core/src/parser.rs
|
|
40
|
+
**Components:** 1
|
|
41
|
+
**LOC owned:** 40
|
|
42
|
+
**Tests:** fast
|
|
43
|
+
**Model:** sonnet
|
|
44
|
+
**Produces:** `struct` accepts everything `record` accepts today (generics `struct Box[T](..)`, embedding, methods block, `mut` fields); `record` still parses as an alias
|
|
45
|
+
e.g. `struct Box[T](value: T)` parses (generic struct)
|
|
46
|
+
e.g. `struct H(Actor, k: Int)` parses (embedding)
|
|
47
|
+
**Test code:** corpus cases for both e.g. lines; existing corpus and parser tests green.
|
|
48
|
+
|
|
49
|
+
### T2 one AST kind and the layout function · Complexity 5 · Est. context 50k
|
|
50
|
+
**Owns:** plum-core/src/ast.rs, plum-core/src/parser.rs, plum-core/src/embeds.rs, plum-core/src/ctors.rs, plum-core/src/traits.rs, plum-core/src/layout.rs, plum-core/src/lib.rs, plum-checker/src/lib.rs, plum-checker/src/unions.rs, plum-core/tests/layout_test.rs
|
|
51
|
+
**Components:** 2
|
|
52
|
+
**LOC owned:** 120
|
|
53
|
+
**Tests:** slow
|
|
54
|
+
**Model:** sonnet
|
|
55
|
+
**Consumes:** `struct` accepts everything `record` accepts today (generics `struct Box[T](..)`, embedding, methods block, `mut` fields); `record` still parses as an alias (T1)
|
|
56
|
+
**Produces:** ast::Enum gets one `isStruct: bool` replacing `isValue` + `isRecord` · layout::structLayout(e: &Enum, all: &[Enum]) -> Layout { Flat, Boxed }
|
|
57
|
+
e.g. `struct Vec2(x: Float, y: Float)` -> Flat
|
|
58
|
+
e.g. `struct Node(kids: List[Node])` -> Boxed (recursive)
|
|
59
|
+
e.g. `struct Walker(Actor, speed: Float)` -> Boxed (embedding)
|
|
60
|
+
**Review Focus:** every former `isValue` site now asks `structLayout(..) == Flat`, every former `isRecord` site asks `isStruct` (or `Boxed` where layout matters); no behaviour change for existing programs.
|
|
61
|
+
**Test code:** layout_test for the e.g. lines + a struct with a `mut` field -> Boxed; gates pass.
|
|
62
|
+
|
|
63
|
+
### T3 codegen uses the layout function · Complexity 4 · Est. context 45k
|
|
64
|
+
**Owns:** plum-wasm-codegen/src/lib.rs, plum-cli/src/main.rs
|
|
65
|
+
**Components:** 1
|
|
66
|
+
**LOC owned:** 80
|
|
67
|
+
**Tests:** slow
|
|
68
|
+
**Model:** sonnet
|
|
69
|
+
**Consumes:** layout::structLayout(e: &Enum, all: &[Enum]) -> Layout { Flat, Boxed } (T2)
|
|
70
|
+
**Produces:** `--explain` prints `layout <Name>: flat|boxed` per struct
|
|
71
|
+
**Test code:** codegen tests + e2e_records pass; a CLI test (plum-cli/tests) for the `--explain` line.
|
|
72
|
+
|
|
73
|
+
## Package B: std and examples
|
|
74
|
+
**Probe entry:** run=plum test plum-std plum-examples
|
|
75
|
+
|
|
76
|
+
### T4 rename record to struct · Complexity 3 · Est. context 40k
|
|
77
|
+
**Owns:** plum-std/*.plum, plum-std/game/*.plum, plum-std/game/*/*.plum, plum-examples/*.plum, plum-examples/games/*/*.plum, plum-examples/games/*/*/*.plum, plum-wasm-codegen/tests/*.rs, plum-wasm-codegen/tests/e2e_types/*.rs, plum-wasm-codegen/tests/e2e_records/*.rs, plum-wasm-codegen/tests/e2e_tail/*.rs, plum-wasm-codegen/tests/e2e_mem/*.rs, plum-checker/tests/*.rs, plum-checker/tests/features/*.rs, plum-core/tests/*.rs, plum-cli/tests/*.rs, plum-lsp/src/*.rs, README.md
|
|
78
|
+
**Components:** 1
|
|
79
|
+
**LOC owned:** 60
|
|
80
|
+
**Tests:** slow
|
|
81
|
+
**Model:** sonnet
|
|
82
|
+
**Consumes:** layout::structLayout(e: &Enum, all: &[Enum]) -> Layout { Flat, Boxed } (T2)
|
|
83
|
+
**Produces:** none
|
|
84
|
+
**Test code:** no `record` keyword left in std, examples, embedded test sources, README; every renamed type keeps its old layout (compare `--explain` before/after for std + examples); gates pass.
|
|
85
|
+
|
|
86
|
+
### T5 remove the record keyword · Complexity 2 · Est. context 30k
|
|
87
|
+
**Owns:** plum-tooling/tree-sitter-plum/grammar.js, plum-tooling/tree-sitter-plum/src/*.c, plum-tooling/tree-sitter-plum/src/*.json, plum-tooling/tree-sitter-plum/queries/plum/*.scm, plum-tooling/tree-sitter-plum/test/corpus/*.txt, plum-tooling/vscode-plum/syntaxes/*.json, plum-core/src/parser.rs
|
|
88
|
+
**Components:** 1
|
|
89
|
+
**LOC owned:** 30
|
|
90
|
+
**Tests:** slow
|
|
91
|
+
**Model:** sonnet
|
|
92
|
+
**Produces:** none
|
|
93
|
+
**Test code:** `record X(..)` is a plain parse error; corpus and gates pass.
|
|
94
|
+
|
|
95
|
+
### T6 verify run B · Complexity 2 · Est. context 30k
|
|
96
|
+
**Owns:** docs/plans/2026-11-04-language-simplification-b.results.md
|
|
97
|
+
**Components:** 1
|
|
98
|
+
**LOC owned:** 40
|
|
99
|
+
**Tests:** slow
|
|
100
|
+
**Model:** sonnet
|
|
101
|
+
**Produces:** none
|
|
102
|
+
**Test code:** workspace binaries, plum test, plum-game host_test; layout of every std/example struct listed (flat vs boxed) and unchanged vs the start; Rust LOC per crate vs start.
|
|
103
|
+
|
|
104
|
+
## Review Focus
|
|
105
|
+
- Package A: deep, state — layout decided in exactly one place; no program changes layout; `==` semantics unchanged.
|
|
106
|
+
- Package B: file — mechanical rename completeness.
|
|
107
|
+
|
|
108
|
+
## Self-review
|
|
109
|
+
Covers run B of the review plan (one product type). Embedding-as-subtyping and trait removal are run C; Option/Result is run D.
|