plum

#treesitter#compiler#wasm

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

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


567dd39 — Peter John 2026-11-04T18:08:00+05:30
docs: compiler cleanup phase 2b plan
docs/plans/2026-11-04-compiler-cleanup-phase2b.md ADDED
@@ -0,0 +1,163 @@
1
+ # Compiler cleanup, phase 2b: one front end, one compile call, no Paren
2
+
3
+ **Review plan:** docs/plans/2026-11-04-compiler-cleanup.review.md
4
+ **Goal:** One front-end lowering entry used by the loader and single-file parsing; `test`-block desugaring moved out of codegen; one compile entry with an options value (fusion options no longer a thread-local); the last walker helpers in `visit.rs`; `Expr::Paren` removed. No behaviour change.
5
+ **Architecture:** Rust workspace (plum-core, plum-checker, plum-wasm-codegen, plum-cli, plum-lsp, plum-host, plum-test-support); plum-runtime and plum-game are separate cargo projects.
6
+ **Tech stack:** Rust 2021, cargo, wasmtime 47, tree-sitter-plum.
7
+ **Detail:** contracts
8
+ **Target-min:** 25
9
+
10
+ ## Global constraints
11
+ - Agents cannot run `cargo test` (hook). Build with `cargo build -p <crate> --tests`, run the binaries in `target/debug/deps` (newest by mtime). `touch` edited .rs files first.
12
+ - No behaviour change: all 25 examples compile to sha256-identical wasm vs the start sha (`.superlite/progress.md`), via `bash scripts/wasm-hashes.sh <sha> OUT` — the start sha builds cleanly. Keep scratch files in your own scratchpad subdirectory.
13
+ - camelCase fns, `#![allow(non_snake_case)]`. Never run `plum format` on real files. Don't commit.
14
+
15
+ ## Main diagram
16
+ ```mermaid
17
+ flowchart LR
18
+ P["parser: parse only"] --> L["lower::frontEnd"]
19
+ Ld["loader"] --> L
20
+ L --> TL["lower tests"]
21
+ TL --> C["checker typedSourceWith(opts)"]
22
+ C --> G["codegen compile(opts)"]
23
+ V["visit.rs helpers"] --> X["Paren removal (last)"]
24
+ G --> X
25
+ ```
26
+
27
+ ## File structure
28
+ - `plum-core/src/lower.rs` — the front-end lowering sequence (traits, ctors, inline-variant checks, member resolution, forms prep).
29
+ - `plum-core/src/tests_lower.rs` — `test` blocks and `assert` into ordinary fns (was in codegen).
30
+
31
+ ## Task graph
32
+ ```mermaid
33
+ flowchart LR
34
+ T1[front-end entry]
35
+ T2[parseSource uses it]
36
+ T3[tests lowering]
37
+ T4[compile options]
38
+ T5[compile callers]
39
+ T6[visit helpers]
40
+ T7[Paren removal]
41
+ T8[verify 2b]
42
+ %% taskgraph
43
+ T1 --> T2
44
+ T1 --> T3
45
+ T3 --> T4
46
+ T4 --> T5
47
+ T2 --> T7
48
+ T5 --> T7
49
+ T6 --> T7
50
+ T7 --> T8
51
+ ```
52
+ Ruling: sequential - codegen lib.rs (T3, T4, T7) and parser.rs (T2, T7) are edited in turn; Paren touches every crate - cost: longer chain.
53
+
54
+ ## Package A: front end
55
+ **Probe entry:** run=bash scripts/wasm-hashes.sh
56
+
57
+ ### T1 front-end entry · Complexity 3 · Est. context 35k
58
+ **Owns:** plum-core/src/lower.rs, plum-core/src/lib.rs, plum-core/src/loader.rs, plum-core/tests/lower_test.rs
59
+ **Components:** 1
60
+ **LOC owned:** 70
61
+ **Tests:** fast
62
+ **Model:** sonnet
63
+ **Produces:** lower::frontEnd(source: &mut Source) -> Result<(), String>
64
+ e.g. frontEnd on a program with `trait Actor(x: Int)` + a record embedding it -> Ok, traits lowered (same AST the loader produced before)
65
+ e.g. frontEnd on a program using an unknown union member -> Err containing the old resolveMembers message
66
+ **Test code:** lower_test.rs: the e.g. lines; loader_test binary unchanged.
67
+ **Steps:** move loader.rs ≈84-100 (lowerTraits, lowerCtors, inlineVariantTypeErrors, resolveMembers, prepareForms, errors joined with "\n") into `frontEnd`; the loader calls it.
68
+
69
+ ### T2 parseSource runs the front end · Complexity 4 · Est. context 45k
70
+ **Owns:** plum-core/src/parser.rs, plum-core/tests/*.rs except lower_test.rs, plum-checker/tests/*.rs except fusion_pipeline_test.rs, plum-checker/tests/features/*, plum-lsp/src/main.rs, plum-lsp/src/completion.rs
71
+ **Components:** 2
72
+ **LOC owned:** 80
73
+ **Tests:** fast
74
+ **Model:** sonnet
75
+ **Consumes:** lower::frontEnd(source: &mut Source) -> Result<(), String> (T1)
76
+ **Produces:** none
77
+ **Review Focus:** `single_file_equals_loader`: for every plum-examples file without imports, `parseSource` gives the same AST as `loadAndMerge` (add this as a test in plum-core/tests/loader_test.rs); errors stay discarded in parseSource as today (callers that need them call frontEnd).
78
+ **Test code:** all plum-core, plum-checker, plum-lsp test binaries pass; any test whose expectation relied on an unlowered AST switches to `parseSourceRaw` (list them).
79
+
80
+ ### T3 tests lowering out of codegen · Complexity 4 · Est. context 45k
81
+ **Owns:** plum-core/src/tests_lower.rs, plum-core/src/lib.rs, plum-wasm-codegen/src/lib.rs, plum-core/tests/tests_lower_test.rs
82
+ **Components:** 1
83
+ **LOC owned:** 260
84
+ **Tests:** slow
85
+ **Model:** sonnet
86
+ **Consumes:** lower::frontEnd(source: &mut Source) -> Result<(), String> (T1)
87
+ **Produces:** tests_lower::lowerTests(source: &Source) -> TestProgram
88
+ e.g. lowerTests(src with `test "a" = expect 1 == 1`) -> TestProgram { names: ["a"], source with one generated fn }
89
+ **Review Focus:** `assert_names_per_compile`: generated temp names start at 0 for each call (no process-global ASSERT_TMP); `plum test` output unchanged.
90
+ **Test code:** tests_lower_test.rs for the e.g. line + two calls give identical output; cli assert_once_test/test_scope_test pass; `plum test plum-std plum-examples` 678 passed.
91
+ **Steps:** move desugarTestsToFns, desugarAssertStmt, assertFailureMessage, TestProgram (codegen lib.rs ≈1812-2076) to plum-core; codegen re-exports `TestProgram`/`desugarTests` for now (T4 removes them).
92
+
93
+ ## Package B: one compile call
94
+ **Probe entry:** run=bash scripts/wasm-hashes.sh
95
+
96
+ ### T4 compile options · Complexity 5 · Est. context 50k
97
+ **Owns:** plum-wasm-codegen/src/lib.rs, plum-checker/src/lib.rs
98
+ **Components:** 2
99
+ **LOC owned:** 120
100
+ **Tests:** slow
101
+ **Model:** sonnet
102
+ **Produces:** plum_wasm_codegen::compile(source: &Source, opts: &CompileOptions) -> Result<Output, String> · CompileOptions { tests: bool, fusion: FusionOptions } · Output { wasm: Vec<u8>, fn_keys: Vec<String>, test_names: Vec<String> } · plum_checker::typedSourceWith(source: &Source, fusion: FusionOptions) -> CheckResult<TypedInfo>
103
+ e.g. compile(src, &CompileOptions::default()) -> Ok(Output{wasm, test_names: [], ..}) same bytes as compileSource(src)
104
+ e.g. compile(src_with_tests, &CompileOptions{tests: true, ..}) -> test_names ["a"]
105
+ **Review Focus:** `no_fusion_thread_local`: `FUSION_OPTIONS`, `setFusionOptions`, `withFusionOptions` removed from plum-checker; `typedSource(s)` == `typedSourceWith(s, FusionOptions::default())`.
106
+ **Test code:** unit tests for the e.g. lines in codegen lib.rs `#[cfg(test)]`; codegen test binaries pass.
107
+ **Steps:** keep `compileSource(source)` as a one-line wrapper over `compile`; delete compileTyped, compileSourceWithFunctions, compileTestSource, compileTestTyped and the desugarTests re-export once T5 moves their callers (mark them `#[deprecated]` thin wrappers in this task so callers still build).
108
+
109
+ ### T5 compile callers · Complexity 3 · Est. context 40k
110
+ **Owns:** plum-cli/src/main.rs, plum-wasm-codegen/tests/codegen_tests.rs, plum-checker/tests/fusion_pipeline_test.rs, plum-game/tests/host_test.rs
111
+ **Components:** 1
112
+ **LOC owned:** 60
113
+ **Tests:** slow
114
+ **Model:** sonnet
115
+ **Consumes:** plum_wasm_codegen::compile(source: &Source, opts: &CompileOptions) -> Result<Output, String> (T4) · plum_checker::typedSourceWith(source: &Source, fusion: FusionOptions) -> CheckResult<TypedInfo> (T4)
116
+ **Produces:** none
117
+ **Review Focus:** `cli_flags`: `--no-fusion` and `--explain-fusion` still work (run `plum run` on an example with each and compare to master's output).
118
+ **Test code:** cli test binaries, codegen_tests, fusion_pipeline_test, plum-game host_test pass; no `#[deprecated]` warnings left; then delete the deprecated wrappers from codegen lib.rs is T7's job (it owns lib.rs next).
119
+
120
+ ## Package C: tidy and remove Paren
121
+ **Probe entry:** run=bash scripts/wasm-hashes.sh
122
+
123
+ ### T6 last walker helpers into visit · Complexity 2 · Est. context 30k
124
+ **Owns:** plum-core/src/visit.rs, plum-core/src/simplify.rs, plum-core/src/drive.rs
125
+ **Components:** 1
126
+ **LOC owned:** 50
127
+ **Tests:** fast
128
+ **Model:** sonnet
129
+ **Produces:** visit::walkRef(e: &Expr, f: &mut dyn FnMut(&Expr)) · visit::walkBlockRef(b: &Block, f: &mut dyn FnMut(&Expr)) · visit::argRef(a: &Arg) -> &Expr · visit::isTag(n: &str) -> bool
130
+ **Test code:** visit_test, drive_test, simplify_test pass; hashes identical.
131
+ **Steps:** move simplify's pub(crate) walkRef/walkBlockRef/argRef/isTag into visit.rs; drive and simplify import them.
132
+
133
+ ### T7 remove Expr::Paren and deprecated wrappers · Complexity 4 · Est. context 50k
134
+ **Owns:** plum-core/src/*.rs, plum-checker/src/*.rs, plum-wasm-codegen/src/lib.rs, plum-lsp/src/*.rs, plum-core/tests/parser_test.rs
135
+ **Components:** 2
136
+ **LOC owned:** 90
137
+ **Tests:** slow
138
+ **Model:** sonnet
139
+ **Produces:** none
140
+ **Review Focus:** `paren_gone`: `grep -rn "Paren" plum-*/src` has no `Expr::Paren`; precedence still right: `(1 + 2) * 3` evaluates to 9 (add to e2e via an existing example or parser_test AST check); tail position through parentheses still marked (tail_test passes).
141
+ **Test code:** all workspace binaries pass; hashes identical.
142
+ **Steps:** parser returns the inner expression for parenthesized_expression (parser.rs ≈1389); delete the variant and every arm (~46); delete codegen's deprecated compile wrappers from T4.
143
+
144
+ ## Package D: verify
145
+ **Probe entry:** run=bash scripts/wasm-hashes.sh
146
+
147
+ ### T8 verify 2b · Complexity 2 · Est. context 30k
148
+ **Owns:** docs/plans/2026-11-04-compiler-cleanup-phase2b.results.md
149
+ **Components:** 1
150
+ **LOC owned:** 40
151
+ **Tests:** slow
152
+ **Model:** sonnet
153
+ **Produces:** none
154
+ **Test code:** all workspace test binaries, plum-game host_test, `plum build` runtime, `plum test plum-std plum-examples` 678; hashes at start sha == final tree.
155
+ **Steps:** results file: hash diff, test totals, Rust LOC and test counts vs the start sha.
156
+
157
+ ## Review Focus
158
+ - Package A: deep, state — single-file vs loader divergence, test name generation.
159
+ - Package B: protocol — CLI flags, public API of plum-wasm-codegen used by cli/lsp/game tests.
160
+ - Package C: deep — operator precedence and tail position without Paren.
161
+
162
+ ## Self-review
163
+ Covers the phase 2 rows left from 2a: one front end (T1, T2, T3), one compile call (T4, T5), Paren removal (T7, moved from phase 1), visit helpers (T6, from a 2a ruling).