plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
ada2b0a
— Peter John
2026-11-04T17:08:16+05:30
docs: phase 2a plan split (rename, generics, host tasks)
docs/plans/2026-11-04-compiler-cleanup-phase2a.md
CHANGED
|
@@ -39,8 +39,11 @@ flowchart LR
|
|
|
39
39
|
T5[forms union_lower]
|
|
40
40
|
T6[uniqueness reuse tail]
|
|
41
41
|
T7[trmc queries]
|
|
42
|
+
T14[rename module]
|
|
42
|
-
T8[generics
|
|
43
|
+
T8[generics module]
|
|
44
|
+
T15[passes ask generics]
|
|
43
45
|
T10[plum-host crate]
|
|
46
|
+
T16[hosts use plum-host]
|
|
44
47
|
T13[test helpers LSP]
|
|
45
48
|
T9[verify 2a]
|
|
46
49
|
%% taskgraph
|
|
@@ -49,21 +52,26 @@ flowchart LR
|
|
|
49
52
|
T1 --> T5
|
|
50
53
|
T1 --> T6
|
|
51
54
|
T1 --> T7
|
|
52
|
-
T2 --> T3
|
|
53
|
-
T2 -->
|
|
55
|
+
T2 --> T14
|
|
54
|
-
T2 --> T5
|
|
55
56
|
T2 --> T6
|
|
56
57
|
T2 --> T7
|
|
58
|
+
T14 --> T3
|
|
59
|
+
T14 --> T4
|
|
60
|
+
T14 --> T5
|
|
57
|
-
|
|
61
|
+
T1 --> T8
|
|
58
|
-
|
|
62
|
+
T14 --> T8
|
|
63
|
+
T8 --> T15
|
|
64
|
+
T3 --> T15
|
|
65
|
+
T5 --> T15
|
|
59
|
-
T7 -->
|
|
66
|
+
T7 --> T15
|
|
67
|
+
T10 --> T16
|
|
60
68
|
T4 --> T9
|
|
61
69
|
T6 --> T9
|
|
62
|
-
T8 --> T9
|
|
63
|
-
|
|
70
|
+
T15 --> T9
|
|
71
|
+
T16 --> T9
|
|
64
72
|
T13 --> T9
|
|
65
73
|
```
|
|
66
|
-
Ruling: sequential -
|
|
74
|
+
Ruling: sequential - T15 edits drive/forms/trmc/union_lower after their conversions, T8 edits checker lib.rs after T1 and core lib.rs after T14; same files - cost: longer chain.
|
|
67
75
|
|
|
68
76
|
## Package A: compiler core
|
|
69
77
|
**Probe entry:** run=bash scripts/wasm-hashes.sh
|
|
@@ -82,7 +90,7 @@ Ruling: sequential - T8 edits drive/forms/trmc after their conversions; same fil
|
|
|
82
90
|
### T2 visit module · Complexity 5 · Est. context 40k
|
|
83
91
|
**Owns:** plum-core/src/visit.rs, plum-core/src/lib.rs, plum-core/tests/visit_test.rs
|
|
84
92
|
**Components:** 1
|
|
85
|
-
**LOC owned:**
|
|
93
|
+
**LOC owned:** 150
|
|
86
94
|
**Tests:** fast
|
|
87
95
|
**Model:** sonnet
|
|
88
96
|
**Produces:**
|
|
@@ -93,18 +101,32 @@ Ruling: sequential - T8 edits drive/forms/trmc after their conversions; same fil
|
|
|
93
101
|
- visit::visitBlock(b: &mut Block, f: &mut dyn FnMut(&mut Expr))
|
|
94
102
|
- visit::visitExpr(e: &mut Expr, f: &mut dyn FnMut(&mut Expr))
|
|
95
103
|
- visit::anyExpr(e: &Expr, pred: &mut dyn FnMut(&Expr) -> bool) -> bool
|
|
96
|
-
- visit::Name enum { Decl, Pat, Plain, Ref }
|
|
97
|
-
- visit::renameStmts(stmts: &mut [Stmt], rn: &mut dyn FnMut(&mut String, Name))
|
|
98
|
-
- visit::substType(t: &Type, map: &HashMap<String, Type>) -> Type
|
|
99
104
|
- visit::patternBinders(p: &CasePattern, out: &mut Vec<String>)
|
|
100
105
|
- visit::unparen(e: &Expr) -> &Expr
|
|
101
106
|
- visit::countVar(body: &FnBody, name: &str) -> usize
|
|
102
107
|
e.g. anyExpr(&parse_expr("f(Try(1))"), &mut |e| matches!(e, Expr::Call{..})) -> true
|
|
103
108
|
e.g. anyExpr(&parse_expr("\"Try(\""), &mut |e| matches!(e, Expr::Call{..})) -> false
|
|
104
|
-
e.g.
|
|
105
|
-
**Review Focus:** `
|
|
106
|
-
**Test code:** `plum-core/tests/visit_test.rs`: the e.g. lines
|
|
107
|
-
**Notes:** copy `drive.rs` 1884-2260 (walkers
|
|
109
|
+
e.g. patternBinders(`Cons(head: h, tail: t)`) -> ["h", "t"]
|
|
110
|
+
**Review Focus:** `pattern_names`: constructor names in patterns are not binders.
|
|
111
|
+
**Test code:** `plum-core/tests/visit_test.rs`: the e.g. lines plus childExprs/stmtBlocks on an if/match/for sample.
|
|
112
|
+
**Notes:** copy `drive.rs` ≈1884-2040 and 2160-2260 (walkers) as the reference behaviour; add only `anyExpr`.
|
|
113
|
+
|
|
114
|
+
### T14 shared rename and type substitution · Complexity 4 · Est. context 35k
|
|
115
|
+
**Owns:** plum-core/src/rename.rs, plum-core/src/lib.rs, plum-core/tests/rename_test.rs
|
|
116
|
+
**Components:** 1
|
|
117
|
+
**LOC owned:** 110
|
|
118
|
+
**Tests:** fast
|
|
119
|
+
**Model:** sonnet
|
|
120
|
+
**Consumes:** visit::childExprs(e: &mut Expr) -> Vec<&mut Expr> (T2)
|
|
121
|
+
**Produces:**
|
|
122
|
+
- rename::Name enum { Decl, Pat, Plain, Ref }
|
|
123
|
+
- rename::renameStmts(stmts: &mut [Stmt], rn: &mut dyn FnMut(&mut String, Name))
|
|
124
|
+
- rename::substType(t: &Type, map: &HashMap<String, Type>) -> Type
|
|
125
|
+
e.g. renameStmts(`x := 1; y := x`, x→x2) -> `x2 := 1; y := x2` (Decl then Ref)
|
|
126
|
+
e.g. substType(&List[T], {T: Int}) -> List[Int]
|
|
127
|
+
**Review Focus:** `rename_shadowing`: a closure param shadowing `x` is reported as Decl and outer renames stop in its body (copy drive.rs ≈2041-2160 exactly); `field_assign_target`: `self.x = v` target is not renamed as a variable.
|
|
128
|
+
**Test code:** `plum-core/tests/rename_test.rs`: e.g. lines, one test per Name kind, the two focus tests.
|
|
129
|
+
**Notes:** `substType` substitutes exactly the map's keys; it never decides what is generic.
|
|
108
130
|
|
|
109
131
|
### T3 drive and simplify use visit · Complexity 4 · Est. context 45k
|
|
110
132
|
**Owns:** plum-core/src/drive.rs, plum-core/src/simplify.rs
|
|
@@ -112,7 +134,7 @@ e.g. substType(&Type{name:"List", generics:[T]}, {T: Int}) -> List[Int]
|
|
|
112
134
|
**LOC owned:** 60
|
|
113
135
|
**Tests:** slow
|
|
114
136
|
**Model:** sonnet
|
|
115
|
-
**Consumes:** visit::childExprs(e: &mut Expr) -> Vec<&mut Expr> (T2) ·
|
|
137
|
+
**Consumes:** visit::childExprs(e: &mut Expr) -> Vec<&mut Expr> (T2) · rename::renameStmts(stmts: &mut [Stmt], rn: &mut dyn FnMut(&mut String, Name)) (T14) · visit::childRefs(e: &Expr) -> Vec<&Expr> (T2) · visit::unparen(e: &Expr) -> &Expr (T2)
|
|
116
138
|
**Produces:** none
|
|
117
139
|
**Test code:** plum-core drive_test + simplify_test binaries pass; wasm hashes identical to T1.
|
|
118
140
|
**Steps:** delete the local walker/rename copies and `isTag`/`isTagName` duplicates, call visit; keep `isGen`/`substType` for T8.
|
|
@@ -123,7 +145,7 @@ e.g. substType(&Type{name:"List", generics:[T]}, {T: Int}) -> List[Int]
|
|
|
123
145
|
**LOC owned:** 50
|
|
124
146
|
**Tests:** slow
|
|
125
147
|
**Model:** sonnet
|
|
126
|
-
**Consumes:** visit::childExprs(e: &mut Expr) -> Vec<&mut Expr> (T2) ·
|
|
148
|
+
**Consumes:** visit::childExprs(e: &mut Expr) -> Vec<&mut Expr> (T2) · rename::renameStmts(stmts: &mut [Stmt], rn: &mut dyn FnMut(&mut String, Name)) (T14) · visit::countVar(body: &FnBody, name: &str) -> usize (T2)
|
|
127
149
|
**Produces:** none
|
|
128
150
|
**Review Focus:** `pattern_decl`: closure_spec's own Name has no `Pat` and renames pattern names as Decl — map its call sites so behaviour is unchanged (pin with a test in `plum-core/tests/closure_spec_test.rs` first); `debug_key`: the `format!("{:?}")` key (≈322) becomes a structural `Expr` equality (ast derives PartialEq).
|
|
129
151
|
**Test code:** closure_spec_test binary passes plus the pinning test; hashes identical.
|
|
@@ -134,10 +156,10 @@ e.g. substType(&Type{name:"List", generics:[T]}, {T: Int}) -> List[Int]
|
|
|
134
156
|
**LOC owned:** 60
|
|
135
157
|
**Tests:** slow
|
|
136
158
|
**Model:** sonnet
|
|
137
|
-
**Consumes:** visit::childExprs(e: &mut Expr) -> Vec<&mut Expr> (T2) · visit::stmtExprs(s: &mut Stmt) -> Vec<&mut Expr> (T2) · visit::stmtBlocks(s: &mut Stmt) -> Vec<&mut Block> (T2) ·
|
|
159
|
+
**Consumes:** visit::childExprs(e: &mut Expr) -> Vec<&mut Expr> (T2) · visit::stmtExprs(s: &mut Stmt) -> Vec<&mut Expr> (T2) · visit::stmtBlocks(s: &mut Stmt) -> Vec<&mut Block> (T2) · rename::substType(t: &Type, map: &HashMap<String, Type>) -> Type (T14) · visit::patternBinders(p: &CasePattern, out: &mut Vec<String>) (T2)
|
|
138
160
|
**Produces:** none
|
|
139
161
|
**Test code:** forms_* and union_lower_test binaries pass; hashes identical.
|
|
140
|
-
**Notes:** forms' rename uses a bool, union_lower's `rn*` renames pairs: express both through `
|
|
162
|
+
**Notes:** forms' rename uses a bool, union_lower's `rn*` renames pairs: express both through `rename::renameStmts`; `forms::mapExprsIn`/`walkStmts` stay public as thin wrappers.
|
|
141
163
|
|
|
142
164
|
### T6 uniqueness, reuse, tail share helpers · Complexity 3 · Est. context 40k
|
|
143
165
|
**Owns:** plum-core/src/uniqueness.rs, plum-core/src/reuse.rs, plum-core/src/tail.rs
|
|
@@ -162,32 +184,56 @@ e.g. substType(&Type{name:"List", generics:[T]}, {T: Int}) -> List[Int]
|
|
|
162
184
|
**Test code:** those two tests in trmc_test.rs (fail before, pass after); all trmc_test pass; hashes identical.
|
|
163
185
|
**Steps:** replace `bannedText(format!("{:?}"..))` (≈127, 188) and `mentions` (≈255) with `anyExpr` matches on the real variants.
|
|
164
186
|
|
|
165
|
-
### T8 generics
|
|
166
|
-
**Owns:** plum-core/src/generics.rs, plum-core/src/lib.rs, plum-
|
|
167
|
-
**Components:**
|
|
168
|
-
**LOC owned:**
|
|
187
|
+
### T8 generics module and checker · Complexity 4 · Est. context 45k
|
|
188
|
+
**Owns:** plum-core/src/generics.rs, plum-core/src/lib.rs, plum-checker/src/lib.rs, plum-core/tests/generics_test.rs
|
|
189
|
+
**Components:** 1
|
|
190
|
+
**LOC owned:** 70
|
|
169
191
|
**Tests:** slow
|
|
170
192
|
**Model:** sonnet
|
|
171
193
|
**Produces:** generics::isGenericName(n: &str) -> bool · generics::fnGenerics(f: &Fn, owner: &[String]) -> Vec<String>
|
|
172
194
|
e.g. fnGenerics(`fun map(self, f: fn(T) -> B) -> List[B]` in `List[T]`, &["T"]) -> ["T","B"]
|
|
173
195
|
e.g. fnGenerics(`fun len(self) -> Int`, &[]) -> []
|
|
174
|
-
|
|
175
|
-
**
|
|
176
|
-
**
|
|
196
|
+
e.g. isGenericName("T") -> true; isGenericName("Int") -> false
|
|
197
|
+
**Review Focus:** `enum_fallback_gone`: checker `enumGenericParams` uses only declared generics (delete the single-letter-field fallback); checker `isGenericName` becomes a re-export/call of `generics::isGenericName`.
|
|
198
|
+
**Test code:** generics_test.rs for the e.g. lines; checker test binaries pass; hashes identical.
|
|
199
|
+
|
|
200
|
+
### T15 passes ask generics · Complexity 3 · Est. context 40k
|
|
201
|
+
**Owns:** plum-core/src/drive.rs, plum-core/src/forms.rs, plum-core/src/trmc.rs, plum-core/src/union_lower.rs
|
|
202
|
+
**Components:** 1
|
|
203
|
+
**LOC owned:** 50
|
|
204
|
+
**Tests:** slow
|
|
205
|
+
**Model:** sonnet
|
|
206
|
+
**Consumes:** generics::isGenericName(n: &str) -> bool (T8) · generics::fnGenerics(f: &Fn, owner: &[String]) -> Vec<String> (T8) · rename::substType(t: &Type, map: &HashMap<String, Type>) -> Type (T14)
|
|
207
|
+
**Produces:** none
|
|
208
|
+
**Review Focus:** `no_spelling_checks_left`: `grep -rn "is_ascii_uppercase" plum-core/src plum-checker/src` shows only generics.rs plus unrelated casing checks, each justified in the report.
|
|
209
|
+
**Test code:** drive/forms/trmc/union_lower test binaries pass; hashes identical.
|
|
210
|
+
**Steps:** drive `isGen`+`substType`, forms `isGenericName`+`substType`, trmc `open`+`subst`, union_lower `substType` → `generics::*` and `rename::substType`; prefer membership in the fn/owner generic set where it is at hand.
|
|
177
211
|
|
|
178
212
|
## Package B: hosts and tests
|
|
179
213
|
**Probe entry:** run=cargo build --workspace --tests
|
|
180
214
|
|
|
181
|
-
### T10 plum-host crate · Complexity
|
|
182
|
-
**Owns:** plum-host/Cargo.toml, plum-host/src/lib.rs, Cargo.toml
|
|
215
|
+
### T10 plum-host crate · Complexity 4 · Est. context 45k
|
|
216
|
+
**Owns:** plum-host/Cargo.toml, plum-host/src/lib.rs, Cargo.toml
|
|
217
|
+
**Components:** 1
|
|
218
|
+
**LOC owned:** 140
|
|
219
|
+
**Tests:** fast
|
|
220
|
+
**Model:** sonnet
|
|
221
|
+
**Produces:** plum_host::engine() -> wasmtime::Engine · plum_host::strBytes(store: &mut impl AsContextMut, s: &Rooted<AnyRef>) -> Result<Vec<u8>> · plum_host::newStr(store: &mut impl AsContextMut, bytes: &[u8]) -> Result<Val> · plum_host::hostImports(store: &mut Store<()>, module: &Module, caps: Caps) -> Result<Vec<Extern>> · plum_host::Caps { fs: bool, http: bool, clock: bool, rng: bool }
|
|
222
|
+
e.g. strBytes(newStr("héllo")) -> b"h\xc3\xa9llo"
|
|
223
|
+
e.g. hostImports for a module importing ("plum","printLn") with Caps::default() -> 1 extern
|
|
224
|
+
**Test code:** plum-host unit tests: Str round trip, hostImports on a tiny wat module.
|
|
225
|
+
**Notes:** port from plum-cli/src/main.rs 284-835 (the superset); keep the 64 MiB GC heap where the CLI uses it today; workspace member.
|
|
226
|
+
|
|
227
|
+
### T16 hosts use plum-host · Complexity 4 · Est. context 45k
|
|
228
|
+
**Owns:** plum-cli/Cargo.toml, plum-cli/src/main.rs, plum-runtime/Cargo.toml, plum-runtime/src/main.rs, plum-game/Cargo.toml, plum-game/src/host.rs
|
|
183
229
|
**Components:** 2
|
|
184
|
-
**LOC owned:**
|
|
230
|
+
**LOC owned:** 80
|
|
185
231
|
**Tests:** slow
|
|
186
232
|
**Model:** sonnet
|
|
187
|
-
**
|
|
188
|
-
**
|
|
189
|
-
**
|
|
190
|
-
**
|
|
233
|
+
**Consumes:** plum_host::hostImports(store: &mut Store<()>, module: &Module, caps: Caps) -> Result<Vec<Extern>> (T10) · plum_host::strBytes(store: &mut impl AsContextMut, s: &Rooted<AnyRef>) -> Result<Vec<u8>> (T10) · plum_host::newStr(store: &mut impl AsContextMut, bytes: &[u8]) -> Result<Val> (T10) · plum_host::engine() -> wasmtime::Engine (T10)
|
|
234
|
+
**Produces:** none
|
|
235
|
+
**Review Focus:** `runtime_build`: `plum build` (cmdBuild ≈1120) still builds a working runtime with the path dep; `game_host`: plum-game host_test passes (`cargo build --tests` in plum-game, run its binaries).
|
|
236
|
+
**Test code:** cli compile_tests/crash_test/host_stub_test pass; plum-game host_test passes; plum-runtime builds.
|
|
191
237
|
|
|
192
238
|
### T13 test helpers and LSP generic fix · Complexity 3 · Est. context 40k
|
|
193
239
|
**Owns:** plum-test-support/src/lib.rs, plum-core/tests/{drive,simplify,uniqueness,record_embed,record_init,record_init_parse,trait_parse,traits,closure_types_parse,match_body_parse,override_parse,whistle,purity,forms_list_range,parser}_test.rs, plum-checker/tests/traits_tests.rs, plum-checker/tests/trait_fields_tests.rs, plum-checker/tests/features/mutability.rs, plum-lsp/src/completion.rs
|
|
@@ -217,4 +263,4 @@ e.g. fnGenerics(`fun len(self) -> Int`, &[]) -> []
|
|
|
217
263
|
- Package B (host/tests): protocol, file — risks: host import names/signatures, Str ABI, runtime build path.
|
|
218
264
|
|
|
219
265
|
## Self-review
|
|
220
|
-
Covers phase 2 rows: determinism (T1), tree walker (T2-T6), real tree queries (T4, T7), generics by scope (T8), host crate (T10), test helpers + LSP fix (T13). Front end, compile call and Paren are phase 2b.
|
|
266
|
+
Covers phase 2 rows: determinism (T1), tree walker (T2, T14, T3-T6), real tree queries (T4, T7), generics by scope (T8, T15), host crate (T10, T16), test helpers + LSP fix (T13). Front end, compile call and Paren are phase 2b.
|