Step 371: Add Common Lisp generator and pipeline routing

This commit is contained in:
Bill
2026-02-16 11:12:38 -07:00
parent 8c1eeae897
commit e9ec11200e
6 changed files with 512 additions and 0 deletions

View File

@@ -2218,4 +2218,13 @@ target_link_libraries(step370_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step371_test tests/step371_test.cpp)
target_include_directories(step371_test PRIVATE src)
target_link_libraries(step371_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

@@ -174,6 +174,10 @@ public:
} else if (language == "wat" || language == "wasm") {
WatGenerator gen;
return gen.generate(ast);
} else if (language == "common-lisp" || language == "commonlisp" ||
language == "lisp" || language == "cl") {
CommonLispGenerator gen;
return gen.generate(ast);
}
return "";
}

View File

@@ -0,0 +1,271 @@
#pragma once
#include "ElispGenerator.h"
#include "ClassDeclaration.h"
#include <sstream>
#include <string>
#include <vector>
class CommonLispGenerator : public ElispGenerator {
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* var : module->getChildren("variables")) {
oss << visitVariable(static_cast<const Variable*>(var)) << "\n";
}
for (auto* cls : module->getChildren("classes")) {
oss << generate(cls) << "\n\n";
}
for (auto* stmt : module->getChildren("statements")) {
std::string text = generate(stmt);
if (!text.empty()) oss << text << "\n";
}
for (size_t i = 0; i < module->getChildren("functions").size(); ++i) {
if (i > 0 || !module->getChildren("variables").empty() || !module->getChildren("classes").empty()) {
oss << "\n";
}
oss << generate(module->getChildren("functions")[i]);
}
return oss.str();
}
std::string visitFunction(const Function* function) override {
std::ostringstream oss;
emitAnnotations(oss, function);
oss << "(defun " << function->name << " (";
auto params = function->getChildren("parameters");
for (size_t i = 0; i < params.size(); ++i) {
if (i > 0) oss << " ";
oss << visitParameter(static_cast<const Parameter*>(params[i]));
}
oss << ")\n";
std::vector<std::string> typeDecls;
for (auto* paramNode : params) {
auto* p = static_cast<const Parameter*>(paramNode);
auto* typeNode = p->getChild("type");
if (!typeNode) continue;
typeDecls.push_back("(type " + mapTypeName(typeNode) + " " + p->name + ")");
}
if (!typeDecls.empty()) {
oss << " (declare";
for (const auto& decl : typeDecls) oss << " " << decl;
oss << ")\n";
}
bool hasException = hasExceptionAnnotation(function);
if (hasException) {
oss << " (handler-case\n";
oss << " (progn\n";
}
const auto& body = function->getChildren("body");
if (body.empty()) {
oss << (hasException ? " nil\n" : " nil\n");
} else {
for (auto* stmt : body) {
std::string line = generate(stmt);
if (line.empty()) continue;
oss << (hasException ? " " : " ") << line << "\n";
}
}
if (hasException) {
oss << " )\n";
oss << " (error (e) e))";
} else {
oss << ")";
}
return oss.str();
}
std::string visitVariable(const Variable* variable) override {
std::ostringstream oss;
std::string name = variable->name;
std::string keyword = isDynamicName(name) ? "defparameter" : "defvar";
oss << "(" << keyword << " " << name;
auto* init = variable->getChild("value");
if (init) oss << " " << generate(init);
else oss << " nil";
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 << "(progn";
for (auto* stmt : body) oss << " " << generate(stmt);
oss << ")";
return oss.str();
}
std::string visitClassDeclaration(const ASTNode* node) override {
auto* cls = static_cast<const ClassDeclaration*>(node);
std::ostringstream oss;
oss << "(defclass " << cls->name << " (";
auto bases = cls->getBases();
for (size_t i = 0; i < bases.size(); ++i) {
if (i > 0) oss << " ";
oss << bases[i].name;
}
oss << ") (";
auto fields = cls->getChildren("fields");
for (size_t i = 0; i < fields.size(); ++i) {
if (i > 0) oss << " ";
auto* var = static_cast<const Variable*>(fields[i]);
oss << "(" << var->name << " :initarg :" << var->name << ")";
}
oss << "))";
return oss.str();
}
std::string visitMethodDeclaration(const ASTNode* node) override {
auto* method = static_cast<const MethodDeclaration*>(node);
std::ostringstream oss;
emitAnnotations(oss, method);
oss << "(defmethod " << method->name << " (";
auto params = method->getChildren("parameters");
for (size_t i = 0; i < params.size(); ++i) {
if (i > 0) oss << " ";
auto* p = static_cast<const Parameter*>(params[i]);
oss << "(" << p->name << " " << mapTypeName(p->getChild("type")) << ")";
}
oss << ")\n";
if (method->getChildren("body").empty()) {
oss << " nil)";
return oss.str();
}
for (auto* stmt : method->getChildren("body")) {
oss << " " << generate(stmt) << "\n";
}
oss << ")";
return oss.str();
}
std::string visitLambdaExpression(const ASTNode* node) override {
auto* lambda = static_cast<const LambdaExpression*>(node);
std::ostringstream oss;
oss << "(lambda (";
auto params = lambda->getChildren("parameters");
for (size_t i = 0; i < params.size(); ++i) {
if (i > 0) oss << " ";
oss << static_cast<const Parameter*>(params[i])->name;
}
oss << ")";
for (auto* stmt : lambda->getChildren("body")) {
oss << " " << generate(stmt);
}
oss << ")";
return oss.str();
}
std::string visitMacroDefinition(const ASTNode* node) override {
auto* macro = static_cast<const MacroDefinition*>(node);
std::ostringstream oss;
oss << "(defmacro " << macro->name << " (";
for (size_t i = 0; i < macro->parameters.size(); ++i) {
if (i > 0) oss << " ";
oss << macro->parameters[i];
}
oss << ") " << (macro->body.empty() ? "nil" : macro->body) << ")";
return oss.str();
}
std::string visitFunctionCall(const FunctionCall* call) override {
std::ostringstream oss;
oss << "(" << call->functionName;
for (auto* arg : call->getChildren("arguments")) {
oss << " " << generate(arg);
}
oss << ")";
return oss.str();
}
std::string visitPrimitiveType(const PrimitiveType* type) override {
return mapTypeName(type);
}
std::string visitOwnerAnnotation(const OwnerAnnotation* annotation) override {
return ";; @owner(" + annotation->strategy + ")";
}
private:
static bool isDynamicName(const std::string& name) {
return name.size() > 2 && name.front() == '*' && name.back() == '*';
}
static std::string mapTypeName(const ASTNode* typeNode) {
if (!typeNode) return "t";
std::string t;
if (typeNode->conceptType == "PrimitiveType") {
t = static_cast<const PrimitiveType*>(typeNode)->kind;
} else if (typeNode->conceptType == "CustomType") {
t = static_cast<const CustomType*>(typeNode)->typeName;
}
if (t == "int" || t == "i32") return "fixnum";
if (t == "long" || t == "i64") return "integer";
if (t == "float" || t == "f32") return "single-float";
if (t == "double" || t == "f64") return "double-float";
if (t == "string") return "string";
if (t == "bool") return "boolean";
if (t.empty()) return "t";
return t;
}
bool hasExceptionAnnotation(const Function* function) const {
for (auto* anno : function->getChildren("annotations")) {
if (anno->conceptType == "ExceptionAnnotation") return true;
}
return false;
}
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

@@ -13,3 +13,4 @@
#include "CSharpGenerator.h"
#include "CGenerator.h"
#include "WatGenerator.h"
#include "CommonLispGenerator.h"

View File

@@ -0,0 +1,182 @@
// Step 371: Common Lisp Generator (12 tests)
#include <cassert>
#include <iostream>
#include <string>
#include "Pipeline.h"
#include "ast/CommonLispGenerator.h"
#include "ast/CommonLispParser.h"
#include "ast/ClassDeclaration.h"
int main() {
int passed = 0;
// Test 1: defun generation
{
CommonLispGenerator gen;
Function fn("f1", "add");
fn.addChild("parameters", new Parameter("p1", "x"));
fn.addChild("parameters", new Parameter("p2", "y"));
auto* ret = new Return();
ret->id = "r1";
ret->setChild("value", new BinaryOperation("b1", "+"));
fn.addChild("body", ret);
auto out = gen.generate(&fn);
assert(out.find("(defun add (x y)") != std::string::npos);
std::cout << "Test 1 PASSED: defun generation\n";
passed++;
}
// Test 2: defclass output
{
CommonLispGenerator gen;
ClassDeclaration cls("c1", "point");
cls.addBase("shape");
cls.addChild("fields", new Variable("v1", "x"));
cls.addChild("fields", new Variable("v2", "y"));
auto out = gen.generate(&cls);
assert(out.find("(defclass point (shape)") != std::string::npos);
assert(out.find("(x :initarg :x)") != std::string::npos);
std::cout << "Test 2 PASSED: defclass output\n";
passed++;
}
// Test 3: lambda output
{
CommonLispGenerator gen;
LambdaExpression lambda("l1");
lambda.addChild("parameters", new Parameter("p1", "x"));
lambda.addChild("body", new ExpressionStatement());
auto out = gen.generate(&lambda);
assert(out.find("(lambda (x)") != std::string::npos);
std::cout << "Test 3 PASSED: lambda output\n";
passed++;
}
// Test 4: let bindings output from block variables
{
CommonLispGenerator gen;
Block block;
block.id = "blk1";
auto* x = new Variable("v1", "x");
x->setChild("value", new IntegerLiteral("i1", 1));
auto* y = new Variable("v2", "y");
y->setChild("value", new IntegerLiteral("i2", 2));
block.addChild("statements", x);
block.addChild("statements", y);
block.addChild("statements", new ExpressionStatement());
auto out = gen.generate(&block);
assert(out.find("(let ((x 1) (y 2))") != std::string::npos);
std::cout << "Test 4 PASSED: let bindings output\n";
passed++;
}
// Test 5: S-expression formatting and balanced parens
{
CommonLispGenerator gen;
Module mod("m1", "demo", "common-lisp");
mod.addChild("functions", new Function("f1", "noop"));
auto out = gen.generate(&mod);
int depth = 0;
for (char c : out) {
if (c == '(') ++depth;
if (c == ')') --depth;
}
assert(depth == 0);
assert(out.find("(defun noop") != std::string::npos);
std::cout << "Test 5 PASSED: s-expression formatting\n";
passed++;
}
// Test 6: type declarations from parameter types
{
CommonLispGenerator gen;
Function fn("f1", "typed");
auto* p = new Parameter("p1", "x");
p->setChild("type", new PrimitiveType("t1", "int"));
fn.addChild("parameters", p);
auto out = gen.generate(&fn);
assert(out.find("(declare (type fixnum x))") != std::string::npos);
std::cout << "Test 6 PASSED: type declaration emission\n";
passed++;
}
// Test 7: Python -> Common Lisp pipeline
{
Pipeline p;
std::string src = "def add(x, y):\n return x + y\n";
auto result = p.run(src, "python", "common-lisp");
assert(result.success);
assert(result.generatedCode.find("(defun") != std::string::npos);
std::cout << "Test 7 PASSED: Python -> Common Lisp pipeline\n";
passed++;
}
// Test 8: Java -> Common Lisp pipeline
{
Pipeline p;
std::string src = "class A { int id(){ return 1; } }";
auto result = p.run(src, "java", "common-lisp");
assert(result.success);
assert(result.generatedCode.find("(defun") != std::string::npos ||
result.generatedCode.find("(defclass") != std::string::npos);
std::cout << "Test 8 PASSED: Java -> Common Lisp pipeline\n";
passed++;
}
// Test 9: semanno comments with ;; prefix
{
CommonLispGenerator gen;
Function fn("f1", "owned");
fn.addChild("annotations", new OwnerAnnotation("a1", "Shared_GC"));
auto out = gen.generate(&fn);
assert(out.find(";; @owner(Shared_GC)") != std::string::npos);
std::cout << "Test 9 PASSED: Semanno comment prefix\n";
passed++;
}
// Test 10: parse -> generate -> parse roundtrip
{
std::string source = "(defun round (x) (+ x 1))";
auto parsed = CommonLispParser::parseCommonLisp(source);
CommonLispGenerator gen;
auto emitted = gen.generate(parsed.get());
auto reparsed = CommonLispParser::parseCommonLisp(emitted);
assert(!reparsed->getChildren("functions").empty());
std::cout << "Test 10 PASSED: roundtrip parse/generate/parse\n";
passed++;
}
// Test 11: CLOS method dispatch output
{
CommonLispGenerator gen;
MethodDeclaration method("m1", "area");
auto* p = new Parameter("p1", "obj");
p->setChild("type", new CustomType("t1", "rectangle"));
method.addChild("parameters", p);
auto out = gen.generate(&method);
assert(out.find("(defmethod area ((obj rectangle))") != std::string::npos);
std::cout << "Test 11 PASSED: CLOS method output\n";
passed++;
}
// Test 12: condition-system wrapper from exception annotation
{
CommonLispGenerator gen;
Function fn("f1", "safe-op");
auto* ex = new ExceptionAnnotation();
ex->id = "a1";
ex->style = "checked";
fn.addChild("annotations", ex);
fn.addChild("body", new ExpressionStatement());
auto out = gen.generate(&fn);
assert(out.find("handler-case") != std::string::npos);
assert(out.find("(error (e) e)") != std::string::npos);
std::cout << "Test 12 PASSED: condition system wrapper\n";
passed++;
}
std::cout << "\nResults: " << passed << "/12\n";
assert(passed == 12);
return 0;
}

View File

@@ -2442,6 +2442,51 @@ surface forms.
- `editor/src/ast/CommonLispParser.h` is within header size limit
(`598` lines <= `600`)
### Step 371: Common Lisp Generator
**Status:** PASS (12/12 tests)
Added a dedicated Common Lisp generator and integrated target-language routing
for Common Lisp aliases in the pipeline.
**Files created:**
- `editor/src/ast/CommonLispGenerator.h` — Common Lisp generation support:
- module/function emission as idiomatic S-expressions
- `defclass` + `defmethod` output for CLOS class/method projection
- typed parameter declarations via `(declare (type ...))`
- `let` emission from block-scoped variable statements
- macro emission via `defmacro`
- function-level exception annotation mapping to `handler-case` wrappers
- comment prefix alignment (`;; `) with explicit owner annotation formatting
- `editor/tests/step371_test.cpp` — 12 tests covering:
1. `defun` generation
2. `defclass` output
3. `lambda` output
4. `let` binding emission
5. S-expression shape / balanced output
6. typed `declare` emission
7. Python -> Common Lisp pipeline route
8. Java -> Common Lisp pipeline route
9. Semanno comment prefix (`;;`)
10. parse -> generate -> parse roundtrip
11. CLOS method dispatch output
12. condition-system wrapper from exception annotation
**Files modified:**
- `editor/src/ast/Generator.h` — include `CommonLispGenerator.h`
- `editor/src/Pipeline.h` — add generate routing for `\"common-lisp\"`,
`\"commonlisp\"`, `\"lisp\"`, and `\"cl\"`
- `editor/CMakeLists.txt``step371_test` target
**Verification run:**
- `step371_test` — PASS (12/12) new step coverage
- `step370_test` — PASS (12/12) regression coverage
- `step369_test` — PASS (8/8) regression coverage
- `step366_test` — PASS (12/12) regression coverage
**Architecture gate check:**
- `editor/src/ast/CommonLispGenerator.h` remains within header-size limit
(`267` lines <= `600`)
# Roadmap Planning — Sprints 12-25+
## Status: Planning Complete (Sprints 12-19 detailed, 20-25 in roadmap.md)