diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index e060291..31e477b 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -2254,4 +2254,13 @@ target_link_libraries(step374_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step375_test tests/step375_test.cpp) +target_include_directories(step375_test PRIVATE src) +target_link_libraries(step375_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 8c05b44..9920c46 100644 --- a/editor/src/Pipeline.h +++ b/editor/src/Pipeline.h @@ -182,6 +182,9 @@ public: language == "lisp" || language == "cl") { CommonLispGenerator gen; return gen.generate(ast); + } else if (language == "scheme" || language == "scm") { + SchemeGenerator gen; + return gen.generate(ast); } return ""; } diff --git a/editor/src/ast/Generator.h b/editor/src/ast/Generator.h index e110385..ba0748f 100644 --- a/editor/src/ast/Generator.h +++ b/editor/src/ast/Generator.h @@ -14,3 +14,4 @@ #include "CGenerator.h" #include "WatGenerator.h" #include "CommonLispGenerator.h" +#include "SchemeGenerator.h" diff --git a/editor/src/ast/SchemeGenerator.h b/editor/src/ast/SchemeGenerator.h new file mode 100644 index 0000000..cda87bb --- /dev/null +++ b/editor/src/ast/SchemeGenerator.h @@ -0,0 +1,159 @@ +#pragma once + +#include "CommonLispGenerator.h" + +class SchemeGenerator : public CommonLispGenerator { +public: + 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* var : module->getChildren("variables")) { + oss << generate(var) << "\n"; + } + if (!module->getChildren("variables").empty()) oss << "\n"; + + for (auto* cls : module->getChildren("classes")) { + oss << generate(cls) << "\n\n"; + } + + for (auto* fn : module->getChildren("functions")) { + oss << generate(fn) << "\n\n"; + } + return oss.str(); + } + + std::string visitFunction(const Function* function) override { + std::ostringstream oss; + emitAnnotations(oss, function); + + oss << "(define (" << function->name; + for (auto* pNode : function->getChildren("parameters")) { + auto* p = static_cast(pNode); + oss << " " << p->name; + } + oss << ")"; + + auto body = function->getChildren("body"); + if (body.empty()) { + oss << " '())"; + return oss.str(); + } + + for (auto* stmt : body) { + oss << " " << generate(stmt); + } + oss << ")"; + return oss.str(); + } + + std::string visitVariable(const Variable* variable) override { + std::ostringstream oss; + oss << "(define " << variable->name; + auto* val = variable->getChild("value"); + if (val) oss << " " << generate(val); + else oss << " '()"; + oss << ")"; + return oss.str(); + } + + std::string visitBlock(const Block* block) override { + std::vector vars; + std::vector body; + for (auto* stmt : block->getChildren("statements")) { + if (stmt->conceptType == "Variable") vars.push_back(static_cast(stmt)); + else body.push_back(stmt); + } + + std::ostringstream oss; + if (!vars.empty()) { + oss << "(let ("; + for (size_t i = 0; i < vars.size(); ++i) { + if (i > 0) oss << " "; + oss << "(" << vars[i]->name; + auto* init = vars[i]->getChild("value"); + if (init) oss << " " << generate(init); + oss << ")"; + } + oss << ")"; + for (auto* stmt : body) oss << " " << generate(stmt); + oss << ")"; + return oss.str(); + } + + oss << "(begin"; + for (auto* stmt : body) oss << " " << generate(stmt); + oss << ")"; + return oss.str(); + } + + std::string visitBooleanLiteral(const BooleanLiteral* lit) override { + return lit->value ? "#t" : "#f"; + } + + std::string visitNullLiteral(const NullLiteral* lit) override { + (void)lit; + return "'()"; + } + + std::string visitFunctionCall(const FunctionCall* call) override { + bool continuation = false; + for (auto* anno : call->getChildren("annotations")) { + if (anno->conceptType == "ExecAnnotation") { + auto* ex = static_cast(anno); + if (ex->mode == "continuation") continuation = true; + } + } + + std::string fnName = call->functionName; + if (continuation || fnName == "call-with-current-continuation") { + fnName = "call/cc"; + } + + std::ostringstream oss; + oss << "(" << fnName; + for (auto* arg : call->getChildren("arguments")) { + oss << " " << generate(arg); + } + oss << ")"; + return oss.str(); + } + + std::string visitClassDeclaration(const ASTNode* node) override { + auto* cls = static_cast(node); + std::ostringstream oss; + oss << "(define-record-type " << cls->name << " (fields"; + for (auto* f : cls->getChildren("fields")) { + oss << " " << static_cast(f)->name; + } + oss << "))"; + return oss.str(); + } + + std::string visitOwnerAnnotation(const OwnerAnnotation* annotation) override { + return "; @owner(strategy=" + annotation->strategy + ")"; + } + + std::string visitTailCallAnnotation(const TailCallAnnotation* annotation) override { + (void)annotation; + return "; @tailcall()"; + } + + std::string visitExecAnnotation(const ExecAnnotation* annotation) override { + std::ostringstream oss; + oss << "; @exec(mode=" << annotation->mode; + if (!annotation->runtimeHint.empty()) oss << ",runtimeHint=" << annotation->runtimeHint; + oss << ")"; + return oss.str(); + } + +private: + void emitAnnotations(std::ostringstream& oss, const ASTNode* node) { + for (auto* anno : node->getChildren("annotations")) { + std::string text = generate(anno); + if (!text.empty()) oss << text << "\n"; + } + } +}; diff --git a/editor/tests/step375_test.cpp b/editor/tests/step375_test.cpp new file mode 100644 index 0000000..2f21894 --- /dev/null +++ b/editor/tests/step375_test.cpp @@ -0,0 +1,172 @@ +// Step 375: Scheme Generator (12 tests) + +#include +#include +#include +#include "Pipeline.h" +#include "ast/SchemeGenerator.h" +#include "ast/SchemeParser.h" + +static int parenBalance(const std::string& s) { + int d = 0; + for (char c : s) { + if (c == '(') ++d; + if (c == ')') --d; + } + return d; +} + +int main() { + int passed = 0; + + // Test 1: define function generation + { + SchemeGenerator gen; + Function fn("f1", "add"); + fn.addChild("parameters", new Parameter("p1", "x")); + fn.addChild("parameters", new Parameter("p2", "y")); + auto out = gen.generate(&fn); + assert(out.find("(define (add x y)") != std::string::npos); + std::cout << "Test 1 PASSED: define function generation\n"; + passed++; + } + + // Test 2: lambda output + { + SchemeGenerator gen; + LambdaExpression lambda("l1"); + lambda.addChild("parameters", new Parameter("p1", "x")); + auto out = gen.generate(&lambda); + assert(out.find("(lambda (x)") != std::string::npos); + std::cout << "Test 2 PASSED: lambda output\n"; + passed++; + } + + // Test 3: let bindings output + { + SchemeGenerator gen; + Block block; + block.id = "b1"; + auto* v = new Variable("v1", "x"); + v->setChild("value", new IntegerLiteral("i1", 1)); + block.addChild("statements", v); + block.addChild("statements", new ExpressionStatement()); + auto out = gen.generate(&block); + assert(out.find("(let ((x 1))") != std::string::npos); + std::cout << "Test 3 PASSED: let binding output\n"; + passed++; + } + + // Test 4: booleans map to #t / #f + { + SchemeGenerator gen; + auto t = gen.generate(new BooleanLiteral("b1", true)); + auto f = gen.generate(new BooleanLiteral("b2", false)); + assert(t == "#t"); + assert(f == "#f"); + std::cout << "Test 4 PASSED: boolean mapping\n"; + passed++; + } + + // Test 5: S-expression formatting / balanced parens + { + SchemeGenerator gen; + Module mod("m1", "scheme_mod", "scheme"); + mod.addChild("functions", new Function("f1", "noop")); + auto out = gen.generate(&mod); + assert(parenBalance(out) == 0); + assert(out.find("(define (noop)") != std::string::npos); + std::cout << "Test 5 PASSED: balanced S-expression output\n"; + passed++; + } + + // Test 6: cross-language Python -> Scheme + { + Pipeline p; + std::string py = "def add(x, y):\n return x + y\n"; + auto res = p.run(py, "python", "scheme"); + assert(res.success); + assert(res.generatedCode.find("(define (add x y)") != std::string::npos); + std::cout << "Test 6 PASSED: Python -> Scheme pipeline\n"; + passed++; + } + + // Test 7: cross-language Common Lisp -> Scheme + { + Pipeline p; + std::string cl = "(defun id (x) x)"; + auto res = p.run(cl, "common-lisp", "scheme"); + assert(res.success); + assert(res.generatedCode.find("(define (id x)") != std::string::npos); + std::cout << "Test 7 PASSED: Common Lisp -> Scheme pipeline\n"; + passed++; + } + + // Test 8: cross-language Scheme -> Python + { + Pipeline p; + std::string scm = "(define (id x) x)"; + auto res = p.run(scm, "scheme", "python"); + assert(res.success); + assert(res.generatedCode.find("def id(x)") != std::string::npos); + std::cout << "Test 8 PASSED: Scheme -> Python pipeline\n"; + passed++; + } + + // Test 9: semanno-style comments use '; ' prefix + { + SchemeGenerator gen; + Function fn("f1", "owned"); + fn.addChild("annotations", new OwnerAnnotation("a1", "Shared_GC")); + auto out = gen.generate(&fn); + assert(out.find("; @owner(strategy=Shared_GC)") != std::string::npos); + std::cout << "Test 9 PASSED: semanno comment prefix\n"; + passed++; + } + + // Test 10: parse -> generate -> parse roundtrip + { + std::string src = "(define (id x) x)"; + auto parsed = SchemeParser::parseScheme(src); + SchemeGenerator gen; + auto emitted = gen.generate(parsed.get()); + auto reparsed = SchemeParser::parseScheme(emitted); + assert(!reparsed->getChildren("functions").empty()); + std::cout << "Test 10 PASSED: parse/generate/parse roundtrip\n"; + passed++; + } + + // Test 11: tail-call annotation preserved in generated comments + { + SchemeGenerator gen; + Function fn("f1", "loopf"); + auto* t = new TailCallAnnotation(); + t->id = "a1"; + fn.addChild("annotations", t); + auto out = gen.generate(&fn); + assert(out.find("; @tailcall()") != std::string::npos); + std::cout << "Test 11 PASSED: tail-call annotation comment\n"; + passed++; + } + + // Test 12: continuation annotation generates call/cc form + { + SchemeGenerator gen; + FunctionCall call; + call.id = "c1"; + call.functionName = "handler"; + auto* ex = new ExecAnnotation(); + ex->id = "a1"; + ex->mode = "continuation"; + call.addChild("annotations", ex); + call.addChild("arguments", new VariableReference("r1", "k")); + auto out = gen.generate(&call); + assert(out.find("(call/cc") != std::string::npos); + std::cout << "Test 12 PASSED: continuation annotation generation\n"; + passed++; + } + + std::cout << "\nResults: " << passed << "/12\n"; + assert(passed == 12); + return 0; +} diff --git a/progress.md b/progress.md index 915d07a..6987641 100644 --- a/progress.md +++ b/progress.md @@ -2611,6 +2611,51 @@ signals. - `editor/src/ast/SchemeParser.h` is within header size limit (`461` lines <= `600`) +### Step 375: Scheme Generator +**Status:** PASS (12/12 tests) + +Added a dedicated Scheme generator and pipeline generation routing for Scheme +targets, with Scheme-specific output conventions and continuation/tail-call +annotation handling. + +**Files created:** +- `editor/src/ast/SchemeGenerator.h` — Scheme generation support: + - module/function/variable emission using `define` forms + - Scheme-flavored block lowering (`let` / `begin`) + - boolean/null mapping to `#t`, `#f`, and `'()` + - continuation-aware call lowering to `call/cc` + - record-style class projection output (`define-record-type`) + - Scheme semanno comment output for owner/tail-call/exec annotations +- `editor/tests/step375_test.cpp` — 12 tests covering: + 1. define-function generation + 2. lambda output + 3. let binding output + 4. boolean mapping + 5. S-expression balance/formatting + 6. Python -> Scheme pipeline route + 7. Common Lisp -> Scheme pipeline route + 8. Scheme -> Python pipeline route + 9. semanno comment prefix behavior + 10. parse -> generate -> parse roundtrip + 11. tail-call annotation preservation in output + 12. continuation annotation mapping to `call/cc` + +**Files modified:** +- `editor/src/ast/Generator.h` — include `SchemeGenerator.h` +- `editor/src/Pipeline.h` — add generate routing for `\"scheme\"` and `\"scm\"` +- `editor/CMakeLists.txt` — `step375_test` target + +**Verification run:** +- `step375_test` — PASS (12/12) new step coverage +- `step374_test` — PASS (12/12) regression coverage +- `step373_test` — PASS (8/8) regression coverage +- `step372_test` — PASS (12/12) regression coverage +- `step371_test` — PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/ast/SchemeGenerator.h` is within header size limit + (`159` lines <= `600`) + # Roadmap Planning — Sprints 12-25+ ## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)