diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index ebe5c88..d34afbc 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2155,4 +2155,13 @@ add_executable(step363_test tests/step363_test.cpp) target_include_directories(step363_test PRIVATE src) target_link_libraries(step363_test PRIVATE nlohmann_json::nlohmann_json) +add_executable(step364_test tests/step364_test.cpp) +target_include_directories(step364_test PRIVATE src) +target_link_libraries(step364_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/CrossLanguageProjector.h b/editor/src/CrossLanguageProjector.h index 4479462..bf26031 100644 --- a/editor/src/CrossLanguageProjector.h +++ b/editor/src/CrossLanguageProjector.h @@ -52,6 +52,14 @@ public: } } + // Copy classes and statements for cross-language structural projection. + for (auto* child : source->getChildren("classes")) { + if (ASTNode* n = cloneNode(child, targetLanguage)) mod->addChild("classes", n); + } + for (auto* child : source->getChildren("statements")) { + if (ASTNode* n = cloneNode(child, targetLanguage)) mod->addChild("statements", n); + } + // Copy module-level variables for (auto* child : source->getChildren("variables")) { if (child->conceptType == "Variable") { @@ -539,7 +547,10 @@ private: return cloneAnnotation(node, targetLanguage); } - return nullptr; + // Generic clone fallback for declaration nodes not yet specialized. + json nodeJson = toJson(node); + nodeJson["id"] = node->id + "_proj"; + return fromJson(nodeJson); } static std::string adaptReclaimStrategy(const std::string& strategy, diff --git a/editor/tests/step364_test.cpp b/editor/tests/step364_test.cpp new file mode 100644 index 0000000..9db76b8 --- /dev/null +++ b/editor/tests/step364_test.cpp @@ -0,0 +1,218 @@ +// Step 364: C Integration + Self-Hosting Prep (8 tests) + +#include +#include +#include +#include +#include "Pipeline.h" +#include "AnnotationInference.h" +#include "MemoryStrategyInference.h" +#include "CrossLanguageProjector.h" +#include "MCPServer.h" +#include "ast/CParser.h" +#include "ast/CGenerator.h" +#include "ast/CppGenerator.h" +#include "ast/PythonGenerator.h" +#include "ast/RustGenerator.h" + +static bool contains(const std::string& text, const std::string& token) { + return text.find(token) != std::string::npos; +} + +static bool hasSuggestion(const std::vector& suggestions, + const std::string& annotationType, + const std::string& strategy) { + for (const auto& s : suggestions) { + if (s.annotationType == annotationType && s.strategy == strategy) return true; + } + return false; +} + +static bool hasInference(const std::vector& inferred, + const std::string& annotationType, + const std::string& value) { + for (const auto& a : inferred) { + if (a.annotationType == annotationType && a.value == value) return true; + } + return false; +} + +int main() { + int passed = 0; + + // Test 1: Parse C -> generate C -> reparse keeps core declarations + { + std::string src = "typedef struct Point { int x; int y; } Point;\\n" + "int add(int a, int b) { return a + b; }\\n"; + auto mod = CParser::parseC(src); + CGenerator gen; + std::string out = gen.generate(mod.get()); + auto roundtrip = CParser::parseC(out); + assert(!roundtrip->getChildren("classes").empty()); + assert(contains(out, "typedef struct Point")); + std::cout << "Test 1 PASSED: C parse/generate roundtrip keeps declarations\\n"; + passed++; + } + + // Test 2: C struct projects as Python class shape + { + auto* cls = new ClassDeclaration("c1", "Point"); + auto* x = new Variable("v1", "x"); + x->setChild("type", new PrimitiveType("t1", "int")); + cls->addChild("fields", x); + + PythonGenerator py; + std::string out = py.generate(cls); + assert(contains(out, "class Point")); + assert(contains(out, "x")); + std::cout << "Test 2 PASSED: C struct emits Python class form\\n"; + passed++; + } + + // Test 3: Python-style class emits C struct + constructor-style function + { + auto* cls = new ClassDeclaration("c1", "Widget"); + auto* field = new Variable("v1", "value"); + field->setChild("type", new PrimitiveType("t1", "int")); + cls->addChild("fields", field); + auto* init = new MethodDeclaration("m1", "init"); + init->className = "Widget"; + init->setChild("returnType", new PrimitiveType("rt", "void")); + cls->addChild("methods", init); + CGenerator cgen; + std::string outStruct = cgen.generate(cls); + std::string outCtor = cgen.generate(init); + assert(contains(outStruct, "typedef struct Widget")); + assert(contains(outCtor, "Widget_init(")); + std::cout << "Test 3 PASSED: Python class shape + constructor-style method generation\\n"; + passed++; + } + + // Test 4: C manual ownership projects to Rust ownership semantics + { + Module cmod("m1", "cmod", "c"); + auto* fn = new Function("f1", "make"); + fn->addChild("annotations", new OwnerAnnotation("a1", "Manual")); + cmod.addChild("functions", fn); + + CrossLanguageProjector projector; + auto rustMod = projector.project(&cmod, "rust"); + auto* rustFn = static_cast(rustMod->getChildren("functions")[0]); + + bool hasBox = false; + for (auto* a : rustFn->getChildren("annotations")) { + if (a->conceptType == "OwnerAnnotation") { + auto* oa = static_cast(a); + hasBox = hasBox || (oa->strategy == "Box"); + } + } + assert(hasBox); + + RustGenerator rust; + std::string out = rust.generate(rustMod.get()); + assert(contains(out, "@owner(Box)")); + std::cout << "Test 4 PASSED: C manual owner adapts to Rust Box\\n"; + passed++; + } + + // Test 5: Pipeline.run C->C++ succeeds and emits C++ translation unit shell + { + Pipeline p; + std::string src = "struct Point { int x; int y; };\\n"; + auto result = p.run(src, "c", "cpp"); + assert(result.success); + assert(!result.generatedCode.empty()); + assert(contains(result.generatedCode, "namespace")); + std::cout << "Test 5 PASSED: pipeline C->C++ run succeeds with generated output\\n"; + passed++; + } + + // Test 6: C inference captures malloc + array access + pointer parameter + { + Module mod("m1", "infer", "c"); + auto* fn = new Function("f1", "process"); + auto* param = new Parameter("p1", "cb"); + param->setChild("type", new PrimitiveType("pt", "void (*)(int)")); + fn->addChild("parameters", param); + + auto* allocCall = new FunctionCall(); + allocCall->id = "fc1"; + allocCall->functionName = "malloc"; + fn->addChild("body", allocCall); + + auto* idx = new IndexAccess(); + idx->id = "idx1"; + idx->setChild("target", new VariableReference("vr1", "arr")); + idx->setChild("index", new VariableReference("vr2", "i")); + auto* stmt = new ExpressionStatement(); + stmt->id = "s1"; + stmt->setChild("expression", idx); + fn->addChild("body", stmt); + mod.addChild("functions", fn); + + MemoryStrategyInference mem; + AnnotationInference anno; + auto suggestions = mem.inferAnnotations(&mod); + auto inferred = anno.inferAll(&mod); + + assert(hasSuggestion(suggestions, "OwnerAnnotation", "Manual")); + assert(hasSuggestion(suggestions, "OwnerAnnotation", "Borrowed")); + assert(hasInference(inferred, "BoundsCheckAnnotation", "unchecked")); + std::cout << "Test 6 PASSED: C inference covers malloc/array/pointer patterns\\n"; + passed++; + } + + // Test 7: Parse simplified C header from dependency-style source + { + std::string header = + "#ifndef TINY_HDR_H\\n" + "#define TINY_HDR_H\\n" + "#include \\n" + "typedef struct Tiny { int size; } Tiny;\\n" + "enum TinyMode { TinyFast = 1, TinySafe = 2 };\\n" + "#endif\\n"; + + auto mod = CParser::parseC(header); + assert(!mod->getChildren("classes").empty()); + assert(!mod->getChildren("statements").empty()); + std::cout << "Test 7 PASSED: simplified C header parses into expected AST\\n"; + passed++; + } + + // Test 8: MCP tools list stable and pipeline tool accepts language parameter + { + MCPServer server; + auto tools = server.getTools(); + const size_t expectedCount = tools.size(); + + json req = { + {"jsonrpc", "2.0"}, + {"id", 1}, + {"method", "tools/list"}, + {"params", json::object()} + }; + auto resp = server.handleRequest(req); + assert(resp.contains("result")); + assert(resp["result"].contains("tools")); + auto listed = resp["result"]["tools"]; + assert(listed.is_array()); + assert(listed.size() == expectedCount); + + bool hasRunPipeline = false; + for (const auto& t : listed) { + if (t.value("name", "") == "whetstone_run_pipeline") { + hasRunPipeline = true; + auto props = t["inputSchema"]["properties"]; + assert(props.contains("sourceLanguage")); + assert(props["sourceLanguage"]["type"] == "string"); + } + } + assert(hasRunPipeline); + std::cout << "Test 8 PASSED: MCP tools stable; run_pipeline accepts language string\\n"; + passed++; + } + + std::cout << "\\nResults: " << passed << "/8\\n"; + assert(passed == 8); + return 0; +} diff --git a/progress.md b/progress.md index 6238405..444b2de 100644 --- a/progress.md +++ b/progress.md @@ -2168,6 +2168,41 @@ gets meaningful defaults and risk signals aligned with explicit-memory patterns. refactor by extracting target adaptation logic into `editor/src/ProjectionAdaptation.h` +### Step 364: C Integration + Self-Hosting Prep +**Status:** PASS (8/8 tests) + +Added phase-level integration coverage for the C pipeline and projection paths, +including pipeline execution, C parser/generator roundtrip checks, ownership +projection behavior, and MCP tool contract stability. + +**Files created:** +- `editor/tests/step364_test.cpp` — 8 integration tests covering: + 1. C parse -> generate -> reparse declaration continuity + 2. C struct -> Python class-form generation + 3. Python class shape -> C struct + constructor-style method generation + 4. C manual ownership -> Rust `Owner(Box)` adaptation + 5. `Pipeline.run(..., \"c\", \"cpp\")` success path with generated output + 6. Combined C inference: malloc + pointer parameter + unchecked index access + 7. simplified dependency-style C header parsing + 8. MCP tools/list stability + run_pipeline language schema acceptance + +**Files modified:** +- `editor/src/CrossLanguageProjector.h` — integration support: + - copy `classes` and `statements` roles during projection + - generic serialization fallback clone for unhandled declaration nodes +- `editor/CMakeLists.txt` — `step364_test` target with parser-linked test deps + +**Verification run:** +- `step364_test` — PASS (8/8) new step coverage +- `step363_test` — PASS (12/12) regression coverage +- `step362_test` — PASS (12/12) regression coverage +- `step361_test` — PASS (12/12) regression coverage +- `step360_test` — PASS (8/8) regression coverage + +**Architecture gate check:** +- `editor/src/CrossLanguageProjector.h` remains within header size limit + (`596` lines <= `600`) + # Roadmap Planning — Sprints 12-25+ ## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)