From ba15bdae033c85bd10c56bc9149add78d1c23b71 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 16 Feb 2026 15:31:23 -0700 Subject: [PATCH] Step 408: add VB.NET generator and generate routing --- editor/CMakeLists.txt | 9 ++ editor/src/Pipeline.h | 3 + editor/src/ast/Generator.h | 1 + editor/src/ast/VBNetGenerator.h | 261 ++++++++++++++++++++++++++++++++ editor/tests/step408_test.cpp | 186 +++++++++++++++++++++++ progress.md | 47 ++++++ 6 files changed, 507 insertions(+) create mode 100644 editor/src/ast/VBNetGenerator.h create mode 100644 editor/tests/step408_test.cpp diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 311fdfd..8a11757 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2551,4 +2551,13 @@ target_link_libraries(step407_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step408_test tests/step408_test.cpp) +target_include_directories(step408_test PRIVATE src) +target_link_libraries(step408_test PRIVATE + nlohmann_json::nlohmann_json + unofficial::tree-sitter::tree-sitter + tree_sitter_python tree_sitter_cpp tree_sitter_elisp + tree_sitter_javascript tree_sitter_typescript + tree_sitter_java tree_sitter_rust tree_sitter_go) + # Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies) diff --git a/editor/src/Pipeline.h b/editor/src/Pipeline.h index fe0e4fc..468e637 100644 --- a/editor/src/Pipeline.h +++ b/editor/src/Pipeline.h @@ -183,6 +183,9 @@ public: } else if (language == "fsharp" || language == "f#" || language == "fs") { FSharpGenerator gen; return gen.generate(ast); + } else if (language == "vbnet" || language == "vb" || language == "vb.net") { + VBNetGenerator gen; + return gen.generate(ast); } else if (language == "c") { CGenerator gen; return gen.generate(ast); diff --git a/editor/src/ast/Generator.h b/editor/src/ast/Generator.h index afad958..44a8cac 100644 --- a/editor/src/ast/Generator.h +++ b/editor/src/ast/Generator.h @@ -12,6 +12,7 @@ #include "KotlinGenerator.h" #include "CSharpGenerator.h" #include "FSharpGenerator.h" +#include "VBNetGenerator.h" #include "CGenerator.h" #include "WatGenerator.h" #include "CommonLispGenerator.h" diff --git a/editor/src/ast/VBNetGenerator.h b/editor/src/ast/VBNetGenerator.h new file mode 100644 index 0000000..14dd7e0 --- /dev/null +++ b/editor/src/ast/VBNetGenerator.h @@ -0,0 +1,261 @@ +#pragma once + +#include "ProjectionGenerator.h" +#include "ClassDeclaration.h" +#include "EnumNamespaceNodes.h" +#include "../SemannoAnnotationImpl.h" + +class VBNetGenerator : public ProjectionGenerator, public SemannoAnnotationImpl { +public: + std::string commentPrefix() const { return "' "; } + + std::string generate(const ASTNode* node) override { + return dispatchGenerate(this, node, "' Unknown concept: "); + } + + std::string visitModule(const Module* module) override { + std::ostringstream oss; + oss << "' Module: " << module->name << "\n\n"; + for (const auto* stmt : module->getChildren("statements")) oss << generate(stmt) << "\n"; + if (!module->getChildren("statements").empty()) oss << "\n"; + for (const auto* var : module->getChildren("variables")) oss << generate(var) << "\n"; + if (!module->getChildren("variables").empty()) oss << "\n"; + for (const auto* fn : module->getChildren("functions")) oss << generate(fn) << "\n"; + return oss.str(); + } + + std::string visitFunction(const Function* function) override { + std::ostringstream oss; + for (const auto* anno : function->getChildren("annotations")) oss << generate(anno) << "\n"; + + auto* retType = function->getChild("returnType"); + const bool hasReturn = retType != nullptr; + oss << (hasReturn ? "Function " : "Sub ") << function->name << "("; + auto params = function->getChildren("parameters"); + for (size_t i = 0; i < params.size(); ++i) { + if (i > 0) oss << ", "; + oss << generate(params[i]); + } + oss << ")"; + if (hasReturn) oss << " As " << generate(retType); + oss << "\n"; + + for (const auto* stmt : function->getChildren("body")) { + oss << " " << generate(stmt) << "\n"; + } + oss << (hasReturn ? "End Function" : "End Sub"); + return oss.str(); + } + + std::string visitVariable(const Variable* variable) override { + std::ostringstream oss; + oss << "Dim " << variable->name; + auto* type = variable->getChild("type"); + if (type) oss << " As " << generate(type); + auto* init = variable->getChild("initializer"); + if (init) oss << " = " << generate(init); + return oss.str(); + } + + std::string visitParameter(const Parameter* parameter) override { + auto* type = parameter->getChild("type"); + if (!type) return parameter->name; + return parameter->name + " As " + generate(type); + } + + std::string visitAssignment(const Assignment* a) override { + auto* t = a->getChild("target"); + auto* v = a->getChild("value"); + return (t ? generate(t) : "") + " = " + (v ? generate(v) : "Nothing"); + } + + std::string visitReturn(const Return* r) override { + auto* v = r->getChild("value"); + return v ? "Return " + generate(v) : "Return"; + } + + std::string visitBinaryOperation(const BinaryOperation* b) override { + auto* l = b->getChild("left"); + auto* r = b->getChild("right"); + return (l ? generate(l) : "") + " " + b->op + " " + (r ? generate(r) : ""); + } + + std::string visitVariableReference(const VariableReference* v) override { return v->variableName; } + std::string visitIntegerLiteral(const IntegerLiteral* l) override { return std::to_string(l->value); } + std::string visitFloatLiteral(const FloatLiteral* l) override { return l->value; } + std::string visitStringLiteral(const StringLiteral* l) override { return "\"" + l->value + "\""; } + std::string visitBooleanLiteral(const BooleanLiteral* l) override { return l->value ? "True" : "False"; } + std::string visitNullLiteral(const NullLiteral*) override { return "Nothing"; } + + std::string visitIfStatement(const IfStatement* s) override { + std::ostringstream oss; + auto* cond = s->getChild("condition"); + oss << "If " << (cond ? generate(cond) : "True") << " Then\n"; + for (const auto* t : s->getChildren("thenBranch")) oss << " " << generate(t) << "\n"; + auto elseBranch = s->getChildren("elseBranch"); + if (!elseBranch.empty()) { + oss << "Else\n"; + for (const auto* e : elseBranch) oss << " " << generate(e) << "\n"; + } + oss << "End If"; + return oss.str(); + } + + std::string visitWhileLoop(const WhileLoop* l) override { + std::ostringstream oss; + auto* cond = l->getChild("condition"); + oss << "While " << (cond ? generate(cond) : "True") << "\n"; + for (const auto* s : l->getChildren("body")) oss << " " << generate(s) << "\n"; + oss << "End While"; + return oss.str(); + } + + std::string visitForLoop(const ForLoop* l) override { + std::ostringstream oss; + auto* iter = l->getChild("iterable"); + oss << "For Each " << (l->iteratorName.empty() ? "item" : l->iteratorName) + << " In " << (iter ? generate(iter) : "collection") << "\n"; + for (const auto* s : l->getChildren("body")) oss << " " << generate(s) << "\n"; + oss << "Next"; + return oss.str(); + } + + std::string visitExpressionStatement(const ExpressionStatement* s) override { + auto* expr = s->getChild("expression"); + return expr ? generate(expr) : ""; + } + + std::string visitUnaryOperation(const UnaryOperation* u) override { + auto* o = u->getChild("operand"); + return u->op + (o ? generate(o) : ""); + } + + std::string visitFunctionCall(const FunctionCall* c) override { + std::ostringstream oss; + oss << c->functionName << "("; + auto args = c->getChildren("arguments"); + for (size_t i = 0; i < args.size(); ++i) { + if (i > 0) oss << ", "; + oss << generate(args[i]); + } + oss << ")"; + return oss.str(); + } + + std::string visitBlock(const Block* b) override { + std::ostringstream oss; + for (const auto* s : b->getChildren("statements")) oss << generate(s) << "\n"; + return trimTrailingNewline(oss.str()); + } + + std::string visitListLiteral(const ListLiteral* l) override { + std::ostringstream oss; + oss << "{"; + auto elems = l->getChildren("elements"); + for (size_t i = 0; i < elems.size(); ++i) { + if (i > 0) oss << ", "; + oss << generate(elems[i]); + } + oss << "}"; + return oss.str(); + } + + std::string visitIndexAccess(const IndexAccess* a) override { + auto* t = a->getChild("target"); + auto* i = a->getChild("index"); + return (t ? generate(t) : "") + "(" + (i ? generate(i) : "") + ")"; + } + + std::string visitMemberAccess(const MemberAccess* a) override { + auto* t = a->getChild("target"); + return (t ? generate(t) : "") + "." + a->memberName; + } + + std::string visitPrimitiveType(const PrimitiveType* t) override { + if (t->kind == "int") return "Integer"; + if (t->kind == "float" || t->kind == "double") return "Double"; + if (t->kind == "string") return "String"; + if (t->kind == "bool") return "Boolean"; + if (t->kind == "void") return "Object"; + return t->kind; + } + std::string visitListType(const ListType* t) override { + auto* e = t->getChild("elementType"); + return "List(Of " + std::string(e ? generate(e) : "Object") + ")"; + } + std::string visitSetType(const SetType* t) override { + auto* e = t->getChild("elementType"); + return "HashSet(Of " + std::string(e ? generate(e) : "Object") + ")"; + } + std::string visitMapType(const MapType* t) override { + auto* k = t->getChild("keyType"); + auto* v = t->getChild("valueType"); + return "Dictionary(Of " + std::string(k ? generate(k) : "Object") + ", " + + std::string(v ? generate(v) : "Object") + ")"; + } + std::string visitTupleType(const TupleType* t) override { + std::ostringstream oss; + oss << "Tuple(Of "; + auto elems = t->getChildren("elementTypes"); + for (size_t i = 0; i < elems.size(); ++i) { + if (i > 0) oss << ", "; + oss << generate(elems[i]); + } + oss << ")"; + return oss.str(); + } + std::string visitArrayType(const ArrayType* t) override { + auto* e = t->getChild("elementType"); + return (e ? generate(e) : "Object") + "()"; + } + std::string visitOptionalType(const OptionalType* t) override { + auto* inner = t->getChild("innerType"); + return "Nullable(Of " + std::string(inner ? generate(inner) : "Object") + ")"; + } + std::string visitCustomType(const CustomType* t) override { return t->typeName; } + + // Subject 1 annotations not covered by SemannoAnnotationImpl. + std::string visitDerefStrategy(const DerefStrategy* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitOptimizationLock(const OptimizationLock* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitLangSpecific(const LangSpecific* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitDeallocateAnnotation(const DeallocateAnnotation* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitLifetimeAnnotation(const LifetimeAnnotation* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitReclaimAnnotation(const ReclaimAnnotation* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitOwnerAnnotation(const OwnerAnnotation* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitAllocateAnnotation(const AllocateAnnotation* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitHotColdAnnotation(const HotColdAnnotation* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitInlineAnnotation(const InlineAnnotation* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitPureAnnotation(const PureAnnotation* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + std::string visitConstExprAnnotation(const ConstExprAnnotation* a) override { return commentPrefix() + SemannoEmitter::emit(a); } + + std::string visitClassDeclaration(const ASTNode* node) override { + auto* cls = static_cast(node); + std::ostringstream oss; + oss << "Class " << cls->name << "\n"; + for (const auto* f : cls->getChildren("fields")) { + oss << " " << generate(f) << "\n"; + } + oss << "End Class"; + return oss.str(); + } + + std::string visitInterfaceDeclaration(const ASTNode* node) override { + auto* iface = static_cast(node); + std::ostringstream oss; + oss << "Interface " << iface->name << "\nEnd Interface"; + return oss.str(); + } + + std::string visitNamespaceDeclaration(const ASTNode* node) override { + auto* ns = static_cast(node); + std::ostringstream oss; + oss << "Module " << ns->name << "\nEnd Module"; + return oss.str(); + } + +private: + static std::string trimTrailingNewline(std::string s) { + while (!s.empty() && (s.back() == '\n' || s.back() == '\r')) s.pop_back(); + return s; + } +}; diff --git a/editor/tests/step408_test.cpp b/editor/tests/step408_test.cpp new file mode 100644 index 0000000..f934f87 --- /dev/null +++ b/editor/tests/step408_test.cpp @@ -0,0 +1,186 @@ +// Step 408: VB.NET Generator Tests (12 tests) + +#include "ast/VBNetGenerator.h" +#include "ast/Module.h" +#include "ast/Function.h" +#include "ast/Variable.h" +#include "ast/Parameter.h" +#include "ast/Statement.h" +#include "ast/Expression.h" +#include "ast/ClassDeclaration.h" +#include "ast/Type.h" +#include "ast/Annotation.h" +#include "Pipeline.h" + +#include +#include + +static int passed = 0, failed = 0; +#define TEST(name) { std::cout << " " << #name << "... "; } +#define PASS() { std::cout << "PASS\n"; ++passed; } +#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; } +#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {} + +void test_generate_sub_form() { + TEST(generate_sub_form); + Function fn("f1", "DoWork"); + fn.addChild("parameters", new Parameter("p1", "x")); + VBNetGenerator gen; + std::string out = gen.generate(&fn); + CHECK(out.find("Sub DoWork(") != std::string::npos, "expected Sub form"); + CHECK(out.find("End Sub") != std::string::npos, "expected End Sub"); + PASS(); +} + +void test_generate_function_form() { + TEST(generate_function_form); + Function fn("f1", "Sum"); + fn.addChild("parameters", new Parameter("p1", "a")); + fn.addChild("parameters", new Parameter("p2", "b")); + fn.setChild("returnType", new PrimitiveType("t1", "int")); + VBNetGenerator gen; + std::string out = gen.generate(&fn); + CHECK(out.find("Function Sum(") != std::string::npos, "expected Function form"); + CHECK(out.find("As Integer") != std::string::npos, "expected return type"); + CHECK(out.find("End Function") != std::string::npos, "expected End Function"); + PASS(); +} + +void test_generate_class() { + TEST(generate_class); + ClassDeclaration cls("c1", "Person"); + cls.addChild("fields", new Variable("v1", "Name")); + VBNetGenerator gen; + std::string out = gen.generate(&cls); + CHECK(out.find("Class Person") != std::string::npos, "expected class declaration"); + CHECK(out.find("End Class") != std::string::npos, "expected End Class"); + PASS(); +} + +void test_generate_interface() { + TEST(generate_interface); + InterfaceDeclaration iface("i1", "IWorker"); + VBNetGenerator gen; + std::string out = gen.generate(&iface); + CHECK(out.find("Interface IWorker") != std::string::npos, "expected interface declaration"); + CHECK(out.find("End Interface") != std::string::npos, "expected End Interface"); + PASS(); +} + +void test_generate_module_namespace() { + TEST(generate_module_namespace); + NamespaceDeclaration ns("n1", "Utils"); + VBNetGenerator gen; + std::string out = gen.generate(&ns); + CHECK(out.find("Module Utils") != std::string::npos, "expected module declaration"); + CHECK(out.find("End Module") != std::string::npos, "expected End Module"); + PASS(); +} + +void test_generate_dim_variable() { + TEST(generate_dim_variable); + Variable v("v1", "count"); + v.setChild("type", new PrimitiveType("t1", "int")); + v.setChild("initializer", new IntegerLiteral("i1", 5)); + VBNetGenerator gen; + std::string out = gen.generate(&v); + CHECK(out.find("Dim count As Integer = 5") != std::string::npos, "expected Dim As Integer"); + PASS(); +} + +void test_generate_if_statement() { + TEST(generate_if_statement); + IfStatement ifs; + ifs.id = "if1"; + ifs.setChild("condition", new BooleanLiteral("b1", true)); + ifs.addChild("thenBranch", new Return()); + VBNetGenerator gen; + std::string out = gen.generate(&ifs); + CHECK(out.find("If True Then") != std::string::npos, "expected If Then"); + CHECK(out.find("End If") != std::string::npos, "expected End If"); + PASS(); +} + +void test_generate_for_each() { + TEST(generate_for_each); + ForLoop loop; + loop.id = "for1"; + loop.iteratorName = "item"; + loop.setChild("iterable", new VariableReference("vr1", "items")); + VBNetGenerator gen; + std::string out = gen.generate(&loop); + CHECK(out.find("For Each item In items") != std::string::npos, "expected For Each"); + CHECK(out.find("Next") != std::string::npos, "expected Next"); + PASS(); +} + +void test_type_mapping() { + TEST(type_mapping); + VBNetGenerator gen; + PrimitiveType tInt("t1", "int"); + PrimitiveType tDouble("t2", "double"); + PrimitiveType tString("t3", "string"); + PrimitiveType tBool("t4", "bool"); + CHECK(gen.generate(&tInt) == "Integer", "int->Integer"); + CHECK(gen.generate(&tDouble) == "Double", "double->Double"); + CHECK(gen.generate(&tString) == "String", "string->String"); + CHECK(gen.generate(&tBool) == "Boolean", "bool->Boolean"); + PASS(); +} + +void test_comment_prefix() { + TEST(comment_prefix); + VBNetGenerator gen; + CHECK(gen.commentPrefix() == "' ", "expected apostrophe comment prefix"); + PASS(); +} + +void test_semanno_output() { + TEST(semanno_output); + Function fn("f1", "Compute"); + auto* risk = new RiskAnnotation(); + risk->id = "r1"; + risk->level = "high"; + fn.addChild("annotations", risk); + VBNetGenerator gen; + std::string out = gen.generate(&fn); + CHECK(out.find("@semanno:risk") != std::string::npos, "expected semanno risk output"); + PASS(); +} + +void test_pipeline_generate_routing() { + TEST(pipeline_generate_routing); + Module mod; + mod.id = "m1"; + mod.name = "demo"; + mod.addChild("functions", new Function("f1", "Main")); + Pipeline p; + std::string o1 = p.generate(&mod, "vbnet"); + std::string o2 = p.generate(&mod, "vb"); + std::string o3 = p.generate(&mod, "vb.net"); + CHECK(o1.find("Sub Main") != std::string::npos, "vbnet route"); + CHECK(o2.find("Sub Main") != std::string::npos, "vb route"); + CHECK(o3.find("Sub Main") != std::string::npos, "vb.net route"); + PASS(); +} + +int main() { + std::cout << "Step 408: VB.NET Generator Tests\n"; + + test_generate_sub_form(); // 1 + test_generate_function_form(); // 2 + test_generate_class(); // 3 + test_generate_interface(); // 4 + test_generate_module_namespace(); // 5 + test_generate_dim_variable(); // 6 + test_generate_if_statement(); // 7 + test_generate_for_each(); // 8 + test_type_mapping(); // 9 + test_comment_prefix(); // 10 + test_semanno_output(); // 11 + test_pipeline_generate_routing(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 2645e05..574c000 100644 --- a/progress.md +++ b/progress.md @@ -3773,6 +3773,53 @@ core block forms and declarations. Covers `Sub`/`Function`, `Class`, - `editor/src/MCPServer.h` (`1679` > `600`) - `editor/src/HeadlessAgentRPCHandler.h` (`2623` > `600`) +### Step 408: VB.NET Generator +**Status:** PASS (12/12 tests) + +Added a dedicated VB.NET generator and pipeline generation routing aliases. +Outputs VB-style `Sub`/`Function` blocks, `Dim` declarations, `If...Then...End If`, +`For Each...Next`, and .NET type-name mappings. + +**Files created:** +- `editor/src/ast/VBNetGenerator.h` — VB.NET generation support: + - module/function/class/interface/module-namespace generation + - `Sub` vs `Function ... As Type` emission + - `Dim name As Type = value` variable emission + - `If ... Then ... End If` and `For Each ... In ... Next` + - .NET type mapping (`Integer`, `Double`, `String`, `Boolean`, `Object`) + - Semanno output with VB comment prefix (`' `) +- `editor/tests/step408_test.cpp` — 12 tests covering: + 1. Sub generation + 2. Function generation with return type + 3. class generation + 4. interface generation + 5. module/namespace generation + 6. Dim variable generation + 7. If generation + 8. For Each generation + 9. type mapping + 10. comment prefix + 11. Semanno annotation output + 12. pipeline generate routing for `vbnet` / `vb` / `vb.net` + +**Files modified:** +- `editor/src/ast/Generator.h` — include `VBNetGenerator.h` +- `editor/src/Pipeline.h` — generate routing for `vbnet`, `vb`, `vb.net` +- `editor/CMakeLists.txt` — `step408_test` target + +**Verification run:** +- `step408_test` — PASS (12/12) new step coverage +- `step407_test` — PASS (12/12) regression coverage +- `step406_test` — PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/ast/VBNetGenerator.h` within header-size limit (`261` <= `600`) +- `editor/tests/step408_test.cpp` within test-file size guidance (`186` lines) +- `editor/src/Pipeline.h` within header-size limit (`247` <= `600`) +- Legacy oversized headers persist: + - `editor/src/MCPServer.h` (`1679` > `600`) + - `editor/src/HeadlessAgentRPCHandler.h` (`2623` > `600`) + # Roadmap Planning — Sprints 12-25+ ## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)