Step 375: Add Scheme generator and pipeline routing

This commit is contained in:
Bill
2026-02-16 11:33:29 -07:00
parent b7918108ea
commit d06c32d5ff
6 changed files with 389 additions and 0 deletions

View File

@@ -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)

View File

@@ -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 "";
}

View File

@@ -14,3 +14,4 @@
#include "CGenerator.h"
#include "WatGenerator.h"
#include "CommonLispGenerator.h"
#include "SchemeGenerator.h"

View File

@@ -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<const Parameter*>(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<const Variable*> vars;
std::vector<ASTNode*> body;
for (auto* stmt : block->getChildren("statements")) {
if (stmt->conceptType == "Variable") vars.push_back(static_cast<const Variable*>(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<const ExecAnnotation*>(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<const ClassDeclaration*>(node);
std::ostringstream oss;
oss << "(define-record-type " << cls->name << " (fields";
for (auto* f : cls->getChildren("fields")) {
oss << " " << static_cast<const Variable*>(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";
}
}
};

View File

@@ -0,0 +1,172 @@
// Step 375: Scheme Generator (12 tests)
#include <cassert>
#include <iostream>
#include <string>
#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;
}

View File

@@ -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)