184 lines
8.3 KiB
Markdown
184 lines
8.3 KiB
Markdown
# Sprint 162 Plan: Fix Class Emission in All Language Generators
|
||
|
||
## Context
|
||
|
||
A/B test run on 2026-02-25 (see `docs/ab_test_ast_vs_language_first_2026-02-25.md`)
|
||
exposed that `whetstone_run_pipeline` Python→C++ emitted an empty namespace when
|
||
given a Python class as input. A full audit of all 8 language generators revealed
|
||
the root cause is systemic and identical across every language:
|
||
|
||
**`visitModule` never calls `getChildren("classes")`.**
|
||
|
||
Every generator's `visitModule` iterates variables and functions only. Parsers
|
||
correctly populate the "classes" role during parsing — `ClassDeclaration` nodes
|
||
are in the AST — but the module walk skips them entirely. Classes are silently
|
||
dropped from all generated output across all 8 languages.
|
||
|
||
The `visitClassDeclaration` implementations are mostly already written and correct:
|
||
|
||
| Language | `visitClassDeclaration` | Line range | Status |
|
||
|----------|------------------------|------------|--------|
|
||
| Python | Yes | PythonGenerator.h:535–562 | Good |
|
||
| Rust | Yes | RustGenerator.h:427–444 | Good — struct + impl |
|
||
| Go | Yes | GoGenerator.h:412–425 | Good — struct + receivers |
|
||
| Java | Yes | JavaGenerator.h:447–483 | Good — handles abstract/extends |
|
||
| JavaScript | Yes | JavaScriptGenerator.h:454–469 | Good — ES6 class syntax |
|
||
| TypeScript | Yes (inherited from JS) | — | Good |
|
||
| Elisp | Yes | ElispGenerator.h:497–513 | Good — cl-defstruct |
|
||
| **C++** | **No** | CppGenerator.h | **Missing entirely** |
|
||
|
||
The fix for 7 languages is adding `getChildren("classes")` iteration to
|
||
`visitModule` — approximately 3 lines each. C++ additionally needs
|
||
`visitClassDeclaration` written from scratch.
|
||
|
||
Java and JavaScript have a secondary issue: their `visitModule` reconstructs
|
||
classes from function-name heuristics instead of the AST. That heuristic path
|
||
must be replaced with the real `getChildren("classes")` iteration.
|
||
|
||
This sprint fixes all 8 generators in one pass. After this sprint, any language
|
||
can emit a class from a parsed AST, and cross-language class transpilation
|
||
(e.g. Python → C++, Python → Rust) works end-to-end.
|
||
|
||
---
|
||
|
||
## Goals
|
||
|
||
1. Add `getChildren("classes")` iteration to `visitModule` in all 8 generators
|
||
2. Write `visitClassDeclaration` for C++ (the only language missing it entirely)
|
||
3. Replace the function-name heuristic in Java and JavaScript `visitModule` with
|
||
real AST class iteration
|
||
4. End-to-end: the exact Python PriorityQueue from the A/B test must produce
|
||
compilable output in C++, Rust, and Go via `run_pipeline`
|
||
|
||
---
|
||
|
||
## Steps
|
||
|
||
### Step 1854: Fix `visitModule` in Python, Rust, Go, Elisp generators (12 tests)
|
||
|
||
For each of these four generators, add a loop after the existing functions
|
||
iteration in `visitModule`:
|
||
|
||
```cpp
|
||
for (auto& cls : node->getChildren("classes")) {
|
||
result += dispatchGenerate(cls);
|
||
}
|
||
```
|
||
|
||
No other changes — `visitClassDeclaration` already works correctly in all four.
|
||
|
||
Tests (12, 3 per language): Python class round-trips through parse→generate,
|
||
Rust class produces `struct` + `impl` block, Go class produces `type X struct`
|
||
with method receivers, Elisp class produces `cl-defstruct` — and for each,
|
||
a module containing both a function and a class emits both (function not lost,
|
||
class not lost).
|
||
|
||
### Step 1855: Fix `visitModule` in Java and JavaScript generators (10 tests)
|
||
|
||
Java and JavaScript use a function-name heuristic to reconstruct classes
|
||
(partitioning function names to guess class membership) instead of reading
|
||
`getChildren("classes")`. Replace the heuristic path with the real AST iteration.
|
||
|
||
For both generators, `visitModule` should iterate `getChildren("classes")`
|
||
using `dispatchGenerate(cls)` — same pattern as Step 1854.
|
||
The existing `visitClassDeclaration` implementations (Java:447–483,
|
||
JavaScript:454–469) remain unchanged.
|
||
|
||
TypeScript inherits from JavaScript and requires no separate change.
|
||
|
||
Tests (10): Java class produces correct `public class X { }` syntax, Java class
|
||
with extends emits `extends`, Java abstract class emits `abstract`, JavaScript
|
||
class produces ES6 `class X { }` syntax, JavaScript class with superclass emits
|
||
`extends`, TypeScript class works via inheritance, Java module with function +
|
||
class emits both, JavaScript module with function + class emits both, no heuristic
|
||
path remains (grep for the old partition logic returns nothing),
|
||
regression: existing function-only modules still work correctly.
|
||
|
||
### Step 1856: Write `visitClassDeclaration` for C++ generator (12 tests)
|
||
|
||
C++ is the only language missing `visitClassDeclaration` entirely. Add it to
|
||
`CppGenerator.h`.
|
||
|
||
The implementation must emit a complete header-only C++ class:
|
||
- `struct` for pure data classes (no methods other than comparators)
|
||
- `class` with `public:` section for classes with methods
|
||
- Fields with inferred types: Python `str` → `std::string`, `int` → `int`,
|
||
`bool` → `bool`, `float` → `double`, unknown → `auto /* TODO: specify type */`
|
||
- Method declarations with `auto` parameter/return types when not inferrable
|
||
- Required `#include` directives (`<string>` when string fields present, etc.)
|
||
|
||
Also add `getChildren("classes")` to C++ `visitModule` (same 3-line pattern
|
||
as Step 1854).
|
||
|
||
Tests (12): empty class emits valid C++ `class X { };`, class with string field
|
||
emits `std::string`, class with int field emits `int`, class with unknown field
|
||
emits `auto` with TODO comment, class with methods emits method declarations,
|
||
visitModule emits both functions and classes, `#include <string>` present when
|
||
string field exists, pragma once present at module level, C++ class output
|
||
parseable by Whetstone's own CppParser, class with `__init__`-style constructor
|
||
emits C++ constructor, class with only fields uses `struct`, class with methods
|
||
uses `class` with `public:`.
|
||
|
||
### Step 1857: End-to-end — Python PriorityQueue → C++, Rust, Go (10 tests)
|
||
|
||
Create `editor/src/Sprint162EndToEnd.h`.
|
||
|
||
Uses the exact Python source from the failing 2026-02-25 A/B run. Runs it
|
||
through `run_pipeline` targeting C++, Rust, and Go. All three must produce
|
||
non-empty, parseable output containing the class definitions.
|
||
|
||
Expected C++ output: contains `class WorkItem`, `class PriorityQueue`,
|
||
`enqueue`, `dequeue`, `std::string`, `int`.
|
||
|
||
Expected Rust output: contains `struct WorkItem`, `impl PriorityQueue`,
|
||
`fn enqueue`, `fn dequeue`.
|
||
|
||
Expected Go output: contains `type WorkItem struct`, `type PriorityQueue struct`,
|
||
method receivers for `enqueue` and `dequeue`.
|
||
|
||
Tests (10): C++ output non-empty, C++ contains "class WorkItem", C++ contains
|
||
"class PriorityQueue", C++ contains "std::string", Rust output non-empty,
|
||
Rust contains "struct WorkItem", Rust contains "impl PriorityQueue", Go output
|
||
non-empty, Go contains "type WorkItem struct", Go contains "type PriorityQueue struct".
|
||
|
||
### Step 1858: Sprint 162 Integration Summary (8 tests)
|
||
|
||
Create `editor/src/Sprint162IntegrationSummary.h`.
|
||
Record: steps_completed=5 (1854–1858), root_cause="visitModule skips
|
||
getChildren(classes) in all 8 generators", languages_fixed=8,
|
||
new_code_written="CppGenerator::visitClassDeclaration only — all other
|
||
visitClassDeclaration implementations pre-existing", ab_test_reference=
|
||
"docs/ab_test_ast_vs_language_first_2026-02-25.md", success=true.
|
||
|
||
Full sprint regression: steps 1854–1858 pass. All existing function-level
|
||
pipeline tests pass (no regression).
|
||
|
||
Tests (8): struct constructable, steps_completed==5, languages_fixed==8,
|
||
root_cause non-empty, all 8 generators emit classes, Python round-trip works,
|
||
C++ round-trip works, success==true.
|
||
|
||
---
|
||
|
||
## Architecture Gate
|
||
|
||
- No new files required except Sprint162IntegrationSummary.h and the C++
|
||
visitClassDeclaration (added inline to CppGenerator.h, ≤ 600 line limit)
|
||
- No new vcpkg dependencies
|
||
- The 3-line `getChildren("classes")` fix must be applied consistently across
|
||
all generators — no one-off solutions
|
||
- Existing function-level output must be unchanged in all generators (regression
|
||
tests in Step 1858 enforce this)
|
||
- Type inference for C++ must be conservative: unknown → `auto` + comment, never
|
||
silently wrong
|
||
- Java/JavaScript heuristic class reconstruction must be fully removed, not
|
||
left alongside the new path
|
||
|
||
---
|
||
|
||
## Note on Scope
|
||
|
||
This sprint does NOT require writing new `visitClassDeclaration` implementations
|
||
for Python, Rust, Go, Java, JavaScript, TypeScript, or Elisp. Those already exist
|
||
and are correct. The entire fix for those 7 languages is the `getChildren("classes")`
|
||
loop in `visitModule`. Only C++ requires new implementation work.
|