diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 8a11757..384a340 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2560,4 +2560,13 @@ target_link_libraries(step408_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step409_test tests/step409_test.cpp) +target_include_directories(step409_test PRIVATE src) +target_link_libraries(step409_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/tests/step409_test.cpp b/editor/tests/step409_test.cpp new file mode 100644 index 0000000..a375888 --- /dev/null +++ b/editor/tests/step409_test.cpp @@ -0,0 +1,153 @@ +// Step 409: .NET Integration Tests (8 tests) + +#include "Pipeline.h" +#include "ast/Annotation.h" +#include "ast/Function.h" +#include "ast/Module.h" +#include "ast/Parameter.h" + +#include +#include +#include + +static int passed = 0; +static int 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_csharp_to_fsharp_generation() { + TEST(csharp_to_fsharp_generation); + Pipeline p; + std::string src = R"( +public int Add(int a, int b) { + return a + b; +} +)"; + auto result = p.run(src, "csharp", "fsharp"); + CHECK(result.success, "pipeline run failed"); + CHECK(!result.generatedCode.empty(), "generated code empty"); + CHECK(result.generatedCode.find("let Add") != std::string::npos, "expected F# let output"); + PASS(); +} + +void test_fsharp_to_csharp_generation() { + TEST(fsharp_to_csharp_generation); + Pipeline p; + std::string src = "let add x y = x + y\n"; + auto result = p.run(src, "fsharp", "csharp"); + CHECK(result.success, "pipeline run failed"); + CHECK(result.generatedCode.find("public void add") != std::string::npos || + result.generatedCode.find("public void Add") != std::string::npos, + "expected C# method output"); + PASS(); +} + +void test_csharp_to_vb_generation() { + TEST(csharp_to_vb_generation); + Pipeline p; + std::string src = R"( +public void Main() { +} +)"; + auto result = p.run(src, "csharp", "vbnet"); + CHECK(result.success, "pipeline run failed"); + CHECK(result.generatedCode.find("Sub Main") != std::string::npos, "expected VB Sub"); + PASS(); +} + +void test_vb_to_fsharp_generation() { + TEST(vb_to_fsharp_generation); + Pipeline p; + std::string src = R"( +Function Sum(a As Integer, b As Integer) As Integer +End Function +)"; + auto result = p.run(src, "vbnet", "fsharp"); + CHECK(result.success, "pipeline run failed"); + CHECK(result.generatedCode.find("let Sum") != std::string::npos, "expected F# let"); + PASS(); +} + +void test_python_to_fsharp_generation() { + TEST(python_to_fsharp_generation); + Pipeline p; + std::string src = R"( +def greet(name): + return name +)"; + auto result = p.run(src, "python", "fsharp"); + CHECK(result.success, "pipeline run failed"); + CHECK(result.generatedCode.find("let greet") != std::string::npos, "expected F# function"); + PASS(); +} + +void test_pipeline_alias_routes_for_dotnet() { + TEST(pipeline_alias_routes_for_dotnet); + Pipeline p; + std::vector d1, d2, d3, d4; + auto m1 = p.parse("let add x y = x + y\n", "f#", d1); + auto m2 = p.parse("let add x y = x + y\n", "fs", d2); + auto m3 = p.parse("Sub Main()\nEnd Sub\n", "vb", d3); + auto m4 = p.parse("Sub Main()\nEnd Sub\n", "vb.net", d4); + CHECK(m1 != nullptr && m2 != nullptr && m3 != nullptr && m4 != nullptr, "alias parse route failed"); + CHECK(m1->targetLanguage == "fsharp", "f# alias target mismatch"); + CHECK(m3->targetLanguage == "vbnet", "vb alias target mismatch"); + PASS(); +} + +void test_shared_type_annotations_emit_for_dotnet_targets() { + TEST(shared_type_annotations_emit_for_dotnet_targets); + Module mod; + mod.id = "m1"; + mod.name = "anno_mod"; + auto* fn = new Function("f1", "Convert"); + auto* t = new TypeStateAnnotation(); + t->id = "a1"; + t->state = "reified"; + fn->addChild("annotations", t); + mod.addChild("functions", fn); + + Pipeline p; + std::string fsharpOut = p.generate(&mod, "fsharp"); + std::string vbOut = p.generate(&mod, "vbnet"); + std::string csharpOut = p.generate(&mod, "csharp"); + + CHECK(fsharpOut.find("@semanno:typestate") != std::string::npos, "fsharp semanno missing"); + CHECK(vbOut.find("@semanno:typestate") != std::string::npos, "vb semanno missing"); + CHECK(csharpOut.find("@semanno:typestate") != std::string::npos, "csharp semanno missing"); + PASS(); +} + +void test_pipeline_run_both_new_languages() { + TEST(pipeline_run_both_new_languages); + Pipeline p; + + auto fsResult = p.run("let inc x = x + 1\n", "fsharp", "fsharp"); + CHECK(fsResult.success, "fsharp run failed"); + CHECK(!fsResult.generatedCode.empty(), "fsharp generated empty"); + + auto vbResult = p.run("Sub Main()\nEnd Sub\n", "vbnet", "vbnet"); + CHECK(vbResult.success, "vbnet run failed"); + CHECK(!vbResult.generatedCode.empty(), "vbnet generated empty"); + PASS(); +} + +int main() { + std::cout << "Step 409: .NET Integration Tests\n"; + + test_csharp_to_fsharp_generation(); // 1 + test_fsharp_to_csharp_generation(); // 2 + test_csharp_to_vb_generation(); // 3 + test_vb_to_fsharp_generation(); // 4 + test_python_to_fsharp_generation(); // 5 + test_pipeline_alias_routes_for_dotnet(); // 6 + test_shared_type_annotations_emit_for_dotnet_targets(); // 7 + test_pipeline_run_both_new_languages(); // 8 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 574c000..1433100 100644 --- a/progress.md +++ b/progress.md @@ -3820,6 +3820,47 @@ Outputs VB-style `Sub`/`Function` blocks, `Dim` declarations, `If...Then...End I - `editor/src/MCPServer.h` (`1679` > `600`) - `editor/src/HeadlessAgentRPCHandler.h` (`2623` > `600`) +### Step 409: .NET Integration +**Status:** PASS (8/8 tests) + +Added phase-level .NET integration validation across C#, F#, and VB.NET parse +and generation routes, including cross-language projection checks and shared +Semanno annotation behavior across all 3 .NET-targeted outputs. + +**Files created:** +- `editor/tests/step409_test.cpp` — 8 integration tests covering: + 1. C# -> F# generation path + 2. F# -> C# generation path + 3. C# -> VB.NET generation path + 4. VB.NET -> F# generation path + 5. Python -> F# generation path + 6. parse alias routing (`f#`, `fs`, `vb`, `vb.net`) + 7. shared type-annotation Semanno emission across C#/F#/VB targets + 8. `Pipeline.run()` success for both new languages as source/target + +**Files modified:** +- `editor/CMakeLists.txt` — `step409_test` target + +**Verification run:** +- `step409_test` — PASS (8/8) new integration coverage +- `step408_test` — PASS (12/12) regression coverage +- `step407_test` — PASS (12/12) regression coverage + +**Phase 17a completion snapshot (Steps 405-409):** +- Step 405: F# parser (12 tests) +- Step 406: F# generator (12 tests) +- Step 407: VB.NET parser (12 tests) +- Step 408: VB.NET generator (12 tests) +- Step 409: .NET integration (8 tests) + +**Architecture gate check (end of Phase 17a):** +- `editor/tests/step409_test.cpp` within test-file size guidance (`153` lines) +- `editor/src/ast/VBNetGenerator.h` within header-size limit (`261` <= `600`) +- `editor/src/ast/VBNetParser.h` within header-size limit (`184` <= `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)