diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index fc9da61..041524c 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2147,4 +2147,8 @@ target_link_libraries(step361_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step362_test tests/step362_test.cpp) +target_include_directories(step362_test PRIVATE src) +target_link_libraries(step362_test PRIVATE nlohmann_json::nlohmann_json) + # 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 667aa8f..af4adde 100644 --- a/editor/src/Pipeline.h +++ b/editor/src/Pipeline.h @@ -159,6 +159,9 @@ public: } else if (language == "csharp") { CSharpGenerator gen; return gen.generate(ast); + } else if (language == "c") { + CGenerator gen; + return gen.generate(ast); } return ""; } diff --git a/editor/src/ast/CGenerator.h b/editor/src/ast/CGenerator.h new file mode 100644 index 0000000..6fb9cf2 --- /dev/null +++ b/editor/src/ast/CGenerator.h @@ -0,0 +1,108 @@ +#pragma once +#include "ClassDeclaration.h" +#include "GenericType.h" +#include "AsyncNodes.h" +#include "CppGenerator.h" + +class CGenerator : public CppGenerator { +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; + for (auto* s : module->getChildren("statements")) oss << generate(s) << "\n"; + if (!module->getChildren("statements").empty()) oss << "\n"; + for (auto* c : module->getChildren("classes")) oss << generate(c) << "\n"; + for (auto* v : module->getChildren("variables")) oss << generate(v) << "\n"; + if (!module->getChildren("variables").empty()) oss << "\n"; + for (auto* f : module->getChildren("functions")) oss << generate(f) << "\n"; + return oss.str(); + } + + std::string visitClassDeclaration(const ASTNode* node) override { + auto* cls = static_cast(node); + std::ostringstream oss; + oss << "typedef struct " << cls->name << " {\n"; + for (auto* f : cls->getChildren("fields")) { + std::string line = generate(f); + if (!line.empty() && line.back() == ';') line.pop_back(); + oss << " " << line << ";\n"; + } + oss << "} " << cls->name << ";"; + return oss.str(); + } + + std::string visitMethodDeclaration(const ASTNode* node) override { + auto* meth = static_cast(node); + std::ostringstream oss; + oss << "void " << meth->className << "_" << meth->name << "("; + auto params = meth->getChildren("parameters"); + for (size_t i = 0; i < params.size(); ++i) { + if (i > 0) oss << ", "; + oss << visitParameter(static_cast(params[i])); + } + oss << ") {\n"; + for (auto* s : meth->getChildren("body")) oss << " " << generate(s) << "\n"; + oss << "}"; + return oss.str(); + } + + std::string visitNullLiteral(const NullLiteral* /*lit*/) override { + return "NULL"; + } + + std::string visitStringLiteral(const StringLiteral* lit) override { + return "\"" + lit->value + "\""; + } + + std::string visitTypeAlias(const ASTNode* node) override { + auto* ta = static_cast(node); + return "typedef " + ta->targetType + " " + ta->aliasName + ";"; + } + + std::string visitEnumDeclaration(const ASTNode* node) override { + auto* e = static_cast(node); + std::ostringstream oss; + oss << "enum " << e->name << " { "; + auto members = e->getChildren("members"); + for (size_t i = 0; i < members.size(); ++i) { + auto* m = static_cast(members[i]); + if (i > 0) oss << ", "; + oss << m->name; + if (!m->value.empty()) oss << " = " << m->value; + } + oss << " };"; + return oss.str(); + } + + std::string visitIncludeDirective(const ASTNode* node) override { + auto* inc = static_cast(node); + if (inc->isSystem) return "#include <" + inc->path + ">"; + return "#include \"" + inc->path + "\""; + } + + std::string visitPragmaDirective(const ASTNode* node) override { + auto* p = static_cast(node); + return "#pragma " + p->directive; + } + + std::string visitMacroDefinition(const ASTNode* node) override { + auto* m = static_cast(node); + std::ostringstream oss; + oss << "#define " << m->name; + if (m->isFunctionLike) { + oss << "("; + for (size_t i = 0; i < m->parameters.size(); ++i) { + if (i > 0) oss << ", "; + oss << m->parameters[i]; + } + oss << ")"; + } + if (!m->body.empty()) oss << " " << m->body; + return oss.str(); + } +}; diff --git a/editor/src/ast/Generator.h b/editor/src/ast/Generator.h index 68b35f2..9171f36 100644 --- a/editor/src/ast/Generator.h +++ b/editor/src/ast/Generator.h @@ -11,3 +11,4 @@ #include "GoGenerator.h" #include "KotlinGenerator.h" #include "CSharpGenerator.h" +#include "CGenerator.h" diff --git a/editor/tests/step362_test.cpp b/editor/tests/step362_test.cpp new file mode 100644 index 0000000..e2caed5 --- /dev/null +++ b/editor/tests/step362_test.cpp @@ -0,0 +1,164 @@ +// Step 362: C Generator (12 tests) + +#include +#include +#include +#include "ast/CGenerator.h" +#include "Pipeline.h" +#include "ast/ClassDeclaration.h" +#include "ast/EnumNamespaceNodes.h" +#include "ast/PreprocessorNodes.h" +#include "ast/Annotation.h" + +int main() { + int passed = 0; + + // Test 1: function generation + { + CGenerator gen; + Function fn("f1", "add"); + fn.setChild("returnType", new PrimitiveType("t1", "int")); + fn.addChild("parameters", new Parameter("p1", "x")); + auto* ret = new Return(); + ret->id = "r1"; + ret->setChild("value", new IntegerLiteral("i1", 1)); + fn.addChild("body", ret); + auto out = gen.generate(&fn); + assert(out.find("int add(") != std::string::npos); + assert(out.find("return 1;") != std::string::npos); + std::cout << "Test 1 PASSED: function generation\n"; + passed++; + } + + // Test 2: struct output from class + { + CGenerator gen; + ClassDeclaration cls("c1", "Point"); + auto* x = new Variable("v1", "x"); + x->setChild("type", new PrimitiveType("t1", "int")); + cls.addChild("fields", x); + auto out = gen.generate(&cls); + assert(out.find("typedef struct Point") != std::string::npos); + assert(out.find("int x;") != std::string::npos); + std::cout << "Test 2 PASSED: struct output\n"; + passed++; + } + + // Test 3: typedef output + { + CGenerator gen; + TypeAlias ta("ta1", "Count", "unsigned int", false); + auto out = gen.generate(&ta); + assert(out == "typedef unsigned int Count;"); + std::cout << "Test 3 PASSED: typedef output\n"; + passed++; + } + + // Test 4: enum output + { + CGenerator gen; + EnumDeclaration e("e1", "Color"); + e.addChild("members", new EnumMember("m1", "RED", "1")); + e.addChild("members", new EnumMember("m2", "GREEN", "2")); + auto out = gen.generate(&e); + assert(out.find("enum Color") != std::string::npos); + assert(out.find("RED = 1") != std::string::npos); + std::cout << "Test 4 PASSED: enum output\n"; + passed++; + } + + // Test 5: preprocessor output + { + CGenerator gen; + IncludeDirective inc("i1", "stdio.h", true); + MacroDefinition mac("m1", "MAX"); + mac.body = "100"; + auto a = gen.generate(&inc); + auto b = gen.generate(&mac); + assert(a == "#include "); + assert(b.find("#define MAX 100") != std::string::npos); + std::cout << "Test 5 PASSED: preprocessor output\n"; + passed++; + } + + // Test 6: pointer type preserved + { + CGenerator gen; + Variable v("v1", "name"); + v.setChild("type", new PrimitiveType("t1", "char*")); + auto out = gen.generate(&v); + assert(out.find("char* name;") != std::string::npos); + std::cout << "Test 6 PASSED: pointer type\n"; + passed++; + } + + // Test 7: owner/manual annotation comment + { + CGenerator gen; + Function fn("f1", "alloc"); + fn.setChild("returnType", new PrimitiveType("t1", "void*")); + auto* owner = new OwnerAnnotation("a1", "Manual"); + fn.addChild("annotations", owner); + auto out = gen.generate(&fn); + assert(out.find("@owner") != std::string::npos || out.find("Owner") != std::string::npos); + std::cout << "Test 7 PASSED: owner annotation comment\n"; + passed++; + } + + // Test 8: class (python-like) to c struct generation + { + CGenerator gen; + ClassDeclaration cls("c1", "Widget"); + auto out = gen.generate(&cls); + assert(out.find("typedef struct Widget") != std::string::npos); + std::cout << "Test 8 PASSED: class to struct generation\n"; + passed++; + } + + // Test 9: c++ method to c-style function + { + CGenerator gen; + MethodDeclaration m("m1", "render"); + m.className = "Widget"; + auto out = gen.generate(&m); + assert(out.find("Widget_render(") != std::string::npos); + std::cout << "Test 9 PASSED: method to c function naming\n"; + passed++; + } + + // Test 10: commentPrefix + { + CGenerator gen; + assert(gen.commentPrefix() == "// "); + std::cout << "Test 10 PASSED: comment prefix\n"; + passed++; + } + + // Test 11: C string literal + null + { + CGenerator gen; + StringLiteral s("s1", "abc"); + NullLiteral n; + auto a = gen.generate(&s); + auto b = gen.generate(&n); + assert(a == "\"abc\""); + assert(b == "NULL"); + std::cout << "Test 11 PASSED: C string/null literal\n"; + passed++; + } + + // Test 12: pipeline generate route for c + { + Pipeline p; + Function fn("f1", "work"); + std::string out = p.generate(&fn, "c"); + assert(!out.empty()); + assert(out.find("work(") != std::string::npos); + std::cout << "Test 12 PASSED: pipeline generate route c\n"; + passed++; + } + + std::cout << "\nResults: " << passed << "/12\n"; + assert(passed == 12); + return 0; +} diff --git a/progress.md b/progress.md index 3c645ae..388acff 100644 --- a/progress.md +++ b/progress.md @@ -2085,6 +2085,44 @@ pipeline entry point in Sprint 14. - `step361_test` — PASS (12/12) new step coverage - `step360_test` — PASS (8/8) regression coverage +### Step 362: C Generator +**Status:** PASS (12/12 tests) + +Added a dedicated C generator and routed pipeline generation for `"c"`. +The generator emits C-oriented forms for structs, enums, typedefs, macros, +includes, and method-name lowering for class-style methods. + +**Files created:** +- `editor/src/ast/CGenerator.h` — C generation support: + - module emission including statements/classes/functions + - `ClassDeclaration` -> `typedef struct ...` + - `MethodDeclaration` -> `ClassName_method(...)` naming + - C null literal output (`NULL`) + - C preprocessor and enum/type-alias output +- `editor/tests/step362_test.cpp` — 12 tests covering: + 1. function generation + 2. struct output + 3. typedef output + 4. enum output + 5. preprocessor output + 6. pointer type output + 7. owner/manual annotation comment presence + 8. class-to-struct generation + 9. method-to-C function naming + 10. comment prefix + 11. string/null literal behavior + 12. pipeline `"c"` generation route + +**Files modified:** +- `editor/src/ast/Generator.h` — include `CGenerator.h` +- `editor/src/Pipeline.h` — add generate routing for language `"c"` +- `editor/CMakeLists.txt` — `step362_test` target + +**Verification run:** +- `step362_test` — PASS (12/12) new step coverage +- `step361_test` — PASS (12/12) regression coverage +- `step360_test` — PASS (8/8) regression coverage + # Roadmap Planning — Sprints 12-25+ ## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)