Files
whetstone_DSL/sprint162_plan.md

8.3 KiB
Raw Blame History

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:535562 Good
Rust Yes RustGenerator.h:427444 Good — struct + impl
Go Yes GoGenerator.h:412425 Good — struct + receivers
Java Yes JavaGenerator.h:447483 Good — handles abstract/extends
JavaScript Yes JavaScriptGenerator.h:454469 Good — ES6 class syntax
TypeScript Yes (inherited from JS) Good
Elisp Yes ElispGenerator.h:497513 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:

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:447483, JavaScript:454469) 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 strstd::string, intint, boolbool, floatdouble, 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 (18541858), 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 18541858 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.