Step 371: Add Common Lisp generator and pipeline routing
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 "";
|
||||
}
|
||||
|
||||
271
editor/src/ast/CommonLispGenerator.h
Normal file
271
editor/src/ast/CommonLispGenerator.h
Normal 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";
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -13,3 +13,4 @@
|
||||
#include "CSharpGenerator.h"
|
||||
#include "CGenerator.h"
|
||||
#include "WatGenerator.h"
|
||||
#include "CommonLispGenerator.h"
|
||||
|
||||
182
editor/tests/step371_test.cpp
Normal file
182
editor/tests/step371_test.cpp
Normal 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;
|
||||
}
|
||||
45
progress.md
45
progress.md
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user