From a0af3a337c04089d6bb2f97f03f5a39f145aebdf Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Feb 2026 19:12:33 -0700 Subject: [PATCH] Step 152: extend cross-language projection --- PROGRESS.md | 1 + editor/CMakeLists.txt | 3 + editor/src/CrossLanguageProjector.h | 349 ++++++++++++++++++++++++++-- editor/tests/step152_test.cpp | 73 ++++++ sprint5_plan.md | 2 +- 5 files changed, 413 insertions(+), 15 deletions(-) create mode 100644 editor/tests/step152_test.cpp diff --git a/PROGRESS.md b/PROGRESS.md index c23dd15..6da8887 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -499,3 +499,4 @@ Sprint 5 in progress. Step 141 (Emacs daemon with user config) done. Next: Step | 2026-02-10 | Codex | Step 149: Java CST-to-AST parsing + generator with class/method/constructor handling, import support, and GC-aware annotations. 8/8 tests pass. | | 2026-02-10 | Codex | Step 150: Rust CST-to-AST parsing + generator with impl methods, imports, and RAII annotations. 7/7 tests pass. | | 2026-02-10 | Codex | Step 151: Go CST-to-AST parsing + generator with receivers, imports, and Go-style control flow. 8/8 tests pass. | +| 2026-02-10 | Codex | Step 152: Cross-language projection extended for new languages with annotation adaptation and richer node cloning. 6/6 tests pass. | diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 735c801..6b1d654 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -856,6 +856,9 @@ target_link_libraries(step151_test PRIVATE unofficial::tree-sitter::tree-sitter tree_sitter_go) +add_executable(step152_test tests/step152_test.cpp) +target_include_directories(step152_test PRIVATE src) + find_package(SDL2 CONFIG REQUIRED) find_package(OpenGL REQUIRED) find_package(glad CONFIG REQUIRED) diff --git a/editor/src/CrossLanguageProjector.h b/editor/src/CrossLanguageProjector.h index 410a61d..a0833f4 100644 --- a/editor/src/CrossLanguageProjector.h +++ b/editor/src/CrossLanguageProjector.h @@ -14,7 +14,10 @@ #include "ast/Function.h" #include "ast/Variable.h" #include "ast/Parameter.h" +#include "ast/Statement.h" +#include "ast/Expression.h" #include "ast/Type.h" +#include "ast/Import.h" #include "ast/Annotation.h" class CrossLanguageProjector { @@ -28,11 +31,21 @@ public: auto mod = std::make_unique( source->id + "_proj", source->name, targetLanguage); + // Copy imports + for (auto* child : source->getChildren("imports")) { + if (child->conceptType == "Import") { + auto* srcImp = static_cast(child); + auto* imp = new Import(srcImp->id + "_proj", srcImp->moduleName, + srcImp->importKind, srcImp->alias); + mod->addChild("imports", imp); + } + } + // Copy functions with annotations and body for (auto* child : source->getChildren("functions")) { if (child->conceptType == "Function") { auto* srcFn = static_cast(child); - Function* newFn = cloneFunction(srcFn); + Function* newFn = cloneFunction(srcFn, targetLanguage); mod->addChild("functions", newFn); } } @@ -42,13 +55,23 @@ public: if (child->conceptType == "Variable") { auto* srcVar = static_cast(child); Variable* newVar = new Variable(srcVar->id + "_proj", srcVar->name); + auto* typeChild = srcVar->getChild("type"); + if (typeChild) { + ASTNode* t = cloneNode(typeChild, targetLanguage); + if (t) newVar->setChild("type", t); + } + auto* init = srcVar->getChild("initializer"); + if (init) { + ASTNode* i = cloneNode(init, targetLanguage); + if (i) newVar->setChild("initializer", i); + } mod->addChild("variables", newVar); } } // Copy module-level annotations for (auto* anno : source->getChildren("annotations")) { - ASTNode* newAnno = cloneAnnotation(anno); + ASTNode* newAnno = cloneAnnotation(anno, targetLanguage); if (newAnno) mod->addChild("annotations", newAnno); } @@ -72,45 +95,49 @@ public: } private: - Function* cloneFunction(const Function* src) const { + Function* cloneFunction(const Function* src, + const std::string& targetLanguage) const { Function* fn = new Function(src->id + "_proj", src->name); // Copy annotations (preserve all, even non-native to target) for (auto* anno : src->getChildren("annotations")) { - ASTNode* newAnno = cloneAnnotation(anno); + ASTNode* newAnno = cloneAnnotation(anno, targetLanguage); if (newAnno) fn->addChild("annotations", newAnno); } // Copy body nodes for (auto* child : src->getChildren("body")) { - ASTNode* newChild = cloneNode(child); + ASTNode* newChild = cloneNode(child, targetLanguage); if (newChild) fn->addChild("body", newChild); } // Copy parameters for (auto* child : src->getChildren("parameters")) { - ASTNode* newChild = cloneNode(child); + ASTNode* newChild = cloneNode(child, targetLanguage); if (newChild) fn->addChild("parameters", newChild); } // Copy return type auto* retType = src->getChild("returnType"); if (retType) { - ASTNode* newRet = cloneNode(retType); + ASTNode* newRet = cloneNode(retType, targetLanguage); if (newRet) fn->setChild("returnType", newRet); } return fn; } - ASTNode* cloneAnnotation(const ASTNode* anno) const { + ASTNode* cloneAnnotation(const ASTNode* anno, + const std::string& targetLanguage) const { if (!anno) return nullptr; const auto& ct = anno->conceptType; if (ct == "ReclaimAnnotation") { auto* src = static_cast(anno); + std::string strategy = adaptReclaimStrategy(src->strategy, targetLanguage); auto* a = new ReclaimAnnotation(src->id + "_proj", src->strategy); + a->strategy = strategy; a->reclaimPattern = src->reclaimPattern; return a; } @@ -168,7 +195,8 @@ private: return nullptr; } - ASTNode* cloneNode(const ASTNode* node) const { + ASTNode* cloneNode(const ASTNode* node, + const std::string& targetLanguage) const { if (!node) return nullptr; const auto& ct = node->conceptType; @@ -178,19 +206,19 @@ private: auto* v = new Variable(src->id + "_proj", src->name); // Clone variable annotations for (auto* anno : src->getChildren("annotations")) { - ASTNode* a = cloneAnnotation(anno); + ASTNode* a = cloneAnnotation(anno, targetLanguage); if (a) v->addChild("annotations", a); } // Clone type child auto* typeChild = src->getChild("type"); if (typeChild) { - ASTNode* t = cloneNode(typeChild); + ASTNode* t = cloneNode(typeChild, targetLanguage); if (t) v->setChild("type", t); } // Clone initializer auto* init = src->getChild("initializer"); if (init) { - ASTNode* i = cloneNode(init); + ASTNode* i = cloneNode(init, targetLanguage); if (i) v->setChild("initializer", i); } return v; @@ -200,26 +228,319 @@ private: auto* p = new Parameter(src->id + "_proj", src->name); auto* typeChild = src->getChild("type"); if (typeChild) { - ASTNode* t = cloneNode(typeChild); + ASTNode* t = cloneNode(typeChild, targetLanguage); if (t) p->setChild("type", t); } + auto* defaultValue = src->getChild("defaultValue"); + if (defaultValue) { + ASTNode* d = cloneNode(defaultValue, targetLanguage); + if (d) p->setChild("defaultValue", d); + } return p; } if (ct == "PrimitiveType") { auto* src = static_cast(node); return new PrimitiveType(src->id + "_proj", src->kind); } + if (ct == "ListType") { + auto* t = new ListType(); + t->id = node->id + "_proj"; + auto* elem = node->getChild("elementType"); + if (elem) { + ASTNode* e = cloneNode(elem, targetLanguage); + if (e) t->setChild("elementType", e); + } + return t; + } + if (ct == "SetType") { + auto* t = new SetType(); + t->id = node->id + "_proj"; + auto* elem = node->getChild("elementType"); + if (elem) { + ASTNode* e = cloneNode(elem, targetLanguage); + if (e) t->setChild("elementType", e); + } + return t; + } + if (ct == "MapType") { + auto* t = new MapType(); + t->id = node->id + "_proj"; + auto* key = node->getChild("keyType"); + auto* val = node->getChild("valueType"); + if (key) { + ASTNode* k = cloneNode(key, targetLanguage); + if (k) t->setChild("keyType", k); + } + if (val) { + ASTNode* v = cloneNode(val, targetLanguage); + if (v) t->setChild("valueType", v); + } + return t; + } + if (ct == "TupleType") { + auto* t = new TupleType(); + t->id = node->id + "_proj"; + for (auto* child : node->getChildren("elementTypes")) { + ASTNode* c = cloneNode(child, targetLanguage); + if (c) t->addChild("elementTypes", c); + } + return t; + } + if (ct == "ArrayType") { + auto* t = new ArrayType(); + t->id = node->id + "_proj"; + auto* elem = node->getChild("elementType"); + if (elem) { + ASTNode* e = cloneNode(elem, targetLanguage); + if (e) t->setChild("elementType", e); + } + auto* size = node->getChild("size"); + if (size) { + ASTNode* s = cloneNode(size, targetLanguage); + if (s) t->setChild("size", s); + } + return t; + } + if (ct == "OptionalType") { + auto* t = new OptionalType(); + t->id = node->id + "_proj"; + auto* inner = node->getChild("innerType"); + if (inner) { + ASTNode* i = cloneNode(inner, targetLanguage); + if (i) t->setChild("innerType", i); + } + return t; + } + if (ct == "CustomType") { + auto* src = static_cast(node); + return new CustomType(src->id + "_proj", src->typeName); + } + if (ct == "Assignment") { + auto* src = static_cast(node); + auto* a = new Assignment(); + a->id = src->id + "_proj"; + auto* target = src->getChild("target"); + auto* value = src->getChild("value"); + if (target) { + ASTNode* t = cloneNode(target, targetLanguage); + if (t) a->setChild("target", t); + } + if (value) { + ASTNode* v = cloneNode(value, targetLanguage); + if (v) a->setChild("value", v); + } + return a; + } + if (ct == "Return") { + auto* src = static_cast(node); + auto* r = new Return(); + r->id = src->id + "_proj"; + auto* val = src->getChild("value"); + if (val) { + ASTNode* v = cloneNode(val, targetLanguage); + if (v) r->setChild("value", v); + } + return r; + } + if (ct == "BinaryOperation") { + auto* src = static_cast(node); + auto* b = new BinaryOperation(); + b->id = src->id + "_proj"; + b->op = src->op; + auto* left = src->getChild("left"); + auto* right = src->getChild("right"); + if (left) { + ASTNode* l = cloneNode(left, targetLanguage); + if (l) b->setChild("left", l); + } + if (right) { + ASTNode* r = cloneNode(right, targetLanguage); + if (r) b->setChild("right", r); + } + return b; + } + if (ct == "UnaryOperation") { + auto* src = static_cast(node); + auto* u = new UnaryOperation(); + u->id = src->id + "_proj"; + u->op = src->op; + auto* operand = src->getChild("operand"); + if (operand) { + ASTNode* o = cloneNode(operand, targetLanguage); + if (o) u->setChild("operand", o); + } + return u; + } + if (ct == "VariableReference") { + auto* src = static_cast(node); + return new VariableReference(src->id + "_proj", src->variableName); + } + if (ct == "IntegerLiteral") { + auto* src = static_cast(node); + return new IntegerLiteral(src->id + "_proj", src->value); + } + if (ct == "FloatLiteral") { + auto* src = static_cast(node); + return new FloatLiteral(src->id + "_proj", src->value); + } + if (ct == "StringLiteral") { + auto* src = static_cast(node); + return new StringLiteral(src->id + "_proj", src->value); + } + if (ct == "BooleanLiteral") { + auto* src = static_cast(node); + return new BooleanLiteral(src->id + "_proj", src->value); + } + if (ct == "NullLiteral") { + auto* n = new NullLiteral(); + n->id = node->id + "_proj"; + return n; + } + if (ct == "IfStatement") { + auto* src = static_cast(node); + auto* s = new IfStatement(); + s->id = src->id + "_proj"; + auto* cond = src->getChild("condition"); + if (cond) { + ASTNode* c = cloneNode(cond, targetLanguage); + if (c) s->setChild("condition", c); + } + for (auto* child : src->getChildren("thenBranch")) { + ASTNode* c = cloneNode(child, targetLanguage); + if (c) s->addChild("thenBranch", c); + } + for (auto* child : src->getChildren("elseBranch")) { + ASTNode* c = cloneNode(child, targetLanguage); + if (c) s->addChild("elseBranch", c); + } + return s; + } + if (ct == "WhileLoop") { + auto* src = static_cast(node); + auto* s = new WhileLoop(); + s->id = src->id + "_proj"; + auto* cond = src->getChild("condition"); + if (cond) { + ASTNode* c = cloneNode(cond, targetLanguage); + if (c) s->setChild("condition", c); + } + for (auto* child : src->getChildren("body")) { + ASTNode* c = cloneNode(child, targetLanguage); + if (c) s->addChild("body", c); + } + return s; + } + if (ct == "ForLoop") { + auto* src = static_cast(node); + auto* s = new ForLoop(); + s->id = src->id + "_proj"; + s->iteratorName = src->iteratorName; + auto* iter = src->getChild("iterable"); + if (iter) { + ASTNode* i = cloneNode(iter, targetLanguage); + if (i) s->setChild("iterable", i); + } + for (auto* child : src->getChildren("body")) { + ASTNode* c = cloneNode(child, targetLanguage); + if (c) s->addChild("body", c); + } + return s; + } + if (ct == "ExpressionStatement") { + auto* src = static_cast(node); + auto* s = new ExpressionStatement(); + s->id = src->id + "_proj"; + auto* expr = src->getChild("expression"); + if (expr) { + ASTNode* e = cloneNode(expr, targetLanguage); + if (e) s->setChild("expression", e); + } + return s; + } + if (ct == "FunctionCall") { + auto* src = static_cast(node); + auto* c = new FunctionCall(); + c->id = src->id + "_proj"; + c->functionName = src->functionName; + for (auto* child : src->getChildren("arguments")) { + ASTNode* a = cloneNode(child, targetLanguage); + if (a) c->addChild("arguments", a); + } + return c; + } + if (ct == "Block") { + auto* src = static_cast(node); + auto* b = new Block(); + b->id = src->id + "_proj"; + for (auto* child : src->getChildren("statements")) { + ASTNode* c = cloneNode(child, targetLanguage); + if (c) b->addChild("statements", c); + } + return b; + } + if (ct == "ListLiteral") { + auto* src = static_cast(node); + auto* l = new ListLiteral(); + l->id = src->id + "_proj"; + for (auto* child : src->getChildren("elements")) { + ASTNode* c = cloneNode(child, targetLanguage); + if (c) l->addChild("elements", c); + } + return l; + } + if (ct == "IndexAccess") { + auto* src = static_cast(node); + auto* a = new IndexAccess(); + a->id = src->id + "_proj"; + auto* target = src->getChild("target"); + auto* index = src->getChild("index"); + if (target) { + ASTNode* t = cloneNode(target, targetLanguage); + if (t) a->setChild("target", t); + } + if (index) { + ASTNode* i = cloneNode(index, targetLanguage); + if (i) a->setChild("index", i); + } + return a; + } + if (ct == "MemberAccess") { + auto* src = static_cast(node); + auto* m = new MemberAccess(); + m->id = src->id + "_proj"; + m->memberName = src->memberName; + auto* target = src->getChild("target"); + if (target) { + ASTNode* t = cloneNode(target, targetLanguage); + if (t) m->setChild("target", t); + } + return m; + } // Fallback: annotations if (ct.find("Annotation") != std::string::npos || ct == "DerefStrategy" || ct == "OptimizationLock" || ct == "LangSpecific") { - return cloneAnnotation(node); + return cloneAnnotation(node, targetLanguage); } return nullptr; } + static std::string adaptReclaimStrategy(const std::string& strategy, + const std::string& targetLanguage) { + if (targetLanguage == "go") { + return "Escape"; + } + if (targetLanguage == "rust" || targetLanguage == "cpp" || + targetLanguage == "java" || targetLanguage == "javascript" || + targetLanguage == "typescript" || targetLanguage == "python" || + targetLanguage == "elisp") { + return "Tracing"; + } + return strategy; + } + // Recursively collect all annotation conceptTypes from a tree std::vector collectAnnotationTypes(const ASTNode* node) const { std::vector types; diff --git a/editor/tests/step152_test.cpp b/editor/tests/step152_test.cpp new file mode 100644 index 0000000..257981a --- /dev/null +++ b/editor/tests/step152_test.cpp @@ -0,0 +1,73 @@ +// Step 152 TDD Test: Cross-language projection for new languages +#include "CrossLanguageProjector.h" +#include "ast/Annotation.h" +#include "ast/Module.h" +#include "ast/Function.h" +#include "ast/Statement.h" +#include "ast/Expression.h" +#include + +static void expect(bool cond, const std::string& name, int& passed, int& failed) { + if (cond) { + std::cout << "Test " << (passed + failed + 1) << " PASS: " << name << "\n"; + ++passed; + } else { + std::cout << "Test " << (passed + failed + 1) << " FAIL: " << name << "\n"; + ++failed; + } +} + +static bool hasReclaimAnnotation(const ASTNode* node, std::string* strategyOut = nullptr) { + if (!node) return false; + for (auto* anno : node->getChildren("annotations")) { + if (anno->conceptType == "ReclaimAnnotation") { + if (strategyOut) { + auto* ra = static_cast(anno); + *strategyOut = ra->strategy; + } + return true; + } + } + return false; +} + +int main() { + int passed = 0; + int failed = 0; + + Module src("mod_src", "Demo", "python"); + auto* fn = new Function("fn_add", "add"); + auto* reclaim = new ReclaimAnnotation("anno_gc", "Tracing"); + fn->addChild("annotations", reclaim); + + auto* ret = new Return(); + ret->id = "ret"; + ret->setChild("value", new IntegerLiteral("int_one", 1)); + fn->addChild("body", ret); + src.addChild("functions", fn); + + CrossLanguageProjector projector; + + auto rust = projector.project(&src, "rust"); + expect(rust != nullptr, "rust projection created", passed, failed); + expect(rust && rust->targetLanguage == "rust", "rust target language set", passed, failed); + + auto go = projector.project(&src, "go"); + expect(go != nullptr, "go projection created", passed, failed); + expect(go && go->targetLanguage == "go", "go target language set", passed, failed); + + bool reclaimFound = false; + std::string strategy; + if (go) { + auto fns = go->getChildren("functions"); + if (!fns.empty() && fns[0]->conceptType == "Function") { + reclaimFound = hasReclaimAnnotation(fns[0], &strategy); + } + } + expect(reclaimFound, "reclaim annotation preserved", passed, failed); + expect(strategy == "Escape", "reclaim adapted for go", passed, failed); + + std::cout << "\n=== Step 152 Results: " << passed << " passed, " + << failed << " failed ===\n"; + return failed == 0 ? 0 : 1; +} diff --git a/sprint5_plan.md b/sprint5_plan.md index d138607..6acfbdc 100644 --- a/sprint5_plan.md +++ b/sprint5_plan.md @@ -267,7 +267,7 @@ adds full semantic support. Close()/Release() patterns, @Owner(Single) → ownership convention comments. *Modifies:* `Parser.h`, `Generator.h` -- [ ] **Step 152: Cross-language projection for new languages** +- [x] **Step 152: Cross-language projection for new languages** Extend `CrossLanguageProjector` to handle projection between all pairs of supported languages (Python, C++, Elisp, JS/TS, Java, Rust, Go). Annotation adaptation rules for each target. Library compatibility